diff options
Diffstat (limited to 'core/tests')
88 files changed, 6748 insertions, 1891 deletions
diff --git a/core/tests/ConnectivityManagerTest/AndroidManifest.xml b/core/tests/ConnectivityManagerTest/AndroidManifest.xml index c318577ff91a..d298d40cafde 100644 --- a/core/tests/ConnectivityManagerTest/AndroidManifest.xml +++ b/core/tests/ConnectivityManagerTest/AndroidManifest.xml @@ -24,23 +24,35 @@ <application> <uses-library android:name="android.test.runner" /> <activity android:name="ConnectivityManagerTestActivity" - android:label="CMTest"> + android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.TEST" /> + <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <!-- This declares that this app uses the instrumentation test runner targeting - the package of browserpowertest. To run the tests use the command: - "adb shell am instrument -w com.android.connectivitymanagertest/.ConnectivityManagerTestRunner" + the package of connectivitymanagertest. To run the tests use the command: + "adb shell am instrument -e ssid <SSID> -w + com.android.connectivitymanagertest/.ConnectivityManagerTestRunner", + the access point <SSID> should be an open AP. --> <instrumentation android:name=".ConnectivityManagerTestRunner" android:targetPackage="com.android.connectivitymanagertest" android:label="Test runner for Connectivity Manager Tests" /> + <!-- + To run the unit tests use the command: + "adb shell am instrument -w + com.android.connectivitymanagertest/.ConnectivityManagerUnitTestRunner" + --> + <instrumentation android:name=".ConnectivityManagerUnitTestRunner" + android:targetPackage="com.android.connectivitymanagertest" + android.label="Test runner for unit tests" + /> + <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> diff --git a/core/tests/ConnectivityManagerTest/res/values/strings.xml b/core/tests/ConnectivityManagerTest/res/values/strings.xml new file mode 100644 index 000000000000..fb6e82f83af8 --- /dev/null +++ b/core/tests/ConnectivityManagerTest/res/values/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">ConnectivityManagerTest</string> +</resources> diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java new file mode 100644 index 000000000000..6adfc7489910 --- /dev/null +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2010, 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.connectivitymanagertest; + +import android.os.Bundle; +import android.test.InstrumentationTestRunner; +import android.test.InstrumentationTestSuite; +import android.util.Log; +import com.android.connectivitymanagertest.unit.WifiSoftAPTest; + +import junit.framework.TestSuite; + +/** + * Instrumentation Test Runner for all unit tests + * + * adb shell am instrument \ + * -w com.android.connectivitymanagertest/.ConnectivityManagerUnitTestRunner + */ + +public class ConnectivityManagerUnitTestRunner extends InstrumentationTestRunner { + @Override + public TestSuite getAllTests() { + TestSuite suite = new InstrumentationTestSuite(this); + suite.addTestSuite(WifiSoftAPTest.class); + return suite; + } + + + @Override + public ClassLoader getLoader() { + return ConnectivityManagerUnitTestRunner.class.getClassLoader(); + } +} diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java index cdaefc853079..ad8d444114d7 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java @@ -91,30 +91,30 @@ public class ConnectivityManagerMobileTest // DISCONNECTING, DISCONNECTED, UNKNOWN private void waitForNetworkState(int networkType, State expectedState, long timeout) { long startTime = System.currentTimeMillis(); - // In case the broadcast is already sent out, no need to wait - if (cmActivity.mCM.getNetworkInfo(networkType).getState() == expectedState) { - return; - } else { - while (true) { - if ((System.currentTimeMillis() - startTime) > timeout) { + while (true) { + if ((System.currentTimeMillis() - startTime) > timeout) { + if (cmActivity.mCM.getNetworkInfo(networkType).getState() != expectedState) { assertFalse("Wait for network state timeout", true); + } else { + // the broadcast has been sent out. the state has been changed. + return; + } + } + Log.v(LOG_TAG, "Wait for the connectivity state for network: " + networkType + + " to be " + expectedState.toString()); + synchronized (cmActivity.connectivityObject) { + try { + cmActivity.connectivityObject.wait(STATE_TRANSITION_SHORT_TIMEOUT); + } catch (InterruptedException e) { + e.printStackTrace(); } - Log.v(LOG_TAG, "Wait for the connectivity state for network: " + networkType + - " to be " + expectedState.toString()); - synchronized (cmActivity.connectivityObject) { - try { - cmActivity.connectivityObject.wait(STATE_TRANSITION_SHORT_TIMEOUT); - } catch (InterruptedException e) { - e.printStackTrace(); - } - if ((cmActivity.mNetworkInfo.getType() != networkType) || - (cmActivity.mNetworkInfo.getState() != expectedState)) { - Log.v(LOG_TAG, "network state for " + cmActivity.mNetworkInfo.getType() + - "is: " + cmActivity.mNetworkInfo.getState()); - continue; - } - break; + if ((cmActivity.mNetworkInfo.getType() != networkType) || + (cmActivity.mNetworkInfo.getState() != expectedState)) { + Log.v(LOG_TAG, "network state for " + cmActivity.mNetworkInfo.getType() + + "is: " + cmActivity.mNetworkInfo.getState()); + continue; } + break; } } } @@ -123,26 +123,26 @@ public class ConnectivityManagerMobileTest // WIFI_STATE_ENALBING, WIFI_STATE_UNKNOWN private void waitForWifiState(int expectedState, long timeout) { long startTime = System.currentTimeMillis(); - if (cmActivity.mWifiState == expectedState) { - return; - } else { - while (true) { - if ((System.currentTimeMillis() - startTime) > timeout) { + while (true) { + if ((System.currentTimeMillis() - startTime) > timeout) { + if (cmActivity.mWifiState != expectedState) { assertFalse("Wait for Wifi state timeout", true); + } else { + return; + } + } + Log.v(LOG_TAG, "Wait for wifi state to be: " + expectedState); + synchronized (cmActivity.wifiObject) { + try { + cmActivity.wifiObject.wait(5*1000); + } catch (InterruptedException e) { + e.printStackTrace(); } - Log.v(LOG_TAG, "Wait for wifi state to be: " + expectedState); - synchronized (cmActivity.wifiObject) { - try { - cmActivity.wifiObject.wait(5*1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - if (cmActivity.mWifiState != expectedState) { - Log.v(LOG_TAG, "Wifi state is: " + cmActivity.mWifiNetworkInfo.getState()); - continue; - } - break; + if (cmActivity.mWifiState != expectedState) { + Log.v(LOG_TAG, "Wifi state is: " + cmActivity.mWifiNetworkInfo.getState()); + continue; } + break; } } } diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java new file mode 100644 index 000000000000..3f43e4851c4f --- /dev/null +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2010 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.connectivitymanagertest.unit; + +import android.content.BroadcastReceiver; +import android.content.Intent; +import android.content.Context; +import android.app.Instrumentation; +import android.os.Handler; +import android.os.Message; +import android.net.ConnectivityManager; +import android.net.wifi.WifiManager; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiConfiguration.KeyMgmt; + +import android.test.suitebuilder.annotation.LargeTest; +import android.test.AndroidTestCase; + +import java.util.ArrayList; + +import android.util.Log; + +/** + * Test Wifi soft AP configuration + */ +public class WifiSoftAPTest extends AndroidTestCase { + + private WifiManager mWifiManager; + private WifiConfiguration mWifiConfig = null; + private final String TAG = "WifiSoftAPTest"; + private final int DURATION = 10000; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE); + assertNotNull(mWifiManager); + assertTrue(mWifiManager.setWifiApEnabled(null, true)); + mWifiConfig = mWifiManager.getWifiApConfiguration(); + if (mWifiConfig != null) { + Log.v(TAG, "mWifiConfig is " + mWifiConfig.toString()); + } else { + Log.v(TAG, "mWifiConfig is null."); + } + } + + @Override + protected void tearDown() throws Exception { + Log.v(TAG, "turn off wifi tethering"); + mWifiManager.setWifiApEnabled(null, false); + super.tearDown(); + } + + // Test case 1: Test the soft AP SSID with letters + @LargeTest + public void testApSsidWithAlphabet() { + WifiConfiguration config = new WifiConfiguration(); + config.SSID = "abcdefghijklmnopqrstuvwxyz"; + config.allowedKeyManagement.set(KeyMgmt.NONE); + mWifiConfig = config; + assertTrue(mWifiManager.setWifiApEnabled(mWifiConfig, true)); + try { + Thread.sleep(DURATION); + } catch (InterruptedException e) { + Log.v(TAG, "exception " + e.getStackTrace()); + assertFalse(true); + } + assertNotNull(mWifiManager.getWifiApConfiguration()); + assertEquals("wifi AP state is not enabled", WifiManager.WIFI_AP_STATE_ENABLED, + mWifiManager.getWifiApState()); + } +} diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk index 245c67cf4002..b49680506802 100644 --- a/core/tests/coretests/Android.mk +++ b/core/tests/coretests/Android.mk @@ -11,8 +11,8 @@ LOCAL_SRC_FILES := \ $(call all-java-files-under, DisabledTestApp/src) \ $(call all-java-files-under, EnabledTestApp/src) -LOCAL_STATIC_JAVA_LIBRARIES += android-common - +LOCAL_DX_FLAGS := --core-library +LOCAL_STATIC_JAVA_LIBRARIES := core-tests-supportlib android-common frameworks-core-util-lib LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_PACKAGE_NAME := FrameworksCoreTests diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index ce73ae1eb433..f09421bca74f 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -36,12 +36,16 @@ android:description="@string/permdesc_testDenied" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> + <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.BROADCAST_STICKY" /> + <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.CLEAR_APP_CACHE" /> <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" /> <uses-permission android:name="android.permission.DELETE_CACHE_FILES" /> + <uses-permission android:name="android.permission.DOWNLOAD_CACHE_NON_PURGEABLE" /> <uses-permission android:name="android.permission.GET_PACKAGE_SIZE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> diff --git a/core/tests/coretests/res/raw/v21_im.vcf b/core/tests/coretests/res/raw/v21_im.vcf new file mode 100644 index 000000000000..cc1aabb74941 --- /dev/null +++ b/core/tests/coretests/res/raw/v21_im.vcf @@ -0,0 +1,5 @@ +BEGIN:VCARD +VERSION:2.1 +X-ANDROID-CUSTOM:vnd.android.cursor.item/nickname;Nick;1;;;;;;;;;;;;; +X-GOOGLE-TALK:hhh@gmail.com +END:VCARD diff --git a/core/tests/coretests/res/raw/v21_invalid_multiple_line.vcf b/core/tests/coretests/res/raw/v21_invalid_multiple_line.vcf new file mode 100644 index 000000000000..9c81fd52a14b --- /dev/null +++ b/core/tests/coretests/res/raw/v21_invalid_multiple_line.vcf @@ -0,0 +1,7 @@ +BEGIN:VCARD +VERSION:2.1 +N:;Omega;;; +EMAIL;INTERNET:"Omega" + <omega@example.com> +FN:Omega +END:VCARD diff --git a/core/tests/coretests/res/raw/v30_comma_separated.vcf b/core/tests/coretests/res/raw/v30_comma_separated.vcf index 98a7f20588a1..f1baf880972f 100644 --- a/core/tests/coretests/res/raw/v30_comma_separated.vcf +++ b/core/tests/coretests/res/raw/v30_comma_separated.vcf @@ -1,5 +1,5 @@ BEGIN:VCARD
VERSION:3.0
-N:F;G;M;;
-TEL;TYPE=PAGER,WORK,MSG:6101231234@pagersample.com
+N;TYPE=PREF,HOME:F;G;M;;
+TEL;TYPE="COMMA,SEPARATED:INSIDE.DQUOTE",PREF:1
END:VCARD
diff --git a/core/tests/coretests/res/raw/v30_multibyte_param.vcf b/core/tests/coretests/res/raw/v30_multibyte_param.vcf new file mode 100644 index 000000000000..cd200e563d11 --- /dev/null +++ b/core/tests/coretests/res/raw/v30_multibyte_param.vcf @@ -0,0 +1,5 @@ +BEGIN:VCARD
+VERSION:3.0
+N:F;G;M;;
+TEL;TYPE="费":1
+END:VCARD
diff --git a/core/tests/coretests/res/raw/v30_pager.vcf b/core/tests/coretests/res/raw/v30_pager.vcf new file mode 100644 index 000000000000..98a7f20588a1 --- /dev/null +++ b/core/tests/coretests/res/raw/v30_pager.vcf @@ -0,0 +1,5 @@ +BEGIN:VCARD
+VERSION:3.0
+N:F;G;M;;
+TEL;TYPE=PAGER,WORK,MSG:6101231234@pagersample.com
+END:VCARD
diff --git a/core/tests/coretests/res/raw/v40_sort_as.vcf b/core/tests/coretests/res/raw/v40_sort_as.vcf new file mode 100644 index 000000000000..6f6bc3b15662 --- /dev/null +++ b/core/tests/coretests/res/raw/v40_sort_as.vcf @@ -0,0 +1,6 @@ +BEGIN:VCARD
+VERSION:4.0
+FN:安藤 ロイド
+N;SORT-AS="あんどう;ろいど":安藤;ロイド;;;
+ORG;TYPE=WORK;SORT-AS="ぐーぐる;けんさくぶもん":グーグル;検索部門
+END:VCARD
diff --git a/core/tests/coretests/src/android/app/activity/LifecycleTest.java b/core/tests/coretests/src/android/app/activity/LifecycleTest.java index 768a9a446e08..ed01fac5e0a9 100644 --- a/core/tests/coretests/src/android/app/activity/LifecycleTest.java +++ b/core/tests/coretests/src/android/app/activity/LifecycleTest.java @@ -18,10 +18,7 @@ package android.app.activity; import android.content.ComponentName; import android.content.Intent; -import android.test.FlakyTest; -import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.Suppress; public class LifecycleTest extends ActivityTestsBase { @@ -37,7 +34,7 @@ public class LifecycleTest extends ActivityTestsBase { LaunchpadActivity.class)); } - @LargeTest + @MediumTest public void testBasic() throws Exception { mIntent = mTopIntent; runLaunchpad(LaunchpadActivity.LIFECYCLE_BASIC); diff --git a/core/tests/coretests/src/android/app/activity/MetaDataTest.java b/core/tests/coretests/src/android/app/activity/MetaDataTest.java index 214bc9106cdc..5b9c0e903135 100644 --- a/core/tests/coretests/src/android/app/activity/MetaDataTest.java +++ b/core/tests/coretests/src/android/app/activity/MetaDataTest.java @@ -27,7 +27,6 @@ import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.os.Bundle; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; import com.android.frameworks.coretests.R; import org.xmlpull.v1.XmlPullParser; @@ -134,7 +133,7 @@ public class MetaDataTest extends AndroidTestCase { assertNull("Meta data returned when not requested", si.metaData); } - @MediumTest + @SmallTest public void testProviderWithData() throws Exception { ComponentName cn = new ComponentName(mContext, LocalProvider.class); ProviderInfo pi = mContext.getPackageManager().resolveContentProvider( diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java index d8d9eba14dc3..149685c2a6fa 100644 --- a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java +++ b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java @@ -78,4 +78,50 @@ public class BluetoothStressTest extends InstrumentationTestCase { mTestUtils.disable(adapter); } + + public void testPair() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sHeadsetAddress); + + mTestUtils.enable(adapter); + mTestUtils.pair(adapter, device); + mTestUtils.unpair(adapter, device); + mTestUtils.disable(adapter); + } + + public void testConnectA2dp() { + int iterations = BluetoothTestRunner.sConnectA2dpIterations; + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sA2dpAddress); + + mTestUtils.enable(adapter); + mTestUtils.pair(adapter, device); + + for (int i = 0; i < iterations; i++) { + mTestUtils.writeOutput("connectA2dp iteration " + (i + 1) + " of " + iterations); + mTestUtils.connectA2dp(adapter, device); + mTestUtils.disconnectA2dp(adapter, device); + } + + // TODO: Unpair from device if device can accept pairing after unpairing + mTestUtils.disable(adapter); + } + + public void testConnectHeadset() { + int iterations = BluetoothTestRunner.sConnectHeadsetIterations; + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sHeadsetAddress); + + mTestUtils.enable(adapter); + mTestUtils.pair(adapter, device); + + for (int i = 0; i < iterations; i++) { + mTestUtils.writeOutput("connectHeadset iteration " + (i + 1) + " of " + iterations); + mTestUtils.connectHeadset(adapter, device); + mTestUtils.disconnectHeadset(adapter, device); + } + + // TODO: Unpair from device if device can accept pairing after unpairing + mTestUtils.disable(adapter); + } } diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java b/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java index cf0ff99d1283..2e6daa33f6e0 100644 --- a/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java +++ b/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java @@ -26,6 +26,11 @@ public class BluetoothTestRunner extends InstrumentationTestRunner { public static int sEnableIterations = 100; public static int sDiscoverableIterations = 1000; public static int sScanIterations = 1000; + public static int sConnectHeadsetIterations = 100; + public static int sConnectA2dpIterations = 100; + + public static String sHeadsetAddress = ""; + public static String sA2dpAddress = ""; @Override public TestSuite getAllTests() { @@ -69,5 +74,33 @@ public class BluetoothTestRunner extends InstrumentationTestRunner { // Invalid argument, fall back to default value } } + + val = arguments.getString("connect_a2dp_iterations"); + if (val != null) { + try { + sConnectA2dpIterations = Integer.parseInt(val); + } catch (NumberFormatException e) { + // Invalid argument, fall back to default value + } + } + + val = arguments.getString("connect_headset_iterations"); + if (val != null) { + try { + sConnectHeadsetIterations = Integer.parseInt(val); + } catch (NumberFormatException e) { + // Invalid argument, fall back to default value + } + } + + val = arguments.getString("headset_address"); + if (val != null) { + sHeadsetAddress = val; + } + + val = arguments.getString("a2dp_address"); + if (val != null) { + sA2dpAddress = val; + } } } diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java index 82de5098e66b..e9311e08f2ea 100644 --- a/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java +++ b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.bluetooth.BluetoothHeadset.ServiceListener; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -57,6 +58,36 @@ public class BluetoothTestUtils extends Assert { */ private static final int CANCEL_DISCOVERY_TIMEOUT = 5000; + /** + * Timeout for {@link BluetoothDevice#createBond()} in ms. + */ + private static final int PAIR_TIMEOUT = 20000; + + /** + * Timeout for {@link BluetoothDevice#removeBond()} in ms. + */ + private static final int UNPAIR_TIMEOUT = 20000; + + /** + * Timeout for {@link BluetoothA2dp#connectSink(BluetoothDevice)} in ms. + */ + private static final int CONNECT_A2DP_TIMEOUT = 20000; + + /** + * Timeout for {@link BluetoothA2dp#disconnectSink(BluetoothDevice)} in ms. + */ + private static final int DISCONNECT_A2DP_TIMEOUT = 20000; + + /** + * Timeout for {@link BluetoothHeadset#connectHeadset(BluetoothDevice)} in ms. + */ + private static final int CONNECT_HEADSET_TIMEOUT = 20000; + + /** + * Timeout for {@link BluetoothHeadset#disconnectHeadset(BluetoothDevice)} in ms. + */ + private static final int DISCONNECT_HEADSET_TIMEOUT = 20000; + private static final int DISCOVERY_STARTED_FLAG = 1; private static final int DISCOVERY_FINISHED_FLAG = 1 << 1; private static final int SCAN_MODE_NONE_FLAG = 1 << 2; @@ -66,6 +97,23 @@ public class BluetoothTestUtils extends Assert { private static final int STATE_TURNING_ON_FLAG = 1 << 6; private static final int STATE_ON_FLAG = 1 << 7; private static final int STATE_TURNING_OFF_FLAG = 1 << 8; + private static final int PAIR_STATE_FLAG = 1 << 9; + private static final int PROFILE_A2DP_FLAG = 1 << 10; + private static final int PROFILE_HEADSET_FLAG = 1 << 11; + + private static final int PAIR_STATE_BONDED = 1; + private static final int PAIR_STATE_BONDING = 1 << 1; + private static final int PAIR_STATE_NONE = 1 << 2; + + private static final int A2DP_STATE_DISCONNECTED = 1; + private static final int A2DP_STATE_CONNECTING = 1 << 1; + private static final int A2DP_STATE_CONNECTED = 1 << 2; + private static final int A2DP_STATE_DISCONNECTING = 1 << 3; + private static final int A2DP_STATE_PLAYING = 1 << 4; + + private static final int HEADSET_STATE_DISCONNECTED = 1; + private static final int HEADSET_STATE_CONNECTING = 1 << 1; + private static final int HEADSET_STATE_CONNECTED = 1 << 2; /** * Time between polls in ms. @@ -76,11 +124,43 @@ public class BluetoothTestUtils extends Assert { private BufferedWriter mOutputWriter; + private BluetoothA2dp mA2dp; + + private BluetoothHeadset mHeadset; + private String mOutputFile; private String mTag; + private class HeadsetServiceListener implements ServiceListener { + private boolean mConnected = false; + + @Override + public void onServiceConnected() { + synchronized (this) { + mConnected = true; + } + } + + @Override + public void onServiceDisconnected() { + synchronized (this) { + mConnected = false; + } + } + + public boolean isConnected() { + synchronized (this) { + return mConnected; + } + } + } + + private HeadsetServiceListener mHeadsetServiceListener = new HeadsetServiceListener(); private class BluetoothReceiver extends BroadcastReceiver { private int mFiredFlags = 0; + private int mPairFiredFlags = 0; + private int mA2dpFiredFlags = 0; + private int mHeadsetFiredFlags = 0; @Override public void onReceive(Context context, Intent intent) { @@ -122,6 +202,58 @@ public class BluetoothTestUtils extends Assert { mFiredFlags |= STATE_TURNING_OFF_FLAG; break; } + } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) { + mFiredFlags |= PAIR_STATE_FLAG; + int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1); + assertNotSame(state, -1); + switch (state) { + case BluetoothDevice.BOND_BONDED: + mPairFiredFlags |= PAIR_STATE_BONDED; + break; + case BluetoothDevice.BOND_BONDING: + mPairFiredFlags |= PAIR_STATE_BONDING; + break; + case BluetoothDevice.BOND_NONE: + mPairFiredFlags |= PAIR_STATE_NONE; + break; + } + } else if (BluetoothA2dp.ACTION_SINK_STATE_CHANGED.equals(intent.getAction())) { + mFiredFlags |= PROFILE_A2DP_FLAG; + int state = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, -1); + assertNotSame(state, -1); + switch (state) { + case BluetoothA2dp.STATE_DISCONNECTED: + mA2dpFiredFlags |= A2DP_STATE_DISCONNECTED; + break; + case BluetoothA2dp.STATE_CONNECTING: + mA2dpFiredFlags |= A2DP_STATE_CONNECTING; + break; + case BluetoothA2dp.STATE_CONNECTED: + mA2dpFiredFlags |= A2DP_STATE_CONNECTED; + break; + case BluetoothA2dp.STATE_DISCONNECTING: + mA2dpFiredFlags |= A2DP_STATE_DISCONNECTING; + break; + case BluetoothA2dp.STATE_PLAYING: + mA2dpFiredFlags |= A2DP_STATE_PLAYING; + break; + } + } else if (BluetoothHeadset.ACTION_STATE_CHANGED.equals(intent.getAction())) { + mFiredFlags |= PROFILE_HEADSET_FLAG; + int state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, + BluetoothHeadset.STATE_ERROR); + assertNotSame(state, BluetoothHeadset.STATE_ERROR); + switch (state) { + case BluetoothHeadset.STATE_DISCONNECTED: + mHeadsetFiredFlags |= HEADSET_STATE_DISCONNECTED; + break; + case BluetoothHeadset.STATE_CONNECTING: + mHeadsetFiredFlags |= HEADSET_STATE_CONNECTING; + break; + case BluetoothHeadset.STATE_CONNECTED: + mHeadsetFiredFlags |= HEADSET_STATE_CONNECTED; + break; + } } } } @@ -132,9 +264,30 @@ public class BluetoothTestUtils extends Assert { } } + public int getPairFiredFlags() { + synchronized (this) { + return mPairFiredFlags; + } + } + + public int getA2dpFiredFlags() { + synchronized (this) { + return mA2dpFiredFlags; + } + } + + public int getHeadsetFiredFlags() { + synchronized (this) { + return mHeadsetFiredFlags; + } + } + public void resetFiredFlags() { synchronized (this) { mFiredFlags = 0; + mPairFiredFlags = 0; + mA2dpFiredFlags = 0; + mHeadsetFiredFlags = 0; } } } @@ -419,6 +572,351 @@ public class BluetoothTestUtils extends Assert { } + public void pair(BluetoothAdapter adapter, BluetoothDevice device) { + int mask = PAIR_STATE_FLAG; + int pairMask = PAIR_STATE_BONDING | PAIR_STATE_BONDED; + mReceiver.resetFiredFlags(); + + if (!adapter.isEnabled()) { + fail("pair() bluetooth not enabled"); + } + + int state = device.getBondState(); + switch (state) { + case BluetoothDevice.BOND_BONDED: + assertTrue(adapter.getBondedDevices().contains(device)); + return; + case BluetoothDevice.BOND_BONDING: + // Don't check for received intents since we might have missed them. + mask = pairMask = 0; + break; + case BluetoothDevice.BOND_NONE: + assertFalse(adapter.getBondedDevices().contains(device)); + assertTrue(device.createBond()); + break; + default: + fail("pair() invalide state: state=" + state); + } + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < PAIR_TIMEOUT) { + state = device.getBondState(); + if (state == BluetoothDevice.BOND_BONDED) { + assertTrue(adapter.getBondedDevices().contains(device)); + if ((mReceiver.getFiredFlags() & mask) == mask + && (mReceiver.getPairFiredFlags() & pairMask) == pairMask) { + writeOutput(String.format("pair() completed in %d ms: device=%s", + (System.currentTimeMillis() - s), device)); + return; + } + } + sleep(POLL_TIME); + } + + int firedFlags = mReceiver.getFiredFlags(); + int pairFiredFlags = mReceiver.getPairFiredFlags(); + mReceiver.resetFiredFlags(); + fail(String.format("pair() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x), " + + "pairFlags=0x%x (expected 0x%x)", state, BluetoothDevice.BOND_BONDED, firedFlags, + mask, pairFiredFlags, pairMask)); + } + + public void unpair(BluetoothAdapter adapter, BluetoothDevice device) { + int mask = PAIR_STATE_FLAG; + int pairMask = PAIR_STATE_NONE; + mReceiver.resetFiredFlags(); + + if (!adapter.isEnabled()) { + fail("unpair() bluetooth not enabled"); + } + + int state = device.getBondState(); + switch (state) { + case BluetoothDevice.BOND_BONDED: + assertTrue(adapter.getBondedDevices().contains(device)); + assertTrue(device.removeBond()); + break; + case BluetoothDevice.BOND_BONDING: + assertTrue(device.removeBond()); + break; + case BluetoothDevice.BOND_NONE: + assertFalse(adapter.getBondedDevices().contains(device)); + return; + default: + fail("unpair() invalid state: state=" + state); + } + + assertTrue(device.removeBond()); + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < UNPAIR_TIMEOUT) { + if (device.getBondState() == BluetoothDevice.BOND_NONE) { + assertFalse(adapter.getBondedDevices().contains(device)); + if ((mReceiver.getFiredFlags() & mask) == mask + && (mReceiver.getPairFiredFlags() & pairMask) == pairMask) { + writeOutput(String.format("unpair() completed in %d ms: device=%s", + (System.currentTimeMillis() - s), device)); + return; + } + } + } + + int firedFlags = mReceiver.getFiredFlags(); + int pairFiredFlags = mReceiver.getPairFiredFlags(); + mReceiver.resetFiredFlags(); + fail(String.format("unpair() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x), " + + "pairFlags=0x%x (expected 0x%x)", state, BluetoothDevice.BOND_BONDED, firedFlags, + mask, pairFiredFlags, pairMask)); + } + + public void connectA2dp(BluetoothAdapter adapter, BluetoothDevice device) { + int mask = PROFILE_A2DP_FLAG; + int a2dpMask1 = A2DP_STATE_CONNECTING | A2DP_STATE_CONNECTED | A2DP_STATE_PLAYING; + int a2dpMask2 = a2dpMask1 ^ A2DP_STATE_CONNECTED; + int a2dpMask3 = a2dpMask1 ^ A2DP_STATE_PLAYING; + mReceiver.resetFiredFlags(); + + if (!adapter.isEnabled()) { + fail("connectA2dp() bluetooth not enabled"); + } + + if (!adapter.getBondedDevices().contains(device)) { + fail("connectA2dp() device not paired: device=" + device); + } + + int state = mA2dp.getSinkState(device); + switch (state) { + case BluetoothA2dp.STATE_CONNECTED: + case BluetoothA2dp.STATE_PLAYING: + assertTrue(mA2dp.isSinkConnected(device)); + return; + case BluetoothA2dp.STATE_DISCONNECTING: + case BluetoothA2dp.STATE_DISCONNECTED: + assertFalse(mA2dp.isSinkConnected(device)); + assertTrue(mA2dp.connectSink(device)); + break; + case BluetoothA2dp.STATE_CONNECTING: + assertFalse(mA2dp.isSinkConnected(device)); + // Don't check for received intents since we might have missed them. + mask = a2dpMask1 = a2dpMask2 = a2dpMask3 = 0; + break; + default: + fail("connectA2dp() invalid state: state=" + state); + } + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < CONNECT_A2DP_TIMEOUT) { + state = mA2dp.getSinkState(device); + if (state == BluetoothA2dp.STATE_CONNECTED || state == BluetoothA2dp.STATE_PLAYING) { + assertTrue(mA2dp.isSinkConnected(device)); + // Check whether STATE_CONNECTING and (STATE_CONNECTED or STATE_PLAYING) intents + // have fired if we are checking if intents should be fired. + int firedFlags = mReceiver.getFiredFlags(); + int a2dpFiredFlags = mReceiver.getA2dpFiredFlags(); + if ((mReceiver.getFiredFlags() & mask) == mask + && ((a2dpFiredFlags & a2dpMask1) == a2dpMask1 + || (a2dpFiredFlags & a2dpMask2) == a2dpMask2 + || (a2dpFiredFlags & a2dpMask3) == a2dpMask3)) { + mReceiver.resetFiredFlags(); + writeOutput(String.format("connectA2dp() completed in %d ms: device=%s", + (System.currentTimeMillis() - s), device)); + return; + } + } + sleep(POLL_TIME); + } + + int firedFlags = mReceiver.getFiredFlags(); + int a2dpFiredFlags = mReceiver.getA2dpFiredFlags(); + mReceiver.resetFiredFlags(); + fail(String.format("connectA2dp() timeout: state=%d (expected %d or %d), " + + "flags=0x%x (expected 0x%x), a2dpFlags=0x%x (expected 0x%x or 0x%x or 0x%x)", + state, BluetoothHeadset.STATE_CONNECTED, BluetoothA2dp.STATE_PLAYING, firedFlags, + mask, a2dpFiredFlags, a2dpMask1, a2dpMask2, a2dpMask3)); + } + + public void disconnectA2dp(BluetoothAdapter adapter, BluetoothDevice device) { + int mask = PROFILE_A2DP_FLAG; + int a2dpMask = A2DP_STATE_DISCONNECTING | A2DP_STATE_DISCONNECTED; + mReceiver.resetFiredFlags(); + + if (!adapter.isEnabled()) { + fail("disconnectA2dp() bluetooth not enabled"); + } + + if (!adapter.getBondedDevices().contains(device)) { + fail("disconnectA2dp() device not paired: device=" + device); + } + + int state = mA2dp.getSinkState(device); + switch (state) { + case BluetoothA2dp.STATE_DISCONNECTED: + assertFalse(mA2dp.isSinkConnected(device)); + return; + case BluetoothA2dp.STATE_CONNECTED: + case BluetoothA2dp.STATE_PLAYING: + assertTrue(mA2dp.isSinkConnected(device)); + assertTrue(mA2dp.disconnectSink(device)); + break; + case BluetoothA2dp.STATE_CONNECTING: + assertFalse(mA2dp.isSinkConnected(device)); + assertTrue(mA2dp.disconnectSink(device)); + break; + case BluetoothA2dp.STATE_DISCONNECTING: + assertFalse(mA2dp.isSinkConnected(device)); + // Don't check for received intents since we might have missed them. + mask = a2dpMask = 0; + break; + default: + fail("disconnectA2dp() invalid state: state=" + state); + } + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < DISCONNECT_A2DP_TIMEOUT) { + state = mA2dp.getSinkState(device); + if (state == BluetoothA2dp.STATE_DISCONNECTED) { + assertFalse(mA2dp.isSinkConnected(device)); + if ((mReceiver.getFiredFlags() & mask) == mask + && (mReceiver.getA2dpFiredFlags() & a2dpMask) == a2dpMask) { + mReceiver.resetFiredFlags(); + writeOutput(String.format("disconnectA2dp() completed in %d ms: device=%s", + (System.currentTimeMillis() - s), device)); + return; + } + } + sleep(POLL_TIME); + } + + int firedFlags = mReceiver.getFiredFlags(); + int a2dpFiredFlags = mReceiver.getA2dpFiredFlags(); + mReceiver.resetFiredFlags(); + fail(String.format("disconnectA2dp() timeout: state=%d (expected %d), " + + "flags=0x%x (expected 0x%x), a2dpFlags=0x%x (expected 0x%x)", state, + BluetoothA2dp.STATE_DISCONNECTED, firedFlags, mask, a2dpFiredFlags, a2dpMask)); + } + + public void connectHeadset(BluetoothAdapter adapter, BluetoothDevice device) { + int mask = PROFILE_HEADSET_FLAG; + int headsetMask = HEADSET_STATE_CONNECTING | HEADSET_STATE_CONNECTED; + mReceiver.resetFiredFlags(); + + if (!adapter.isEnabled()) { + fail("connectHeadset() bluetooth not enabled"); + } + + if (!adapter.getBondedDevices().contains(device)) { + fail("connectHeadset() device not paired: device=" + device); + } + + while (!mHeadsetServiceListener.isConnected()) { + sleep(POLL_TIME); + } + + int state = mHeadset.getState(device); + switch (state) { + case BluetoothHeadset.STATE_CONNECTED: + assertTrue(mHeadset.isConnected(device)); + return; + case BluetoothHeadset.STATE_DISCONNECTED: + assertFalse(mHeadset.isConnected(device)); + mHeadset.connectHeadset(device); + break; + case BluetoothHeadset.STATE_CONNECTING: + assertFalse(mHeadset.isConnected(device)); + // Don't check for received intents since we might have missed them. + mask = headsetMask = 0; + break; + case BluetoothHeadset.STATE_ERROR: + fail("connectHeadset() error state"); + break; + default: + fail("connectHeadset() invalid state: state=" + state); + } + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < CONNECT_HEADSET_TIMEOUT) { + state = mHeadset.getState(device); + if (state == BluetoothHeadset.STATE_CONNECTED) { + assertTrue(mHeadset.isConnected(device)); + if ((mReceiver.getFiredFlags() & mask) == mask + && (mReceiver.getHeadsetFiredFlags() & headsetMask) == headsetMask) { + mReceiver.resetFiredFlags(); + writeOutput(String.format("connectHeadset() completed in %d ms: device=%s", + (System.currentTimeMillis() - s), device)); + return; + } + } + sleep(POLL_TIME); + } + + int firedFlags = mReceiver.getFiredFlags(); + int headsetFiredFlags = mReceiver.getHeadsetFiredFlags(); + mReceiver.resetFiredFlags(); + fail(String.format("connectHeadset() timeout: state=%d (expected %d), " + + "flags=0x%x (expected 0x%x), headsetFlags=0x%s (expected 0x%x)", state, + BluetoothHeadset.STATE_CONNECTED, firedFlags, mask, headsetFiredFlags, + headsetMask)); + } + + public void disconnectHeadset(BluetoothAdapter adapter, BluetoothDevice device) { + int mask = PROFILE_HEADSET_FLAG; + int headsetMask = HEADSET_STATE_DISCONNECTED; + mReceiver.resetFiredFlags(); + + if (!adapter.isEnabled()) { + fail("disconnectHeadset() bluetooth not enabled"); + } + + if (!adapter.getBondedDevices().contains(device)) { + fail("disconnectHeadset() device not paired: device=" + device); + } + + while (!mHeadsetServiceListener.isConnected()) { + sleep(POLL_TIME); + } + + int state = mHeadset.getState(device); + switch (state) { + case BluetoothHeadset.STATE_CONNECTED: + mHeadset.disconnectHeadset(device); + break; + case BluetoothHeadset.STATE_CONNECTING: + mHeadset.disconnectHeadset(device); + break; + case BluetoothHeadset.STATE_DISCONNECTED: + return; + case BluetoothHeadset.STATE_ERROR: + fail("disconnectHeadset() error state"); + break; + default: + fail("disconnectHeadset() invalid state: state=" + state); + } + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < DISCONNECT_HEADSET_TIMEOUT) { + state = mHeadset.getState(device); + if (state == BluetoothHeadset.STATE_DISCONNECTED) { + assertFalse(mHeadset.isConnected(device)); + if ((mReceiver.getFiredFlags() & mask) == mask + && (mReceiver.getHeadsetFiredFlags() & headsetMask) == headsetMask) { + mReceiver.resetFiredFlags(); + writeOutput(String.format("disconnectHeadset() completed in %d ms: device=%s", + (System.currentTimeMillis() - s), device)); + return; + } + } + sleep(POLL_TIME); + } + + int firedFlags = mReceiver.getFiredFlags(); + int headsetFiredFlags = mReceiver.getHeadsetFiredFlags(); + mReceiver.resetFiredFlags(); + fail(String.format("disconnectHeadset() timeout: state=%d (expected %d), " + + "flags=0x%x (expected 0x%x), headsetFlags=0x%s (expected 0x%x)", state, + BluetoothHeadset.STATE_DISCONNECTED, firedFlags, mask, headsetFiredFlags, + headsetMask)); + } + public void writeOutput(String s) { Log.i(mTag, s); if (mOutputWriter == null) { diff --git a/core/tests/coretests/src/android/content/BrickDeniedTest.java b/core/tests/coretests/src/android/content/BrickDeniedTest.java index c7d0b7acab66..3d246b457ed5 100644 --- a/core/tests/coretests/src/android/content/BrickDeniedTest.java +++ b/core/tests/coretests/src/android/content/BrickDeniedTest.java @@ -16,13 +16,12 @@ package android.content; -import android.content.Intent; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.SmallTest; /** Test to make sure brick intents <b>don't</b> work without permission. */ public class BrickDeniedTest extends AndroidTestCase { - @MediumTest + @SmallTest public void testBrick() { // Try both the old and new brick intent names. Neither should work, // since this test application doesn't have the required permission. diff --git a/core/tests/coretests/src/android/content/MemoryFileProviderTest.java b/core/tests/coretests/src/android/content/MemoryFileProviderTest.java index 6708af6e83d8..62b4e7e1ce96 100644 --- a/core/tests/coretests/src/android/content/MemoryFileProviderTest.java +++ b/core/tests/coretests/src/android/content/MemoryFileProviderTest.java @@ -16,10 +16,11 @@ package android.content; -import android.content.ContentResolver; import android.net.Uri; import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.SmallTest; import java.io.InputStream; import java.util.Arrays; @@ -46,7 +47,7 @@ public class MemoryFileProviderTest extends AndroidTestCase { } // tests that we don't leak file descriptors or virtual address space - @MediumTest + @LargeTest public void testClose() throws Exception { ContentResolver resolver = getContext().getContentResolver(); // open enough file descriptors that we will crash something if we leak FDs @@ -65,7 +66,7 @@ public class MemoryFileProviderTest extends AndroidTestCase { } // tests that we haven't broken AssestFileDescriptors for normal files. - @MediumTest + @SmallTest public void testFile() throws Exception { ContentResolver resolver = getContext().getContentResolver(); Uri uri = Uri.parse("content://android.content.MemoryFileProvider/file"); diff --git a/core/tests/coretests/src/android/content/SyncStorageEngineTest.java b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java index f84051276bc0..0b494a768552 100644 --- a/core/tests/coretests/src/android/content/SyncStorageEngineTest.java +++ b/core/tests/coretests/src/android/content/SyncStorageEngineTest.java @@ -18,17 +18,19 @@ package android.content; import com.android.internal.os.AtomicFile; +import android.accounts.Account; +import android.os.Bundle; import android.test.AndroidTestCase; import android.test.RenamingDelegatingContext; -import android.test.suitebuilder.annotation.SmallTest; -import android.test.mock.MockContext; import android.test.mock.MockContentResolver; -import android.accounts.Account; -import android.os.Bundle; +import android.test.mock.MockContext; +import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.SmallTest; -import java.util.List; import java.io.File; import java.io.FileOutputStream; +import java.util.List; public class SyncStorageEngineTest extends AndroidTestCase { @@ -57,7 +59,7 @@ public class SyncStorageEngineTest extends AndroidTestCase { /** * Test that we can create, remove and retrieve periodic syncs */ - @SmallTest + @MediumTest public void testPeriodics() throws Exception { final Account account1 = new Account("a@example.com", "example.type"); final Account account2 = new Account("b@example.com", "example.type.2"); @@ -114,7 +116,7 @@ public class SyncStorageEngineTest extends AndroidTestCase { } } - @SmallTest + @LargeTest public void testAuthorityPersistence() throws Exception { final Account account1 = new Account("a@example.com", "example.type"); final Account account2 = new Account("b@example.com", "example.type.2"); @@ -197,7 +199,7 @@ public class SyncStorageEngineTest extends AndroidTestCase { assertEquals(0, engine.getIsSyncable(account2, authority2)); } - @SmallTest + @MediumTest public void testAuthorityParsing() throws Exception { final Account account = new Account("account1", "type1"); final String authority1 = "auth1"; @@ -299,7 +301,7 @@ public class SyncStorageEngineTest extends AndroidTestCase { assertEquals(sync3s, syncs.get(0)); } - @SmallTest + @MediumTest public void testAuthorityRenaming() throws Exception { final Account account1 = new Account("acc1", "type1"); final Account account2 = new Account("acc2", "type2"); diff --git a/core/tests/coretests/src/android/content/pm/AppCacheTest.java b/core/tests/coretests/src/android/content/pm/AppCacheTest.java index dbb10b1ba36d..dd252203b808 100755 --- a/core/tests/coretests/src/android/content/pm/AppCacheTest.java +++ b/core/tests/coretests/src/android/content/pm/AppCacheTest.java @@ -16,32 +16,25 @@ package android.content.pm; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; - import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.ApplicationInfo; -import android.content.pm.IPackageDataObserver; -import android.content.pm.IPackageStatsObserver; -import android.content.pm.PackageStats; -import android.content.pm.IPackageManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.StatFs; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; -import android.test.suitebuilder.annotation.Suppress; import android.util.Log; -import android.os.Handler; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.StatFs; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; public class AppCacheTest extends AndroidTestCase { private static final boolean localLOGV = false; @@ -627,7 +620,8 @@ public class AppCacheTest extends AndroidTestCase { } } - @SmallTest + // TODO: flaky test, omit from LargeTest for now + //@LargeTest public void testFreeStorage() throws Exception { boolean TRACKING = true; StatFs st = new StatFs("/data"); diff --git a/core/tests/coretests/src/android/content/pm/ComponentTest.java b/core/tests/coretests/src/android/content/pm/ComponentTest.java index ebfbd683fcfb..f1a2a9bd172e 100644 --- a/core/tests/coretests/src/android/content/pm/ComponentTest.java +++ b/core/tests/coretests/src/android/content/pm/ComponentTest.java @@ -16,6 +16,11 @@ package android.content.pm; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; +import static android.content.pm.PackageManager.GET_DISABLED_COMPONENTS; + import com.android.frameworks.coretests.enabled_app.DisabledActivity; import com.android.frameworks.coretests.enabled_app.DisabledProvider; import com.android.frameworks.coretests.enabled_app.DisabledReceiver; @@ -27,21 +32,9 @@ import com.android.frameworks.coretests.enabled_app.EnabledService; import android.content.ComponentName; import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.ComponentInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.test.suitebuilder.annotation.LargeTest; +import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; -import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; -import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; -import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; -import static android.content.pm.PackageManager.GET_DISABLED_COMPONENTS; -import android.content.pm.ProviderInfo; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.test.AndroidTestCase; import java.util.List; @@ -134,7 +127,7 @@ public class ComponentTest extends AndroidTestCase { assertNotNull(mContext); } - @MediumTest + @SmallTest public void testResolveDisabledActivity() throws Exception { mPackageManager.setComponentEnabledSetting(DISABLED_ACTIVITY_COMPONENTNAME, COMPONENT_ENABLED_STATE_DEFAULT, @@ -150,7 +143,7 @@ public class ComponentTest extends AndroidTestCase { assertFalse(info2.activityInfo.enabled); } - @MediumTest + @SmallTest public void testResolveEnabledActivity() throws Exception { mPackageManager.setComponentEnabledSetting(ENABLED_ACTIVITY_COMPONENTNAME, COMPONENT_ENABLED_STATE_DEFAULT, @@ -183,7 +176,7 @@ public class ComponentTest extends AndroidTestCase { assertFalse(info.activityInfo.enabled); } - @MediumTest + @SmallTest public void testQueryEnabledActivity() throws Exception { mPackageManager.setComponentEnabledSetting(ENABLED_ACTIVITY_COMPONENTNAME, COMPONENT_ENABLED_STATE_DEFAULT, @@ -218,7 +211,7 @@ public class ComponentTest extends AndroidTestCase { assertFalse(activityInfo.enabled); } - @MediumTest + @SmallTest public void testGetEnabledActivityInfo() throws Exception { mPackageManager.setComponentEnabledSetting(ENABLED_ACTIVITY_COMPONENTNAME, COMPONENT_ENABLED_STATE_DEFAULT, @@ -253,7 +246,7 @@ public class ComponentTest extends AndroidTestCase { assertEquals(1, infoList.size()); } - @LargeTest + @MediumTest public void testDisableActivity() throws Exception { mPackageManager.setComponentEnabledSetting(ENABLED_ACTIVITY_COMPONENTNAME, COMPONENT_ENABLED_STATE_DEFAULT, @@ -281,7 +274,7 @@ public class ComponentTest extends AndroidTestCase { assertEquals(0, infoList.size()); } - @MediumTest + @SmallTest public void testResolveDisabledService() throws Exception { mPackageManager.setComponentEnabledSetting(DISABLED_SERVICE_COMPONENTNAME, COMPONENT_ENABLED_STATE_DEFAULT, @@ -297,7 +290,7 @@ public class ComponentTest extends AndroidTestCase { assertFalse(info2.serviceInfo.enabled); } - @MediumTest + @SmallTest public void testResolveEnabledService() throws Exception { mPackageManager.setComponentEnabledSetting(ENABLED_SERVICE_COMPONENTNAME, COMPONENT_ENABLED_STATE_DEFAULT, @@ -310,7 +303,7 @@ public class ComponentTest extends AndroidTestCase { assertTrue(info.serviceInfo.enabled); } - @MediumTest + @SmallTest public void testQueryDisabledService() throws Exception { mPackageManager.setComponentEnabledSetting(DISABLED_SERVICE_COMPONENTNAME, COMPONENT_ENABLED_STATE_DEFAULT, @@ -330,7 +323,7 @@ public class ComponentTest extends AndroidTestCase { assertFalse(info.serviceInfo.enabled); } - @MediumTest + @SmallTest public void testQueryEnabledService() throws Exception { mPackageManager.setComponentEnabledSetting(ENABLED_SERVICE_COMPONENTNAME, COMPONENT_ENABLED_STATE_DEFAULT, @@ -365,7 +358,7 @@ public class ComponentTest extends AndroidTestCase { assertFalse(serviceInfo.enabled); } - @MediumTest + @SmallTest public void testGetEnabledServiceInfo() throws Exception { mPackageManager.setComponentEnabledSetting(ENABLED_SERVICE_COMPONENTNAME, COMPONENT_ENABLED_STATE_DEFAULT, @@ -396,7 +389,7 @@ public class ComponentTest extends AndroidTestCase { assertFalse(info2.serviceInfo.enabled); } - @LargeTest + @MediumTest public void testDisableService() throws Exception { mPackageManager.setComponentEnabledSetting(ENABLED_SERVICE_COMPONENTNAME, COMPONENT_ENABLED_STATE_DEFAULT, @@ -420,7 +413,7 @@ public class ComponentTest extends AndroidTestCase { assertTrue(info3.serviceInfo.enabled); } - @MediumTest + @SmallTest public void testQueryDisabledReceiver() throws Exception { mPackageManager.setComponentEnabledSetting(DISABLED_RECEIVER_COMPONENTNAME, COMPONENT_ENABLED_STATE_DEFAULT, @@ -440,7 +433,7 @@ public class ComponentTest extends AndroidTestCase { assertFalse(info.activityInfo.enabled); } - @MediumTest + @SmallTest public void testQueryEnabledReceiver() throws Exception { mPackageManager.setComponentEnabledSetting(ENABLED_RECEIVER_COMPONENTNAME, COMPONENT_ENABLED_STATE_DEFAULT, @@ -475,7 +468,7 @@ public class ComponentTest extends AndroidTestCase { assertFalse(activityInfo.enabled); } - @MediumTest + @SmallTest public void testGetEnabledReceiverInfo() throws Exception { mPackageManager.setComponentEnabledSetting(ENABLED_RECEIVER_COMPONENTNAME, COMPONENT_ENABLED_STATE_DEFAULT, @@ -530,7 +523,7 @@ public class ComponentTest extends AndroidTestCase { } } - @MediumTest + @SmallTest public void testResolveEnabledProvider() throws Exception { mPackageManager.setComponentEnabledSetting(ENABLED_PROVIDER_COMPONENTNAME, COMPONENT_ENABLED_STATE_DEFAULT, @@ -542,7 +535,7 @@ public class ComponentTest extends AndroidTestCase { assertTrue(providerInfo.enabled); } - @MediumTest + @SmallTest public void testResolveDisabledProvider() throws Exception { mPackageManager.setComponentEnabledSetting(DISABLED_PROVIDER_COMPONENTNAME, COMPONENT_ENABLED_STATE_DEFAULT, @@ -594,7 +587,7 @@ public class ComponentTest extends AndroidTestCase { assertNull(providerInfo2); } - @MediumTest + @SmallTest public void testQueryEnabledProvider() throws Exception { mPackageManager.setComponentEnabledSetting(ENABLED_PROVIDER_COMPONENTNAME, COMPONENT_ENABLED_STATE_DEFAULT, diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java index 1f2e97c738c2..975a4c2ddc7b 100755 --- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java @@ -23,22 +23,10 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.ApplicationInfo; -import android.content.pm.IPackageMoveObserver; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageParser; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import android.net.Uri; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.Suppress; -import android.util.DisplayMetrics; -import android.util.Log; import android.os.Environment; import android.os.FileUtils; import android.os.IBinder; @@ -52,6 +40,7 @@ import android.os.storage.StorageResultCode; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.LargeTest; import android.util.DisplayMetrics; import android.util.Log; @@ -80,7 +69,7 @@ public class PackageManagerTests extends AndroidTestCase { @Override protected void setUp() throws Exception { super.setUp(); - mOrigState = getMediaState(); + mOrigState = checkMediaState(Environment.MEDIA_MOUNTED); if (!mountMedia()) { Log.i(TAG, "sdcard not mounted? Some of these tests might fail"); } @@ -89,12 +78,12 @@ public class PackageManagerTests extends AndroidTestCase { @Override protected void tearDown() throws Exception { // Restore media state. - boolean newState = getMediaState(); + boolean newState = checkMediaState(Environment.MEDIA_MOUNTED); if (newState != mOrigState) { if (mOrigState) { - getMs().mountVolume(Environment.getExternalStorageDirectory().getPath()); + mountMedia(); } else { - getMs().unmountVolume(Environment.getExternalStorageDirectory().getPath(), true); + unmountMedia(); } } super.tearDown(); @@ -369,6 +358,7 @@ public class PackageManagerTests extends AndroidTestCase { assertTrue((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0); assertEquals(srcPath, drmInstallPath); assertEquals(publicSrcPath, appInstallPath); + assertTrue(info.nativeLibraryDir.startsWith(dataDir.getPath())); } else { assertFalse((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0); int rLoc = getInstallLoc(flags, expInstallLocation, pkgLen); @@ -376,10 +366,12 @@ public class PackageManagerTests extends AndroidTestCase { assertEquals(srcPath, appInstallPath); assertEquals(publicSrcPath, appInstallPath); assertFalse((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); + assertTrue(info.nativeLibraryDir.startsWith(dataDir.getPath())); } else if (rLoc == INSTALL_LOC_SD){ assertTrue((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); assertTrue(srcPath.startsWith(SECURE_CONTAINERS_PREFIX)); assertTrue(publicSrcPath.startsWith(SECURE_CONTAINERS_PREFIX)); + assertTrue(info.nativeLibraryDir.startsWith(SECURE_CONTAINERS_PREFIX)); } else { // TODO handle error. Install should have failed. } @@ -575,18 +567,19 @@ public class PackageManagerTests extends AndroidTestCase { return ip; } - @MediumTest + @LargeTest public void testInstallNormalInternal() { sampleInstallFromRawResource(0, true); } - @MediumTest + @LargeTest public void testInstallFwdLockedInternal() { sampleInstallFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, true); } - @MediumTest + @LargeTest public void testInstallSdcard() { + mountMedia(); sampleInstallFromRawResource(PackageManager.INSTALL_EXTERNAL, true); } @@ -675,33 +668,33 @@ public class PackageManagerTests extends AndroidTestCase { } } - @MediumTest + @LargeTest public void testReplaceFailNormalInternal() { sampleReplaceFromRawResource(0); } - @MediumTest + @LargeTest public void testReplaceFailFwdLockedInternal() { sampleReplaceFromRawResource(PackageManager.INSTALL_FORWARD_LOCK); } - @MediumTest + @LargeTest public void testReplaceFailSdcard() { sampleReplaceFromRawResource(PackageManager.INSTALL_EXTERNAL); } - @MediumTest + @LargeTest public void testReplaceNormalInternal() { sampleReplaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING); } - @MediumTest + @LargeTest public void testReplaceFwdLockedInternal() { sampleReplaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FORWARD_LOCK); } - @MediumTest + @LargeTest public void testReplaceSdcard() { sampleReplaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_EXTERNAL); @@ -815,32 +808,32 @@ public class PackageManagerTests extends AndroidTestCase { } } - @MediumTest + @LargeTest public void testDeleteNormalInternal() { deleteFromRawResource(0, 0); } - @MediumTest + @LargeTest public void testDeleteFwdLockedInternal() { deleteFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, 0); } - @MediumTest + @LargeTest public void testDeleteSdcard() { deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, 0); } - @MediumTest + @LargeTest public void testDeleteNormalInternalRetainData() { deleteFromRawResource(0, PackageManager.DONT_DELETE_DATA); } - @MediumTest + @LargeTest public void testDeleteFwdLockedInternalRetainData() { deleteFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, PackageManager.DONT_DELETE_DATA); } - @MediumTest + @LargeTest public void testDeleteSdcardRetainData() { deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.DONT_DELETE_DATA); } @@ -924,41 +917,62 @@ public class PackageManagerTests extends AndroidTestCase { return null; } - boolean getMediaState() { + boolean checkMediaState(String desired) { try { - String mPath = Environment.getExternalStorageDirectory().getPath(); - String state = getMs().getVolumeState(mPath); - return Environment.MEDIA_MOUNTED.equals(state); + String mPath = Environment.getExternalStorageDirectory().getPath(); + String actual = getMs().getVolumeState(mPath); + if (desired.equals(actual)) { + return true; + } else { + return false; + } } catch (RemoteException e) { + Log.e(TAG, "Exception while checking media state", e); return false; } } boolean mountMedia() { - if (getMediaState()) { + if (checkMediaState(Environment.MEDIA_MOUNTED)) { return true; } + + final String path = Environment.getExternalStorageDirectory().toString(); + StorageListener observer = new StorageListener(Environment.MEDIA_MOUNTED); + StorageManager sm = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE); + sm.registerListener(observer); try { - String mPath = Environment.getExternalStorageDirectory().toString(); - int ret = getMs().mountVolume(mPath); - return ret == StorageResultCode.OperationSucceeded; - } catch (RemoteException e) { + // Wait on observer + synchronized (observer) { + int ret = getMs().mountVolume(path); + if (ret != StorageResultCode.OperationSucceeded) { + throw new Exception("Could not mount the media"); + } + long waitTime = 0; + while ((!observer.isDone()) && (waitTime < MAX_WAIT_TIME)) { + observer.wait(WAIT_TIME_INCR); + waitTime += WAIT_TIME_INCR; + } + if (!observer.isDone()) { + throw new Exception("Timed out waiting for unmount media notification"); + } + return true; + } + } catch (Exception e) { + Log.e(TAG, "Exception : " + e); return false; + } finally { + sm.unregisterListener(observer); } } private boolean unmountMedia() { - String path = Environment.getExternalStorageDirectory().getPath(); - try { - String state = getMs().getVolumeState(path); - if (Environment.MEDIA_UNMOUNTED.equals(state)) { - return true; - } - } catch (RemoteException e) { - failStr(e); + if (checkMediaState(Environment.MEDIA_UNMOUNTED)) { + return true; } - StorageListener observer = new StorageListener(); + final String path = Environment.getExternalStorageDirectory().getPath(); + StorageListener observer = new StorageListener(Environment.MEDIA_UNMOUNTED); StorageManager sm = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE); sm.registerListener(observer); try { @@ -987,7 +1001,7 @@ public class PackageManagerTests extends AndroidTestCase { // Install pkg on sdcard InstallParams ip = sampleInstallFromRawResource(PackageManager.INSTALL_EXTERNAL, false); if (localLOGV) Log.i(TAG, "Installed pkg on sdcard"); - boolean origState = getMediaState(); + boolean origState = checkMediaState(Environment.MEDIA_MOUNTED); boolean registeredReceiver = false; SdMountReceiver receiver = new SdMountReceiver(new String[]{ip.pkg.packageName}); try { @@ -1040,7 +1054,7 @@ public class PackageManagerTests extends AndroidTestCase { * (Use PackageManagerService private api for now) * Make sure the installed package is available. */ - @MediumTest + @LargeTest public void testMountSdNormalInternal() { assertTrue(mountFromRawResource()); } @@ -1071,31 +1085,31 @@ public class PackageManagerTests extends AndroidTestCase { } catch (NameNotFoundException e) {} } - @MediumTest + @LargeTest public void testManifestInstallLocationInternal() { installFromRawResource("install.apk", R.raw.install_loc_internal, 0, true, false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY); } - @MediumTest + @LargeTest public void testManifestInstallLocationSdcard() { installFromRawResource("install.apk", R.raw.install_loc_sdcard, 0, true, false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL); } - @MediumTest + @LargeTest public void testManifestInstallLocationAuto() { installFromRawResource("install.apk", R.raw.install_loc_auto, 0, true, false, -1, PackageInfo.INSTALL_LOCATION_AUTO); } - @MediumTest + @LargeTest public void testManifestInstallLocationUnspecified() { installFromRawResource("install.apk", R.raw.install_loc_unspecified, 0, true, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); } - @MediumTest + @LargeTest public void testManifestInstallLocationFwdLockedFlagSdcard() { installFromRawResource("install.apk", R.raw.install_loc_unspecified, PackageManager.INSTALL_FORWARD_LOCK | @@ -1104,7 +1118,7 @@ public class PackageManagerTests extends AndroidTestCase { PackageInfo.INSTALL_LOCATION_UNSPECIFIED); } - @MediumTest + @LargeTest public void testManifestInstallLocationFwdLockedSdcard() { installFromRawResource("install.apk", R.raw.install_loc_sdcard, PackageManager.INSTALL_FORWARD_LOCK, true, false, @@ -1117,7 +1131,7 @@ public class PackageManagerTests extends AndroidTestCase { * the package via flag to install on sdcard. Make sure the new flag overrides * the old install location. */ - @MediumTest + @LargeTest public void testReplaceFlagInternalSdcard() { int iFlags = 0; int rFlags = PackageManager.INSTALL_EXTERNAL; @@ -1139,7 +1153,7 @@ public class PackageManagerTests extends AndroidTestCase { * the package with no flags or manifest option and make sure the old * install location is retained. */ - @MediumTest + @LargeTest public void testReplaceFlagSdcardInternal() { int iFlags = PackageManager.INSTALL_EXTERNAL; int rFlags = 0; @@ -1156,7 +1170,7 @@ public class PackageManagerTests extends AndroidTestCase { } } - @MediumTest + @LargeTest public void testManifestInstallLocationReplaceInternalSdcard() { int iFlags = 0; int iApk = R.raw.install_loc_internal; @@ -1179,7 +1193,7 @@ public class PackageManagerTests extends AndroidTestCase { } } - @MediumTest + @LargeTest public void testManifestInstallLocationReplaceSdcardInternal() { int iFlags = 0; int iApk = R.raw.install_loc_sdcard; @@ -1378,8 +1392,10 @@ public class PackageManagerTests extends AndroidTestCase { assertNotNull(info); if ((moveFlags & PackageManager.MOVE_INTERNAL) != 0) { assertTrue((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0); + assertTrue(info.nativeLibraryDir.startsWith(info.dataDir)); } else if ((moveFlags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0){ assertTrue((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); + assertTrue(info.nativeLibraryDir.startsWith(SECURE_CONTAINERS_PREFIX)); } } } catch (NameNotFoundException e) { @@ -1401,7 +1417,7 @@ public class PackageManagerTests extends AndroidTestCase { fail, result); } - @MediumTest + @LargeTest public void testMoveAppInternalToExternal() { int installFlags = PackageManager.INSTALL_INTERNAL; int moveFlags = PackageManager.MOVE_EXTERNAL_MEDIA; @@ -1410,7 +1426,7 @@ public class PackageManagerTests extends AndroidTestCase { sampleMoveFromRawResource(installFlags, moveFlags, fail, result); } - @MediumTest + @LargeTest public void testMoveAppInternalToInternal() { int installFlags = PackageManager.INSTALL_INTERNAL; int moveFlags = PackageManager.MOVE_INTERNAL; @@ -1419,7 +1435,7 @@ public class PackageManagerTests extends AndroidTestCase { sampleMoveFromRawResource(installFlags, moveFlags, fail, result); } - @MediumTest + @LargeTest public void testMoveAppExternalToExternal() { int installFlags = PackageManager.INSTALL_EXTERNAL; int moveFlags = PackageManager.MOVE_EXTERNAL_MEDIA; @@ -1427,7 +1443,7 @@ public class PackageManagerTests extends AndroidTestCase { int result = PackageManager.MOVE_FAILED_INVALID_LOCATION; sampleMoveFromRawResource(installFlags, moveFlags, fail, result); } - @MediumTest + @LargeTest public void testMoveAppExternalToInternal() { int installFlags = PackageManager.INSTALL_EXTERNAL; int moveFlags = PackageManager.MOVE_INTERNAL; @@ -1435,7 +1451,7 @@ public class PackageManagerTests extends AndroidTestCase { int result = PackageManager.MOVE_SUCCEEDED; sampleMoveFromRawResource(installFlags, moveFlags, fail, result); } - @MediumTest + @LargeTest public void testMoveAppForwardLocked() { int installFlags = PackageManager.INSTALL_FORWARD_LOCK; int moveFlags = PackageManager.MOVE_EXTERNAL_MEDIA; @@ -1443,7 +1459,7 @@ public class PackageManagerTests extends AndroidTestCase { int result = PackageManager.MOVE_FAILED_FORWARD_LOCKED; sampleMoveFromRawResource(installFlags, moveFlags, fail, result); } - @MediumTest + @LargeTest public void testMoveAppFailInternalToExternalDelete() { int installFlags = 0; int moveFlags = PackageManager.MOVE_EXTERNAL_MEDIA; @@ -1477,9 +1493,9 @@ public class PackageManagerTests extends AndroidTestCase { * Test that an install error code is returned when media is unmounted * and package installed on sdcard via package manager flag. */ - @MediumTest + @LargeTest public void testInstallSdcardUnmount() { - boolean origState = getMediaState(); + boolean origState = checkMediaState(Environment.MEDIA_MOUNTED); try { // Unmount sdcard assertTrue(unmountMedia()); @@ -1502,24 +1518,24 @@ public class PackageManagerTests extends AndroidTestCase { * Unmount sdcard. Try installing an app with manifest option to install * on sdcard. Make sure it gets installed on internal flash. */ - @MediumTest + @LargeTest public void testInstallManifestSdcardUnmount() { - boolean origState = getMediaState(); - try { - // Unmount sdcard - assertTrue(unmountMedia()); - InstallParams ip = new InstallParams("install.apk", R.raw.install_loc_sdcard); - installFromRawResource(ip, 0, true, false, -1, - PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY); - } finally { - // Restore original media state - if (origState) { - mountMedia(); - } else { - unmountMedia(); - } - } - } + boolean origState = checkMediaState(Environment.MEDIA_MOUNTED); + try { + // Unmount sdcard + assertTrue(unmountMedia()); + InstallParams ip = new InstallParams("install.apk", R.raw.install_loc_sdcard); + installFromRawResource(ip, 0, true, false, -1, + PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY); + } finally { + // Restore original media state + if (origState) { + mountMedia(); + } else { + unmountMedia(); + } + } + } /*---------- Recommended install location tests ----*/ /* Precedence: FlagManifestExistingUser @@ -1534,14 +1550,14 @@ public class PackageManagerTests extends AndroidTestCase { /* * Install an app on internal flash */ - @MediumTest + @LargeTest public void testFlagI() { sampleInstallFromRawResource(PackageManager.INSTALL_INTERNAL, true); } /* * Install an app on sdcard. */ - @MediumTest + @LargeTest public void testFlagE() { sampleInstallFromRawResource(PackageManager.INSTALL_EXTERNAL, true); } @@ -1549,14 +1565,14 @@ public class PackageManagerTests extends AndroidTestCase { /* * Install an app on sdcard. */ - @MediumTest + @LargeTest public void testFlagF() { sampleInstallFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, true); } /* * Install an app with both internal and external flags set. should fail */ - @MediumTest + @LargeTest public void testFlagIE() { installFromRawResource("install.apk", R.raw.install, PackageManager.INSTALL_EXTERNAL | PackageManager.INSTALL_INTERNAL, @@ -1568,7 +1584,7 @@ public class PackageManagerTests extends AndroidTestCase { /* * Install an app with both internal and external flags set. should fail */ - @MediumTest + @LargeTest public void testFlagIF() { sampleInstallFromRawResource(PackageManager.INSTALL_FORWARD_LOCK | PackageManager.INSTALL_INTERNAL, true); @@ -1576,7 +1592,7 @@ public class PackageManagerTests extends AndroidTestCase { /* * Install an app with both internal and external flags set. should fail */ - @MediumTest + @LargeTest public void testFlagEF() { installFromRawResource("install.apk", R.raw.install, PackageManager.INSTALL_FORWARD_LOCK | PackageManager.INSTALL_EXTERNAL, @@ -1587,7 +1603,7 @@ public class PackageManagerTests extends AndroidTestCase { /* * Install an app with both internal and external flags set. should fail */ - @MediumTest + @LargeTest public void testFlagIEF() { installFromRawResource("install.apk", R.raw.install, PackageManager.INSTALL_FORWARD_LOCK | PackageManager.INSTALL_INTERNAL | @@ -1600,7 +1616,7 @@ public class PackageManagerTests extends AndroidTestCase { * Install an app with both internal and manifest option set. * should install on internal. */ - @MediumTest + @LargeTest public void testFlagIManifestI() { installFromRawResource("install.apk", R.raw.install_loc_internal, PackageManager.INSTALL_INTERNAL, @@ -1612,7 +1628,7 @@ public class PackageManagerTests extends AndroidTestCase { * Install an app with both internal and manifest preference for * preferExternal. Should install on internal. */ - @MediumTest + @LargeTest public void testFlagIManifestE() { installFromRawResource("install.apk", R.raw.install_loc_sdcard, PackageManager.INSTALL_INTERNAL, @@ -1624,7 +1640,7 @@ public class PackageManagerTests extends AndroidTestCase { * Install an app with both internal and manifest preference for * auto. should install internal. */ - @MediumTest + @LargeTest public void testFlagIManifestA() { installFromRawResource("install.apk", R.raw.install_loc_auto, PackageManager.INSTALL_INTERNAL, @@ -1636,7 +1652,7 @@ public class PackageManagerTests extends AndroidTestCase { * Install an app with both external and manifest option set. * should install externally. */ - @MediumTest + @LargeTest public void testFlagEManifestI() { installFromRawResource("install.apk", R.raw.install_loc_internal, PackageManager.INSTALL_EXTERNAL, @@ -1648,7 +1664,7 @@ public class PackageManagerTests extends AndroidTestCase { * Install an app with both external and manifest preference for * preferExternal. Should install externally. */ - @MediumTest + @LargeTest public void testFlagEManifestE() { installFromRawResource("install.apk", R.raw.install_loc_sdcard, PackageManager.INSTALL_EXTERNAL, @@ -1660,7 +1676,7 @@ public class PackageManagerTests extends AndroidTestCase { * Install an app with both external and manifest preference for * auto. should install on external media. */ - @MediumTest + @LargeTest public void testFlagEManifestA() { installFromRawResource("install.apk", R.raw.install_loc_auto, PackageManager.INSTALL_EXTERNAL, @@ -1672,7 +1688,7 @@ public class PackageManagerTests extends AndroidTestCase { * Install an app with fwd locked flag set and install location set to * internal. should install internally. */ - @MediumTest + @LargeTest public void testFlagFManifestI() { installFromRawResource("install.apk", R.raw.install_loc_internal, PackageManager.INSTALL_EXTERNAL, @@ -1684,7 +1700,7 @@ public class PackageManagerTests extends AndroidTestCase { * Install an app with fwd locked flag set and install location set to * preferExternal. should install internally. */ - @MediumTest + @LargeTest public void testFlagFManifestE() { installFromRawResource("install.apk", R.raw.install_loc_sdcard, PackageManager.INSTALL_EXTERNAL, @@ -1696,7 +1712,7 @@ public class PackageManagerTests extends AndroidTestCase { * Install an app with fwd locked flag set and install location set to * auto. should install internally. */ - @MediumTest + @LargeTest public void testFlagFManifestA() { installFromRawResource("install.apk", R.raw.install_loc_auto, PackageManager.INSTALL_EXTERNAL, @@ -1711,7 +1727,7 @@ public class PackageManagerTests extends AndroidTestCase { * location should be honoured. * testFlagI/E/F/ExistingI/E - */ - @MediumTest + @LargeTest public void testFlagIExistingI() { int iFlags = PackageManager.INSTALL_INTERNAL; int rFlags = PackageManager.INSTALL_INTERNAL | PackageManager.INSTALL_REPLACE_EXISTING; @@ -1728,7 +1744,7 @@ public class PackageManagerTests extends AndroidTestCase { false, -1, -1); } - @MediumTest + @LargeTest public void testFlagIExistingE() { int iFlags = PackageManager.INSTALL_EXTERNAL; int rFlags = PackageManager.INSTALL_INTERNAL | PackageManager.INSTALL_REPLACE_EXISTING; @@ -1745,7 +1761,7 @@ public class PackageManagerTests extends AndroidTestCase { false, -1, -1); } - @MediumTest + @LargeTest public void testFlagEExistingI() { int iFlags = PackageManager.INSTALL_INTERNAL; int rFlags = PackageManager.INSTALL_EXTERNAL | PackageManager.INSTALL_REPLACE_EXISTING; @@ -1762,7 +1778,7 @@ public class PackageManagerTests extends AndroidTestCase { false, -1, -1); } - @MediumTest + @LargeTest public void testFlagEExistingE() { int iFlags = PackageManager.INSTALL_EXTERNAL; int rFlags = PackageManager.INSTALL_EXTERNAL | PackageManager.INSTALL_REPLACE_EXISTING; @@ -1779,7 +1795,7 @@ public class PackageManagerTests extends AndroidTestCase { false, -1, -1); } - @MediumTest + @LargeTest public void testFlagFExistingI() { int iFlags = PackageManager.INSTALL_INTERNAL; int rFlags = PackageManager.INSTALL_FORWARD_LOCK | PackageManager.INSTALL_REPLACE_EXISTING; @@ -1796,7 +1812,7 @@ public class PackageManagerTests extends AndroidTestCase { false, -1, -1); } - @MediumTest + @LargeTest public void testFlagFExistingE() { int iFlags = PackageManager.INSTALL_EXTERNAL; int rFlags = PackageManager.INSTALL_FORWARD_LOCK | PackageManager.INSTALL_REPLACE_EXISTING; @@ -1820,7 +1836,7 @@ public class PackageManagerTests extends AndroidTestCase { * public void testManifestI/E/A * TODO out of memory fall back behaviour. */ - @MediumTest + @LargeTest public void testManifestI() { installFromRawResource("install.apk", R.raw.install_loc_internal, 0, @@ -1828,7 +1844,7 @@ public class PackageManagerTests extends AndroidTestCase { false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY); } - @MediumTest + @LargeTest public void testManifestE() { installFromRawResource("install.apk", R.raw.install_loc_sdcard, 0, @@ -1836,7 +1852,7 @@ public class PackageManagerTests extends AndroidTestCase { false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL); } - @MediumTest + @LargeTest public void testManifestA() { installFromRawResource("install.apk", R.raw.install_loc_auto, 0, @@ -1851,7 +1867,7 @@ public class PackageManagerTests extends AndroidTestCase { * TODO add out of memory fall back behaviour. * testManifestI/E/AExistingI/E */ - @MediumTest + @LargeTest public void testManifestIExistingI() { int iFlags = PackageManager.INSTALL_INTERNAL; int rFlags = PackageManager.INSTALL_REPLACE_EXISTING; @@ -1868,7 +1884,7 @@ public class PackageManagerTests extends AndroidTestCase { false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY); } - @MediumTest + @LargeTest public void testManifestIExistingE() { int iFlags = PackageManager.INSTALL_EXTERNAL; int rFlags = PackageManager.INSTALL_REPLACE_EXISTING; @@ -1885,7 +1901,7 @@ public class PackageManagerTests extends AndroidTestCase { false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY); } - @MediumTest + @LargeTest public void testManifestEExistingI() { int iFlags = PackageManager.INSTALL_INTERNAL; int rFlags = PackageManager.INSTALL_REPLACE_EXISTING; @@ -1902,7 +1918,7 @@ public class PackageManagerTests extends AndroidTestCase { false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL); } - @MediumTest + @LargeTest public void testManifestEExistingE() { int iFlags = PackageManager.INSTALL_EXTERNAL; int rFlags = PackageManager.INSTALL_REPLACE_EXISTING; @@ -1919,7 +1935,7 @@ public class PackageManagerTests extends AndroidTestCase { false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL); } - @MediumTest + @LargeTest public void testManifestAExistingI() { int iFlags = PackageManager.INSTALL_INTERNAL; int rFlags = PackageManager.INSTALL_REPLACE_EXISTING; @@ -1936,7 +1952,7 @@ public class PackageManagerTests extends AndroidTestCase { false, -1, PackageInfo.INSTALL_LOCATION_AUTO); } - @MediumTest + @LargeTest public void testManifestAExistingE() { int iFlags = PackageManager.INSTALL_EXTERNAL; int rFlags = PackageManager.INSTALL_REPLACE_EXISTING; @@ -1993,37 +2009,37 @@ public class PackageManagerTests extends AndroidTestCase { setInstallLoc(origSetting); } } - @MediumTest + @LargeTest public void testExistingIUserI() { int userSetting = PackageHelper.APP_INSTALL_INTERNAL; int iFlags = PackageManager.INSTALL_INTERNAL; setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY); } - @MediumTest + @LargeTest public void testExistingIUserE() { int userSetting = PackageHelper.APP_INSTALL_EXTERNAL; int iFlags = PackageManager.INSTALL_INTERNAL; setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY); } - @MediumTest + @LargeTest public void testExistingIUserA() { int userSetting = PackageHelper.APP_INSTALL_AUTO; int iFlags = PackageManager.INSTALL_INTERNAL; setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY); } - @MediumTest + @LargeTest public void testExistingEUserI() { int userSetting = PackageHelper.APP_INSTALL_INTERNAL; int iFlags = PackageManager.INSTALL_EXTERNAL; setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL); } - @MediumTest + @LargeTest public void testExistingEUserE() { int userSetting = PackageHelper.APP_INSTALL_EXTERNAL; int iFlags = PackageManager.INSTALL_EXTERNAL; setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL); } - @MediumTest + @LargeTest public void testExistingEUserA() { int userSetting = PackageHelper.APP_INSTALL_AUTO; int iFlags = PackageManager.INSTALL_EXTERNAL; @@ -2066,19 +2082,19 @@ public class PackageManagerTests extends AndroidTestCase { setInstallLoc(origSetting); } } - @MediumTest + @LargeTest public void testUserI() { int userSetting = PackageHelper.APP_INSTALL_INTERNAL; int iloc = getExpectedInstallLocation(userSetting); setUserX(true, userSetting, iloc); } - @MediumTest + @LargeTest public void testUserE() { int userSetting = PackageHelper.APP_INSTALL_EXTERNAL; int iloc = getExpectedInstallLocation(userSetting); setUserX(true, userSetting, iloc); } - @MediumTest + @LargeTest public void testUserA() { int userSetting = PackageHelper.APP_INSTALL_AUTO; int iloc = getExpectedInstallLocation(userSetting); @@ -2088,19 +2104,19 @@ public class PackageManagerTests extends AndroidTestCase { * The following set of tests turn on/off the basic * user setting for turning on install location. */ - @MediumTest + @LargeTest public void testUserPrefOffUserI() { int userSetting = PackageHelper.APP_INSTALL_INTERNAL; int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED; setUserX(false, userSetting, iloc); } - @MediumTest + @LargeTest public void testUserPrefOffUserE() { int userSetting = PackageHelper.APP_INSTALL_EXTERNAL; int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED; setUserX(false, userSetting, iloc); } - @MediumTest + @LargeTest public void testUserPrefOffA() { int userSetting = PackageHelper.APP_INSTALL_AUTO; int iloc = PackageInfo.INSTALL_LOCATION_UNSPECIFIED; @@ -2277,10 +2293,10 @@ public class PackageManagerTests extends AndroidTestCase { /* * Ensure that permissions are properly declared. */ - @MediumTest + @LargeTest public void testInstallOnSdPermissionsUnmount() { InstallParams ip = null; - boolean origMediaState = getMediaState(); + boolean origMediaState = checkMediaState(Environment.MEDIA_MOUNTED); try { // **: Upon installing a package, are its declared permissions published? int iFlags = PackageManager.INSTALL_INTERNAL; @@ -2309,10 +2325,12 @@ public class PackageManagerTests extends AndroidTestCase { * Please note that this test is very closely tied to the framework's * naming convention for secure containers. */ - @MediumTest + @LargeTest public void testInstallSdcardStaleContainer() { - boolean origMediaState = getMediaState(); + boolean origMediaState = checkMediaState(Environment.MEDIA_MOUNTED); try { + // Mount media first + mountMedia(); String outFileName = "install.apk"; int rawResId = R.raw.install; PackageManager pm = mContext.getPackageManager(); @@ -2351,9 +2369,9 @@ public class PackageManagerTests extends AndroidTestCase { * The app is then re-installed on internal storage. The sdcard is mounted * and verified that the re-installation on internal storage takes precedence. */ - @MediumTest + @LargeTest public void testInstallSdcardStaleContainerReinstall() { - boolean origMediaState = getMediaState(); + boolean origMediaState = checkMediaState(Environment.MEDIA_MOUNTED); try { // Mount media first mountMedia(); @@ -2386,7 +2404,6 @@ public class PackageManagerTests extends AndroidTestCase { } else { unmountMedia(); } - } } /* @@ -2429,7 +2446,7 @@ public class PackageManagerTests extends AndroidTestCase { * Test that an app signed with two certificates can be upgraded by the * same app signed with two certificates. */ - @MediumTest + @LargeTest public void testReplaceMatchAllCerts() { replaceCerts(APP1_CERT1_CERT2, APP1_CERT1_CERT2, true, false, -1); } @@ -2438,7 +2455,7 @@ public class PackageManagerTests extends AndroidTestCase { * Test that an app signed with two certificates cannot be upgraded * by an app signed with a different certificate. */ - @MediumTest + @LargeTest public void testReplaceMatchNoCerts1() { replaceCerts(APP1_CERT1_CERT2, APP1_CERT3, true, true, PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES); @@ -2447,7 +2464,7 @@ public class PackageManagerTests extends AndroidTestCase { * Test that an app signed with two certificates cannot be upgraded * by an app signed with a different certificate. */ - @MediumTest + @LargeTest public void testReplaceMatchNoCerts2() { replaceCerts(APP1_CERT1_CERT2, APP1_CERT3_CERT4, true, true, PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES); @@ -2456,7 +2473,7 @@ public class PackageManagerTests extends AndroidTestCase { * Test that an app signed with two certificates cannot be upgraded by * an app signed with a subset of initial certificates. */ - @MediumTest + @LargeTest public void testReplaceMatchSomeCerts1() { replaceCerts(APP1_CERT1_CERT2, APP1_CERT1, true, true, PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES); @@ -2465,7 +2482,7 @@ public class PackageManagerTests extends AndroidTestCase { * Test that an app signed with two certificates cannot be upgraded by * an app signed with the last certificate. */ - @MediumTest + @LargeTest public void testReplaceMatchSomeCerts2() { replaceCerts(APP1_CERT1_CERT2, APP1_CERT2, true, true, PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES); @@ -2474,7 +2491,7 @@ public class PackageManagerTests extends AndroidTestCase { * Test that an app signed with a certificate can be upgraded by app * signed with a superset of certificates. */ - @MediumTest + @LargeTest public void testReplaceMatchMoreCerts() { replaceCerts(APP1_CERT1, APP1_CERT1_CERT2, true, true, PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES); @@ -2484,7 +2501,7 @@ public class PackageManagerTests extends AndroidTestCase { * signed with a superset of certificates. Then verify that the an app * signed with the original set of certs cannot upgrade the new one. */ - @MediumTest + @LargeTest public void testReplaceMatchMoreCertsReplaceSomeCerts() { InstallParams ip = replaceCerts(APP1_CERT1, APP1_CERT1_CERT2, false, true, PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES); @@ -2508,37 +2525,37 @@ public class PackageManagerTests extends AndroidTestCase { private void checkSignatures(int apk1, int apk2, int expMatchResult) { checkSharedSignatures(apk1, apk2, true, false, -1, expMatchResult); } - @MediumTest + @LargeTest public void testCheckSignaturesAllMatch() { int apk1 = APP1_CERT1_CERT2; int apk2 = APP2_CERT1_CERT2; checkSignatures(apk1, apk2, PackageManager.SIGNATURE_MATCH); } - @MediumTest + @LargeTest public void testCheckSignaturesNoMatch() { int apk1 = APP1_CERT1; int apk2 = APP2_CERT2; checkSignatures(apk1, apk2, PackageManager.SIGNATURE_NO_MATCH); } - @MediumTest + @LargeTest public void testCheckSignaturesSomeMatch1() { int apk1 = APP1_CERT1_CERT2; int apk2 = APP2_CERT1; checkSignatures(apk1, apk2, PackageManager.SIGNATURE_NO_MATCH); } - @MediumTest + @LargeTest public void testCheckSignaturesSomeMatch2() { int apk1 = APP1_CERT1_CERT2; int apk2 = APP2_CERT2; checkSignatures(apk1, apk2, PackageManager.SIGNATURE_NO_MATCH); } - @MediumTest + @LargeTest public void testCheckSignaturesMoreMatch() { int apk1 = APP1_CERT1; int apk2 = APP2_CERT1_CERT2; checkSignatures(apk1, apk2, PackageManager.SIGNATURE_NO_MATCH); } - @MediumTest + @LargeTest public void testCheckSignaturesUnknown() { int apk1 = APP1_CERT1_CERT2; int apk2 = APP2_CERT1_CERT2; @@ -2567,7 +2584,7 @@ public class PackageManagerTests extends AndroidTestCase { } } } - @MediumTest + @LargeTest public void testInstallNoCertificates() { int apk1 = APP1_UNSIGNED; String apk1Name = "install1.apk"; @@ -2620,7 +2637,7 @@ public class PackageManagerTests extends AndroidTestCase { } } } - @MediumTest + @LargeTest public void testCheckSignaturesSharedAllMatch() { int apk1 = SHARED1_CERT1_CERT2; int apk2 = SHARED2_CERT1_CERT2; @@ -2629,7 +2646,7 @@ public class PackageManagerTests extends AndroidTestCase { int expMatchResult = PackageManager.SIGNATURE_MATCH; checkSharedSignatures(apk1, apk2, true, fail, retCode, expMatchResult); } - @MediumTest + @LargeTest public void testCheckSignaturesSharedNoMatch() { int apk1 = SHARED1_CERT1; int apk2 = SHARED2_CERT2; @@ -2641,7 +2658,7 @@ public class PackageManagerTests extends AndroidTestCase { /* * Test that an app signed with cert1 and cert2 cannot be replaced when signed with cert1 alone. */ - @MediumTest + @LargeTest public void testCheckSignaturesSharedSomeMatch1() { int apk1 = SHARED1_CERT1_CERT2; int apk2 = SHARED2_CERT1; @@ -2653,7 +2670,7 @@ public class PackageManagerTests extends AndroidTestCase { /* * Test that an app signed with cert1 and cert2 cannot be replaced when signed with cert2 alone. */ - @MediumTest + @LargeTest public void testCheckSignaturesSharedSomeMatch2() { int apk1 = SHARED1_CERT1_CERT2; int apk2 = SHARED2_CERT2; @@ -2662,7 +2679,7 @@ public class PackageManagerTests extends AndroidTestCase { int expMatchResult = -1; checkSharedSignatures(apk1, apk2, true, fail, retCode, expMatchResult); } - @MediumTest + @LargeTest public void testCheckSignaturesSharedUnknown() { int apk1 = SHARED1_CERT1_CERT2; int apk2 = SHARED2_CERT1_CERT2; @@ -2688,7 +2705,7 @@ public class PackageManagerTests extends AndroidTestCase { } } - @MediumTest + @LargeTest public void testReplaceFirstSharedMatchAllCerts() { int apk1 = SHARED1_CERT1; int apk2 = SHARED2_CERT1; @@ -2696,7 +2713,7 @@ public class PackageManagerTests extends AndroidTestCase { checkSignatures(apk1, apk2, PackageManager.SIGNATURE_MATCH); replaceCerts(apk1, rapk1, true, false, -1); } - @MediumTest + @LargeTest public void testReplaceSecondSharedMatchAllCerts() { int apk1 = SHARED1_CERT1; int apk2 = SHARED2_CERT1; @@ -2704,7 +2721,7 @@ public class PackageManagerTests extends AndroidTestCase { checkSignatures(apk1, apk2, PackageManager.SIGNATURE_MATCH); replaceCerts(apk2, rapk2, true, false, -1); } - @MediumTest + @LargeTest public void testReplaceFirstSharedMatchSomeCerts() { int apk1 = SHARED1_CERT1_CERT2; int apk2 = SHARED2_CERT1_CERT2; @@ -2715,7 +2732,7 @@ public class PackageManagerTests extends AndroidTestCase { installFromRawResource("install.apk", rapk1, PackageManager.INSTALL_REPLACE_EXISTING, true, fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); } - @MediumTest + @LargeTest public void testReplaceSecondSharedMatchSomeCerts() { int apk1 = SHARED1_CERT1_CERT2; int apk2 = SHARED2_CERT1_CERT2; @@ -2726,7 +2743,7 @@ public class PackageManagerTests extends AndroidTestCase { installFromRawResource("install.apk", rapk2, PackageManager.INSTALL_REPLACE_EXISTING, true, fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); } - @MediumTest + @LargeTest public void testReplaceFirstSharedMatchNoCerts() { int apk1 = SHARED1_CERT1; int apk2 = SHARED2_CERT1; @@ -2737,7 +2754,7 @@ public class PackageManagerTests extends AndroidTestCase { installFromRawResource("install.apk", rapk1, PackageManager.INSTALL_REPLACE_EXISTING, true, fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); } - @MediumTest + @LargeTest public void testReplaceSecondSharedMatchNoCerts() { int apk1 = SHARED1_CERT1; int apk2 = SHARED2_CERT1; @@ -2748,7 +2765,7 @@ public class PackageManagerTests extends AndroidTestCase { installFromRawResource("install.apk", rapk2, PackageManager.INSTALL_REPLACE_EXISTING, true, fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); } - @MediumTest + @LargeTest public void testReplaceFirstSharedMatchMoreCerts() { int apk1 = SHARED1_CERT1; int apk2 = SHARED2_CERT1; @@ -2759,7 +2776,7 @@ public class PackageManagerTests extends AndroidTestCase { installFromRawResource("install.apk", rapk1, PackageManager.INSTALL_REPLACE_EXISTING, true, fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); } - @MediumTest + @LargeTest public void testReplaceSecondSharedMatchMoreCerts() { int apk1 = SHARED1_CERT1; int apk2 = SHARED2_CERT1; diff --git a/core/tests/coretests/src/android/database/sqlite/AbstractJDBCDriverTest.java b/core/tests/coretests/src/android/database/sqlite/AbstractJDBCDriverTest.java deleted file mode 100644 index 19c7bcb03cc9..000000000000 --- a/core/tests/coretests/src/android/database/sqlite/AbstractJDBCDriverTest.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2008 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.database.sqlite; - -import java.io.File; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import junit.framework.TestCase; -import android.test.suitebuilder.annotation.MediumTest; - -/** - * Tests for the most commonly used methods of sql like creating a connection, - * inserting, selecting, updating. - */ -public abstract class AbstractJDBCDriverTest extends TestCase { - - @MediumTest - public void testJDBCDriver() throws Exception { - Connection firstConnection = null; - Connection secondConnection = null; - File dbFile = getDbFile(); - String connectionURL = getConnectionURL(); - Statement firstStmt = null; - Statement secondStmt = null; - try { - Class.forName(getJDBCDriverClassName()); - firstConnection = DriverManager.getConnection(connectionURL); - secondConnection = DriverManager.getConnection(connectionURL); - - String[] ones = {"hello!", "goodbye"}; - short[] twos = {10, 20}; - String[] onesUpdated = new String[ones.length]; - for (int i = 0; i < ones.length; i++) { - onesUpdated[i] = ones[i] + twos[i]; - } - firstStmt = firstConnection.createStatement(); - firstStmt.execute("create table tbl1(one varchar(10), two smallint)"); - secondStmt = secondConnection.createStatement(); - - autoCommitInsertSelectTest(firstStmt, ones, twos); - updateSelectCommitSelectTest(firstStmt, secondStmt, ones, onesUpdated, twos); - updateSelectRollbackSelectTest(firstStmt, secondStmt, onesUpdated, ones, twos); - } finally { - closeConnections(firstConnection, secondConnection, dbFile, firstStmt, secondStmt); - } - } - - protected abstract String getJDBCDriverClassName(); - protected abstract String getConnectionURL(); - protected abstract File getDbFile(); - - private void closeConnections(Connection firstConnection, Connection secondConnection, - File dbFile, Statement firstStmt, Statement secondStmt) { - String failText = null; - try { - if (firstStmt != null) { - firstStmt.execute("drop table tbl1"); - } - } catch (SQLException e) { - failText = e.getLocalizedMessage(); - } - try { - if (firstStmt != null) { - firstStmt.close(); - } - } catch (SQLException e) { - failText = e.getLocalizedMessage(); - } - try { - if (firstConnection != null) { - firstConnection.close(); - } - } catch (SQLException e) { - failText = e.getLocalizedMessage(); - } - try { - if (secondStmt != null) { - secondStmt.close(); - } - } catch (SQLException e) { - failText = e.getLocalizedMessage(); - } - try { - if (secondConnection != null) { - secondConnection.close(); - } - } catch (SQLException e) { - failText = e.getLocalizedMessage(); - } - dbFile.delete(); - assertNull(failText, failText); - } - - /** - * Inserts the values from 'ones' with the values from 'twos' into 'tbl1' - * @param stmt the statement to use for the inserts. - * @param ones the string values to insert into tbl1. - * @param twos the corresponding numerical values to insert into tbl1. - * @throws SQLException in case of a problem during insert. - */ - private void autoCommitInsertSelectTest(Statement stmt, String[] ones, - short[] twos) throws SQLException { - for (int i = 0; i < ones.length; i++) { - stmt.execute("insert into tbl1 values('" + ones[i] + "'," + twos[i] - + ")"); - } - assertAllFromTbl1(stmt, ones, twos); - } - - /** - * Asserts that all values that where added to tbl1 are actually in tbl1. - * @param stmt the statement to use for the select. - * @param ones the string values that where added. - * @param twos the numerical values that where added. - * @throws SQLException in case of a problem during select. - */ - private void assertAllFromTbl1(Statement stmt, String[] ones, short[] twos) - throws SQLException { - ResultSet rs = stmt.executeQuery("select * from tbl1"); - int i = 0; - for (; rs.next(); i++) { - assertTrue(i < ones.length); - assertEquals(ones[i], rs.getString("one")); - assertEquals(twos[i], rs.getShort("two")); - } - assertEquals(i, ones.length); - } - - /** - * Tests the results of an update followed bz a select on a diffrent statement. - * After that the first statement commits its update. and now the second - * statement should also be able to see the changed values in a select. - * @param firstStmt the statement to use for the update and commit. - * @param secondStmt the statement that should be used to check if the commit works - * @param ones the original string values. - * @param onesUpdated the updated string values. - * @param twos the numerical values. - * @throws SQLException in case of a problem during any of the executed commands. - */ - private void updateSelectCommitSelectTest(Statement firstStmt, - Statement secondStmt, String[] ones, String[] onesUpdated, - short[] twos) throws SQLException { - firstStmt.getConnection().setAutoCommit(false); - try { - updateOnes(firstStmt, onesUpdated, twos); - assertAllFromTbl1(secondStmt, ones, twos); - firstStmt.getConnection().commit(); - assertAllFromTbl1(secondStmt, onesUpdated, twos); - } finally { - firstStmt.getConnection().setAutoCommit(true); - } - } - - /** - * Tests if an update followed by a select works. After that a rollback will - * be made and again a select should show that the rollback worked. - * @param firstStmt the statement to use for the update and the rollback - * @param secondStmt the statement to use for checking if the rollback worked as intended. - * @param ones the original string values. - * @param onesUpdated the updated string values. - * @param twos the nomerical values. - * @throws SQLException in case of a problem during any command. - */ - private void updateSelectRollbackSelectTest(Statement firstStmt, - Statement secondStmt, String[] ones, String[] onesUpdated, - short[] twos) throws SQLException { - firstStmt.getConnection().setAutoCommit(false); - try { - updateOnes(firstStmt, onesUpdated, twos); - assertAllFromTbl1(secondStmt, ones, twos); - firstStmt.getConnection().rollback(); - assertAllFromTbl1(secondStmt, ones, twos); - } finally { - firstStmt.getConnection().setAutoCommit(true); - } - } - - /** - * updates the sring values. the original values are stored in 'ones' - * and the updated values in 'ones_updated' - * @param stmt the statement to use for the update. - * @param onesUpdated the new string values. - * @param twos the numerical values. - * @throws SQLException in case of a problem during update. - */ - private void updateOnes(Statement stmt, String[] onesUpdated, short[] twos) - throws SQLException { - for (int i = 0; i < onesUpdated.length; i++) { - stmt.execute("UPDATE tbl1 SET one = '" + onesUpdated[i] - + "' WHERE two = " + twos[i]); - } - } -} diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteJDBCDriverTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteJDBCDriverTest.java deleted file mode 100644 index 8e677a5b8702..000000000000 --- a/core/tests/coretests/src/android/database/sqlite/SQLiteJDBCDriverTest.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2008 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.database.sqlite; - -import java.io.File; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.sql.Statement; -import android.test.suitebuilder.annotation.MediumTest; - -/** - * Minimal test for JDBC driver - */ -public class SQLiteJDBCDriverTest extends AbstractJDBCDriverTest { - - private File dbFile; - - @Override - protected void setUp() throws Exception { - super.setUp(); - dbFile = File.createTempFile("sqliteTestDB", null); - } - - @Override - protected void tearDown() throws Exception { - if(dbFile != null) { - dbFile.delete(); - } - super.tearDown(); - } - - @Override - protected String getConnectionURL() { - return "jdbc:sqlite:/" + dbFile; - } - - @Override - protected File getDbFile() { - return dbFile; - } - - @Override - protected String getJDBCDriverClassName() { - return "SQLite.JDBCDriver"; - } - - // Regression test for (Noser) #255: PreparedStatement.executeUpdate results - // in VM crashing with SIGABRT. - @MediumTest - public void test_connection3() throws Exception { - PreparedStatement prst = null; - Statement st = null; - Connection conn = null; - try { - Class.forName("SQLite.JDBCDriver").newInstance(); - if (dbFile.exists()) { - dbFile.delete(); - } - conn = DriverManager.getConnection("jdbc:sqlite:/" - + dbFile.getPath()); - assertNotNull(conn); - - // create table - st = conn.createStatement(); - String sql = "CREATE TABLE zoo (ZID INTEGER NOT NULL, family VARCHAR (20) NOT NULL, name VARCHAR (20) NOT NULL, PRIMARY KEY(ZID) )"; - st.executeUpdate(sql); - - String update = "update zoo set family = ? where name = ?;"; - prst = conn.prepareStatement(update); - prst.setString(1, "cat"); - prst.setString(2, "Yasha"); - // st = conn.createStatement(); - // st.execute("select * from zoo where family = 'cat'"); - // ResultSet rs = st.getResultSet(); - // assertEquals(0, getCount(rs)); - prst.executeUpdate(); - // st.execute("select * from zoo where family = 'cat'"); - // ResultSet rs1 = st.getResultSet(); - // assertEquals(1, getCount(rs1)); - try { - prst = conn.prepareStatement(""); - prst.execute(); - fail("SQLException is not thrown"); - } catch (SQLException e) { - // expected - } - - try { - conn.prepareStatement(null); - fail("NPE is not thrown"); - } catch (Exception e) { - // expected - } - try { - st = conn.createStatement(); - st.execute("drop table if exists zoo"); - - } catch (SQLException e) { - fail("Couldn't drop table: " + e.getMessage()); - } finally { - try { - st.close(); - conn.close(); - } catch (SQLException ee) { - } - } - } finally { - try { - if (prst != null) { - prst.close(); - } - if (st != null) { - st.close(); - } - } catch (SQLException ee) { - } - } - - } - -} diff --git a/core/tests/coretests/src/android/net/DownloadManagerBaseTest.java b/core/tests/coretests/src/android/net/DownloadManagerBaseTest.java new file mode 100644 index 000000000000..ee0f5f14516d --- /dev/null +++ b/core/tests/coretests/src/android/net/DownloadManagerBaseTest.java @@ -0,0 +1,888 @@ +/* + * Copyright (C) 2010 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.net; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.database.Cursor; +import android.net.ConnectivityManager; +import android.net.DownloadManager; +import android.net.NetworkInfo; +import android.net.DownloadManager.Query; +import android.net.DownloadManager.Request; +import android.net.wifi.WifiManager; +import android.os.Environment; +import android.os.ParcelFileDescriptor; +import android.os.SystemClock; +import android.os.ParcelFileDescriptor.AutoCloseInputStream; +import android.provider.Settings; +import android.test.InstrumentationTestCase; +import android.util.Log; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URL; +import java.util.concurrent.TimeoutException; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.Vector; + +import junit.framework.AssertionFailedError; + +import coretestutils.http.MockResponse; +import coretestutils.http.MockWebServer; + +/** + * Base class for Instrumented tests for the Download Manager. + */ +public class DownloadManagerBaseTest extends InstrumentationTestCase { + + protected DownloadManager mDownloadManager = null; + protected MockWebServer mServer = null; + protected String mFileType = "text/plain"; + protected Context mContext = null; + protected static final int DEFAULT_FILE_SIZE = 130 * 1024; // 130kb + protected static final int FILE_BLOCK_READ_SIZE = 1024 * 1024; + + protected static final String LOG_TAG = "android.net.DownloadManagerBaseTest"; + protected static final int HTTP_OK = 200; + protected static final int HTTP_PARTIAL_CONTENT = 206; + protected static final int HTTP_NOT_FOUND = 404; + protected static final int HTTP_SERVICE_UNAVAILABLE = 503; + protected String DEFAULT_FILENAME = "somefile.txt"; + + protected static final int DEFAULT_MAX_WAIT_TIME = 2 * 60 * 1000; // 2 minutes + protected static final int DEFAULT_WAIT_POLL_TIME = 5 * 1000; // 5 seconds + + protected static final int WAIT_FOR_DOWNLOAD_POLL_TIME = 1 * 1000; // 1 second + protected static final int MAX_WAIT_FOR_DOWNLOAD_TIME = 5 * 60 * 1000; // 5 minutes + + // Just a few popular file types used to return from a download + protected enum DownloadFileType { + PLAINTEXT, + APK, + GIF, + GARBAGE, + UNRECOGNIZED, + ZIP + } + + protected enum DataType { + TEXT, + BINARY + } + + public static class LoggingRng extends Random { + + /** + * Constructor + * + * Creates RNG with self-generated seed value. + */ + public LoggingRng() { + this(SystemClock.uptimeMillis()); + } + + /** + * Constructor + * + * Creats RNG with given initial seed value + + * @param seed The initial seed value + */ + public LoggingRng(long seed) { + super(seed); + Log.i(LOG_TAG, "Seeding RNG with value: " + seed); + } + } + + public static class MultipleDownloadsCompletedReceiver extends BroadcastReceiver { + private volatile int mNumDownloadsCompleted = 0; + + /** + * {@inheritDoc} + */ + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equalsIgnoreCase(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) { + ++mNumDownloadsCompleted; + Log.i(LOG_TAG, "MultipleDownloadsCompletedReceiver got intent: " + + intent.getAction() + " --> total count: " + mNumDownloadsCompleted); + } + } + + /** + * Gets the number of times the {@link #onReceive} callback has been called for the + * {@link DownloadManager.ACTION_DOWNLOAD_COMPLETED} action, indicating the number of + * downloads completed thus far. + * + * @return the number of downloads completed so far. + */ + public int numDownloadsCompleted() { + return mNumDownloadsCompleted; + } + } + + public static class WiFiChangedReceiver extends BroadcastReceiver { + private Context mContext = null; + + /** + * Constructor + * + * Sets the current state of WiFi. + * + * @param context The current app {@link Context}. + */ + public WiFiChangedReceiver(Context context) { + mContext = context; + } + + /** + * {@inheritDoc} + */ + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equalsIgnoreCase(ConnectivityManager.CONNECTIVITY_ACTION)) { + Log.i(LOG_TAG, "ConnectivityManager state change: " + intent.getAction()); + synchronized (this) { + this.notify(); + } + } + } + + /** + * Gets the current state of WiFi. + * + * @return Returns true if WiFi is on, false otherwise. + */ + public boolean getWiFiIsOn() { + ConnectivityManager connManager = (ConnectivityManager)mContext.getSystemService( + Context.CONNECTIVITY_SERVICE); + NetworkInfo info = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + return info.isConnected(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void setUp() throws Exception { + mContext = getInstrumentation().getContext(); + mDownloadManager = (DownloadManager)mContext.getSystemService(Context.DOWNLOAD_SERVICE); + mServer = new MockWebServer(); + // Note: callers overriding this should call mServer.play() with the desired port # + } + + /** + * Helper to enqueue a response from the MockWebServer. + * + * @param status The HTTP status code to return for this response + * @param body The body to return in this response + * @return Returns the mock web server response that was queued (which can be modified) + */ + protected MockResponse enqueueResponse(int status, byte[] body) { + return doEnqueueResponse(status).setBody(body); + + } + + /** + * Helper to enqueue a response from the MockWebServer. + * + * @param status The HTTP status code to return for this response + * @param bodyFile The body to return in this response + * @return Returns the mock web server response that was queued (which can be modified) + */ + protected MockResponse enqueueResponse(int status, File bodyFile) { + return doEnqueueResponse(status).setBody(bodyFile); + } + + /** + * Helper for enqueue'ing a response from the MockWebServer. + * + * @param status The HTTP status code to return for this response + * @return Returns the mock web server response that was queued (which can be modified) + */ + protected MockResponse doEnqueueResponse(int status) { + MockResponse response = new MockResponse().setResponseCode(status); + response.addHeader("Content-type", mFileType); + mServer.enqueue(response); + return response; + } + + /** + * Helper to generate a random blob of bytes. + * + * @param size The size of the data to generate + * @param type The type of data to generate: currently, one of {@link DataType.TEXT} or + * {@link DataType.BINARY}. + * @return The random data that is generated. + */ + protected byte[] generateData(int size, DataType type) { + return generateData(size, type, null); + } + + /** + * Helper to generate a random blob of bytes using a given RNG. + * + * @param size The size of the data to generate + * @param type The type of data to generate: currently, one of {@link DataType.TEXT} or + * {@link DataType.BINARY}. + * @param rng (optional) The RNG to use; pass null to use + * @return The random data that is generated. + */ + protected byte[] generateData(int size, DataType type, Random rng) { + int min = Byte.MIN_VALUE; + int max = Byte.MAX_VALUE; + + // Only use chars in the HTTP ASCII printable character range for Text + if (type == DataType.TEXT) { + min = 32; + max = 126; + } + byte[] result = new byte[size]; + Log.i(LOG_TAG, "Generating data of size: " + size); + + if (rng == null) { + rng = new LoggingRng(); + } + + for (int i = 0; i < size; ++i) { + result[i] = (byte) (min + rng.nextInt(max - min + 1)); + } + return result; + } + + /** + * Helper to verify the size of a file. + * + * @param pfd The input file to compare the size of + * @param size The expected size of the file + */ + protected void verifyFileSize(ParcelFileDescriptor pfd, long size) { + assertEquals(pfd.getStatSize(), size); + } + + /** + * Helper to verify the contents of a downloaded file versus a byte[]. + * + * @param actual The file of whose contents to verify + * @param expected The data we expect to find in the aforementioned file + * @throws IOException if there was a problem reading from the file + */ + protected void verifyFileContents(ParcelFileDescriptor actual, byte[] expected) + throws IOException { + AutoCloseInputStream input = new ParcelFileDescriptor.AutoCloseInputStream(actual); + long fileSize = actual.getStatSize(); + + assertTrue(fileSize <= Integer.MAX_VALUE); + assertEquals(expected.length, fileSize); + + byte[] actualData = new byte[expected.length]; + assertEquals(input.read(actualData), fileSize); + compareByteArrays(actualData, expected); + } + + /** + * Helper to compare 2 byte arrays. + * + * @param actual The array whose data we want to verify + * @param expected The array of data we expect to see + */ + protected void compareByteArrays(byte[] actual, byte[] expected) { + assertEquals(actual.length, expected.length); + int length = actual.length; + for (int i = 0; i < length; ++i) { + // assert has a bit of overhead, so only do the assert when the values are not the same + if (actual[i] != expected[i]) { + fail("Byte arrays are not equal."); + } + } + } + + /** + * Verifies the contents of a downloaded file versus the contents of a File. + * + * @param pfd The file whose data we want to verify + * @param file The file containing the data we expect to see in the aforementioned file + * @throws IOException If there was a problem reading either of the two files + */ + protected void verifyFileContents(ParcelFileDescriptor pfd, File file) throws IOException { + byte[] actual = new byte[FILE_BLOCK_READ_SIZE]; + byte[] expected = new byte[FILE_BLOCK_READ_SIZE]; + + AutoCloseInputStream input = new ParcelFileDescriptor.AutoCloseInputStream(pfd); + + assertEquals(file.length(), pfd.getStatSize()); + + DataInputStream inFile = new DataInputStream(new FileInputStream(file)); + int actualRead = 0; + int expectedRead = 0; + + while (((actualRead = input.read(actual)) != -1) && + ((expectedRead = inFile.read(expected)) != -1)) { + assertEquals(actualRead, expectedRead); + compareByteArrays(actual, expected); + } + } + + /** + * Sets the MIME type of file that will be served from the mock server + * + * @param type The MIME type to return from the server + */ + protected void setServerMimeType(DownloadFileType type) { + mFileType = getMimeMapping(type); + } + + /** + * Gets the MIME content string for a given type + * + * @param type The MIME type to return + * @return the String representation of that MIME content type + */ + protected String getMimeMapping(DownloadFileType type) { + switch (type) { + case APK: + return "application/vnd.android.package-archive"; + case GIF: + return "image/gif"; + case ZIP: + return "application/x-zip-compressed"; + case GARBAGE: + return "zip\\pidy/doo/da"; + case UNRECOGNIZED: + return "application/new.undefined.type.of.app"; + } + return "text/plain"; + } + + /** + * Gets the Uri that should be used to access the mock server + * + * @param filename The name of the file to try to retrieve from the mock server + * @return the Uri to use for access the file on the mock server + */ + protected Uri getServerUri(String filename) throws Exception { + URL url = mServer.getUrl("/" + filename); + return Uri.parse(url.toString()); + } + + /** + * Gets the Uri that should be used to access the mock server + * + * @param filename The name of the file to try to retrieve from the mock server + * @return the Uri to use for access the file on the mock server + */ + protected void logDBColumnData(Cursor cursor, String column) { + int index = cursor.getColumnIndex(column); + Log.i(LOG_TAG, "columnName: " + column); + Log.i(LOG_TAG, "columnValue: " + cursor.getString(index)); + } + + /** + * Helper to create and register a new MultipleDownloadCompletedReciever + * + * This is used to track many simultaneous downloads by keeping count of all the downloads + * that have completed. + * + * @return A new receiver that records and can be queried on how many downloads have completed. + */ + protected MultipleDownloadsCompletedReceiver registerNewMultipleDownloadsReceiver() { + MultipleDownloadsCompletedReceiver receiver = new MultipleDownloadsCompletedReceiver(); + mContext.registerReceiver(receiver, new IntentFilter( + DownloadManager.ACTION_DOWNLOAD_COMPLETE)); + return receiver; + } + + /** + * Helper to verify a standard single-file download from the mock server, and clean up after + * verification + * + * Note that this also calls the Download manager's remove, which cleans up the file from cache. + * + * @param requestId The id of the download to remove + * @param fileData The data to verify the file contains + */ + protected void verifyAndCleanupSingleFileDownload(long requestId, byte[] fileData) + throws Exception { + int fileSize = fileData.length; + ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(requestId); + Cursor cursor = mDownloadManager.query(new Query().setFilterById(requestId)); + + try { + assertEquals(1, cursor.getCount()); + assertTrue(cursor.moveToFirst()); + + mServer.checkForExceptions(); + + verifyFileSize(pfd, fileSize); + verifyFileContents(pfd, fileData); + } finally { + pfd.close(); + cursor.close(); + mDownloadManager.remove(requestId); + } + } + + /** + * Enables or disables WiFi. + * + * Note: Needs the following permissions: + * android.permission.ACCESS_WIFI_STATE + * android.permission.CHANGE_WIFI_STATE + * @param enable true if it should be enabled, false if it should be disabled + */ + protected void setWiFiStateOn(boolean enable) throws Exception { + WifiManager manager = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE); + + manager.setWifiEnabled(enable); + + String timeoutMessage = "Timed out waiting for Wifi to be " + + (enable ? "enabled!" : "disabled!"); + + WiFiChangedReceiver receiver = new WiFiChangedReceiver(mContext); + mContext.registerReceiver(receiver, new IntentFilter( + ConnectivityManager.CONNECTIVITY_ACTION)); + + synchronized (receiver) { + long timeoutTime = SystemClock.elapsedRealtime() + DEFAULT_MAX_WAIT_TIME; + boolean timedOut = false; + + while (receiver.getWiFiIsOn() != enable && !timedOut) { + try { + receiver.wait(DEFAULT_MAX_WAIT_TIME); + + if (SystemClock.elapsedRealtime() > timeoutTime) { + timedOut = true; + } + } + catch (InterruptedException e) { + // ignore InterruptedExceptions + } + } + if (timedOut) { + fail(timeoutMessage); + } + } + assertEquals(enable, receiver.getWiFiIsOn()); + } + + /** + * Helper to enables or disables airplane mode. If successful, it also broadcasts an intent + * indicating that the mode has changed. + * + * Note: Needs the following permission: + * android.permission.WRITE_SETTINGS + * @param enable true if airplane mode should be ON, false if it should be OFF + */ + protected void setAirplaneModeOn(boolean enable) throws Exception { + int state = enable ? 1 : 0; + + // Change the system setting + Settings.System.putInt(mContext.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, + state); + + String timeoutMessage = "Timed out waiting for airplane mode to be " + + (enable ? "enabled!" : "disabled!"); + + // wait for airplane mode to change state + int currentWaitTime = 0; + while (Settings.System.getInt(mContext.getContentResolver(), + Settings.System.AIRPLANE_MODE_ON, -1) != state) { + timeoutWait(currentWaitTime, DEFAULT_WAIT_POLL_TIME, DEFAULT_MAX_WAIT_TIME, + timeoutMessage); + } + + // Post the intent + Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); + intent.putExtra("state", true); + mContext.sendBroadcast(intent); + } + + /** + * Helper to create a large file of random data on the SD card. + * + * @param filename (optional) The name of the file to create on the SD card; pass in null to + * use a default temp filename. + * @param type The type of file to create + * @param subdirectory If not null, the subdirectory under the SD card where the file should go + * @return The File that was created + * @throws IOException if there was an error while creating the file. + */ + protected File createFileOnSD(String filename, long fileSize, DataType type, + String subdirectory) throws IOException { + + // Build up the file path and name + String sdPath = Environment.getExternalStorageDirectory().getPath(); + StringBuilder fullPath = new StringBuilder(sdPath); + if (subdirectory != null) { + fullPath.append(File.separatorChar).append(subdirectory); + } + + File file = null; + if (filename == null) { + file = File.createTempFile("DMTEST_", null, new File(fullPath.toString())); + } + else { + fullPath.append(File.separatorChar).append(filename); + file = new File(fullPath.toString()); + file.createNewFile(); + } + + // Fill the file with random data + DataOutputStream output = new DataOutputStream(new FileOutputStream(file)); + final int CHUNK_SIZE = 1000000; // copy random data in 1000000-char chunks + long remaining = fileSize; + int nextChunkSize = CHUNK_SIZE; + byte[] randomData = null; + Random rng = new LoggingRng(); + + try { + while (remaining > 0) { + if (remaining < CHUNK_SIZE) { + nextChunkSize = (int)remaining; + remaining = 0; + } + else { + remaining -= CHUNK_SIZE; + } + + randomData = generateData(nextChunkSize, type, rng); + output.write(randomData); + } + } catch (IOException e) { + Log.e(LOG_TAG, "Error writing to file " + file.getAbsolutePath()); + file.delete(); + throw e; + } finally { + output.close(); + } + return file; + } + + /** + * Helper to wait for a particular download to finish, or else a timeout to occur + * + * @param id The download id to query on (wait for) + */ + protected void waitForDownloadOrTimeout(long id) throws TimeoutException, + InterruptedException { + waitForDownloadOrTimeout(id, WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME); + } + + /** + * Helper to wait for a particular download to finish, or else a timeout to occur + * + * @param id The download id to query on (wait for) + * @param poll The amount of time to wait + * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete + */ + protected void waitForDownloadOrTimeout(long id, long poll, long timeoutMillis) + throws TimeoutException, InterruptedException { + doWaitForDownloadsOrTimeout(new Query().setFilterById(id), poll, timeoutMillis); + } + + /** + * Helper to wait for all downloads to finish, or else a specified timeout to occur + * + * @param poll The amount of time to wait + * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete + */ + protected void waitForDownloadsOrTimeout(long poll, long timeoutMillis) throws TimeoutException, + InterruptedException { + doWaitForDownloadsOrTimeout(new Query(), poll, timeoutMillis); + } + + /** + * Helper to wait for all downloads to finish, or else a timeout to occur, but does not throw + * + * @param id The id of the download to query against + * @param poll The amount of time to wait + * @param timeoutMillis The max time (in ms) to wait for the download(s) to complete + * @return true if download completed successfully (didn't timeout), false otherwise + */ + protected boolean waitForDownloadOrTimeoutNoThrow(long id, long poll, long timeoutMillis) { + try { + doWaitForDownloadsOrTimeout(new Query().setFilterById(id), poll, timeoutMillis); + } catch (TimeoutException e) { + return false; + } + return true; + } + + /** + * Helper function to synchronously wait, or timeout if the maximum threshold has been exceeded. + * + * @param currentTotalWaitTime The total time waited so far + * @param poll The amount of time to wait + * @param maxTimeoutMillis The total wait time threshold; if we've waited more than this long, + * we timeout and fail + * @param timedOutMessage The message to display in the failure message if we timeout + * @return The new total amount of time we've waited so far + * @throws TimeoutException if timed out waiting for SD card to mount + */ + protected int timeoutWait(int currentTotalWaitTime, long poll, long maxTimeoutMillis, + String timedOutMessage) throws TimeoutException { + long now = SystemClock.elapsedRealtime(); + long end = now + poll; + + // if we get InterruptedException's, ignore them and just keep sleeping + while (now < end) { + try { + Thread.sleep(end - now); + } catch (InterruptedException e) { + // ignore interrupted exceptions + } + now = SystemClock.elapsedRealtime(); + } + + currentTotalWaitTime += poll; + if (currentTotalWaitTime > maxTimeoutMillis) { + throw new TimeoutException(timedOutMessage); + } + return currentTotalWaitTime; + } + + /** + * Helper to wait for all downloads to finish, or else a timeout to occur + * + * @param query The query to pass to the download manager + * @param poll The poll time to wait between checks + * @param timeoutMillis The max amount of time (in ms) to wait for the download(s) to complete + */ + protected void doWaitForDownloadsOrTimeout(Query query, long poll, long timeoutMillis) + throws TimeoutException { + int currentWaitTime = 0; + while (true) { + query.setFilterByStatus(DownloadManager.STATUS_PENDING | DownloadManager.STATUS_PAUSED + | DownloadManager.STATUS_RUNNING); + Cursor cursor = mDownloadManager.query(query); + + try { + // If we've finished the downloads then we're done + if (cursor.getCount() == 0) { + break; + } + currentWaitTime = timeoutWait(currentWaitTime, poll, timeoutMillis, + "Timed out waiting for all downloads to finish"); + } finally { + cursor.close(); + } + } + } + + /** + * Synchronously waits for external store to be mounted (eg: SD Card). + * + * @throws InterruptedException if interrupted + * @throws Exception if timed out waiting for SD card to mount + */ + protected void waitForExternalStoreMount() throws Exception { + String extStorageState = Environment.getExternalStorageState(); + int currentWaitTime = 0; + while (!extStorageState.equals(Environment.MEDIA_MOUNTED)) { + Log.i(LOG_TAG, "Waiting for SD card..."); + currentWaitTime = timeoutWait(currentWaitTime, DEFAULT_WAIT_POLL_TIME, + DEFAULT_MAX_WAIT_TIME, "Timed out waiting for SD Card to be ready!"); + extStorageState = Environment.getExternalStorageState(); + } + } + + /** + * Synchronously waits for a download to start. + * + * @param dlRequest the download request id used by Download Manager to track the download. + * @throws Exception if timed out while waiting for SD card to mount + */ + protected void waitForDownloadToStart(long dlRequest) throws Exception { + Cursor cursor = getCursor(dlRequest); + try { + int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS); + int value = cursor.getInt(columnIndex); + int currentWaitTime = 0; + + while (value != DownloadManager.STATUS_RUNNING && + (value != DownloadManager.STATUS_FAILED) && + (value != DownloadManager.STATUS_SUCCESSFUL)) { + Log.i(LOG_TAG, "Waiting for download to start..."); + currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME, + MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for download to start!"); + cursor.requery(); + assertTrue(cursor.moveToFirst()); + columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS); + value = cursor.getInt(columnIndex); + } + assertFalse("Download failed immediately after start", + value == DownloadManager.STATUS_FAILED); + } finally { + cursor.close(); + } + } + + /** + * Synchronously waits for a file to increase in size (such as to monitor that a download is + * progressing). + * + * @param file The file whose size to track. + * @throws Exception if timed out while waiting for the file to grow in size. + */ + protected void waitForFileToGrow(File file) throws Exception { + int currentWaitTime = 0; + + // File may not even exist yet, so wait until it does (or we timeout) + while (!file.exists()) { + Log.i(LOG_TAG, "Waiting for file to exist..."); + currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME, + MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be created."); + } + + // Get original file size... + long originalSize = file.length(); + + while (file.length() <= originalSize) { + Log.i(LOG_TAG, "Waiting for file to be written to..."); + currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME, + MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be written to."); + } + } + + /** + * Helper to remove all downloads that are registered with the DL Manager. + * + * Note: This gives us a clean slate b/c it includes downloads that are pending, running, + * paused, or have completed. + */ + protected void removeAllCurrentDownloads() { + Log.i(LOG_TAG, "Removing all current registered downloads..."); + Cursor cursor = mDownloadManager.query(new Query()); + try { + if (cursor.moveToFirst()) { + do { + int index = cursor.getColumnIndex(DownloadManager.COLUMN_ID); + long downloadId = cursor.getLong(index); + + mDownloadManager.remove(downloadId); + } while (cursor.moveToNext()); + } + } finally { + cursor.close(); + } + } + + /** + * Helper to perform a standard enqueue of data to the mock server. + * + * @param body The body to return in the response from the server + */ + protected long doStandardEnqueue(byte[] body) throws Exception { + // Prepare the mock server with a standard response + enqueueResponse(HTTP_OK, body); + return doCommonStandardEnqueue(); + } + + /** + * Helper to perform a standard enqueue of data to the mock server. + * + * @param body The body to return in the response from the server, contained in the file + */ + protected long doStandardEnqueue(File body) throws Exception { + // Prepare the mock server with a standard response + enqueueResponse(HTTP_OK, body); + return doCommonStandardEnqueue(); + } + + /** + * Helper to do the additional steps (setting title and Uri of default filename) when + * doing a standard enqueue request to the server. + */ + protected long doCommonStandardEnqueue() throws Exception { + Uri uri = getServerUri(DEFAULT_FILENAME); + Request request = new Request(uri); + request.setTitle(DEFAULT_FILENAME); + + long dlRequest = mDownloadManager.enqueue(request); + Log.i(LOG_TAG, "request ID: " + dlRequest); + return dlRequest; + } + + /** + * Helper to verify an int value in a Cursor + * + * @param cursor The cursor containing the query results + * @param columnName The name of the column to query + * @param expected The expected int value + */ + protected void verifyInt(Cursor cursor, String columnName, int expected) { + int index = cursor.getColumnIndex(columnName); + int actual = cursor.getInt(index); + assertEquals(expected, actual); + } + + /** + * Helper to verify a String value in a Cursor + * + * @param cursor The cursor containing the query results + * @param columnName The name of the column to query + * @param expected The expected String value + */ + protected void verifyString(Cursor cursor, String columnName, String expected) { + int index = cursor.getColumnIndex(columnName); + String actual = cursor.getString(index); + Log.i(LOG_TAG, ": " + actual); + assertEquals(expected, actual); + } + + /** + * Performs a query based on ID and returns a Cursor for the query. + * + * @param id The id of the download in DL Manager; pass -1 to query all downloads + * @return A cursor for the query results + */ + protected Cursor getCursor(long id) throws Exception { + Query query = new Query(); + if (id != -1) { + query.setFilterById(id); + } + + Cursor cursor = mDownloadManager.query(query); + int currentWaitTime = 0; + + try { + while (!cursor.moveToFirst()) { + Thread.sleep(DEFAULT_WAIT_POLL_TIME); + currentWaitTime += DEFAULT_WAIT_POLL_TIME; + if (currentWaitTime > DEFAULT_MAX_WAIT_TIME) { + fail("timed out waiting for a non-null query result"); + } + cursor.requery(); + } + } catch (Exception e) { + cursor.close(); + throw e; + } + return cursor; + } + +}
\ No newline at end of file diff --git a/core/tests/coretests/src/android/net/DownloadManagerIntegrationTest.java b/core/tests/coretests/src/android/net/DownloadManagerIntegrationTest.java new file mode 100644 index 000000000000..be3cbf729458 --- /dev/null +++ b/core/tests/coretests/src/android/net/DownloadManagerIntegrationTest.java @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2010 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.net; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.database.Cursor; +import android.net.DownloadManager.Query; +import android.net.DownloadManager.Request; +import android.net.DownloadManagerBaseTest.DataType; +import android.net.DownloadManagerBaseTest.MultipleDownloadsCompletedReceiver; +import android.net.wifi.WifiManager; +import android.os.Environment; +import android.os.ParcelFileDescriptor; +import android.os.SystemClock; +import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Log; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.net.URL; +import java.util.Random; + +import junit.framework.AssertionFailedError; + +import coretestutils.http.MockResponse; +import coretestutils.http.MockWebServer; + +/** + * Integration tests of the DownloadManager API. + */ +public class DownloadManagerIntegrationTest extends DownloadManagerBaseTest { + + private static String LOG_TAG = "android.net.DownloadManagerIntegrationTest"; + private static String PROHIBITED_DIRECTORY = "/system"; + protected MultipleDownloadsCompletedReceiver mReceiver = null; + + /** + * {@inheritDoc} + */ + @Override + public void setUp() throws Exception { + super.setUp(); + setWiFiStateOn(true); + mServer.play(); + removeAllCurrentDownloads(); + mReceiver = registerNewMultipleDownloadsReceiver(); + } + + /** + * {@inheritDoc} + */ + @Override + public void tearDown() throws Exception { + super.tearDown(); + setWiFiStateOn(true); + + if (mReceiver != null) { + mContext.unregisterReceiver(mReceiver); + mReceiver = null; + removeAllCurrentDownloads(); + } + } + + /** + * Helper that does the actual basic download verification. + */ + protected void doBasicDownload(byte[] blobData) throws Exception { + long dlRequest = doStandardEnqueue(blobData); + + // wait for the download to complete + waitForDownloadOrTimeout(dlRequest); + + verifyAndCleanupSingleFileDownload(dlRequest, blobData); + assertEquals(1, mReceiver.numDownloadsCompleted()); + } + + /** + * Test a basic download of a binary file 500k in size. + */ + @LargeTest + public void testBasicBinaryDownload() throws Exception { + int fileSize = 500 * 1024; // 500k + byte[] blobData = generateData(fileSize, DataType.BINARY); + + doBasicDownload(blobData); + } + + /** + * Tests the basic downloading of a text file 300000 bytes in size. + */ + @LargeTest + public void testBasicTextDownload() throws Exception { + int fileSize = 300000; + byte[] blobData = generateData(fileSize, DataType.TEXT); + + doBasicDownload(blobData); + } + + /** + * Tests when the server drops the connection after all headers (but before any data send). + */ + @LargeTest + public void testDropConnection_headers() throws Exception { + byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT); + + MockResponse response = enqueueResponse(HTTP_OK, blobData); + response.setCloseConnectionAfterHeader("content-length"); + long dlRequest = doCommonStandardEnqueue(); + + // Download will never complete when header is dropped + boolean success = waitForDownloadOrTimeoutNoThrow(dlRequest, DEFAULT_WAIT_POLL_TIME, + DEFAULT_MAX_WAIT_TIME); + + assertFalse(success); + } + + /** + * Tests that we get an error code when the server drops the connection during a download. + */ + @LargeTest + public void testServerDropConnection_body() throws Exception { + byte[] blobData = generateData(25000, DataType.TEXT); // file size = 25000 bytes + + MockResponse response = enqueueResponse(HTTP_OK, blobData); + response.setCloseConnectionAfterXBytes(15382); + long dlRequest = doCommonStandardEnqueue(); + waitForDownloadOrTimeout(dlRequest); + + Cursor cursor = getCursor(dlRequest); + try { + verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED); + verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE, + DownloadManager.ERROR_CANNOT_RESUME); + } finally { + cursor.close(); + } + // Even tho the server drops the connection, we should still get a completed notification + assertEquals(1, mReceiver.numDownloadsCompleted()); + } + + /** + * Attempts to download several files simultaneously + */ + @LargeTest + public void testMultipleDownloads() throws Exception { + // need to be sure all current downloads have stopped first + removeAllCurrentDownloads(); + int NUM_FILES = 50; + int MAX_FILE_SIZE = 500 * 1024; // 500 kb + + Random r = new LoggingRng(); + for (int i=0; i<NUM_FILES; ++i) { + int size = r.nextInt(MAX_FILE_SIZE); + byte[] blobData = generateData(size, DataType.TEXT); + + Uri uri = getServerUri(DEFAULT_FILENAME); + Request request = new Request(uri); + request.setTitle(String.format("%s--%d", DEFAULT_FILENAME, i)); + + // Prepare the mock server with a standard response + enqueueResponse(HTTP_OK, blobData); + + Log.i(LOG_TAG, "request: " + i); + mDownloadManager.enqueue(request); + } + + waitForDownloadsOrTimeout(WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME); + Cursor cursor = mDownloadManager.query(new Query()); + try { + assertEquals(NUM_FILES, cursor.getCount()); + + if (cursor.moveToFirst()) { + do { + int status = cursor.getInt(cursor.getColumnIndex( + DownloadManager.COLUMN_STATUS)); + String filename = cursor.getString(cursor.getColumnIndex( + DownloadManager.COLUMN_URI)); + String errorString = String.format( + "File %s failed to download successfully. Status code: %d", + filename, status); + assertEquals(errorString, DownloadManager.STATUS_SUCCESSFUL, status); + } while (cursor.moveToNext()); + } + + assertEquals(NUM_FILES, mReceiver.numDownloadsCompleted()); + } finally { + cursor.close(); + } + } + + /** + * Tests trying to download to SD card when the file with same name already exists. + */ + @LargeTest + public void testDownloadToExternal_fileExists() throws Exception { + File existentFile = createFileOnSD(null, 1, DataType.TEXT, null); + byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT); + + // Prepare the mock server with a standard response + enqueueResponse(HTTP_OK, blobData); + + try { + Uri uri = getServerUri(DEFAULT_FILENAME); + Request request = new Request(uri); + + Uri localUri = Uri.fromFile(existentFile); + Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath()); + request.setDestinationUri(localUri); + + long dlRequest = mDownloadManager.enqueue(request); + + // wait for the download to complete + waitForDownloadOrTimeout(dlRequest); + Cursor cursor = getCursor(dlRequest); + + try { + verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED); + verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE, + DownloadManager.ERROR_FILE_ERROR); + } finally { + cursor.close(); + } + } finally { + existentFile.delete(); + } + } + + /** + * Tests trying to download a file to SD card. + */ + @LargeTest + public void testDownloadToExternal() throws Exception { + String localDownloadDirectory = Environment.getExternalStorageDirectory().getPath(); + File downloadedFile = new File(localDownloadDirectory, DEFAULT_FILENAME); + // make sure the file doesn't already exist in the directory + downloadedFile.delete(); + + try { + byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT); + + // Prepare the mock server with a standard response + enqueueResponse(HTTP_OK, blobData); + + Uri uri = getServerUri(DEFAULT_FILENAME); + Request request = new Request(uri); + + Uri localUri = Uri.fromFile(downloadedFile); + Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath()); + request.setDestinationUri(localUri); + + long dlRequest = mDownloadManager.enqueue(request); + + // wait for the download to complete + waitForDownloadOrTimeout(dlRequest); + + verifyAndCleanupSingleFileDownload(dlRequest, blobData); + + assertEquals(1, mReceiver.numDownloadsCompleted()); + } finally { + downloadedFile.delete(); + } + } + + /** + * Tests trying to download a file to the system partition. + */ + @LargeTest + public void testDownloadToProhibitedDirectory() throws Exception { + File downloadedFile = new File(PROHIBITED_DIRECTORY, DEFAULT_FILENAME); + try { + byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT); + + // Prepare the mock server with a standard response + enqueueResponse(HTTP_OK, blobData); + + Uri uri = getServerUri(DEFAULT_FILENAME); + Request request = new Request(uri); + + Uri localUri = Uri.fromFile(downloadedFile); + Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath()); + request.setDestinationUri(localUri); + + try { + mDownloadManager.enqueue(request); + fail("Failed to throw SecurityException when trying to write to /system."); + } catch (SecurityException s) { + assertFalse(downloadedFile.exists()); + } + } finally { + // Just in case file somehow got created, make sure to delete it + downloadedFile.delete(); + } + } + + /** + * Tests that a download set for Wifi does not progress while Wifi is disabled, but resumes + * once Wifi is re-enabled. + */ + @LargeTest + public void testDownloadNoWifi() throws Exception { + long timeout = 60 * 1000; // wait only 60 seconds before giving up + int fileSize = 140 * 1024; // 140k + byte[] blobData = generateData(fileSize, DataType.TEXT); + + setWiFiStateOn(false); + enqueueResponse(HTTP_OK, blobData); + + try { + Uri uri = getServerUri(DEFAULT_FILENAME); + Request request = new Request(uri); + request.setAllowedNetworkTypes(Request.NETWORK_WIFI); + + long dlRequest = mDownloadManager.enqueue(request); + + // wait for the download to complete + boolean success = waitForDownloadOrTimeoutNoThrow(dlRequest, + WAIT_FOR_DOWNLOAD_POLL_TIME, timeout); + assertFalse("Download proceeded without Wifi connection!", success); + + setWiFiStateOn(true); + waitForDownloadOrTimeout(dlRequest); + + assertEquals(1, mReceiver.numDownloadsCompleted()); + } finally { + setWiFiStateOn(true); + } + } + + /** + * Tests trying to download two large files (50M bytes, followed by 60M bytes) + */ + @LargeTest + public void testInsufficientSpaceSingleFiles() throws Exception { + long fileSize1 = 50000000L; + long fileSize2 = 60000000L; + File largeFile1 = createFileOnSD(null, fileSize1, DataType.TEXT, null); + File largeFile2 = createFileOnSD(null, fileSize2, DataType.TEXT, null); + + try { + long dlRequest = doStandardEnqueue(largeFile1); + waitForDownloadOrTimeout(dlRequest); + ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(dlRequest); + verifyFileContents(pfd, largeFile1); + verifyFileSize(pfd, largeFile1.length()); + + dlRequest = doStandardEnqueue(largeFile2); + waitForDownloadOrTimeout(dlRequest); + Cursor cursor = getCursor(dlRequest); + try { + verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE, + DownloadManager.ERROR_INSUFFICIENT_SPACE); + } finally { + cursor.close(); + } + } finally { + largeFile1.delete(); + largeFile2.delete(); + } + } +} diff --git a/core/tests/coretests/src/android/net/DownloadManagerStressTest.java b/core/tests/coretests/src/android/net/DownloadManagerStressTest.java new file mode 100644 index 000000000000..9fa8620b0931 --- /dev/null +++ b/core/tests/coretests/src/android/net/DownloadManagerStressTest.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2010 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.net; + +import java.io.File; +import java.util.Random; + +import android.database.Cursor; +import android.net.DownloadManager.Query; +import android.net.DownloadManager.Request; +import android.os.ParcelFileDescriptor; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; + + +public class DownloadManagerStressTest extends DownloadManagerBaseTest { + private static String LOG_TAG = "android.net.DownloadManagerStressTest"; + + /** + * {@inheritDoc} + */ + @Override + public void setUp() throws Exception { + super.setUp(); + mServer.play(0); + removeAllCurrentDownloads(); + } + + /** + * Attempts to downloading thousands of files simultaneously + */ + public void testDownloadThousands() throws Exception { + int NUM_FILES = 1500; + int MAX_FILE_SIZE = 3000; + long[] reqs = new long[NUM_FILES]; + + // need to be sure all current downloads have stopped first + MultipleDownloadsCompletedReceiver receiver = registerNewMultipleDownloadsReceiver(); + Cursor cursor = null; + try { + Random r = new LoggingRng(); + for (int i = 0; i < NUM_FILES; ++i) { + int size = r.nextInt(MAX_FILE_SIZE); + byte[] blobData = generateData(size, DataType.TEXT); + + Uri uri = getServerUri(DEFAULT_FILENAME); + Request request = new Request(uri); + request.setTitle(String.format("%s--%d", DEFAULT_FILENAME, i)); + + // Prepare the mock server with a standard response + enqueueResponse(HTTP_OK, blobData); + + Log.i(LOG_TAG, "issuing request: " + i); + long reqId = mDownloadManager.enqueue(request); + reqs[i] = reqId; + } + + // wait for the download to complete or timeout + waitForDownloadsOrTimeout(WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME); + cursor = mDownloadManager.query(new Query()); + assertEquals(NUM_FILES, cursor.getCount()); + Log.i(LOG_TAG, "Verified number of downloads in download manager is what we expect."); + while (cursor.moveToNext()) { + int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)); + String filename = cursor.getString(cursor.getColumnIndex( + DownloadManager.COLUMN_URI)); + String errorString = String.format("File %s failed to download successfully. " + + "Status code: %d", filename, status); + assertEquals(errorString, DownloadManager.STATUS_SUCCESSFUL, status); + } + Log.i(LOG_TAG, "Verified each download was successful."); + assertEquals(NUM_FILES, receiver.numDownloadsCompleted()); + Log.i(LOG_TAG, "Verified number of completed downloads in our receiver."); + + // Verify that for each request, we can open the downloaded file + for (int i = 0; i < NUM_FILES; ++i) { + ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(reqs[i]); + pfd.close(); + } + Log.i(LOG_TAG, "Verified we can open each file."); + } finally { + if (cursor != null) { + cursor.close(); + } + mContext.unregisterReceiver(receiver); + removeAllCurrentDownloads(); + } + } + + /** + * Tests trying to download a large file (50M bytes). + */ + public void testDownloadLargeFile() throws Exception { + long fileSize = 50000000L; // note: kept relatively small to not exceed /cache dir size + File largeFile = createFileOnSD(null, fileSize, DataType.TEXT, null); + MultipleDownloadsCompletedReceiver receiver = registerNewMultipleDownloadsReceiver(); + + try { + long dlRequest = doStandardEnqueue(largeFile); + + // wait for the download to complete + waitForDownloadOrTimeout(dlRequest); + + ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(dlRequest); + verifyFileContents(pfd, largeFile); + verifyFileSize(pfd, largeFile.length()); + + assertEquals(1, receiver.numDownloadsCompleted()); + mContext.unregisterReceiver(receiver); + } catch (Exception e) { + throw e; + } finally { + largeFile.delete(); + } + } + + /** + * Tests trying to download a large file (~300M bytes) when there's not enough space in cache + */ + public void testInsufficientSpace() throws Exception { + long fileSize = 300000000L; + File largeFile = createFileOnSD(null, fileSize, DataType.TEXT, null); + + Cursor cursor = null; + try { + long dlRequest = doStandardEnqueue(largeFile); + + // wait for the download to complete + waitForDownloadOrTimeout(dlRequest); + + cursor = getCursor(dlRequest); + verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED); + verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE, + DownloadManager.ERROR_INSUFFICIENT_SPACE); + } finally { + if (cursor != null) { + cursor.close(); + } + largeFile.delete(); + } + } +} diff --git a/core/tests/coretests/src/android/net/http/HttpsThroughHttpProxyTest.java b/core/tests/coretests/src/android/net/http/HttpsThroughHttpProxyTest.java new file mode 100644 index 000000000000..95aad91a6f10 --- /dev/null +++ b/core/tests/coretests/src/android/net/http/HttpsThroughHttpProxyTest.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2010 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.net.http; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.List; +import junit.framework.TestCase; +import libcore.javax.net.ssl.TestSSLContext; +import org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.conn.params.ConnRoutePNames; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.ssl.AllowAllHostnameVerifier; +import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.impl.client.DefaultHttpClient; +import tests.http.MockResponse; +import tests.http.MockWebServer; +import tests.http.RecordedRequest; + +public class HttpsThroughHttpProxyTest extends TestCase { + + public void testConnectViaHttps() throws IOException, InterruptedException { + TestSSLContext testSSLContext = TestSSLContext.create(); + + MockWebServer server = new MockWebServer(); + server.useHttps(testSSLContext.serverContext.getSocketFactory(), false); + server.enqueue(new MockResponse() + .setResponseCode(200) + .setBody("this response comes via HTTPS")); + server.play(); + + HttpClient httpClient = new DefaultHttpClient(); + SSLSocketFactory sslSocketFactory = new SSLSocketFactory( + testSSLContext.clientContext.getSocketFactory()); + sslSocketFactory.setHostnameVerifier(new AllowAllHostnameVerifier()); + httpClient.getConnectionManager().getSchemeRegistry() + .register(new Scheme("https", sslSocketFactory, server.getPort())); + + HttpResponse response = httpClient.execute( + new HttpGet("https://localhost:" + server.getPort() + "/foo")); + assertEquals("this response comes via HTTPS", contentToString(response)); + + RecordedRequest request = server.takeRequest(); + assertEquals("GET /foo HTTP/1.1", request.getRequestLine()); + } + + /** + * http://code.google.com/p/android/issues/detail?id=2690 + */ + public void testConnectViaProxy() throws IOException, InterruptedException { + MockWebServer proxy = new MockWebServer(); + MockResponse mockResponse = new MockResponse() + .setResponseCode(200) + .setBody("this response comes via a proxy"); + proxy.enqueue(mockResponse); + proxy.play(); + + HttpClient httpProxyClient = new DefaultHttpClient(); + httpProxyClient.getParams().setParameter( + ConnRoutePNames.DEFAULT_PROXY, new HttpHost("localhost", proxy.getPort())); + + HttpResponse response = httpProxyClient.execute(new HttpGet("http://android.com/foo")); + assertEquals("this response comes via a proxy", contentToString(response)); + + RecordedRequest request = proxy.takeRequest(); + assertEquals("GET http://android.com/foo HTTP/1.1", request.getRequestLine()); + assertContains(request.getHeaders(), "Host: android.com"); + } + + public void testConnectViaHttpProxyToHttps() throws IOException, InterruptedException { + TestSSLContext testSSLContext = TestSSLContext.create(); + + MockWebServer proxy = new MockWebServer(); + proxy.useHttps(testSSLContext.serverContext.getSocketFactory(), true); + MockResponse connectResponse = new MockResponse() + .setResponseCode(200); + connectResponse.getHeaders().clear(); + proxy.enqueue(connectResponse); + proxy.enqueue(new MockResponse() + .setResponseCode(200) + .setBody("this response comes via a secure proxy")); + proxy.play(); + + HttpClient httpProxyClient = new DefaultHttpClient(); + HttpHost proxyHost = new HttpHost("localhost", proxy.getPort()); + httpProxyClient.getParams().setParameter( + ConnRoutePNames.DEFAULT_PROXY, proxyHost); + SSLSocketFactory sslSocketFactory = new SSLSocketFactory( + testSSLContext.clientContext.getSocketFactory()); + sslSocketFactory.setHostnameVerifier(new AllowAllHostnameVerifier()); + httpProxyClient.getConnectionManager().getSchemeRegistry() + .register(new Scheme("https", sslSocketFactory, 443)); + + HttpResponse response = httpProxyClient.execute(new HttpGet("https://android.com/foo")); + assertEquals("this response comes via a secure proxy", contentToString(response)); + + RecordedRequest connect = proxy.takeRequest(); + assertEquals("Connect line failure on proxy " + proxyHost.toHostString(), + "CONNECT android.com:443 HTTP/1.1", connect.getRequestLine()); + assertContains(connect.getHeaders(), "Host: android.com"); + + RecordedRequest get = proxy.takeRequest(); + assertEquals("GET /foo HTTP/1.1", get.getRequestLine()); + assertContains(get.getHeaders(), "Host: android.com"); + } + + private void assertContains(List<String> headers, String header) { + assertTrue(headers.toString(), headers.contains(header)); + } + + private String contentToString(HttpResponse response) throws IOException { + StringWriter writer = new StringWriter(); + char[] buffer = new char[1024]; + Reader reader = new InputStreamReader(response.getEntity().getContent()); + int length; + while ((length = reader.read(buffer)) != -1) { + writer.write(buffer, 0, length); + } + reader.close(); + return writer.toString(); + } +} diff --git a/core/tests/coretests/src/android/os/FileObserverTest.java b/core/tests/coretests/src/android/os/FileObserverTest.java index ca4e0d657c46..93e27af44170 100644 --- a/core/tests/coretests/src/android/os/FileObserverTest.java +++ b/core/tests/coretests/src/android/os/FileObserverTest.java @@ -19,9 +19,8 @@ package android.os; import com.google.android.collect.Lists; import com.google.android.collect.Maps; -import android.os.FileObserver; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.MediumTest; import android.util.Log; import java.io.File; @@ -69,7 +68,7 @@ public class FileObserverTest extends AndroidTestCase { } } - @LargeTest + @MediumTest public void testRun() throws Exception { // make file changes and wait for them assertTrue(mTestFile.exists()); diff --git a/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java b/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java index 89b3fb61ddd2..68209876319a 100644 --- a/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java +++ b/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java @@ -16,23 +16,15 @@ package android.os; -import junit.framework.TestCase; +import com.android.internal.util.HierarchicalState; +import com.android.internal.util.HierarchicalStateMachine; +import com.android.internal.util.ProcessedMessages; -import android.os.Debug; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; -import android.os.SystemClock; +import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; - import android.util.Log; -import com.android.internal.util.HierarchicalStateMachine; -import com.android.internal.util.HierarchicalState; -import com.android.internal.util.ProcessedMessages; - -import java.util.ArrayList; -import java.util.Arrays; +import junit.framework.TestCase; /** * Test for HierarchicalStateMachine. @@ -74,15 +66,15 @@ public class HierarchicalStateMachineTest extends TestCase { if (isQuit(message)) { mQuitCount += 1; if (mQuitCount > 2) { - // Returning false to actually quit - return false; + // Returning NOT_HANDLED to actually quit + return NOT_HANDLED; } else { // Do NOT quit - return true; + return HANDLED; } } else { // All other message are handled - return true; + return HANDLED; } } } @@ -172,12 +164,18 @@ public class HierarchicalStateMachineTest extends TestCase { class S1 extends HierarchicalState { @Override protected void enter() { + // Test that message is HSM_INIT_CMD + assertEquals(HSM_INIT_CMD, getCurrentMessage().what); + // Test that a transition in enter and the initial state works mS1EnterCount += 1; transitionTo(mS2); Log.d(TAG, "S1.enter"); } @Override protected void exit() { + // Test that message is HSM_INIT_CMD + assertEquals(HSM_INIT_CMD, getCurrentMessage().what); + mS1ExitCount += 1; Log.d(TAG, "S1.exit"); } @@ -185,10 +183,16 @@ public class HierarchicalStateMachineTest extends TestCase { class S2 extends HierarchicalState { @Override protected void enter() { + // Test that message is HSM_INIT_CMD + assertEquals(HSM_INIT_CMD, getCurrentMessage().what); + mS2EnterCount += 1; Log.d(TAG, "S2.enter"); } @Override protected void exit() { + // Test that message is TEST_CMD_1 + assertEquals(TEST_CMD_1, getCurrentMessage().what); + // Test transition in exit work mS2ExitCount += 1; transitionTo(mS4); @@ -196,10 +200,10 @@ public class HierarchicalStateMachineTest extends TestCase { } @Override protected boolean processMessage(Message message) { // Start a transition to S3 but it will be - // changed to a transition to S4 + // changed to a transition to S4 in exit transitionTo(mS3); Log.d(TAG, "S2.processMessage"); - return true; + return HANDLED; } } @@ -264,7 +268,7 @@ public class HierarchicalStateMachineTest extends TestCase { } synchronized (smEnterExitTranstionToTest) { - smEnterExitTranstionToTest.sendMessage(1); + smEnterExitTranstionToTest.sendMessage(TEST_CMD_1); try { // wait for the messages to be handled @@ -321,7 +325,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_6) { transitionToHaltingState(); } - return true; + return HANDLED; } } @@ -415,7 +419,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(1, mExitCount); transitionToHaltingState(); } - return true; + return HANDLED; } @Override protected void exit() { @@ -437,7 +441,7 @@ public class HierarchicalStateMachineTest extends TestCase { private int mExitCount; } - @SmallTest + @MediumTest public void testStateMachine1() throws Exception { StateMachine1 sm1 = new StateMachine1("sm1"); sm1.start(); @@ -510,7 +514,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionTo(mS2); } - return true; + return HANDLED; } @Override protected void exit() { @@ -523,7 +527,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionToHaltingState(); } - return true; + return HANDLED; } } @@ -542,7 +546,7 @@ public class HierarchicalStateMachineTest extends TestCase { private boolean mDidExit = false; } - @SmallTest + @MediumTest public void testStateMachine2() throws Exception { StateMachine2 sm2 = new StateMachine2("sm2"); sm2.start(); @@ -612,13 +616,13 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionToHaltingState(); } - return true; + return HANDLED; } } class ChildState extends HierarchicalState { @Override protected boolean processMessage(Message message) { - return false; + return NOT_HANDLED; } } @@ -634,7 +638,7 @@ public class HierarchicalStateMachineTest extends TestCase { private ChildState mChildState = new ChildState(); } - @SmallTest + @MediumTest public void testStateMachine3() throws Exception { StateMachine3 sm3 = new StateMachine3("sm3"); sm3.start(); @@ -697,20 +701,20 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionToHaltingState(); } - return true; + return HANDLED; } } class ChildState1 extends HierarchicalState { @Override protected boolean processMessage(Message message) { transitionTo(mChildState2); - return true; + return HANDLED; } } class ChildState2 extends HierarchicalState { @Override protected boolean processMessage(Message message) { - return false; + return NOT_HANDLED; } } @@ -727,7 +731,7 @@ public class HierarchicalStateMachineTest extends TestCase { private ChildState2 mChildState2 = new ChildState2(); } - @SmallTest + @MediumTest public void testStateMachine4() throws Exception { StateMachine4 sm4 = new StateMachine4("sm4"); sm4.start(); @@ -794,7 +798,7 @@ public class HierarchicalStateMachineTest extends TestCase { mParentState1EnterCount += 1; } @Override protected boolean processMessage(Message message) { - return true; + return HANDLED; } @Override protected void exit() { mParentState1ExitCount += 1; @@ -822,7 +826,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(0, mChildState5ExitCount); transitionTo(mChildState2); - return true; + return HANDLED; } @Override protected void exit() { mChildState1ExitCount += 1; @@ -850,7 +854,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(0, mChildState5ExitCount); transitionTo(mChildState5); - return true; + return HANDLED; } @Override protected void exit() { mChildState2ExitCount += 1; @@ -878,7 +882,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(1, mChildState5ExitCount); transitionToHaltingState(); - return true; + return HANDLED; } @Override protected void exit() { mParentState2ExitCount += 1; @@ -906,7 +910,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(1, mChildState5ExitCount); transitionTo(mChildState4); - return true; + return HANDLED; } @Override protected void exit() { mChildState3ExitCount += 1; @@ -934,7 +938,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(1, mChildState5ExitCount); transitionTo(mParentState2); - return true; + return HANDLED; } @Override protected void exit() { mChildState4ExitCount += 1; @@ -962,7 +966,7 @@ public class HierarchicalStateMachineTest extends TestCase { assertEquals(0, mChildState5ExitCount); transitionTo(mChildState3); - return true; + return HANDLED; } @Override protected void exit() { mChildState5ExitCount += 1; @@ -1001,7 +1005,7 @@ public class HierarchicalStateMachineTest extends TestCase { private int mChildState5ExitCount = 0; } - @SmallTest + @MediumTest public void testStateMachine5() throws Exception { StateMachine5 sm5 = new StateMachine5("sm5"); sm5.start(); @@ -1108,7 +1112,7 @@ public class HierarchicalStateMachineTest extends TestCase { mArrivalTimeMsg2 = SystemClock.elapsedRealtime(); transitionToHaltingState(); } - return true; + return HANDLED; } @Override protected void exit() { @@ -1129,7 +1133,7 @@ public class HierarchicalStateMachineTest extends TestCase { private long mArrivalTimeMsg2; } - @SmallTest + @MediumTest public void testStateMachine6() throws Exception { long sentTimeMsg2; final int DELAY_TIME = 250; @@ -1190,7 +1194,7 @@ public class HierarchicalStateMachineTest extends TestCase { class S1 extends HierarchicalState { @Override protected boolean processMessage(Message message) { transitionTo(mS2); - return true; + return HANDLED; } @Override protected void exit() { sendMessage(TEST_CMD_2); @@ -1216,7 +1220,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (mMsgCount == 2) { transitionToHaltingState(); } - return true; + return HANDLED; } @Override protected void exit() { @@ -1239,7 +1243,7 @@ public class HierarchicalStateMachineTest extends TestCase { private long mArrivalTimeMsg3; } - @SmallTest + @MediumTest public void testStateMachine7() throws Exception { long sentTimeMsg2; final int SM7_DELAY_FUDGE = 20; @@ -1300,7 +1304,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_2) { transitionToHaltingState(); } - return false; + return NOT_HANDLED; } } @@ -1369,7 +1373,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (message.what == TEST_CMD_4) { transitionToHaltingState(); } - return true; + return HANDLED; } } @@ -1391,7 +1395,7 @@ public class HierarchicalStateMachineTest extends TestCase { private static int sharedCounter = 0; private static Object waitObject = new Object(); - @SmallTest + @MediumTest public void testStateMachineSharedThread() throws Exception { if (DBG) Log.d(TAG, "testStateMachineSharedThread E"); @@ -1436,7 +1440,7 @@ public class HierarchicalStateMachineTest extends TestCase { if (DBG) Log.d(TAG, "testStateMachineSharedThread X"); } - @SmallTest + @MediumTest public void testHsm1() throws Exception { if (DBG) Log.d(TAG, "testHsm1 E"); @@ -1563,10 +1567,10 @@ class Hsm1 extends HierarchicalStateMachine { if (message.what == CMD_1) { // Transition to ourself to show that enter/exit is called transitionTo(mS1); - return true; + return HANDLED; } else { // Let parent process all other messages - return false; + return NOT_HANDLED; } } @Override protected void exit() { @@ -1618,7 +1622,7 @@ class Hsm1 extends HierarchicalStateMachine { transitionToHaltingState(); break; } - return true; + return HANDLED; } @Override protected void exit() { Log.d(TAG, "P2.exit"); diff --git a/core/tests/coretests/src/android/os/PerformanceCollectorTest.java b/core/tests/coretests/src/android/os/PerformanceCollectorTest.java index a382239e7869..7533c84673de 100644 --- a/core/tests/coretests/src/android/os/PerformanceCollectorTest.java +++ b/core/tests/coretests/src/android/os/PerformanceCollectorTest.java @@ -16,11 +16,8 @@ package android.os; -import android.os.Bundle; -import android.os.Parcelable; -import android.os.PerformanceCollector; -import android.os.Process; import android.os.PerformanceCollector.PerformanceResultsWriter; +import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; import java.lang.reflect.Field; @@ -56,7 +53,7 @@ public class PerformanceCollectorTest extends TestCase { assertEquals(2, snapshot.size()); } - @SmallTest + @MediumTest public void testEndSnapshotNoWriter() throws Exception { mPerfCollector.beginSnapshot("testEndSnapshotNoWriter"); workForRandomLongPeriod(); @@ -116,7 +113,7 @@ public class PerformanceCollectorTest extends TestCase { assertEquals(2, snapshot.size()); } - @SmallTest + @MediumTest public void testEndSnapshot() throws Exception { MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter(); mPerfCollector.setPerformanceResultsWriter(writer); @@ -232,7 +229,7 @@ public class PerformanceCollectorTest extends TestCase { assertEquals("Hello World", results.getString("testAddMeasurementStringNonEmpty")); } - @SmallTest + @MediumTest public void testSimpleSequence() throws Exception { MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter(); mPerfCollector.setPerformanceResultsWriter(writer); @@ -264,7 +261,7 @@ public class PerformanceCollectorTest extends TestCase { verifyTimingBundle(timing, labels); } - @SmallTest + @MediumTest public void testLongSequence() throws Exception { MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter(); mPerfCollector.setPerformanceResultsWriter(writer); @@ -350,7 +347,7 @@ public class PerformanceCollectorTest extends TestCase { * Verify that snapshotting and timing do not interfere w/ each other, * by staggering calls to snapshot and timing functions. */ - @SmallTest + @MediumTest public void testOutOfOrderSequence() { MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter(); mPerfCollector.setPerformanceResultsWriter(writer); diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java index e089b3e0768e..9893c161fec8 100644 --- a/core/tests/coretests/src/android/os/PowerManagerTest.java +++ b/core/tests/coretests/src/android/os/PowerManagerTest.java @@ -17,9 +17,8 @@ package android.os; import android.content.Context; -import android.os.PowerManager; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.SmallTest; public class PowerManagerTest extends AndroidTestCase { @@ -39,7 +38,7 @@ public class PowerManagerTest extends AndroidTestCase { * * @throws Exception */ - @MediumTest + @SmallTest public void testPreconditions() throws Exception { assertNotNull(mPm); } @@ -49,7 +48,7 @@ public class PowerManagerTest extends AndroidTestCase { * * @throws Exception */ - @MediumTest + @SmallTest public void testNewWakeLock() throws Exception { PowerManager.WakeLock wl = mPm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "FULL_WAKE_LOCK"); doTestWakeLock(wl); @@ -74,7 +73,7 @@ public class PowerManagerTest extends AndroidTestCase { * * @throws Exception */ - @MediumTest + @SmallTest public void testBadNewWakeLock() throws Exception { final int badFlags = PowerManager.SCREEN_BRIGHT_WAKE_LOCK diff --git a/core/tests/coretests/src/android/os/storage/StorageListener.java b/core/tests/coretests/src/android/os/storage/StorageListener.java index d6dae2227cda..6a26b88d5c44 100644 --- a/core/tests/coretests/src/android/os/storage/StorageListener.java +++ b/core/tests/coretests/src/android/os/storage/StorageListener.java @@ -21,21 +21,24 @@ import android.util.Log; public class StorageListener extends StorageEventListener { private static final boolean localLOGV = true; - public static final String TAG="StorageListener"; + public static final String TAG = "StorageListener"; - String oldState; - String newState; - String path; + private String mTargetState; private boolean doneFlag = false; + + public StorageListener(String targetState) { + mTargetState = targetState; + } + @Override public void onStorageStateChanged(String path, String oldState, String newState) { if (localLOGV) Log.i(TAG, "Storage state changed from " + oldState + " to " + newState); + synchronized (this) { - this.oldState = oldState; - this.newState = newState; - this.path = path; - doneFlag = true; - notifyAll(); + if (mTargetState.equals(newState)) { + doneFlag = true; + notifyAll(); + } } } diff --git a/core/tests/coretests/src/android/pim/vcard/ContentValuesVerifier.java b/core/tests/coretests/src/android/pim/vcard/ContentValuesVerifier.java deleted file mode 100644 index b9e9875cd9a7..000000000000 --- a/core/tests/coretests/src/android/pim/vcard/ContentValuesVerifier.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2009 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.pim.vcard; - -import android.pim.vcard.VCardConfig; -import android.pim.vcard.VCardEntry; -import android.pim.vcard.VCardEntryConstructor; -import android.pim.vcard.VCardEntryHandler; -import android.pim.vcard.VCardParser; -import android.pim.vcard.VCardParser_V21; -import android.pim.vcard.VCardParser_V30; -import android.pim.vcard.exception.VCardException; -import android.test.AndroidTestCase; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -/* package */ class ContentValuesVerifier implements VCardEntryHandler { - private AndroidTestCase mTestCase; - private List<ContentValuesVerifierElem> mContentValuesVerifierElemList = - new ArrayList<ContentValuesVerifierElem>(); - private int mIndex; - - public ContentValuesVerifierElem addElem(AndroidTestCase androidTestCase) { - mTestCase = androidTestCase; - ContentValuesVerifierElem importVerifier = new ContentValuesVerifierElem(androidTestCase); - mContentValuesVerifierElemList.add(importVerifier); - return importVerifier; - } - - public void verify(int resId, int vCardType) throws IOException, VCardException { - verify(mTestCase.getContext().getResources().openRawResource(resId), vCardType); - } - - public void verify(int resId, int vCardType, final VCardParser vCardParser) - throws IOException, VCardException { - verify(mTestCase.getContext().getResources().openRawResource(resId), - vCardType, vCardParser); - } - - public void verify(InputStream is, int vCardType) throws IOException, VCardException { - final VCardParser vCardParser; - if (VCardConfig.isV30(vCardType)) { - vCardParser = new VCardParser_V30(true); // use StrictParsing - } else { - vCardParser = new VCardParser_V21(); - } - verify(is, vCardType, vCardParser); - } - - public void verify(InputStream is, int vCardType, final VCardParser vCardParser) - throws IOException, VCardException { - VCardEntryConstructor builder = - new VCardEntryConstructor(null, null, false, vCardType, null); - builder.addEntryHandler(this); - try { - vCardParser.parse(is, builder); - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - } - } - } - } - - public void onStart() { - for (ContentValuesVerifierElem elem : mContentValuesVerifierElemList) { - elem.onParsingStart(); - } - } - - public void onEntryCreated(VCardEntry entry) { - mTestCase.assertTrue(mIndex < mContentValuesVerifierElemList.size()); - mContentValuesVerifierElemList.get(mIndex).onEntryCreated(entry); - mIndex++; - } - - public void onEnd() { - for (ContentValuesVerifierElem elem : mContentValuesVerifierElemList) { - elem.onParsingEnd(); - elem.verifyResolver(); - } - } -} diff --git a/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java b/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java index 004a197352d9..2bec46287fae 100644 --- a/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java +++ b/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java @@ -17,7 +17,10 @@ package android.pim.vcard; import android.content.ContentValues; -import android.pim.vcard.VCardConfig; +import android.pim.vcard.test_utils.ContactEntry; +import android.pim.vcard.test_utils.PropertyNodesVerifierElem; +import android.pim.vcard.test_utils.PropertyNodesVerifierElem.TypeSet; +import android.pim.vcard.test_utils.VCardTestsBase; import android.provider.ContactsContract.CommonDataKinds.Email; import android.provider.ContactsContract.CommonDataKinds.Event; import android.provider.ContactsContract.CommonDataKinds.Im; @@ -31,8 +34,6 @@ import android.provider.ContactsContract.CommonDataKinds.StructuredName; import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; import android.provider.ContactsContract.CommonDataKinds.Website; -import android.pim.vcard.PropertyNodesVerifierElem.TypeSet; - import java.util.Arrays; /** @@ -56,7 +57,6 @@ public class VCardExporterTests extends VCardTestsBase { } private void testStructuredNameBasic(int vcardType) { - final boolean isV30 = VCardConfig.isV30(vcardType); mVerifier.initForExportTest(vcardType); mVerifier.addInputEntry().addContentValues(StructuredName.CONTENT_ITEM_TYPE) .put(StructuredName.FAMILY_NAME, "AppropriateFamilyName") @@ -64,28 +64,15 @@ public class VCardExporterTests extends VCardTestsBase { .put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName") .put(StructuredName.PREFIX, "AppropriatePrefix") .put(StructuredName.SUFFIX, "AppropriateSuffix") - .put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily") - .put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven") - .put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle"); + .put(StructuredName.DISPLAY_NAME, "DISPLAY NAME"); - PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem() + mVerifier.addPropertyNodesVerifierElem() .addExpectedNodeWithOrder("N", "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;" + "AppropriatePrefix;AppropriateSuffix", Arrays.asList("AppropriateFamilyName", "AppropriateGivenName", "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix")) - .addExpectedNodeWithOrder("FN", - "AppropriatePrefix AppropriateGivenName " - + "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix") - .addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven") - .addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle") - .addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily"); - - if (isV30) { - elem.addExpectedNode("SORT-STRING", - "AppropriatePhoneticGiven AppropriatePhoneticMiddle " - + "AppropriatePhoneticFamily"); - } + .addExpectedNodeWithOrder("FN", "DISPLAY NAME"); } public void testStructuredNameBasicV21() { @@ -96,6 +83,10 @@ public class VCardExporterTests extends VCardTestsBase { testStructuredNameBasic(V30); } + public void testStructuredNameBasicV40() { + testStructuredNameBasic(V40); + } + /** * Test that only "primary" StructuredName is emitted, so that our vCard file * will not confuse the external importer, assuming there may be some importer @@ -103,18 +94,15 @@ public class VCardExporterTests extends VCardTestsBase { * Note that more than one "N", "FN", etc. properties are acceptable in vCard spec. */ private void testStructuredNameUsePrimaryCommon(int vcardType) { - final boolean isV30 = (vcardType == V30); mVerifier.initForExportTest(vcardType); - ContactEntry entry = mVerifier.addInputEntry(); + final ContactEntry entry = mVerifier.addInputEntry(); entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1") .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1") .put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName1") .put(StructuredName.PREFIX, "DoNotEmitPrefix1") .put(StructuredName.SUFFIX, "DoNotEmitSuffix1") - .put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily1") - .put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven1") - .put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle1"); + .put(StructuredName.DISPLAY_NAME, "DoNotEmitDisplayName1"); // With "IS_PRIMARY=1". This is what we should use. entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) @@ -123,41 +111,30 @@ public class VCardExporterTests extends VCardTestsBase { .put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName") .put(StructuredName.PREFIX, "AppropriatePrefix") .put(StructuredName.SUFFIX, "AppropriateSuffix") - .put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily") - .put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven") - .put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle") + .put(StructuredName.DISPLAY_NAME, "AppropriateDisplayName") .put(StructuredName.IS_PRIMARY, 1); // With "IS_PRIMARY=1", but we should ignore this time, since this is second, not first. + // vCard 2.1 does not specify anything about the number of N properties. We choose not + // emitting this property. + // vCard 3.0 does (There must be one N property) + // vCard 4.0 (rev13) does (cardinality (0, 1)). entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName2") .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName2") .put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName2") .put(StructuredName.PREFIX, "DoNotEmitPrefix2") .put(StructuredName.SUFFIX, "DoNotEmitSuffix2") - .put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily2") - .put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven2") - .put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle2") + .put(StructuredName.DISPLAY_NAME, "DoNotEmitDisplayName2") .put(StructuredName.IS_PRIMARY, 1); - PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem() + mVerifier.addPropertyNodesVerifierElem() .addExpectedNodeWithOrder("N", "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;" + "AppropriatePrefix;AppropriateSuffix", Arrays.asList("AppropriateFamilyName", "AppropriateGivenName", "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix")) - .addExpectedNodeWithOrder("FN", - "AppropriatePrefix AppropriateGivenName " - + "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix") - .addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven") - .addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle") - .addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily"); - - if (isV30) { - elem.addExpectedNode("SORT-STRING", - "AppropriatePhoneticGiven AppropriatePhoneticMiddle " - + "AppropriatePhoneticFamily"); - } + .addExpectedNodeWithOrder("FN", "AppropriateDisplayName"); } public void testStructuredNameUsePrimaryV21() { @@ -168,14 +145,152 @@ public class VCardExporterTests extends VCardTestsBase { testStructuredNameUsePrimaryCommon(V30); } + public void testStructuredNameUsePrimaryV40() { + testStructuredNameUsePrimaryCommon(V40); + } + /** * Tests that only "super primary" StructuredName is emitted. * See also the comment in {@link #testStructuredNameUsePrimaryCommon(int)}. */ private void testStructuredNameUseSuperPrimaryCommon(int vcardType) { - final boolean isV30 = (vcardType == V30); mVerifier.initForExportTest(vcardType); - ContactEntry entry = mVerifier.addInputEntry(); + final ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1") + .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1") + .put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName1") + .put(StructuredName.PREFIX, "DoNotEmitPrefix1") + .put(StructuredName.SUFFIX, "DoNotEmitSuffix1") + .put(StructuredName.DISPLAY_NAME, "DoNotEmitDisplay1"); + + // With "IS_PRIMARY=1", but we should ignore this time. + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName2") + .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName2") + .put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName2") + .put(StructuredName.PREFIX, "DoNotEmitPrefix2") + .put(StructuredName.SUFFIX, "DoNotEmitSuffix2") + .put(StructuredName.DISPLAY_NAME, "DoNotEmitDisplay2") + .put(StructuredName.IS_PRIMARY, 1); + + // With "IS_SUPER_PRIMARY=1". This is what we should use. + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "AppropriateFamilyName") + .put(StructuredName.GIVEN_NAME, "AppropriateGivenName") + .put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName") + .put(StructuredName.PREFIX, "AppropriatePrefix") + .put(StructuredName.SUFFIX, "AppropriateSuffix") + .put(StructuredName.DISPLAY_NAME, "AppropriateDisplayName") + .put(StructuredName.IS_SUPER_PRIMARY, 1); + + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName3") + .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName3") + .put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName3") + .put(StructuredName.PREFIX, "DoNotEmitPrefix3") + .put(StructuredName.SUFFIX, "DoNotEmitSuffix3") + .put(StructuredName.DISPLAY_NAME, "DoNotEmitDisplay3") + .put(StructuredName.IS_PRIMARY, 1); + + final PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem(); + elem.addExpectedNodeWithOrder("N", + "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;" + + "AppropriatePrefix;AppropriateSuffix", + Arrays.asList("AppropriateFamilyName", "AppropriateGivenName", + "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix")); + + elem.addExpectedNodeWithOrder("FN", "AppropriateDisplayName"); + } + + public void testStructuredNameUseSuperPrimaryV21() { + testStructuredNameUseSuperPrimaryCommon(V21); + } + + public void testStructuredNameUseSuperPrimaryV30() { + testStructuredNameUseSuperPrimaryCommon(V30); + } + + public void testStructuredNameUseSuperPrimaryV40() { + testStructuredNameUseSuperPrimaryCommon(V40); + } + + /** + * Tests phonetic names field are handled correctly. + * + * vCard 2.1 does not have any field corresponding to them. + * vCard 3.0 has SORT-STRING property, which does not support multiple values inside it. + * vCard 4.0 (rev13) has SORT-AS parameter, which has three values (family, given, middle) + * inside it. + */ + private void testStructuredNamePhoneticNameCommon(int vcardType) { + mVerifier.initForExportTest(vcardType); + final ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "AppropriateFamilyName") + .put(StructuredName.GIVEN_NAME, "AppropriateGivenName") + .put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName") + .put(StructuredName.PREFIX, "AppropriatePrefix") + .put(StructuredName.SUFFIX, "AppropriateSuffix") + .put(StructuredName.DISPLAY_NAME, "AppropriateDisplayName") + .put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily") + .put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven") + .put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle"); + + final PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem(); + if (VCardConfig.isVersion40(vcardType)) { + final ContentValues contentValues = new ContentValues(); + contentValues.put("SORT-AS", + "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName"); + // vCard 4.0 (rev13) now uses SORT-AS parameter, which is not compatible with + // either 2.1 nor 3.0. + elem.addExpectedNodeWithOrder("N", + "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;" + + "AppropriatePrefix;AppropriateSuffix", + Arrays.asList("AppropriateFamilyName", "AppropriateGivenName", + "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"), + contentValues); + } else { + elem.addExpectedNodeWithOrder("N", + "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;" + + "AppropriatePrefix;AppropriateSuffix", + Arrays.asList("AppropriateFamilyName", "AppropriateGivenName", + "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix")); + if (VCardConfig.isVersion30(vcardType)) { + elem.addExpectedNode("SORT-STRING", + "AppropriatePhoneticGiven AppropriatePhoneticMiddle" + + " AppropriatePhoneticFamily"); + } + } + + elem.addExpectedNodeWithOrder("FN", "AppropriateDisplayName") + .addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven") + .addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle") + .addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily"); + } + + public void testStructuredNamePhoneticNameV21() { + testStructuredNamePhoneticNameCommon(V21); + } + + public void testStructuredNamePhoneticNameV30() { + testStructuredNamePhoneticNameCommon(V30); + } + + public void testStructuredNamePhoneticNameV40() { + testStructuredNamePhoneticNameCommon(V40); + } + + // TODO: need to add test cases confirming escaping, empty values, etc. + + /** + * Confirms all the other sides of the handling is correctly interpreted at one time. + * + * A kind of regression test for StructuredName handling. + */ + private void testStructuredNameComplicatedCommon(int vcardType) { + mVerifier.initForExportTest(vcardType); + final ContactEntry entry = mVerifier.addInputEntry(); entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1") .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1") @@ -221,32 +336,50 @@ public class VCardExporterTests extends VCardTestsBase { .put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle3") .put(StructuredName.IS_PRIMARY, 1); - PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("N", - "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;" - + "AppropriatePrefix;AppropriateSuffix", - Arrays.asList("AppropriateFamilyName", "AppropriateGivenName", - "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix")) - .addExpectedNodeWithOrder("FN", - "AppropriatePrefix AppropriateGivenName " - + "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix") - .addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven") - .addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle") - .addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily"); - - if (isV30) { - elem.addExpectedNode("SORT-STRING", - "AppropriatePhoneticGiven AppropriatePhoneticMiddle" - + " AppropriatePhoneticFamily"); + final PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem(); + if (VCardConfig.isVersion40(vcardType)) { + final ContentValues contentValues = new ContentValues(); + contentValues.put("SORT-AS", + "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName"); + // vCard 4.0 (rev13) now uses SORT-AS parameter, which is not compatible with + // either 2.1 nor 3.0. + elem.addExpectedNodeWithOrder("N", + "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;" + + "AppropriatePrefix;AppropriateSuffix", + Arrays.asList("AppropriateFamilyName", "AppropriateGivenName", + "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"), + contentValues); + } else { + elem.addExpectedNodeWithOrder("N", + "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;" + + "AppropriatePrefix;AppropriateSuffix", + Arrays.asList("AppropriateFamilyName", "AppropriateGivenName", + "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix")); + if (VCardConfig.isVersion30(vcardType)) { + elem.addExpectedNode("SORT-STRING", + "AppropriatePhoneticGiven AppropriatePhoneticMiddle" + + " AppropriatePhoneticFamily"); + } } + + elem.addExpectedNodeWithOrder("FN", + "AppropriatePrefix AppropriateGivenName " + + "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix") + .addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven") + .addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle") + .addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily"); } - public void testStructuredNameUseSuperPrimaryV21() { - testStructuredNameUseSuperPrimaryCommon(V21); + public void testStructuredNameComplicatedV21() { + testStructuredNameComplicatedCommon(V21); } - public void testStructuredNameUseSuperPrimaryV30() { - testStructuredNameUseSuperPrimaryCommon(V30); + public void testStructuredNameComplicatedV30() { + testStructuredNameComplicatedCommon(V30); + } + + public void testStructuredNameComplicatedV40() { + testStructuredNameComplicatedCommon(V40); } public void testNickNameV30() { @@ -258,6 +391,15 @@ public class VCardExporterTests extends VCardTestsBase { .addExpectedNodeWithOrder("NICKNAME", "Nicky"); } + public void testNickNameV40() { + mVerifier.initForExportTest(V40); + mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE) + .put(Nickname.NAME, "Nicky"); + + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNodeWithOrder("NICKNAME", "Nicky"); + } + private void testPhoneBasicCommon(int vcardType) { mVerifier.initForExportTest(vcardType); mVerifier.addInputEntry().addContentValues(Phone.CONTENT_ITEM_TYPE) @@ -275,6 +417,20 @@ public class VCardExporterTests extends VCardTestsBase { testPhoneBasicCommon(V30); } + public void testPhoneBasicV40() { + testPhoneBasicCommon(V40); + } + + public void testPhoneRefrainFormatting() { + mVerifier.initForExportTest(V21 | VCardConfig.FLAG_REFRAIN_PHONE_NUMBER_FORMATTING); + mVerifier.addInputEntry().addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "1234567890(abcdefghijklmnopqrstuvwxyz)") + .put(Phone.TYPE, Phone.TYPE_HOME); + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("TEL", "1234567890(abcdefghijklmnopqrstuvwxyz)", + new TypeSet("HOME")); + } + /** * Tests that vCard composer emits corresponding type param which we expect. */ @@ -357,6 +513,10 @@ public class VCardExporterTests extends VCardTestsBase { testPhoneVariousTypeSupport(V30); } + public void testPhoneVariousTypeSupportV40() { + testPhoneVariousTypeSupport(V40); + } + /** * Tests that "PREF"s are emitted appropriately. */ @@ -393,6 +553,10 @@ public class VCardExporterTests extends VCardTestsBase { testPhonePrefHandlingCommon(V30); } + public void testPhonePrefHandlingV40() { + testPhonePrefHandlingCommon(V40); + } + private void testMiscPhoneTypeHandling(int vcardType) { mVerifier.initForExportTest(vcardType); ContactEntry entry = mVerifier.addInputEntry(); @@ -428,14 +592,26 @@ public class VCardExporterTests extends VCardTestsBase { .put(Phone.TYPE, Phone.TYPE_CUSTOM) .put(Phone.LABEL, "invalid"); PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName(); - elem.addExpectedNode("TEL", "1", new TypeSet("MODEM")) - .addExpectedNode("TEL", "2", new TypeSet("MSG")) - .addExpectedNode("TEL", "3", new TypeSet("BBS")) - .addExpectedNode("TEL", "4", new TypeSet("VIDEO")) - .addExpectedNode("TEL", "5", new TypeSet("VOICE")) - .addExpectedNode("TEL", "6", new TypeSet("CELL")) - .addExpectedNode("TEL", "7", new TypeSet("CELL")) - .addExpectedNode("TEL", "8", new TypeSet("X-invalid")); + if (VCardConfig.isVersion30(vcardType) || VCardConfig.isVersion40(vcardType)) { + // vCard 3.0 accepts "invalid". Also stop using toUpper() + elem.addExpectedNode("TEL", "1", new TypeSet("Modem")) + .addExpectedNode("TEL", "2", new TypeSet("MSG")) + .addExpectedNode("TEL", "3", new TypeSet("BBS")) + .addExpectedNode("TEL", "4", new TypeSet("VIDEO")) + .addExpectedNode("TEL", "5", new TypeSet("VOICE")) + .addExpectedNode("TEL", "6", new TypeSet("CELL")) + .addExpectedNode("TEL", "7", new TypeSet("CELL")) + .addExpectedNode("TEL", "8", new TypeSet("invalid")); + } else { + elem.addExpectedNode("TEL", "1", new TypeSet("MODEM")) + .addExpectedNode("TEL", "2", new TypeSet("MSG")) + .addExpectedNode("TEL", "3", new TypeSet("BBS")) + .addExpectedNode("TEL", "4", new TypeSet("VIDEO")) + .addExpectedNode("TEL", "5", new TypeSet("VOICE")) + .addExpectedNode("TEL", "6", new TypeSet("CELL")) + .addExpectedNode("TEL", "7", new TypeSet("CELL")) + .addExpectedNode("TEL", "8", new TypeSet("X-invalid")); + } } public void testPhoneTypeHandlingV21() { @@ -446,6 +622,10 @@ public class VCardExporterTests extends VCardTestsBase { testMiscPhoneTypeHandling(V30); } + public void testPhoneTypeHandlingV40() { + testMiscPhoneTypeHandling(V40); + } + private void testEmailBasicCommon(int vcardType) { mVerifier.initForExportTest(vcardType); mVerifier.addInputEntry().addContentValues(Email.CONTENT_ITEM_TYPE) @@ -462,6 +642,10 @@ public class VCardExporterTests extends VCardTestsBase { testEmailBasicCommon(V30); } + public void testEmailBasicV40() { + testEmailBasicCommon(V40); + } + private void testEmailVariousTypeSupportCommon(int vcardType) { mVerifier.initForExportTest(vcardType); ContactEntry entry = mVerifier.addInputEntry(); @@ -492,6 +676,10 @@ public class VCardExporterTests extends VCardTestsBase { testEmailVariousTypeSupportCommon(V30); } + public void testEmailVariousTypeSupportV40() { + testEmailVariousTypeSupportCommon(V40); + } + private void testEmailPrefHandlingCommon(int vcardType) { mVerifier.initForExportTest(vcardType); ContactEntry entry = mVerifier.addInputEntry(); @@ -516,6 +704,10 @@ public class VCardExporterTests extends VCardTestsBase { testEmailPrefHandlingCommon(V30); } + public void testEmailPrefHandlingV40() { + testEmailPrefHandlingCommon(V40); + } + private void testPostalAddressCommon(int vcardType) { mVerifier.initForExportTest(vcardType); mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) @@ -547,6 +739,10 @@ public class VCardExporterTests extends VCardTestsBase { testPostalAddressCommon(V30); } + public void testPostalAddressV40() { + testPostalAddressCommon(V40); + } + private void testPostalAddressNonNeighborhood(int vcardType) { mVerifier.initForExportTest(vcardType); mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) @@ -564,6 +760,10 @@ public class VCardExporterTests extends VCardTestsBase { testPostalAddressNonNeighborhood(V30); } + public void testPostalAddressNonNeighborhoodV40() { + testPostalAddressNonNeighborhood(V40); + } + private void testPostalAddressNonCity(int vcardType) { mVerifier.initForExportTest(vcardType); mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) @@ -581,6 +781,10 @@ public class VCardExporterTests extends VCardTestsBase { testPostalAddressNonCity(V30); } + public void testPostalAddressNonCityV40() { + testPostalAddressNonCity(V40); + } + private void testPostalOnlyWithFormattedAddressCommon(int vcardType) { mVerifier.initForExportTest(vcardType); mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) @@ -601,6 +805,10 @@ public class VCardExporterTests extends VCardTestsBase { testPostalOnlyWithFormattedAddressCommon(V30); } + public void testPostalOnlyWithFormattedAddressV40() { + testPostalOnlyWithFormattedAddressCommon(V40); + } + /** * Tests that the vCard composer honors formatted data when it is available * even when it is partial. @@ -626,6 +834,10 @@ public class VCardExporterTests extends VCardTestsBase { testPostalWithBothStructuredAndFormattedCommon(V30); } + public void testPostalWithBothStructuredAndFormattedV40() { + testPostalWithBothStructuredAndFormattedCommon(V40); + } + private void testOrganizationCommon(int vcardType) { mVerifier.initForExportTest(vcardType); ContactEntry entry = mVerifier.addInputEntry(); @@ -663,6 +875,10 @@ public class VCardExporterTests extends VCardTestsBase { testOrganizationCommon(V30); } + public void testOrganizationV40() { + testOrganizationCommon(V40); + } + private void testImVariousTypeSupportCommon(int vcardType) { mVerifier.initForExportTest(vcardType); ContactEntry entry = mVerifier.addInputEntry(); @@ -715,6 +931,10 @@ public class VCardExporterTests extends VCardTestsBase { testImVariousTypeSupportCommon(V30); } + public void testImBasicV40() { + testImVariousTypeSupportCommon(V40); + } + private void testImPrefHandlingCommon(int vcardType) { mVerifier.initForExportTest(vcardType); ContactEntry entry = mVerifier.addInputEntry(); @@ -740,6 +960,10 @@ public class VCardExporterTests extends VCardTestsBase { testImPrefHandlingCommon(V30); } + public void testImPrefHandlingV40() { + testImPrefHandlingCommon(V40); + } + private void testWebsiteCommon(int vcardType) { mVerifier.initForExportTest(vcardType); ContactEntry entry = mVerifier.addInputEntry(); @@ -764,6 +988,10 @@ public class VCardExporterTests extends VCardTestsBase { testWebsiteCommon(V30); } + public void testWebsiteV40() { + testWebsiteCommon(V40); + } + private String getAndroidPropValue(final String mimeType, String value, Integer type) { return getAndroidPropValue(mimeType, value, type, null); } @@ -817,6 +1045,10 @@ public class VCardExporterTests extends VCardTestsBase { testEventCommon(V30); } + public void testEventV40() { + testEventCommon(V40); + } + private void testNoteCommon(int vcardType) { mVerifier.initForExportTest(vcardType); ContactEntry entry = mVerifier.addInputEntry(); @@ -838,8 +1070,13 @@ public class VCardExporterTests extends VCardTestsBase { testNoteCommon(V30); } + public void testNoteV40() { + testNoteCommon(V40); + } + private void testPhotoCommon(int vcardType) { - final boolean isV30 = vcardType == V30; + final boolean useB = + (VCardConfig.isVersion30(vcardType) || VCardConfig.isVersion40(vcardType)); mVerifier.initForExportTest(vcardType); ContactEntry entry = mVerifier.addInputEntry(); entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) @@ -848,7 +1085,7 @@ public class VCardExporterTests extends VCardTestsBase { .put(Photo.PHOTO, sPhotoByteArray); ContentValues contentValuesForPhoto = new ContentValues(); - contentValuesForPhoto.put("ENCODING", (isV30 ? "b" : "BASE64")); + contentValuesForPhoto.put("ENCODING", (useB ? "b" : "BASE64")); mVerifier.addPropertyNodesVerifierElem() .addExpectedNode("FN", "PhotoTest") .addExpectedNode("N", "PhotoTest;;;;", @@ -865,6 +1102,10 @@ public class VCardExporterTests extends VCardTestsBase { testPhotoCommon(V30); } + public void testPhotoV40() { + testPhotoCommon(V40); + } + private void testRelationCommon(int vcardType) { mVerifier.initForExportTest(vcardType); mVerifier.addInputEntry().addContentValues(Relation.CONTENT_ITEM_TYPE) @@ -935,18 +1176,22 @@ public class VCardExporterTests extends VCardTestsBase { entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) .put(StructuredName.IS_PRIMARY, 1); // Empty name. Should be ignored. entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "family1"); // Not primary. Should be ignored. + .put(StructuredName.FAMILY_NAME, "family1") // Not primary. Should be ignored. + .put(StructuredName.DISPLAY_NAME, "display"); entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) .put(StructuredName.IS_PRIMARY, 1) - .put(StructuredName.FAMILY_NAME, "family2"); // This entry is what we want. + .put(StructuredName.FAMILY_NAME, "family2") // This entry is what we want. + .put(StructuredName.DISPLAY_NAME, "display"); entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) .put(StructuredName.IS_PRIMARY, 1) - .put(StructuredName.FAMILY_NAME, "family3"); + .put(StructuredName.FAMILY_NAME, "family3") + .put(StructuredName.DISPLAY_NAME, "display"); entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "family4"); + .put(StructuredName.FAMILY_NAME, "family4") + .put(StructuredName.DISPLAY_NAME, "display"); mVerifier.addPropertyNodesVerifierElem() .addExpectedNode("N", Arrays.asList("family2", "", "", "", "")) - .addExpectedNode("FN", "family2"); + .addExpectedNode("FN", "display"); } public void testPickUpNonEmptyContentValuesV21() { @@ -956,4 +1201,23 @@ public class VCardExporterTests extends VCardTestsBase { public void testPickUpNonEmptyContentValuesV30() { testPickUpNonEmptyContentValuesCommon(V30); } + + public void testPickUpNonEmptyContentValuesV40() { + testPickUpNonEmptyContentValuesCommon(V40); + } + + public void testUseMultiByteTypeV30() { + mVerifier.initForExportTest(V30); + final ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "\u96FB\u8A71") + .put(Phone.NUMBER, "1"); + mVerifier.addLineVerifierElem() + .addExpected("N:") + .addExpected("FN:") + .addExpected("TEL;TYPE=\u96FB\u8A71:1"); + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("TEL", "1", new TypeSet("\u96FB\u8A71")); + } } diff --git a/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java b/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java index 21f22540c697..09e2914420da 100644 --- a/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java +++ b/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java @@ -15,11 +15,17 @@ */ package android.pim.vcard; +import com.android.frameworks.coretests.R; + import android.content.ContentValues; -import android.pim.vcard.VCardConfig; -import android.provider.ContactsContract.Data; +import android.pim.vcard.test_utils.PropertyNodesVerifierElem.TypeSet; +import android.pim.vcard.test_utils.VCardTestsBase; +import android.pim.vcard.test_utils.ContentValuesVerifier; +import android.pim.vcard.test_utils.ContentValuesVerifierElem; import android.provider.ContactsContract.CommonDataKinds.Email; import android.provider.ContactsContract.CommonDataKinds.Event; +import android.provider.ContactsContract.CommonDataKinds.Im; +import android.provider.ContactsContract.CommonDataKinds.Nickname; import android.provider.ContactsContract.CommonDataKinds.Note; import android.provider.ContactsContract.CommonDataKinds.Organization; import android.provider.ContactsContract.CommonDataKinds.Phone; @@ -27,9 +33,7 @@ import android.provider.ContactsContract.CommonDataKinds.Photo; import android.provider.ContactsContract.CommonDataKinds.StructuredName; import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; import android.provider.ContactsContract.CommonDataKinds.Website; - -import com.android.frameworks.coretests.R; -import android.pim.vcard.PropertyNodesVerifierElem.TypeSet; +import android.provider.ContactsContract.Data; import java.util.Arrays; @@ -405,12 +409,12 @@ public class VCardImporterTests extends VCardTestsBase { public void testV21SimpleCase1_Parsing() { mVerifier.initForImportTest(V21, R.raw.v21_simple_1); - mVerifier.addPropertyNodesVerifierElem() + mVerifier.addPropertyNodesVerifierElemWithoutVersion() // no "VERSION:2.1" line. .addExpectedNodeWithOrder("N", "Ando;Roid;", Arrays.asList("Ando", "Roid", "")); } public void testV21SimpleCase1_Type_Generic() { - mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8, R.raw.v21_simple_1); + mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_GENERIC, R.raw.v21_simple_1); mVerifier.addContentValuesVerifierElem() .addExpected(StructuredName.CONTENT_ITEM_TYPE) .put(StructuredName.FAMILY_NAME, "Ando") @@ -419,7 +423,7 @@ public class VCardImporterTests extends VCardTestsBase { } public void testV21SimpleCase1_Type_Japanese() { - mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS, R.raw.v21_simple_1); + mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_simple_1); mVerifier.addContentValuesVerifierElem() .addExpected(StructuredName.CONTENT_ITEM_TYPE) .put(StructuredName.FAMILY_NAME, "Ando") @@ -431,7 +435,7 @@ public class VCardImporterTests extends VCardTestsBase { } public void testV21SimpleCase2() { - mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS, R.raw.v21_simple_2); + mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_simple_2); mVerifier.addContentValuesVerifierElem() .addExpected(StructuredName.CONTENT_ITEM_TYPE) .put(StructuredName.DISPLAY_NAME, "Ando Roid"); @@ -454,7 +458,6 @@ public class VCardImporterTests extends VCardTestsBase { public void testV21BackslashCase_Parsing() { mVerifier.initForImportTest(V21, R.raw.v21_backslash); mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("VERSION", "2.1") .addExpectedNodeWithOrder("N", ";A;B\\;C\\;;D;:E;\\\\;", Arrays.asList("", "A;B\\", "C\\;", "D", ":E", "\\\\", "")) .addExpectedNodeWithOrder("FN", "A;B\\C\\;D:E\\\\"); @@ -549,11 +552,11 @@ public class VCardImporterTests extends VCardTestsBase { public void testV21ComplicatedCase_Parsing() { mVerifier.initForImportTest(V21, R.raw.v21_complicated); mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("VERSION", "2.1") .addExpectedNodeWithOrder("N", "Gump;Forrest;Hoge;Pos;Tao", Arrays.asList("Gump", "Forrest", "Hoge", "Pos", "Tao")) .addExpectedNodeWithOrder("FN", "Joe Due") - .addExpectedNodeWithOrder("ORG", "Gump Shrimp Co.;Sales Dept.;Manager;Fish keeper", + .addExpectedNodeWithOrder("ORG", + "Gump Shrimp Co.;Sales Dept.;Manager;Fish keeper", Arrays.asList("Gump Shrimp Co.", "Sales Dept.;Manager", "Fish keeper")) .addExpectedNodeWithOrder("ROLE", "Fish Cake Keeper!") .addExpectedNodeWithOrder("TITLE", "Shrimp Man") @@ -583,7 +586,8 @@ public class VCardImporterTests extends VCardTestsBase { .addExpectedNodeWithOrder("EMAIL", "forrestgump@walladalla.com", new TypeSet("PREF", "INTERNET")) .addExpectedNodeWithOrder("EMAIL", "cell@example.com", new TypeSet("CELL")) - .addExpectedNodeWithOrder("NOTE", "The following note is the example from RFC 2045.") + .addExpectedNodeWithOrder("NOTE", + "The following note is the example from RFC 2045.") .addExpectedNodeWithOrder("NOTE", "Now's the time for all folk to come to the aid of their country.", null, null, mContentValuesForQP, null, null) @@ -677,12 +681,24 @@ public class VCardImporterTests extends VCardTestsBase { .put(Website.TYPE, Website.TYPE_HOMEPAGE); } + public void testInvalidMultipleLineV21() { + mVerifier.initForImportTest(V21, R.raw.v21_invalid_multiple_line); + ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); + elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.GIVEN_NAME, "Omega") + .put(StructuredName.DISPLAY_NAME, "Omega"); + elem.addExpected(Email.CONTENT_ITEM_TYPE) + .put(Email.TYPE, Email.TYPE_CUSTOM) + .put(Email.LABEL, "INTERNET") + .put(Email.ADDRESS, "\"Omega\" <omega@example.com>"); + } + public void testV30Simple_Parsing() { mVerifier.initForImportTest(V30, R.raw.v30_simple); mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("VERSION", "3.0") .addExpectedNodeWithOrder("FN", "And Roid") - .addExpectedNodeWithOrder("N", "And;Roid;;;", Arrays.asList("And", "Roid", "", "", "")) + .addExpectedNodeWithOrder("N", "And;Roid;;;", + Arrays.asList("And", "Roid", "", "", "")) .addExpectedNodeWithOrder("ORG", "Open;Handset; Alliance", Arrays.asList("Open", "Handset", " Alliance")) .addExpectedNodeWithOrder("SORT-STRING", "android") @@ -717,10 +733,8 @@ public class VCardImporterTests extends VCardTestsBase { // Though Japanese careers append ";;;;" at the end of the value of "SOUND", // vCard 2.1/3.0 specification does not allow multiple values. // Do not need to handle it as multiple values. - mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS, - R.raw.v21_japanese_1); + mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_japanese_1); mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("VERSION", "2.1", null, null, null, null, null) .addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9;;;;", Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9", "", "", "", ""), null, mContentValuesForSJis, null, null) @@ -752,37 +766,35 @@ public class VCardImporterTests extends VCardTestsBase { /** * Verifies vCard with Japanese can be parsed correctly with - * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_GENERIC_UTF8}. + * {@link com.android.vcard.VCardConfig#VCARD_TYPE_V21_GENERIC}. */ public void testV21Japanese1_Type_Generic_Utf8() { testV21Japanese1Common( - R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8, false); + R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_GENERIC, false); } /** * Verifies vCard with Japanese can be parsed correctly with - * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE_SJIS}. + * {@link com.android.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE}. */ public void testV21Japanese1_Type_Japanese_Sjis() { testV21Japanese1Common( - R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS, true); + R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE, true); } /** * Verifies vCard with Japanese can be parsed correctly with - * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE_UTF8}. + * {@link com.android.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE}. * since vCard 2.1 specifies the charset of each line if it contains non-Ascii. */ public void testV21Japanese1_Type_Japanese_Utf8() { testV21Japanese1Common( - R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8, true); + R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE, true); } public void testV21Japanese2_Parsing() { - mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS, - R.raw.v21_japanese_2); + mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_japanese_2); mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("VERSION", "2.1") .addExpectedNodeWithOrder("N", "\u5B89\u85E4;\u30ED\u30A4\u30C9\u0031;;;", Arrays.asList("\u5B89\u85E4", "\u30ED\u30A4\u30C9\u0031", "", "", ""), @@ -838,10 +850,8 @@ public class VCardImporterTests extends VCardTestsBase { } public void testV21MultipleEntryCase_Parse() { - mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS, - R.raw.v21_multiple_entry); + mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_multiple_entry); mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("VERSION", "2.1") .addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033;;;;", Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0033", "", "", "", ""), null, mContentValuesForSJis, null, null) @@ -854,7 +864,6 @@ public class VCardImporterTests extends VCardTestsBase { .addExpectedNodeWithOrder("TEL", "11", new TypeSet("X-NEC-SCHOOL")) .addExpectedNodeWithOrder("TEL", "12", new TypeSet("FAX", "HOME")); mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("VERSION", "2.1") .addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0034;;;;", Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0034", "", "", "", ""), null, mContentValuesForSJis, null, null) @@ -867,7 +876,6 @@ public class VCardImporterTests extends VCardTestsBase { .addExpectedNodeWithOrder("TEL", "15", new TypeSet("X-NEC-FAMILY")) .addExpectedNodeWithOrder("TEL", "16", new TypeSet("X-NEC-GIRL")); mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("VERSION", "2.1") .addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0035;;;;", Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0035", "", "", "", ""), null, mContentValuesForSJis, null, null) @@ -882,8 +890,7 @@ public class VCardImporterTests extends VCardTestsBase { } public void testV21MultipleEntryCase() { - mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS, - R.raw.v21_multiple_entry); + mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_multiple_entry); ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033") @@ -957,7 +964,6 @@ public class VCardImporterTests extends VCardTestsBase { ContentValues contentValuesForValue = new ContentValues(); contentValuesForValue.put("VALUE", "DATE"); mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("VERSION", "2.1") .addExpectedNodeWithOrder("N", Arrays.asList("Example", "", "", "", "")) .addExpectedNodeWithOrder("FN", "Example") .addExpectedNodeWithOrder("ANNIVERSARY", "20091010", contentValuesForValue) @@ -974,6 +980,9 @@ public class VCardImporterTests extends VCardTestsBase { elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) .put(StructuredName.FAMILY_NAME, "Example") .put(StructuredName.DISPLAY_NAME, "Example"); + elem.addExpected(Event.CONTENT_ITEM_TYPE) + .put(Event.TYPE, Event.TYPE_ANNIVERSARY) + .put(Event.START_DATE, "20091010"); } public void testTolerateInvalidCommentLikeLineV21() { @@ -988,16 +997,15 @@ public class VCardImporterTests extends VCardTestsBase { } public void testPagerV30_Parse() { - mVerifier.initForImportTest(V30, R.raw.v30_comma_separated); + mVerifier.initForImportTest(V30, R.raw.v30_pager); mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("VERSION", "3.0") .addExpectedNodeWithOrder("N", Arrays.asList("F", "G", "M", "", "")) .addExpectedNodeWithOrder("TEL", "6101231234@pagersample.com", new TypeSet("WORK", "MSG", "PAGER")); } public void testPagerV30() { - mVerifier.initForImportTest(V30, R.raw.v30_comma_separated); + mVerifier.initForImportTest(V30, R.raw.v30_pager); ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) .put(StructuredName.FAMILY_NAME, "F") @@ -1008,4 +1016,93 @@ public class VCardImporterTests extends VCardTestsBase { .put(Phone.TYPE, Phone.TYPE_PAGER) .put(Phone.NUMBER, "6101231234@pagersample.com"); } + + public void testMultiBytePropV30_Parse() { + mVerifier.initForImportTest(V30, R.raw.v30_multibyte_param); + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNodeWithOrder("N", Arrays.asList("F", "G", "M", "", "")) + .addExpectedNodeWithOrder("TEL", "1", new TypeSet("\u8D39")); + } + + public void testMultiBytePropV30() { + mVerifier.initForImportTest(V30, R.raw.v30_multibyte_param); + final ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); + elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "F") + .put(StructuredName.MIDDLE_NAME, "M") + .put(StructuredName.GIVEN_NAME, "G") + .put(StructuredName.DISPLAY_NAME, "G M F"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "\u8D39") + .put(Phone.NUMBER, "1"); + } + + public void testCommaSeparatedParamsV30_Parse() { + mVerifier.initForImportTest(V30, R.raw.v30_comma_separated); + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNodeWithOrder("N", Arrays.asList("F", "G", "M", "", ""), + new TypeSet("PREF", "HOME")) + .addExpectedNodeWithOrder("TEL", "1", + new TypeSet("COMMA,SEPARATED:INSIDE.DQUOTE", "PREF")); + } + + public void testSortAsV40_Parse() { + mVerifier.initForImportTest(V40, R.raw.v40_sort_as); + + final ContentValues contentValuesForSortAsN = new ContentValues(); + contentValuesForSortAsN.put("SORT-AS", + "\u3042\u3093\u3069\u3046;\u308D\u3044\u3069"); + final ContentValues contentValuesForSortAsOrg = new ContentValues(); + contentValuesForSortAsOrg.put("SORT-AS", + "\u3050\u30FC\u3050\u308B;\u3051\u3093\u3055\u304F\u3076\u3082\u3093"); + + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNodeWithOrder("FN", "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9") + .addExpectedNodeWithOrder("N", + Arrays.asList("\u5B89\u85E4", "\u30ED\u30A4\u30C9", "", "", ""), + contentValuesForSortAsN) + .addExpectedNodeWithOrder("ORG", + Arrays.asList("\u30B0\u30FC\u30B0\u30EB", "\u691C\u7D22\u90E8\u9580"), + contentValuesForSortAsOrg, new TypeSet("WORK")); + } + + public void testSortAsV40() { + mVerifier.initForImportTest(V40, R.raw.v40_sort_as); + final ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); + elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4") + .put(StructuredName.GIVEN_NAME, "\u30ED\u30A4\u30C9") + .put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9") + .put(StructuredName.PHONETIC_FAMILY_NAME, "\u3042\u3093\u3069\u3046") + .put(StructuredName.PHONETIC_GIVEN_NAME, + "\u308D\u3044\u3069"); + elem.addExpected(Organization.CONTENT_ITEM_TYPE) + .put(Organization.TYPE, Organization.TYPE_WORK) + .put(Organization.COMPANY, "\u30B0\u30FC\u30B0\u30EB") + .put(Organization.DEPARTMENT, "\u691C\u7D22\u90E8\u9580") + .put(Organization.PHONETIC_NAME, + "\u3050\u30FC\u3050\u308B\u3051\u3093\u3055\u304F\u3076\u3082\u3093"); + } + + public void testIMV21_Parse() { + mVerifier.initForImportTest(V21, R.raw.v21_im); + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNodeWithOrder("X-ANDROID-CUSTOM", + Arrays.asList("vnd.android.cursor.item/nickname", "Nick", "1", + "", "", "", "", "", "", "", "", "", "", "", "", "")) // 13 + .addExpectedNodeWithOrder("X-GOOGLE-TALK", "hhh@gmail.com"); + } + + public void testIMV21() { + mVerifier.initForImportTest(V21, R.raw.v21_im); + final ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); + elem.addExpected(Nickname.CONTENT_ITEM_TYPE) + .put(Nickname.NAME, "Nick") + .put(Nickname.TYPE, "1"); + elem.addExpected(Im.CONTENT_ITEM_TYPE) + .put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK) + .put(Im.TYPE, Im.TYPE_HOME) + .put(Im.DATA, "hhh@gmail.com"); + } } diff --git a/core/tests/coretests/src/android/pim/vcard/VCardJapanizationTests.java b/core/tests/coretests/src/android/pim/vcard/VCardJapanizationTests.java index 5b60342c872b..8a99419dfa11 100644 --- a/core/tests/coretests/src/android/pim/vcard/VCardJapanizationTests.java +++ b/core/tests/coretests/src/android/pim/vcard/VCardJapanizationTests.java @@ -13,19 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package android.pim.vcard; import android.content.ContentValues; -import android.pim.vcard.VCardConfig; +import android.pim.vcard.test_utils.ContactEntry; +import android.pim.vcard.test_utils.ContentValuesBuilder; +import android.pim.vcard.test_utils.PropertyNodesVerifierElem; +import android.pim.vcard.test_utils.PropertyNodesVerifierElem.TypeSet; +import android.pim.vcard.test_utils.VCardTestsBase; import android.provider.ContactsContract.CommonDataKinds.Nickname; import android.provider.ContactsContract.CommonDataKinds.Note; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.CommonDataKinds.StructuredName; import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; -import android.pim.vcard.PropertyNodesVerifierElem.TypeSet; - import java.util.Arrays; public class VCardJapanizationTests extends VCardTestsBase { @@ -39,7 +40,7 @@ public class VCardJapanizationTests extends VCardTestsBase { .put(StructuredName.PREFIX, "Dr.") .put(StructuredName.SUFFIX, "Ph.D"); ContentValues contentValues = - (VCardConfig.isV30(vcardType) ? null : mContentValuesForQPAndUtf8); + (VCardConfig.isVersion21(vcardType) ? mContentValuesForQPAndUtf8 : null); mVerifier.addPropertyNodesVerifierElem() .addExpectedNode("FN", "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D", contentValues) @@ -50,15 +51,15 @@ public class VCardJapanizationTests extends VCardTestsBase { } public void testNameUtf8V21() { - testNameUtf8Common(VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8); + testNameUtf8Common(VCardConfig.VCARD_TYPE_V21_JAPANESE); } public void testNameUtf8V30() { - testNameUtf8Common(VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8); + testNameUtf8Common(VCardConfig.VCARD_TYPE_V30_JAPANESE); } public void testNameShiftJis() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V30_JAPANESE_SJIS); + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V30_JAPANESE, "Shift_JIS"); ContactEntry entry = mVerifier.addInputEntry(); entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) .put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069") @@ -80,7 +81,7 @@ public class VCardJapanizationTests extends VCardTestsBase { * DoCoMo phones require all name elements should be in "family name" field. */ public void testNameDoCoMo() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO); + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); ContactEntry entry = mVerifier.addInputEntry(); entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) .put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069") @@ -105,8 +106,8 @@ public class VCardJapanizationTests extends VCardTestsBase { .addExpectedNode("X-DCM-HMN-MODE", ""); } - private void testPhoneticNameCommon(int vcardType) { - mVerifier.initForExportTest(vcardType); + private void testPhoneticNameCommon(int vcardType, String charset) { + mVerifier.initForExportTest(vcardType, charset); ContactEntry entry = mVerifier.addInputEntry(); entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) .put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060") @@ -114,10 +115,10 @@ public class VCardJapanizationTests extends VCardTestsBase { .put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046"); final ContentValues contentValues = - (VCardConfig.usesShiftJis(vcardType) ? - (VCardConfig.isV30(vcardType) ? mContentValuesForSJis : - mContentValuesForQPAndSJis) : - (VCardConfig.isV30(vcardType) ? null : mContentValuesForQPAndUtf8)); + ("SHIFT_JIS".equalsIgnoreCase(charset) ? + (VCardConfig.isVersion21(vcardType) ? mContentValuesForQPAndSJis : + mContentValuesForSJis) : + (VCardConfig.isVersion21(vcardType) ? mContentValuesForQPAndUtf8 : null)); PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName(); elem.addExpectedNode("X-PHONETIC-LAST-NAME", "\u3084\u307E\u3060", contentValues) @@ -126,7 +127,7 @@ public class VCardJapanizationTests extends VCardTestsBase { contentValues) .addExpectedNode("X-PHONETIC-FIRST-NAME", "\u305F\u308D\u3046", contentValues); - if (VCardConfig.isV30(vcardType)) { + if (!VCardConfig.isVersion21(vcardType)) { elem.addExpectedNode("SORT-STRING", "\u3084\u307E\u3060 \u30DF\u30C9\u30EB\u30CD\u30FC\u30E0 \u305F\u308D\u3046", contentValues); @@ -142,23 +143,23 @@ public class VCardJapanizationTests extends VCardTestsBase { } public void testPhoneticNameForJapaneseV21Utf8() { - testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8); + testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, null); } public void testPhoneticNameForJapaneseV21Sjis() { - testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS); + testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, "Shift_JIS"); } public void testPhoneticNameForJapaneseV30Utf8() { - testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8); + testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE, null); } public void testPhoneticNameForJapaneseV30SJis() { - testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE_SJIS); + testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE, "Shift_JIS"); } public void testPhoneticNameForMobileV21_1() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE); + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE, "Shift_JIS"); ContactEntry entry = mVerifier.addInputEntry(); entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) .put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060") @@ -182,7 +183,7 @@ public class VCardJapanizationTests extends VCardTestsBase { } public void testPhoneticNameForMobileV21_2() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE); + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE, "Shift_JIS"); ContactEntry entry = mVerifier.addInputEntry(); entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) .put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060") @@ -198,8 +199,8 @@ public class VCardJapanizationTests extends VCardTestsBase { .put(StructuredName.DISPLAY_NAME, "\uFF94\uFF8F\uFF80\uFF9E \uFF80\uFF9B\uFF73"); } - private void testPostalAddressWithJapaneseCommon(int vcardType) { - mVerifier.initForExportTest(vcardType); + private void testPostalAddressWithJapaneseCommon(int vcardType, String charset) { + mVerifier.initForExportTest(vcardType, charset); ContactEntry entry = mVerifier.addInputEntry(); entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) .put(StructuredPostal.POBOX, "\u79C1\u66F8\u7BB107") @@ -214,11 +215,11 @@ public class VCardJapanizationTests extends VCardTestsBase { .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM) .put(StructuredPostal.LABEL, "\u304A\u3082\u3061\u304B\u3048\u308A"); - ContentValues contentValues = (VCardConfig.usesShiftJis(vcardType) ? - (VCardConfig.isV30(vcardType) ? mContentValuesForSJis : - mContentValuesForQPAndSJis) : - (VCardConfig.isV30(vcardType) ? mContentValuesForUtf8 : - mContentValuesForQPAndUtf8)); + ContentValues contentValues = ("UTF-8".equalsIgnoreCase(charset) ? + (VCardConfig.isVersion21(vcardType) ? mContentValuesForQPAndSJis : + mContentValuesForSJis) : + (VCardConfig.isVersion21(vcardType) ? mContentValuesForQPAndUtf8 : + mContentValuesForUtf8)); PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName(); // LABEL must be ignored in vCard 2.1. As for vCard 3.0, the current behavior is @@ -240,7 +241,7 @@ public class VCardJapanizationTests extends VCardTestsBase { .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME); } public void testPostalAddresswithJapaneseV21() { - testPostalAddressWithJapaneseCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS); + testPostalAddressWithJapaneseCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, "Shift_JIS"); } /** @@ -248,7 +249,7 @@ public class VCardJapanizationTests extends VCardTestsBase { * Prefered type must (should?) be: HOME > WORK > OTHER > CUSTOM */ public void testPostalAdrressForDoCoMo_1() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO); + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); ContactEntry entry = mVerifier.addInputEntry(); entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK) @@ -276,7 +277,7 @@ public class VCardJapanizationTests extends VCardTestsBase { } public void testPostalAdrressForDoCoMo_2() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO); + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); ContactEntry entry = mVerifier.addInputEntry(); entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) .put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER) @@ -301,7 +302,7 @@ public class VCardJapanizationTests extends VCardTestsBase { } public void testPostalAdrressForDoCoMo_3() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO); + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); ContactEntry entry = mVerifier.addInputEntry(); entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM) @@ -329,7 +330,7 @@ public class VCardJapanizationTests extends VCardTestsBase { * Verifies the vCard exporter tolerates null TYPE. */ public void testPostalAdrressForDoCoMo_4() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO); + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); ContactEntry entry = mVerifier.addInputEntry(); entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) .put(StructuredPostal.POBOX, "1"); @@ -371,15 +372,15 @@ public class VCardJapanizationTests extends VCardTestsBase { } public void testJapanesePhoneNumberV21_1() { - testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8); + testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE); } public void testJapanesePhoneNumberV30() { - testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8); + testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE); } public void testJapanesePhoneNumberDoCoMo() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO); + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); ContactEntry entry = mVerifier.addInputEntry(); entry.addContentValues(Phone.CONTENT_ITEM_TYPE) .put(Phone.NUMBER, "0312341234") @@ -399,7 +400,7 @@ public class VCardJapanizationTests extends VCardTestsBase { } public void testNoteDoCoMo() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO); + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); ContactEntry entry = mVerifier.addInputEntry(); entry.addContentValues(Note.CONTENT_ITEM_TYPE) .put(Note.NOTE, "note1"); @@ -421,7 +422,7 @@ public class VCardJapanizationTests extends VCardTestsBase { } public void testAndroidCustomV21() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8); + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_GENERIC); mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE) .put(Nickname.NAME, "\u304D\u3083\u30FC\u30A8\u30C3\u30C1\u30FC"); mVerifier.addPropertyNodesVerifierElemWithEmptyName() diff --git a/core/tests/coretests/src/android/pim/vcard/VCardTestRunner.java b/core/tests/coretests/src/android/pim/vcard/VCardTestRunner.java new file mode 100644 index 000000000000..a3a4393ef395 --- /dev/null +++ b/core/tests/coretests/src/android/pim/vcard/VCardTestRunner.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 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.pim.vcard; + +import android.test.InstrumentationTestRunner; +import android.test.InstrumentationTestSuite; + +import junit.framework.TestSuite; + +/** + * Usage: adb shell am instrument -w com.android.vcard.tests/.VCardTestRunnerTestRunner + */ +public class VCardTestRunner extends InstrumentationTestRunner { + @Override + public TestSuite getAllTests() { + TestSuite suite = new InstrumentationTestSuite(this); + suite.addTestSuite(VCardUtilsTests.class); + suite.addTestSuite(VCardTestUtilsTests.class); + suite.addTestSuite(VCardImporterTests.class); + suite.addTestSuite(VCardExporterTests.class); + suite.addTestSuite(VCardJapanizationTests.class); + return suite; + } + + @Override + public ClassLoader getLoader() { + return VCardTestRunner.class.getClassLoader(); + } +}
\ No newline at end of file diff --git a/core/tests/coretests/src/android/pim/vcard/VCardTestUtilsTests.java b/core/tests/coretests/src/android/pim/vcard/VCardTestUtilsTests.java new file mode 100644 index 000000000000..3ff5cf7a5158 --- /dev/null +++ b/core/tests/coretests/src/android/pim/vcard/VCardTestUtilsTests.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2010 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.pim.vcard; + +import com.android.frameworks.coretests.R; + +import android.pim.vcard.test_utils.VCardVerifier; +import android.provider.ContactsContract.CommonDataKinds.StructuredName; +import android.test.AndroidTestCase; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; + +import java.util.Arrays; + +/** + * Tests confirming utilities for vCard tests work fine. + * + * Now that the foundation classes for vCard test cases became too complicated to + * rely on without testing itself. + */ +public class VCardTestUtilsTests extends AndroidTestCase { + public void testShouldFailAtPropertyNodeVerification() { + boolean failureDetected = false; + try { + final VCardVerifier verifier = new VCardVerifier(this); + verifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_GENERIC, R.raw.v21_backslash); + verifier.addPropertyNodesVerifierElem() + .addExpectedNodeWithOrder("N", ";A;B\\;C\\;;D;:E;\\\\;--", // wrong + Arrays.asList("", "A;B\\", "C\\;", "D", ":E", "\\\\", "")) + .addExpectedNodeWithOrder("FN", "A;B\\C\\;D:E\\\\"); + verifier.verify(); + } catch (AssertionFailedError e) { + failureDetected = true; + } + if (!failureDetected) { + TestCase.fail("Test case that should fail actually succeeded."); + } + } + + public void testShouldFailAtContentValueVerification() { + boolean failureDetected = false; + try { + final VCardVerifier verifier = new VCardVerifier(this); + verifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_GENERIC, R.raw.v21_backslash); + verifier.addContentValuesVerifierElem() + .addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.GIVEN_NAME, "A;B\\") + .put(StructuredName.MIDDLE_NAME, "C\\;") + .put(StructuredName.PREFIX, "D") + .put(StructuredName.SUFFIX, ":E"); + // DISPLAY_NAME is missing. + verifier.verify(); + } catch (AssertionFailedError e) { + failureDetected = true; + } + if (!failureDetected) { + TestCase.fail("Test case that should fail actually succeeded."); + } + } + + public void testShouldFailAtLineVerification() { + boolean failureDetected = false; + try { + final VCardVerifier verifier = new VCardVerifier(this); + verifier.initForExportTest(VCardConfig.VCARD_TYPE_V30_GENERIC); + verifier.addInputEntry().addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "\\") + .put(StructuredName.GIVEN_NAME, ";") + .put(StructuredName.MIDDLE_NAME, ",") + .put(StructuredName.PREFIX, "\n") + .put(StructuredName.DISPLAY_NAME, "[<{Unescaped:Asciis}>]"); + verifier.addLineVerifierElem() + .addExpected("TEL:1") // wrong + .addExpected("FN:[<{Unescaped:Asciis}>]"); + verifier.verify(); + } catch (AssertionFailedError e) { + failureDetected = true; + } + if (!failureDetected) { + TestCase.fail("Test case that should fail actually succeeded."); + } + } + +} diff --git a/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java b/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java deleted file mode 100644 index 0857e0c6e3ad..000000000000 --- a/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2009 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.pim.vcard; - -import android.content.ContentProvider; -import android.content.ContentProviderOperation; -import android.content.ContentProviderResult; -import android.content.ContentValues; -import android.content.EntityIterator; -import android.content.res.AssetFileDescriptor; -import android.database.Cursor; -import android.database.CursorWindow; -import android.database.IBulkCursor; -import android.database.IContentObserver; -import android.net.Uri; -import android.os.IBinder; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; -import android.pim.vcard.VCardConfig; -import android.test.AndroidTestCase; -import android.util.Log; - -import java.util.ArrayList; - -/** - * Almost a dead copy of android.test.mock.MockContentProvider, but different in that this - * class extends ContentProvider, not implementing IContentProvider, - * so that MockContentResolver is able to accept this class :( - */ -class MockContentProvider extends ContentProvider { - @Override - public boolean onCreate() { - return true; - } - - @Override - public int bulkInsert(Uri url, ContentValues[] initialValues) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @SuppressWarnings("unused") - public IBulkCursor bulkQuery(Uri url, String[] projection, String selection, - String[] selectionArgs, String sortOrder, IContentObserver observer, - CursorWindow window) throws RemoteException { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - @SuppressWarnings("unused") - public int delete(Uri url, String selection, String[] selectionArgs) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - public String getType(Uri url) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - public Uri insert(Uri url, ContentValues initialValues) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - public ParcelFileDescriptor openFile(Uri url, String mode) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - public AssetFileDescriptor openAssetFile(Uri uri, String mode) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, - String sortOrder) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - public int update(Uri url, ContentValues values, String selection, String[] selectionArgs) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - public IBinder asBinder() { - throw new UnsupportedOperationException("unimplemented mock method"); - } -} - -/** - * BaseClass for vCard unit tests with utility classes. - * Please do not add each unit test here. - */ -/* package */ class VCardTestsBase extends AndroidTestCase { - public static final int V21 = VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8; - public static final int V30 = VCardConfig.VCARD_TYPE_V30_GENERIC_UTF8; - - // Do not modify these during tests. - protected final ContentValues mContentValuesForQP; - protected final ContentValues mContentValuesForSJis; - protected final ContentValues mContentValuesForUtf8; - protected final ContentValues mContentValuesForQPAndSJis; - protected final ContentValues mContentValuesForQPAndUtf8; - protected final ContentValues mContentValuesForBase64V21; - protected final ContentValues mContentValuesForBase64V30; - - protected VCardVerifier mVerifier; - private boolean mSkipVerification; - - public VCardTestsBase() { - super(); - mContentValuesForQP = new ContentValues(); - mContentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE"); - mContentValuesForSJis = new ContentValues(); - mContentValuesForSJis.put("CHARSET", "SHIFT_JIS"); - mContentValuesForUtf8 = new ContentValues(); - mContentValuesForUtf8.put("CHARSET", "UTF-8"); - mContentValuesForQPAndSJis = new ContentValues(); - mContentValuesForQPAndSJis.put("ENCODING", "QUOTED-PRINTABLE"); - mContentValuesForQPAndSJis.put("CHARSET", "SHIFT_JIS"); - mContentValuesForQPAndUtf8 = new ContentValues(); - mContentValuesForQPAndUtf8.put("ENCODING", "QUOTED-PRINTABLE"); - mContentValuesForQPAndUtf8.put("CHARSET", "UTF-8"); - mContentValuesForBase64V21 = new ContentValues(); - mContentValuesForBase64V21.put("ENCODING", "BASE64"); - mContentValuesForBase64V30 = new ContentValues(); - mContentValuesForBase64V30.put("ENCODING", "b"); - } - - @Override - public void testAndroidTestCaseSetupProperly() { - super.testAndroidTestCaseSetupProperly(); - mSkipVerification = true; - } - - @Override - public void setUp() throws Exception{ - super.setUp(); - mVerifier = new VCardVerifier(this); - mSkipVerification = false; - } - - @Override - public void tearDown() throws Exception { - super.tearDown(); - if (!mSkipVerification) { - mVerifier.verify(); - } - } -} diff --git a/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java b/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java index 59299f9dff35..251fab212768 100644 --- a/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java +++ b/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package android.pim.vcard; -import android.pim.vcard.VCardUtils; +import android.text.TextUtils; import junit.framework.TestCase; @@ -82,4 +81,38 @@ public class VCardUtilsTests extends TestCase { assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i))); } } + + public void testToStringAvailableAsV30ParamValue() { + // Smoke tests. + assertEquals("HOME", VCardUtils.toStringAsV30ParamValue("HOME")); + assertEquals("TEL", VCardUtils.toStringAsV30ParamValue("TEL")); + assertEquals("PAGER", VCardUtils.toStringAsV30ParamValue("PAGER")); + + assertTrue(TextUtils.isEmpty(VCardUtils.toStringAsV30ParamValue(""))); + assertTrue(TextUtils.isEmpty(VCardUtils.toStringAsV30ParamValue(null))); + assertTrue(TextUtils.isEmpty(VCardUtils.toStringAsV30ParamValue(" \t"))); + + // non-Ascii must be allowed + assertEquals("\u4E8B\u52D9\u6240", + VCardUtils.toStringAsV30ParamValue("\u4E8B\u52D9\u6240")); + // Reported as bug report. + assertEquals("\u8D39", VCardUtils.toStringAsV30ParamValue("\u8D39")); + assertEquals("\"comma,separated\"", + VCardUtils.toStringAsV30ParamValue("comma,separated")); + assertEquals("\"colon:aware\"", + VCardUtils.toStringAsV30ParamValue("colon:aware")); + // CTL characters. + assertEquals("CTLExample", + VCardUtils.toStringAsV30ParamValue("CTL\u0001Example")); + assertTrue(TextUtils.isEmpty( + VCardUtils.toStringAsV30ParamValue("\u0001\u0002\u0003"))); + // DQUOTE must be removed. + assertEquals("quoted", + VCardUtils.toStringAsV30ParamValue("\"quoted\"")); + // DQUOTE must be removed basically, but we should detect a space, which + // require us to use DQUOTE again. + // Right-side has one more illegal dquote to test quote-handle code thoroughly. + assertEquals("\"Already quoted\"", + VCardUtils.toStringAsV30ParamValue("\"Already quoted\"\"")); + } } diff --git a/core/tests/coretests/src/android/pim/vcard/VCardVerifier.java b/core/tests/coretests/src/android/pim/vcard/VCardVerifier.java deleted file mode 100644 index bfc315829c2d..000000000000 --- a/core/tests/coretests/src/android/pim/vcard/VCardVerifier.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (C) 2009 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.pim.vcard; - -import android.content.ContentProvider; -import android.content.ContentResolver; -import android.content.Context; -import android.content.EntityIterator; -import android.net.Uri; -import android.pim.vcard.VCardComposer; -import android.pim.vcard.VCardConfig; -import android.pim.vcard.VCardEntryConstructor; -import android.pim.vcard.VCardInterpreter; -import android.pim.vcard.VCardInterpreterCollection; -import android.pim.vcard.VCardParser; -import android.pim.vcard.VCardParser_V21; -import android.pim.vcard.VCardParser_V30; -import android.pim.vcard.exception.VCardException; -import android.test.AndroidTestCase; -import android.test.mock.MockContext; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Method; -import java.util.Arrays; - -/* package */ class CustomMockContext extends MockContext { - final ContentResolver mResolver; - public CustomMockContext(ContentResolver resolver) { - mResolver = resolver; - } - - @Override - public ContentResolver getContentResolver() { - return mResolver; - } -} - -/* package */ class VCardVerifier { - private class VCardVerifierInternal implements VCardComposer.OneEntryHandler { - public boolean onInit(Context context) { - return true; - } - public boolean onEntryCreated(String vcard) { - verifyOneVCard(vcard); - return true; - } - public void onTerminate() { - } - } - - private final AndroidTestCase mTestCase; - private final VCardVerifierInternal mVCardVerifierInternal; - private int mVCardType; - private boolean mIsV30; - private boolean mIsDoCoMo; - - // Only one of them must be non-empty. - private ExportTestResolver mExportTestResolver; - private InputStream mInputStream; - - // To allow duplication, use list instead of set. - // When null, we don't need to do the verification. - private PropertyNodesVerifier mPropertyNodesVerifier; - private LineVerifier mLineVerifier; - private ContentValuesVerifier mContentValuesVerifier; - private boolean mInitialized; - private boolean mVerified = false; - - public VCardVerifier(AndroidTestCase androidTestCase) { - mTestCase = androidTestCase; - mVCardVerifierInternal = new VCardVerifierInternal(); - mExportTestResolver = null; - mInputStream = null; - mInitialized = false; - mVerified = false; - } - - public void initForExportTest(int vcardType) { - if (mInitialized) { - mTestCase.fail("Already initialized"); - } - mExportTestResolver = new ExportTestResolver(mTestCase); - mVCardType = vcardType; - mIsV30 = VCardConfig.isV30(vcardType); - mIsDoCoMo = VCardConfig.isDoCoMo(vcardType); - mInitialized = true; - } - - public void initForImportTest(int vcardType, int resId) { - if (mInitialized) { - mTestCase.fail("Already initialized"); - } - mVCardType = vcardType; - mIsV30 = VCardConfig.isV30(vcardType); - mIsDoCoMo = VCardConfig.isDoCoMo(vcardType); - setInputResourceId(resId); - mInitialized = true; - } - - private void setInputResourceId(int resId) { - InputStream inputStream = mTestCase.getContext().getResources().openRawResource(resId); - if (inputStream == null) { - mTestCase.fail("Wrong resId: " + resId); - } - setInputStream(inputStream); - } - - private void setInputStream(InputStream inputStream) { - if (mExportTestResolver != null) { - mTestCase.fail("addInputEntry() is called."); - } else if (mInputStream != null) { - mTestCase.fail("InputStream is already set"); - } - mInputStream = inputStream; - } - - public ContactEntry addInputEntry() { - if (!mInitialized) { - mTestCase.fail("Not initialized"); - } - if (mInputStream != null) { - mTestCase.fail("setInputStream is called"); - } - return mExportTestResolver.addInputContactEntry(); - } - - public PropertyNodesVerifierElem addPropertyNodesVerifierElem() { - if (!mInitialized) { - mTestCase.fail("Not initialized"); - } - if (mPropertyNodesVerifier == null) { - mPropertyNodesVerifier = new PropertyNodesVerifier(mTestCase); - } - PropertyNodesVerifierElem elem = - mPropertyNodesVerifier.addPropertyNodesVerifierElem(); - elem.addExpectedNodeWithOrder("VERSION", (mIsV30 ? "3.0" : "2.1")); - - return elem; - } - - public PropertyNodesVerifierElem addPropertyNodesVerifierElemWithEmptyName() { - if (!mInitialized) { - mTestCase.fail("Not initialized"); - } - PropertyNodesVerifierElem elem = addPropertyNodesVerifierElem(); - if (mIsV30) { - elem.addExpectedNodeWithOrder("N", "").addExpectedNodeWithOrder("FN", ""); - } else if (mIsDoCoMo) { - elem.addExpectedNodeWithOrder("N", ""); - } - return elem; - } - - public LineVerifierElem addLineVerifierElem() { - if (!mInitialized) { - mTestCase.fail("Not initialized"); - } - if (mLineVerifier == null) { - mLineVerifier = new LineVerifier(mTestCase, mVCardType); - } - return mLineVerifier.addLineVerifierElem(); - } - - public ContentValuesVerifierElem addContentValuesVerifierElem() { - if (!mInitialized) { - mTestCase.fail("Not initialized"); - } - if (mContentValuesVerifier == null) { - mContentValuesVerifier = new ContentValuesVerifier(); - } - - return mContentValuesVerifier.addElem(mTestCase); - } - - private void verifyOneVCard(final String vcard) { - // Log.d("@@@", vcard); - final VCardInterpreter builder; - if (mContentValuesVerifier != null) { - final VNodeBuilder vnodeBuilder = mPropertyNodesVerifier; - final VCardEntryConstructor vcardDataBuilder = - new VCardEntryConstructor(mVCardType); - vcardDataBuilder.addEntryHandler(mContentValuesVerifier); - if (mPropertyNodesVerifier != null) { - builder = new VCardInterpreterCollection(Arrays.asList( - mPropertyNodesVerifier, vcardDataBuilder)); - } else { - builder = vnodeBuilder; - } - } else { - if (mPropertyNodesVerifier != null) { - builder = mPropertyNodesVerifier; - } else { - return; - } - } - - final VCardParser parser = - (mIsV30 ? new VCardParser_V30(true) : new VCardParser_V21()); - InputStream is = null; - try { - String charset = - (VCardConfig.usesShiftJis(mVCardType) ? "SHIFT_JIS" : "UTF-8"); - is = new ByteArrayInputStream(vcard.getBytes(charset)); - mTestCase.assertEquals(true, parser.parse(is, null, builder)); - } catch (IOException e) { - mTestCase.fail("Unexpected IOException: " + e.getMessage()); - } catch (VCardException e) { - mTestCase.fail("Unexpected VCardException: " + e.getMessage()); - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - } - } - } - } - - public void verify() { - if (!mInitialized) { - mTestCase.fail("Not initialized."); - } - if (mVerified) { - mTestCase.fail("verify() was called twice."); - } - if (mInputStream != null) { - try { - verifyForImportTest(); - } catch (IOException e) { - mTestCase.fail("IOException was thrown: " + e.getMessage()); - } catch (VCardException e) { - mTestCase.fail("VCardException was thrown: " + e.getMessage()); - } - } else if (mExportTestResolver != null){ - verifyForExportTest(); - } else { - mTestCase.fail("No input is determined"); - } - mVerified = true; - } - - private void verifyForImportTest() throws IOException, VCardException { - if (mLineVerifier != null) { - mTestCase.fail("Not supported now."); - } - if (mContentValuesVerifier != null) { - mContentValuesVerifier.verify(mInputStream, mVCardType); - } - } - - public static EntityIterator mockGetEntityIteratorMethod( - final ContentResolver resolver, - final Uri uri, final String selection, - final String[] selectionArgs, final String sortOrder) { - final ContentProvider provider = - resolver.acquireContentProviderClient(uri).getLocalContentProvider(); - return ((ExportTestProvider)provider).queryEntities( - uri, selection, selectionArgs, sortOrder); - } - - private Method getMockGetEntityIteratorMethod() - throws SecurityException, NoSuchMethodException { - return this.getClass().getMethod("mockGetEntityIteratorMethod", - ContentResolver.class, Uri.class, String.class, String[].class, String.class); - } - - private void verifyForExportTest() { - final VCardComposer composer = - new VCardComposer(new CustomMockContext(mExportTestResolver), mVCardType); - composer.addHandler(mLineVerifier); - composer.addHandler(mVCardVerifierInternal); - if (!composer.init(VCardComposer.CONTACTS_TEST_CONTENT_URI, null, null, null)) { - mTestCase.fail("init() failed. Reason: " + composer.getErrorReason()); - } - mTestCase.assertFalse(composer.isAfterLast()); - try { - while (!composer.isAfterLast()) { - try { - final Method mockGetEntityIteratorMethod = getMockGetEntityIteratorMethod(); - mTestCase.assertTrue( - composer.createOneEntry(getMockGetEntityIteratorMethod())); - } catch (Exception e) { - e.printStackTrace(); - mTestCase.fail(); - } - } - } finally { - composer.terminate(); - } - } -} diff --git a/core/tests/coretests/src/android/pim/vcard/test_utils/ContactEntry.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ContactEntry.java new file mode 100644 index 000000000000..843750e789b3 --- /dev/null +++ b/core/tests/coretests/src/android/pim/vcard/test_utils/ContactEntry.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 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.pim.vcard.test_utils; + +import android.content.ContentValues; +import android.provider.ContactsContract.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * <p> + * The class representing one contact, which should contain multiple ContentValues like + * StructuredName, Email, etc. + * </p> + */ +public final class ContactEntry { + private final List<ContentValues> mContentValuesList = new ArrayList<ContentValues>(); + + public ContentValuesBuilder addContentValues(String mimeType) { + ContentValues contentValues = new ContentValues(); + contentValues.put(Data.MIMETYPE, mimeType); + mContentValuesList.add(contentValues); + return new ContentValuesBuilder(contentValues); + } + + public List<ContentValues> getList() { + return mContentValuesList; + } +}
\ No newline at end of file diff --git a/core/tests/coretests/src/android/pim/vcard/ContentValuesBuilder.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesBuilder.java index b3c0773903fa..bdcccf98e6d9 100644 --- a/core/tests/coretests/src/android/pim/vcard/ContentValuesBuilder.java +++ b/core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesBuilder.java @@ -13,8 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package android.pim.vcard; +package android.pim.vcard.test_utils; import android.content.ContentValues; @@ -22,7 +21,7 @@ import android.content.ContentValues; * ContentValues-like class which enables users to chain put() methods and restricts * the other methods. */ -/* package */ class ContentValuesBuilder { +public class ContentValuesBuilder { private final ContentValues mContentValues; public ContentValuesBuilder(final ContentValues contentValues) { @@ -34,6 +33,7 @@ import android.content.ContentValues; return this; } + /* public ContentValuesBuilder put(String key, Byte value) { mContentValues.put(key, value); return this; @@ -42,13 +42,14 @@ import android.content.ContentValues; public ContentValuesBuilder put(String key, Short value) { mContentValues.put(key, value); return this; - } + }*/ public ContentValuesBuilder put(String key, Integer value) { mContentValues.put(key, value); return this; } + /* public ContentValuesBuilder put(String key, Long value) { mContentValues.put(key, value); return this; @@ -67,7 +68,7 @@ import android.content.ContentValues; public ContentValuesBuilder put(String key, Boolean value) { mContentValues.put(key, value); return this; - } + }*/ public ContentValuesBuilder put(String key, byte[] value) { mContentValues.put(key, value); diff --git a/core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesVerifier.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesVerifier.java new file mode 100644 index 000000000000..d0613c65c256 --- /dev/null +++ b/core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesVerifier.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2009 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.pim.vcard.test_utils; + +import android.pim.vcard.VCardEntry; +import android.pim.vcard.VCardEntryHandler; +import android.test.AndroidTestCase; + +import java.util.ArrayList; +import java.util.List; + +public class ContentValuesVerifier implements VCardEntryHandler { + private List<ContentValuesVerifierElem> mContentValuesVerifierElemList = + new ArrayList<ContentValuesVerifierElem>(); + private int mIndex; + + public ContentValuesVerifierElem addElem(AndroidTestCase androidTestCase) { + ContentValuesVerifierElem elem = new ContentValuesVerifierElem(androidTestCase); + mContentValuesVerifierElemList.add(elem); + return elem; + } + + public void onStart() { + for (ContentValuesVerifierElem elem : mContentValuesVerifierElemList) { + elem.onParsingStart(); + } + } + + public void onEntryCreated(VCardEntry entry) { + AndroidTestCase.assertTrue(mIndex < mContentValuesVerifierElemList.size()); + mContentValuesVerifierElemList.get(mIndex).onEntryCreated(entry); + mIndex++; + } + + public void onEnd() { + for (ContentValuesVerifierElem elem : mContentValuesVerifierElemList) { + elem.onParsingEnd(); + elem.verifyResolver(); + } + } +} diff --git a/core/tests/coretests/src/android/pim/vcard/ContentValuesVerifierElem.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesVerifierElem.java index 2edbb36d4220..03c18e1f0c69 100644 --- a/core/tests/coretests/src/android/pim/vcard/ContentValuesVerifierElem.java +++ b/core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesVerifierElem.java @@ -13,17 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.pim.vcard; +package android.pim.vcard.test_utils; import android.content.ContentValues; -import android.pim.vcard.VCardConfig; import android.pim.vcard.VCardEntry; import android.pim.vcard.VCardEntryCommitter; import android.pim.vcard.VCardEntryConstructor; import android.pim.vcard.VCardEntryHandler; import android.pim.vcard.VCardParser; -import android.pim.vcard.VCardParser_V21; -import android.pim.vcard.VCardParser_V30; +import android.pim.vcard.VCardUtils; import android.pim.vcard.exception.VCardException; import android.provider.ContactsContract.Data; import android.test.AndroidTestCase; @@ -31,7 +29,7 @@ import android.test.AndroidTestCase; import java.io.IOException; import java.io.InputStream; -/* package */ class ContentValuesVerifierElem { +public class ContentValuesVerifierElem { private final AndroidTestCase mTestCase; private final ImportTestResolver mResolver; private final VCardEntryHandler mHandler; @@ -49,20 +47,14 @@ import java.io.InputStream; return new ContentValuesBuilder(contentValues); } - public void verify(int resId, int vCardType) + public void verify(int resId, int vcardType) throws IOException, VCardException { - verify(mTestCase.getContext().getResources().openRawResource(resId), vCardType); + verify(mTestCase.getContext().getResources().openRawResource(resId), vcardType); } - public void verify(InputStream is, int vCardType) throws IOException, VCardException { - final VCardParser vCardParser; - if (VCardConfig.isV30(vCardType)) { - vCardParser = new VCardParser_V30(true); // use StrictParsing - } else { - vCardParser = new VCardParser_V21(); - } - VCardEntryConstructor builder = - new VCardEntryConstructor(null, null, false, vCardType, null); + public void verify(InputStream is, int vcardType) throws IOException, VCardException { + final VCardParser vCardParser = VCardUtils.getAppropriateParser(vcardType); + final VCardEntryConstructor builder = new VCardEntryConstructor(vcardType, null); builder.addEntryHandler(mHandler); try { vCardParser.parse(is, builder); diff --git a/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ExportTestProvider.java index 5968e83647d1..e095258ff592 100644 --- a/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java +++ b/core/tests/coretests/src/android/pim/vcard/test_utils/ExportTestProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * Copyright (C) 2010 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 @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ -package android.pim.vcard; +package android.pim.vcard.test_utils; import android.content.ContentResolver; import android.content.ContentValues; @@ -21,10 +21,12 @@ import android.content.Entity; import android.content.EntityIterator; import android.database.Cursor; import android.net.Uri; +import android.pim.vcard.VCardComposer; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.RawContacts; -import android.test.mock.MockContentResolver; +import android.test.AndroidTestCase; +import android.test.mock.MockContentProvider; import android.test.mock.MockCursor; import junit.framework.TestCase; @@ -33,78 +35,44 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; -/* package */ public class ExportTestResolver extends MockContentResolver { - ExportTestProvider mProvider; - public ExportTestResolver(TestCase testCase) { - mProvider = new ExportTestProvider(testCase); - addProvider(VCardComposer.VCARD_TEST_AUTHORITY, mProvider); - addProvider(RawContacts.CONTENT_URI.getAuthority(), mProvider); - } - - public ContactEntry addInputContactEntry() { - return mProvider.buildInputEntry(); - } -} +public class ExportTestProvider extends MockContentProvider { + final private ArrayList<ContactEntry> mContactEntryList = new ArrayList<ContactEntry>(); -/* package */ class MockEntityIterator implements EntityIterator { - List<Entity> mEntityList; - Iterator<Entity> mIterator; + private static class MockEntityIterator implements EntityIterator { + List<Entity> mEntityList; + Iterator<Entity> mIterator; - public MockEntityIterator(List<ContentValues> contentValuesList) { - mEntityList = new ArrayList<Entity>(); - Entity entity = new Entity(new ContentValues()); - for (ContentValues contentValues : contentValuesList) { - entity.addSubValue(Data.CONTENT_URI, contentValues); + public MockEntityIterator(List<ContentValues> contentValuesList) { + mEntityList = new ArrayList<Entity>(); + Entity entity = new Entity(new ContentValues()); + for (ContentValues contentValues : contentValuesList) { + entity.addSubValue(Data.CONTENT_URI, contentValues); + } + mEntityList.add(entity); + mIterator = mEntityList.iterator(); } - mEntityList.add(entity); - mIterator = mEntityList.iterator(); - } - public boolean hasNext() { - return mIterator.hasNext(); - } - - public Entity next() { - return mIterator.next(); - } - - public void remove() { - throw new UnsupportedOperationException("remove not supported"); - } + public boolean hasNext() { + return mIterator.hasNext(); + } - public void reset() { - mIterator = mEntityList.iterator(); - } + public Entity next() { + return mIterator.next(); + } - public void close() { - } -} + public void remove() { + throw new UnsupportedOperationException("remove not supported"); + } -/** - * Represents one contact, which should contain multiple ContentValues like - * StructuredName, Email, etc. - */ -/* package */ class ContactEntry { - private final List<ContentValues> mContentValuesList = new ArrayList<ContentValues>(); - - public ContentValuesBuilder addContentValues(String mimeType) { - ContentValues contentValues = new ContentValues(); - contentValues.put(Data.MIMETYPE, mimeType); - mContentValuesList.add(contentValues); - return new ContentValuesBuilder(contentValues); - } + public void reset() { + mIterator = mEntityList.iterator(); + } - public List<ContentValues> getList() { - return mContentValuesList; + public void close() { + } } -} - -/* package */ class ExportTestProvider extends MockContentProvider { - final private TestCase mTestCase; - final private ArrayList<ContactEntry> mContactEntryList = new ArrayList<ContactEntry>(); - public ExportTestProvider(TestCase testCase) { - mTestCase = testCase; + public ExportTestProvider(AndroidTestCase androidTestCase) { } public ContactEntry buildInputEntry() { @@ -121,27 +89,17 @@ import java.util.List; * We still keep using this method since we don't have a propeer way to know * which value in the ContentValue corresponds to the entry in Contacts database. * </p> - * <p> - * Detail: - * There's an easy way to know which index "family name" corresponds to, via - * {@link android.provider.ContactsContract}. - * FAMILY_NAME equals DATA3, so the corresponding index - * for "family name" should be 2 (note that index is 0-origin). - * However, we cannot know what the index 2 corresponds to; it may be "family name", - * "label" for now, but may be the other some column in the future. We don't have - * convenient way to know the original data structure. - * </p> */ public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs, String sortOrder) { - mTestCase.assertTrue(uri != null); - mTestCase.assertTrue(ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())); + TestCase.assertTrue(uri != null); + TestCase.assertTrue(ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())); final String authority = uri.getAuthority(); - mTestCase.assertTrue(RawContacts.CONTENT_URI.getAuthority().equals(authority)); - mTestCase.assertTrue((Data.CONTACT_ID + "=?").equals(selection)); - mTestCase.assertEquals(1, selectionArgs.length); + TestCase.assertTrue(RawContacts.CONTENT_URI.getAuthority().equals(authority)); + TestCase.assertTrue((Data.CONTACT_ID + "=?").equals(selection)); + TestCase.assertEquals(1, selectionArgs.length); final int id = Integer.parseInt(selectionArgs[0]); - mTestCase.assertTrue(id >= 0 && id < mContactEntryList.size()); + TestCase.assertTrue(id >= 0 && id < mContactEntryList.size()); return new MockEntityIterator(mContactEntryList.get(id).getList()); } @@ -149,11 +107,11 @@ import java.util.List; @Override public Cursor query(Uri uri,String[] projection, String selection, String[] selectionArgs, String sortOrder) { - mTestCase.assertTrue(VCardComposer.CONTACTS_TEST_CONTENT_URI.equals(uri)); + TestCase.assertTrue(VCardComposer.CONTACTS_TEST_CONTENT_URI.equals(uri)); // In this test, following arguments are not supported. - mTestCase.assertNull(selection); - mTestCase.assertNull(selectionArgs); - mTestCase.assertNull(sortOrder); + TestCase.assertNull(selection); + TestCase.assertNull(selectionArgs); + TestCase.assertNull(sortOrder); return new MockCursor() { int mCurrentPosition = -1; @@ -191,14 +149,14 @@ import java.util.List; @Override public int getColumnIndex(String columnName) { - mTestCase.assertEquals(Contacts._ID, columnName); + TestCase.assertEquals(Contacts._ID, columnName); return 0; } @Override public int getInt(int columnIndex) { - mTestCase.assertEquals(0, columnIndex); - mTestCase.assertTrue(mCurrentPosition >= 0 + TestCase.assertEquals(0, columnIndex); + TestCase.assertTrue(mCurrentPosition >= 0 && mCurrentPosition < mContactEntryList.size()); return mCurrentPosition; } @@ -213,4 +171,4 @@ import java.util.List; } }; } -} +}
\ No newline at end of file diff --git a/core/tests/coretests/src/android/pim/vcard/test_utils/ExportTestResolver.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ExportTestResolver.java new file mode 100644 index 000000000000..606d91afaa34 --- /dev/null +++ b/core/tests/coretests/src/android/pim/vcard/test_utils/ExportTestResolver.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2009 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.pim.vcard.test_utils; + +import android.pim.vcard.VCardComposer; +import android.provider.ContactsContract.RawContacts; +import android.test.AndroidTestCase; +import android.test.mock.MockContentResolver; + +public class ExportTestResolver extends MockContentResolver { + private final ExportTestProvider mProvider; + public ExportTestResolver(AndroidTestCase androidTestCase) { + mProvider = new ExportTestProvider(androidTestCase); + addProvider(VCardComposer.VCARD_TEST_AUTHORITY, mProvider); + addProvider(RawContacts.CONTENT_URI.getAuthority(), mProvider); + } + + public ContactEntry addInputContactEntry() { + return mProvider.buildInputEntry(); + } + + public ExportTestProvider getProvider() { + return mProvider; + } +} diff --git a/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ImportTestProvider.java index c3f6f798ba7c..df89491afab5 100644 --- a/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java +++ b/core/tests/coretests/src/android/pim/vcard/test_utils/ImportTestProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * Copyright (C) 2010 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. @@ -13,14 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.pim.vcard; +package android.pim.vcard.test_utils; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; import android.content.ContentValues; import android.net.Uri; -import android.provider.ContactsContract.Data; -import android.provider.ContactsContract.RawContacts; import android.provider.ContactsContract.CommonDataKinds.Email; import android.provider.ContactsContract.CommonDataKinds.Event; import android.provider.ContactsContract.CommonDataKinds.GroupMembership; @@ -34,7 +32,10 @@ import android.provider.ContactsContract.CommonDataKinds.Relation; import android.provider.ContactsContract.CommonDataKinds.StructuredName; import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; import android.provider.ContactsContract.CommonDataKinds.Website; -import android.test.mock.MockContentResolver; +import android.provider.ContactsContract.Data; +import android.provider.ContactsContract.RawContacts; +import android.test.AndroidTestCase; +import android.test.mock.MockContentProvider; import android.text.TextUtils; import junit.framework.TestCase; @@ -45,43 +46,12 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; -import java.util.Map.Entry; - -/* package */ class ImportTestResolver extends MockContentResolver { - final ImportTestProvider mProvider; - - public ImportTestResolver(TestCase testCase) { - mProvider = new ImportTestProvider(testCase); - } - - @Override - public ContentProviderResult[] applyBatch(String authority, - ArrayList<ContentProviderOperation> operations) { - equalsString(authority, RawContacts.CONTENT_URI.toString()); - return mProvider.applyBatch(operations); - } - public void addExpectedContentValues(ContentValues expectedContentValues) { - mProvider.addExpectedContentValues(expectedContentValues); - } - - public void verify() { - mProvider.verify(); - } - - private static boolean equalsString(String a, String b) { - if (a == null || a.length() == 0) { - return b == null || b.length() == 0; - } else { - return a.equals(b); - } - } -} - -/* package */ class ImportTestProvider extends MockContentProvider { +public class ImportTestProvider extends MockContentProvider { private static final Set<String> sKnownMimeTypeSet = new HashSet<String>(Arrays.asList(StructuredName.CONTENT_ITEM_TYPE, Nickname.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE, @@ -94,10 +64,7 @@ import java.util.Map.Entry; final Map<String, Collection<ContentValues>> mMimeTypeToExpectedContentValues; - private final TestCase mTestCase; - - public ImportTestProvider(TestCase testCase) { - mTestCase = testCase; + public ImportTestProvider(AndroidTestCase androidTestCase) { mMimeTypeToExpectedContentValues = new HashMap<String, Collection<ContentValues>>(); for (String acceptanbleMimeType : sKnownMimeTypeSet) { @@ -112,7 +79,7 @@ import java.util.Map.Entry; public void addExpectedContentValues(ContentValues expectedContentValues) { final String mimeType = expectedContentValues.getAsString(Data.MIMETYPE); if (!sKnownMimeTypeSet.contains(mimeType)) { - mTestCase.fail(String.format( + TestCase.fail(String.format( "Unknow MimeType %s in the test code. Test code should be broken.", mimeType)); } @@ -126,7 +93,7 @@ import java.util.Map.Entry; public ContentProviderResult[] applyBatch( ArrayList<ContentProviderOperation> operations) { if (operations == null) { - mTestCase.fail("There is no operation."); + TestCase.fail("There is no operation."); } final int size = operations.size(); @@ -147,12 +114,12 @@ import java.util.Map.Entry; fakeResultArray, i); final Uri uri = operation.getUri(); if (uri.equals(RawContacts.CONTENT_URI)) { - mTestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_NAME)); - mTestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_TYPE)); + TestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_NAME)); + TestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_TYPE)); } else if (uri.equals(Data.CONTENT_URI)) { final String mimeType = actualContentValues.getAsString(Data.MIMETYPE); if (!sKnownMimeTypeSet.contains(mimeType)) { - mTestCase.fail(String.format( + TestCase.fail(String.format( "Unknown MimeType %s. Probably added after developing this test", mimeType)); } @@ -184,7 +151,7 @@ import java.util.Map.Entry; final Collection<ContentValues> contentValuesCollection = mMimeTypeToExpectedContentValues.get(mimeType); if (contentValuesCollection.isEmpty()) { - mTestCase.fail("ContentValues for MimeType " + mimeType + TestCase.fail("ContentValues for MimeType " + mimeType + " is not expected at all (" + actualContentValues + ")"); } boolean checked = false; @@ -196,23 +163,25 @@ import java.util.Map.Entry; + convertToEasilyReadableString(actualContentValues));*/ if (equalsForContentValues(expectedContentValues, actualContentValues)) { - mTestCase.assertTrue(contentValuesCollection.remove(expectedContentValues)); + TestCase.assertTrue(contentValuesCollection.remove(expectedContentValues)); checked = true; break; } } if (!checked) { final StringBuilder builder = new StringBuilder(); + builder.append("\n"); builder.append("Unexpected: "); builder.append(convertToEasilyReadableString(actualContentValues)); - builder.append("\nExpected: "); + builder.append("\n"); + builder.append("Expected : "); for (ContentValues expectedContentValues : contentValuesCollection) { builder.append(convertToEasilyReadableString(expectedContentValues)); } - mTestCase.fail(builder.toString()); + TestCase.fail(builder.toString()); } } else { - mTestCase.fail("Unexpected Uri has come: " + uri); + TestCase.fail("Unexpected Uri has come: " + uri); } } // for (int i = 0; i < size; i++) { return fakeResultArray; @@ -231,7 +200,7 @@ import java.util.Map.Entry; final String failMsg = "There is(are) remaining expected ContentValues instance(s): \n" + builder.toString(); - mTestCase.fail(failMsg); + TestCase.fail(failMsg); } } @@ -251,7 +220,7 @@ import java.util.Map.Entry; if (Data.MIMETYPE.equals(key)) { mimeTypeValue = valueString; } else { - mTestCase.assertNotNull(key); + TestCase.assertNotNull(key); sortedMap.put(key, valueString); } } @@ -272,7 +241,7 @@ import java.util.Map.Entry; } private static boolean equalsForContentValues( - ContentValues expected, ContentValues actual) { + final ContentValues expected, final ContentValues actual) { if (expected == actual) { return true; } else if (expected == null || actual == null || expected.size() != actual.size()) { @@ -285,15 +254,21 @@ import java.util.Map.Entry; if (!actual.containsKey(key)) { return false; } + // Type mismatch usuall happens as importer doesn't care the type of each value. + // For example, variable type might be Integer when importing the type of TEL, + // while variable type would be String when importing the type of RELATION. + final Object actualValue = actual.get(key); if (value instanceof byte[]) { - Object actualValue = actual.get(key); if (!Arrays.equals((byte[])value, (byte[])actualValue)) { + byte[] e = (byte[])value; + byte[] a = (byte[])actualValue; return false; } - } else if (!value.equals(actual.get(key))) { - return false; + } else if (!value.equals(actualValue) && + !value.toString().equals(actualValue.toString())) { + return false; } } return true; } -} +}
\ No newline at end of file diff --git a/core/tests/coretests/src/android/pim/vcard/test_utils/ImportTestResolver.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ImportTestResolver.java new file mode 100644 index 000000000000..1822afefe71f --- /dev/null +++ b/core/tests/coretests/src/android/pim/vcard/test_utils/ImportTestResolver.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009 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.pim.vcard.test_utils; + +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentValues; +import android.provider.ContactsContract.RawContacts; +import android.test.AndroidTestCase; +import android.test.mock.MockContentResolver; + +import java.util.ArrayList; + +public class ImportTestResolver extends MockContentResolver { + private final ImportTestProvider mProvider; + + public ImportTestResolver(AndroidTestCase androidTestCase) { + mProvider = new ImportTestProvider(androidTestCase); + } + + @Override + public ContentProviderResult[] applyBatch(String authority, + ArrayList<ContentProviderOperation> operations) { + equalsString(authority, RawContacts.CONTENT_URI.toString()); + return mProvider.applyBatch(operations); + } + + public void addExpectedContentValues(ContentValues expectedContentValues) { + mProvider.addExpectedContentValues(expectedContentValues); + } + + public void verify() { + mProvider.verify(); + } + + private static boolean equalsString(String a, String b) { + if (a == null || a.length() == 0) { + return b == null || b.length() == 0; + } else { + return a.equals(b); + } + } +} diff --git a/core/tests/coretests/src/android/pim/vcard/LineVerifier.java b/core/tests/coretests/src/android/pim/vcard/test_utils/LineVerifier.java index cef15fd762c0..e314ac5798b1 100644 --- a/core/tests/coretests/src/android/pim/vcard/LineVerifier.java +++ b/core/tests/coretests/src/android/pim/vcard/test_utils/LineVerifier.java @@ -13,39 +13,40 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.pim.vcard; +package android.pim.vcard.test_utils; import android.content.Context; import android.pim.vcard.VCardComposer; +import android.test.AndroidTestCase; import junit.framework.TestCase; import java.util.ArrayList; -class LineVerifier implements VCardComposer.OneEntryHandler { - private final TestCase mTestCase; +public class LineVerifier implements VCardComposer.OneEntryHandler { + private final AndroidTestCase mAndroidTestCase; private final ArrayList<LineVerifierElem> mLineVerifierElemList; private int mVCardType; private int index; - public LineVerifier(TestCase testCase, int vcardType) { - mTestCase = testCase; + public LineVerifier(AndroidTestCase androidTestCase, int vcardType) { + mAndroidTestCase = androidTestCase; mLineVerifierElemList = new ArrayList<LineVerifierElem>(); mVCardType = vcardType; } public LineVerifierElem addLineVerifierElem() { - LineVerifierElem lineVerifier = new LineVerifierElem(mTestCase, mVCardType); + LineVerifierElem lineVerifier = new LineVerifierElem(mAndroidTestCase, mVCardType); mLineVerifierElemList.add(lineVerifier); return lineVerifier; } public void verify(String vcard) { if (index >= mLineVerifierElemList.size()) { - mTestCase.fail("Insufficient number of LineVerifier (" + index + ")"); + TestCase.fail("Insufficient number of LineVerifier (" + index + ")"); } - LineVerifierElem lineVerifier = mLineVerifierElemList.get(index); + final LineVerifierElem lineVerifier = mLineVerifierElemList.get(index); lineVerifier.verify(vcard); index++; diff --git a/core/tests/coretests/src/android/pim/vcard/LineVerifierElem.java b/core/tests/coretests/src/android/pim/vcard/test_utils/LineVerifierElem.java index b23b29be8a6a..e23a9cded618 100644 --- a/core/tests/coretests/src/android/pim/vcard/LineVerifierElem.java +++ b/core/tests/coretests/src/android/pim/vcard/test_utils/LineVerifierElem.java @@ -13,9 +13,10 @@ * License for the specific language governing permissions and limitations under * the License. */ -package android.pim.vcard; +package android.pim.vcard.test_utils; import android.pim.vcard.VCardConfig; +import android.test.AndroidTestCase; import android.text.TextUtils; import junit.framework.TestCase; @@ -23,14 +24,12 @@ import junit.framework.TestCase; import java.util.ArrayList; import java.util.List; -class LineVerifierElem { - private final TestCase mTestCase; +public class LineVerifierElem { private final List<String> mExpectedLineList = new ArrayList<String>(); - private final boolean mIsV30; + private final int mVCardType; - public LineVerifierElem(TestCase testCase, int vcardType) { - mTestCase = testCase; - mIsV30 = VCardConfig.isV30(vcardType); + public LineVerifierElem(AndroidTestCase androidTestCase, int vcardType) { + mVCardType = vcardType; } public LineVerifierElem addExpected(final String line) { @@ -55,21 +54,23 @@ class LineVerifierElem { if ("BEGIN:VCARD".equalsIgnoreCase(line)) { if (beginExists) { - mTestCase.fail("Multiple \"BEGIN:VCARD\" line found"); + TestCase.fail("Multiple \"BEGIN:VCARD\" line found"); } else { beginExists = true; continue; } } else if ("END:VCARD".equalsIgnoreCase(line)) { if (endExists) { - mTestCase.fail("Multiple \"END:VCARD\" line found"); + TestCase.fail("Multiple \"END:VCARD\" line found"); } else { endExists = true; continue; } - } else if ((mIsV30 ? "VERSION:3.0" : "VERSION:2.1").equalsIgnoreCase(line)) { + } else if ((VCardConfig.isVersion21(mVCardType) ? "VERSION:2.1" : + (VCardConfig.isVersion30(mVCardType) ? "VERSION:3.0" : + "VERSION:4.0")).equalsIgnoreCase(line)) { if (versionExists) { - mTestCase.fail("Multiple VERSION line + found"); + TestCase.fail("Multiple VERSION line + found"); } else { versionExists = true; continue; @@ -77,18 +78,16 @@ class LineVerifierElem { } if (!beginExists) { - mTestCase.fail("Property other than BEGIN came before BEGIN property: " - + line); + TestCase.fail("Property other than BEGIN came before BEGIN property: " + line); } else if (endExists) { - mTestCase.fail("Property other than END came after END property: " - + line); + TestCase.fail("Property other than END came after END property: " + line); } final int index = mExpectedLineList.indexOf(line); if (index >= 0) { mExpectedLineList.remove(index); } else { - mTestCase.fail("Unexpected line: " + line); + TestCase.fail("Unexpected line: " + line); } } @@ -99,7 +98,7 @@ class LineVerifierElem { buffer.append("\n"); } - mTestCase.fail("Expected line(s) not found:" + buffer.toString()); + TestCase.fail("Expected line(s) not found:" + buffer.toString()); } } } diff --git a/core/tests/coretests/src/android/pim/vcard/PropertyNode.java b/core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNode.java index 2c1f6d28de98..de7ad8eaa64e 100644 --- a/core/tests/coretests/src/android/pim/vcard/PropertyNode.java +++ b/core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNode.java @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.pim.vcard; +package android.pim.vcard.test_utils; import android.content.ContentValues; import android.pim.vcard.VCardEntry; -import android.util.Log; import java.util.ArrayList; import java.util.Arrays; @@ -26,12 +25,18 @@ import java.util.List; import java.util.Set; /** + * <p> + * The class representing one property (e.g. "N;ENCODING=UTF-8:family:given:middle:prefix:suffix"). + * </p> + * <p> * Previously used in main vCard handling code but now exists only for testing. - * + * </p> + * <p> * Especially useful for testing parser code (VCardParser), since all properties can be * checked via this class unlike {@link VCardEntry}, which only emits the result of * interpretation of the content of each vCard. We cannot know whether vCard parser or - * ContactStruct is wrong withouth this class. + * {@link VCardEntry} is wrong without this class. + * </p> */ public class PropertyNode { public String propName; @@ -43,7 +48,8 @@ public class PropertyNode { */ public byte[] propValue_bytes; - /** param store: key=paramType, value=paramValue + /** + * param store: key=paramType, value=paramValue * Note that currently PropertyNode class does not support multiple param-values * defined in vCard 3.0 (See also RFC 2426). multiple-values are stored as * one String value like "A,B", not ["A", "B"]... diff --git a/core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNodesVerifier.java b/core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNodesVerifier.java new file mode 100644 index 000000000000..422928494594 --- /dev/null +++ b/core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNodesVerifier.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2009 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.pim.vcard.test_utils; + +import android.pim.vcard.VCardParser; +import android.pim.vcard.VCardUtils; +import android.pim.vcard.exception.VCardException; +import android.test.AndroidTestCase; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public class PropertyNodesVerifier extends VNodeBuilder { + private final List<PropertyNodesVerifierElem> mPropertyNodesVerifierElemList; + private final AndroidTestCase mAndroidTestCase; + private int mIndex; + + public PropertyNodesVerifier(AndroidTestCase testCase) { + super(); + mPropertyNodesVerifierElemList = new ArrayList<PropertyNodesVerifierElem>(); + mAndroidTestCase = testCase; + } + + public PropertyNodesVerifierElem addPropertyNodesVerifierElem() { + PropertyNodesVerifierElem elem = new PropertyNodesVerifierElem(mAndroidTestCase); + mPropertyNodesVerifierElemList.add(elem); + return elem; + } + + public void verify(int resId, int vcardType) throws IOException, VCardException { + verify(mAndroidTestCase.getContext().getResources().openRawResource(resId), vcardType); + } + + public void verify(int resId, int vcardType, final VCardParser parser) + throws IOException, VCardException { + verify(mAndroidTestCase.getContext().getResources().openRawResource(resId), + vcardType, parser); + } + + public void verify(InputStream is, int vcardType) throws IOException, VCardException { + final VCardParser parser = VCardUtils.getAppropriateParser(vcardType); + verify(is, vcardType, parser); + } + + public void verify(InputStream is, int vcardType, final VCardParser parser) + throws IOException, VCardException { + try { + parser.parse(is, this); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + } + } + } + } + + @Override + public void endEntry() { + super.endEntry(); + AndroidTestCase.assertTrue(mIndex < mPropertyNodesVerifierElemList.size()); + AndroidTestCase.assertTrue(mIndex < vNodeList.size()); + mPropertyNodesVerifierElemList.get(mIndex).verify(vNodeList.get(mIndex)); + mIndex++; + } +} diff --git a/core/tests/coretests/src/android/pim/vcard/PropertyNodesVerifier.java b/core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNodesVerifierElem.java index cfdd0743fa5c..44c6abc9d0ae 100644 --- a/core/tests/coretests/src/android/pim/vcard/PropertyNodesVerifier.java +++ b/core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNodesVerifierElem.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * Copyright (C) 2010 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. @@ -13,87 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.pim.vcard; +package android.pim.vcard.test_utils; import android.content.ContentValues; -import android.pim.vcard.VCardConfig; -import android.pim.vcard.VCardParser; -import android.pim.vcard.VCardParser_V21; -import android.pim.vcard.VCardParser_V30; -import android.pim.vcard.exception.VCardException; import android.test.AndroidTestCase; import junit.framework.TestCase; -import java.io.IOException; -import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; -/* package */ class PropertyNodesVerifier extends VNodeBuilder { - private final List<PropertyNodesVerifierElem> mPropertyNodesVerifierElemList; - private final AndroidTestCase mAndroidTestCase; - private int mIndex; - - public PropertyNodesVerifier(AndroidTestCase testCase) { - mPropertyNodesVerifierElemList = new ArrayList<PropertyNodesVerifierElem>(); - mAndroidTestCase = testCase; - } - - public PropertyNodesVerifierElem addPropertyNodesVerifierElem() { - PropertyNodesVerifierElem elem = new PropertyNodesVerifierElem(mAndroidTestCase); - mPropertyNodesVerifierElemList.add(elem); - return elem; - } - - public void verify(int resId, int vCardType) - throws IOException, VCardException { - verify(mAndroidTestCase.getContext().getResources().openRawResource(resId), vCardType); - } - - public void verify(int resId, int vCardType, final VCardParser vCardParser) - throws IOException, VCardException { - verify(mAndroidTestCase.getContext().getResources().openRawResource(resId), - vCardType, vCardParser); - } - - public void verify(InputStream is, int vCardType) throws IOException, VCardException { - final VCardParser vCardParser; - if (VCardConfig.isV30(vCardType)) { - vCardParser = new VCardParser_V30(true); // Use StrictParsing. - } else { - vCardParser = new VCardParser_V21(); - } - verify(is, vCardType, vCardParser); - } - - public void verify(InputStream is, int vCardType, final VCardParser vCardParser) - throws IOException, VCardException { - try { - vCardParser.parse(is, this); - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - } - } - } - } - - @Override - public void endEntry() { - super.endEntry(); - mAndroidTestCase.assertTrue(mIndex < mPropertyNodesVerifierElemList.size()); - mAndroidTestCase.assertTrue(mIndex < vNodeList.size()); - mPropertyNodesVerifierElemList.get(mIndex).verify(vNodeList.get(mIndex)); - mIndex++; - } -} - /** * Utility class which verifies input VNode. * @@ -102,7 +34,7 @@ import java.util.List; * If the node does not exist in the "ordered list", the class refers to * "unorderd expected property set" and checks the node is expected somewhere. */ -/* package */ class PropertyNodesVerifierElem { +public class PropertyNodesVerifierElem { public static class TypeSet extends HashSet<String> { public TypeSet(String ... array) { super(Arrays.asList(array)); @@ -119,12 +51,10 @@ import java.util.List; // Intentionally use ArrayList instead of Set, assuming there may be more than one // exactly same objects. private final ArrayList<PropertyNode> mUnorderedNodeList; - private final TestCase mTestCase; - public PropertyNodesVerifierElem(TestCase testCase) { + public PropertyNodesVerifierElem(AndroidTestCase androidTestCase) { mOrderedNodeMap = new HashMap<String, List<PropertyNode>>(); mUnorderedNodeList = new ArrayList<PropertyNode>(); - mTestCase = testCase; } // WithOrder @@ -170,6 +100,12 @@ import java.util.List; paramMap_TYPE, null); } + public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, + List<String> propValueList, ContentValues paramMap, TypeSet paramMap_TYPE) { + return addExpectedNodeWithOrder(propName, null, propValueList, null, paramMap, + paramMap_TYPE, null); + } + public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue, ContentValues paramMap, TypeSet paramMap_TYPE) { return addExpectedNodeWithOrder(propName, propValue, null, null, @@ -183,12 +119,18 @@ import java.util.List; } public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue, + List<String> propValueList, ContentValues paramMap) { + return addExpectedNodeWithOrder(propName, propValue, propValueList, null, paramMap, + null, null); + } + + public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue, List<String> propValueList, byte[] propValue_bytes, ContentValues paramMap, TypeSet paramMap_TYPE, GroupSet propGroupSet) { if (propValue == null && propValueList != null) { propValue = concatinateListWithSemiColon(propValueList); } - PropertyNode propertyNode = new PropertyNode(propName, + final PropertyNode propertyNode = new PropertyNode(propName, propValue, propValueList, propValue_bytes, paramMap, paramMap_TYPE, propGroupSet); List<PropertyNode> expectedNodeList = mOrderedNodeMap.get(propName); @@ -267,8 +209,9 @@ import java.util.List; for (PropertyNode actualNode : vnode.propList) { verifyNode(actualNode.propName, actualNode); } + if (!mOrderedNodeMap.isEmpty() || !mUnorderedNodeList.isEmpty()) { - List<String> expectedProps = new ArrayList<String>(); + final List<String> expectedProps = new ArrayList<String>(); for (List<PropertyNode> nodes : mOrderedNodeMap.values()) { for (PropertyNode node : nodes) { if (!expectedProps.contains(node.propName)) { @@ -281,18 +224,19 @@ import java.util.List; expectedProps.add(node.propName); } } - mTestCase.fail("Expected property " + Arrays.toString(expectedProps.toArray()) + TestCase.fail("Expected property " + Arrays.toString(expectedProps.toArray()) + " was not found."); } } private void verifyNode(final String propName, final PropertyNode actualNode) { - List<PropertyNode> expectedNodeList = mOrderedNodeMap.get(propName); + final List<PropertyNode> expectedNodeList = mOrderedNodeMap.get(propName); final int size = (expectedNodeList != null ? expectedNodeList.size() : 0); if (size > 0) { for (int i = 0; i < size; i++) { - PropertyNode expectedNode = expectedNodeList.get(i); - List<PropertyNode> expectedButDifferentValueList = new ArrayList<PropertyNode>(); + final PropertyNode expectedNode = expectedNodeList.get(i); + final List<PropertyNode> expectedButDifferentValueList = + new ArrayList<PropertyNode>(); if (expectedNode.propName.equals(propName)) { if (expectedNode.equals(actualNode)) { expectedNodeList.remove(i); @@ -318,7 +262,7 @@ import java.util.List; expectedButDifferentValueList); } else { // There's no expected node with same propName. - mTestCase.fail("Unexpected property \"" + propName + "\" exists."); + TestCase.fail("Unexpected property \"" + propName + "\" exists."); } } } else { @@ -333,7 +277,7 @@ import java.util.List; expectedButDifferentValueList); } else { // There's no expected node with same propName. - mTestCase.fail("Unexpected property \"" + propName + "\" exists."); + TestCase.fail("Unexpected property \"" + propName + "\" exists."); } } } @@ -379,7 +323,7 @@ import java.util.List; builder.append(expectedNode.toString()); builder.append("\n"); } - mTestCase.fail("Property \"" + propName + "\" has wrong value.\n" + TestCase.fail("Property \"" + propName + "\" has wrong value.\n" + builder.toString() + " actual: " + actualNode.toString()); } diff --git a/core/tests/coretests/src/android/pim/vcard/test_utils/VCardTestsBase.java b/core/tests/coretests/src/android/pim/vcard/test_utils/VCardTestsBase.java new file mode 100644 index 000000000000..e7413ab29534 --- /dev/null +++ b/core/tests/coretests/src/android/pim/vcard/test_utils/VCardTestsBase.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2009 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.pim.vcard.test_utils; + +import android.content.ContentValues; +import android.pim.vcard.VCardConfig; +import android.test.AndroidTestCase; + +/** + * BaseClass for vCard unit tests with utility classes. + * Please do not add each unit test here. + */ +public class VCardTestsBase extends AndroidTestCase { + public static final int V21 = VCardConfig.VCARD_TYPE_V21_GENERIC; + public static final int V30 = VCardConfig.VCARD_TYPE_V30_GENERIC; + public static final int V40 = VCardConfig.VCARD_TYPE_V40_GENERIC; + + // Do not modify these during tests. + protected final ContentValues mContentValuesForQP; + protected final ContentValues mContentValuesForSJis; + protected final ContentValues mContentValuesForUtf8; + protected final ContentValues mContentValuesForQPAndSJis; + protected final ContentValues mContentValuesForQPAndUtf8; + protected final ContentValues mContentValuesForBase64V21; + protected final ContentValues mContentValuesForBase64V30; + + protected VCardVerifier mVerifier; + private boolean mSkipVerification; + + public VCardTestsBase() { + super(); + // Not using constants in vCard code since it may be wrong. + mContentValuesForQP = new ContentValues(); + mContentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE"); + mContentValuesForSJis = new ContentValues(); + mContentValuesForSJis.put("CHARSET", "SHIFT_JIS"); + mContentValuesForUtf8 = new ContentValues(); + mContentValuesForUtf8.put("CHARSET", "UTF-8"); + mContentValuesForQPAndSJis = new ContentValues(); + mContentValuesForQPAndSJis.put("ENCODING", "QUOTED-PRINTABLE"); + mContentValuesForQPAndSJis.put("CHARSET", "SHIFT_JIS"); + mContentValuesForQPAndUtf8 = new ContentValues(); + mContentValuesForQPAndUtf8.put("ENCODING", "QUOTED-PRINTABLE"); + mContentValuesForQPAndUtf8.put("CHARSET", "UTF-8"); + mContentValuesForBase64V21 = new ContentValues(); + mContentValuesForBase64V21.put("ENCODING", "BASE64"); + mContentValuesForBase64V30 = new ContentValues(); + mContentValuesForBase64V30.put("ENCODING", "b"); + } + + @Override + public void testAndroidTestCaseSetupProperly() { + super.testAndroidTestCaseSetupProperly(); + mSkipVerification = true; + } + + @Override + public void setUp() throws Exception{ + super.setUp(); + mVerifier = new VCardVerifier(this); + mSkipVerification = false; + } + + @Override + public void tearDown() throws Exception { + super.tearDown(); + if (!mSkipVerification) { + mVerifier.verify(); + } + } +} diff --git a/core/tests/coretests/src/android/pim/vcard/test_utils/VCardVerifier.java b/core/tests/coretests/src/android/pim/vcard/test_utils/VCardVerifier.java new file mode 100644 index 000000000000..7379a5b947e0 --- /dev/null +++ b/core/tests/coretests/src/android/pim/vcard/test_utils/VCardVerifier.java @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2009 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.pim.vcard.test_utils; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.EntityIterator; +import android.net.Uri; +import android.pim.vcard.VCardComposer; +import android.pim.vcard.VCardConfig; +import android.pim.vcard.VCardEntryConstructor; +import android.pim.vcard.VCardInterpreter; +import android.pim.vcard.VCardInterpreterCollection; +import android.pim.vcard.VCardParser; +import android.pim.vcard.VCardUtils; +import android.pim.vcard.exception.VCardException; +import android.test.AndroidTestCase; +import android.test.mock.MockContext; +import android.text.TextUtils; +import android.util.Log; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.util.Arrays; + +/** + * <p> + * The class lets users checks that given expected vCard data are same as given actual vCard data. + * Able to verify both vCard importer/exporter. + * </p> + * <p> + * First a user has to initialize the object by calling either + * {@link #initForImportTest(int, int)} or {@link #initForExportTest(int)}. + * "Round trip test" (import -> export -> import, or export -> import -> export) is not supported. + * </p> + */ +public class VCardVerifier { + private static final String LOG_TAG = "VCardVerifier"; + + private static class CustomMockContext extends MockContext { + final ContentResolver mResolver; + public CustomMockContext(ContentResolver resolver) { + mResolver = resolver; + } + + @Override + public ContentResolver getContentResolver() { + return mResolver; + } + } + + private class VCardVerifierInternal implements VCardComposer.OneEntryHandler { + public boolean onInit(Context context) { + return true; + } + public boolean onEntryCreated(String vcard) { + verifyOneVCardForExport(vcard); + return true; + } + public void onTerminate() { + } + } + + private final AndroidTestCase mAndroidTestCase; + private final VCardVerifierInternal mVCardVerifierInternal; + private int mVCardType; + private boolean mIsDoCoMo; + + // Only one of them must be non-empty. + private ExportTestResolver mExportTestResolver; + private InputStream mInputStream; + + // To allow duplication, use list instead of set. + // When null, we don't need to do the verification. + private PropertyNodesVerifier mPropertyNodesVerifier; + private LineVerifier mLineVerifier; + private ContentValuesVerifier mContentValuesVerifier; + private boolean mInitialized; + private boolean mVerified = false; + private String mCharset; + + // Called by VCardTestsBase + public VCardVerifier(AndroidTestCase androidTestCase) { + mAndroidTestCase = androidTestCase; + mVCardVerifierInternal = new VCardVerifierInternal(); + mExportTestResolver = null; + mInputStream = null; + mInitialized = false; + mVerified = false; + } + + // Should be called at the beginning of each import test. + public void initForImportTest(int vcardType, int resId) { + if (mInitialized) { + AndroidTestCase.fail("Already initialized"); + } + mVCardType = vcardType; + mIsDoCoMo = VCardConfig.isDoCoMo(vcardType); + setInputResourceId(resId); + mInitialized = true; + } + + // Should be called at the beginning of each export test. + public void initForExportTest(int vcardType) { + initForExportTest(vcardType, "UTF-8"); + } + + public void initForExportTest(int vcardType, String charset) { + if (mInitialized) { + AndroidTestCase.fail("Already initialized"); + } + mExportTestResolver = new ExportTestResolver(mAndroidTestCase); + mVCardType = vcardType; + mIsDoCoMo = VCardConfig.isDoCoMo(vcardType); + mInitialized = true; + if (TextUtils.isEmpty(charset)) { + mCharset = "UTF-8"; + } else { + mCharset = charset; + } + } + + private void setInputResourceId(int resId) { + InputStream inputStream = mAndroidTestCase.getContext().getResources().openRawResource(resId); + if (inputStream == null) { + AndroidTestCase.fail("Wrong resId: " + resId); + } + setInputStream(inputStream); + } + + private void setInputStream(InputStream inputStream) { + if (mExportTestResolver != null) { + AndroidTestCase.fail("addInputEntry() is called."); + } else if (mInputStream != null) { + AndroidTestCase.fail("InputStream is already set"); + } + mInputStream = inputStream; + } + + public ContactEntry addInputEntry() { + if (!mInitialized) { + AndroidTestCase.fail("Not initialized"); + } + if (mInputStream != null) { + AndroidTestCase.fail("setInputStream is called"); + } + return mExportTestResolver.addInputContactEntry(); + } + + public PropertyNodesVerifierElem addPropertyNodesVerifierElemWithoutVersion() { + if (!mInitialized) { + AndroidTestCase.fail("Not initialized"); + } + if (mPropertyNodesVerifier == null) { + mPropertyNodesVerifier = new PropertyNodesVerifier(mAndroidTestCase); + } + return mPropertyNodesVerifier.addPropertyNodesVerifierElem(); + } + + public PropertyNodesVerifierElem addPropertyNodesVerifierElem() { + final PropertyNodesVerifierElem elem = addPropertyNodesVerifierElemWithoutVersion(); + final String versionString; + if (VCardConfig.isVersion21(mVCardType)) { + versionString = "2.1"; + } else if (VCardConfig.isVersion30(mVCardType)) { + versionString = "3.0"; + } else if (VCardConfig.isVersion40(mVCardType)) { + versionString = "4.0"; + } else { + throw new RuntimeException("Unexpected vcard type during a unit test"); + } + elem.addExpectedNodeWithOrder("VERSION", versionString); + + return elem; + } + + public PropertyNodesVerifierElem addPropertyNodesVerifierElemWithEmptyName() { + if (!mInitialized) { + AndroidTestCase.fail("Not initialized"); + } + final PropertyNodesVerifierElem elem = addPropertyNodesVerifierElem(); + if (VCardConfig.isVersion40(mVCardType)) { + elem.addExpectedNodeWithOrder("FN", ""); + } else if (VCardConfig.isVersion30(mVCardType)) { + elem.addExpectedNodeWithOrder("N", ""); + elem.addExpectedNodeWithOrder("FN", ""); + } else if (mIsDoCoMo) { + elem.addExpectedNodeWithOrder("N", ""); + } + return elem; + } + + public LineVerifierElem addLineVerifierElem() { + if (!mInitialized) { + AndroidTestCase.fail("Not initialized"); + } + if (mLineVerifier == null) { + mLineVerifier = new LineVerifier(mAndroidTestCase, mVCardType); + } + return mLineVerifier.addLineVerifierElem(); + } + + public ContentValuesVerifierElem addContentValuesVerifierElem() { + if (!mInitialized) { + AndroidTestCase.fail("Not initialized"); + } + if (mContentValuesVerifier == null) { + mContentValuesVerifier = new ContentValuesVerifier(); + } + + return mContentValuesVerifier.addElem(mAndroidTestCase); + } + + /** + * Sets up sub-verifiers correctly and try parse given vCard as InputStream. + * Errors around InputStream must be handled outside this method. + * + * Used both from {@link #verifyForImportTest()} and from {@link #verifyForExportTest()}. + */ + private void verifyWithInputStream(InputStream is) throws IOException { + final VCardInterpreter interpreter; + if (mContentValuesVerifier != null) { + final VCardEntryConstructor constructor = new VCardEntryConstructor(mVCardType); + constructor.addEntryHandler(mContentValuesVerifier); + if (mPropertyNodesVerifier != null) { + interpreter = new VCardInterpreterCollection(Arrays.asList( + mPropertyNodesVerifier, constructor)); + } else { + interpreter = constructor; + } + } else { + if (mPropertyNodesVerifier != null) { + interpreter = mPropertyNodesVerifier; + } else { + interpreter = null; + } + } + + try { + // Note: we must not specify charset toward vCard parsers. This code checks whether + // those parsers are able to encode given binary without any extra information for + // charset. + final VCardParser parser = VCardUtils.getAppropriateParser(mVCardType); + parser.parse(is, interpreter); + } catch (VCardException e) { + AndroidTestCase.fail("Unexpected VCardException: " + e.getMessage()); + } + } + + private void verifyOneVCardForExport(final String vcard) { + Log.d(LOG_TAG, vcard); + InputStream is = null; + try { + is = new ByteArrayInputStream(vcard.getBytes(mCharset)); + verifyWithInputStream(is); + } catch (IOException e) { + AndroidTestCase.fail("Unexpected IOException: " + e.getMessage()); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + AndroidTestCase.fail("Unexpected IOException: " + e.getMessage()); + } + } + } + } + + public void verify() { + if (!mInitialized) { + TestCase.fail("Not initialized."); + } + if (mVerified) { + TestCase.fail("verify() was called twice."); + } + + if (mInputStream != null) { + if (mExportTestResolver != null){ + TestCase.fail("There are two input sources."); + } + verifyForImportTest(); + } else if (mExportTestResolver != null){ + verifyForExportTest(); + } else { + TestCase.fail("No input is determined"); + } + mVerified = true; + } + + private void verifyForImportTest() { + if (mLineVerifier != null) { + AndroidTestCase.fail("Not supported now."); + } + + try { + verifyWithInputStream(mInputStream); + } catch (IOException e) { + AndroidTestCase.fail("IOException was thrown: " + e.getMessage()); + } finally { + if (mInputStream != null) { + try { + mInputStream.close(); + } catch (IOException e) { + } + } + } + } + + public static EntityIterator mockGetEntityIteratorMethod( + final ContentResolver resolver, + final Uri uri, final String selection, + final String[] selectionArgs, final String sortOrder) { + if (ExportTestResolver.class.equals(resolver.getClass())) { + return ((ExportTestResolver)resolver).getProvider().queryEntities( + uri, selection, selectionArgs, sortOrder); + } + + Log.e(LOG_TAG, "Unexpected provider given."); + return null; + } + + private Method getMockGetEntityIteratorMethod() + throws SecurityException, NoSuchMethodException { + return this.getClass().getMethod("mockGetEntityIteratorMethod", + ContentResolver.class, Uri.class, String.class, String[].class, String.class); + } + + private void verifyForExportTest() { + final VCardComposer composer = + new VCardComposer(new CustomMockContext(mExportTestResolver), mVCardType, mCharset); + composer.addHandler(mLineVerifier); + composer.addHandler(mVCardVerifierInternal); + if (!composer.init(VCardComposer.CONTACTS_TEST_CONTENT_URI, null, null, null)) { + AndroidTestCase.fail("init() failed. Reason: " + composer.getErrorReason()); + } + AndroidTestCase.assertFalse(composer.isAfterLast()); + try { + while (!composer.isAfterLast()) { + try { + final Method mockGetEntityIteratorMethod = getMockGetEntityIteratorMethod(); + AndroidTestCase.assertNotNull(mockGetEntityIteratorMethod); + AndroidTestCase.assertTrue( + composer.createOneEntry(mockGetEntityIteratorMethod)); + } catch (Exception e) { + e.printStackTrace(); + AndroidTestCase.fail(e.toString()); + } + } + } finally { + composer.terminate(); + } + } +} diff --git a/core/tests/coretests/src/android/pim/vcard/VNode.java b/core/tests/coretests/src/android/pim/vcard/test_utils/VNode.java index 79f10dc92e19..b890e2c2be60 100644 --- a/core/tests/coretests/src/android/pim/vcard/VNode.java +++ b/core/tests/coretests/src/android/pim/vcard/test_utils/VNode.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.pim.vcard; +package android.pim.vcard.test_utils; import java.util.ArrayList; diff --git a/core/tests/coretests/src/android/pim/vcard/VNodeBuilder.java b/core/tests/coretests/src/android/pim/vcard/test_utils/VNodeBuilder.java index 0e6c3259b5fa..883703407352 100644 --- a/core/tests/coretests/src/android/pim/vcard/VNodeBuilder.java +++ b/core/tests/coretests/src/android/pim/vcard/test_utils/VNodeBuilder.java @@ -13,18 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.pim.vcard; +package android.pim.vcard.test_utils; import android.content.ContentValues; -import android.pim.vcard.VCardInterpreter; import android.pim.vcard.VCardConfig; +import android.pim.vcard.VCardInterpreter; +import android.pim.vcard.VCardUtils; +import android.util.Base64; import android.util.CharsetUtils; import android.util.Log; -import org.apache.commons.codec.DecoderException; -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.codec.net.QuotedPrintableCodec; - import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.charset.Charset; @@ -32,34 +30,29 @@ import java.util.ArrayList; import java.util.List; /** - * Store the parse result to custom datastruct: VNode, PropertyNode + * <p> + * The class storing the parse result to custom datastruct: + * {@link VNode}, and {@link PropertyNode}. * Maybe several vcard instance, so use vNodeList to store. - * VNode: standy by a vcard instance. - * PropertyNode: standy by a property line of a card. - * - * Previously used in main vCard handling code but now exists only for testing. + * </p> + * <p> + * This is called VNode, not VCardNode, since it was used for expressing vCalendar (iCal). + * </p> */ public class VNodeBuilder implements VCardInterpreter { static private String LOG_TAG = "VNodeBuilder"; - /** - * If there's no other information available, this class uses this charset for encoding - * byte arrays. - */ - static public String TARGET_CHARSET = "UTF-8"; - - /** type=VNode */ public List<VNode> vNodeList = new ArrayList<VNode>(); private int mNodeListPos = 0; private VNode mCurrentVNode; private PropertyNode mCurrentPropNode; private String mCurrentParamType; - + /** * The charset using which VParser parses the text. */ private String mSourceCharset; - + /** * The charset with which byte array is encoded to String. */ @@ -68,27 +61,15 @@ public class VNodeBuilder implements VCardInterpreter { private boolean mStrictLineBreakParsing; public VNodeBuilder() { - this(VCardConfig.DEFAULT_CHARSET, TARGET_CHARSET, false); + this(VCardConfig.DEFAULT_IMPORT_CHARSET, false); } - public VNodeBuilder(String charset, boolean strictLineBreakParsing) { - this(null, charset, strictLineBreakParsing); - } - - /** - * @hide sourceCharset is temporal. - */ - public VNodeBuilder(String sourceCharset, String targetCharset, - boolean strictLineBreakParsing) { - if (sourceCharset != null) { - mSourceCharset = sourceCharset; - } else { - mSourceCharset = VCardConfig.DEFAULT_CHARSET; - } + public VNodeBuilder(String targetCharset, boolean strictLineBreakParsing) { + mSourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET; if (targetCharset != null) { mTargetCharset = targetCharset; } else { - mTargetCharset = TARGET_CHARSET; + mTargetCharset = VCardConfig.DEFAULT_IMPORT_CHARSET; } mStrictLineBreakParsing = strictLineBreakParsing; } @@ -149,7 +130,6 @@ public class VNodeBuilder implements VCardInterpreter { mCurrentPropNode.propName = name; } - // Used only in VCard. public void propertyGroup(String group) { mCurrentPropNode.propGroupSet.add(group); } @@ -159,6 +139,12 @@ public class VNodeBuilder implements VCardInterpreter { } public void propertyParamValue(String value) { + if (!VCardUtils.containsOnlyAlphaDigitHyphen(value)) { + value = VCardUtils.convertStringCharset(value, + VCardConfig.DEFAULT_INTERMEDIATE_CHARSET, + VCardConfig.DEFAULT_IMPORT_CHARSET); + } + if (mCurrentParamType == null || mCurrentParamType.equalsIgnoreCase("TYPE")) { mCurrentPropNode.paramMap_TYPE.add(value); @@ -192,71 +178,11 @@ public class VNodeBuilder implements VCardInterpreter { encoding = encoding.toUpperCase(); if (encoding.equals("BASE64") || encoding.equals("B")) { // Assume BASE64 is used only when the number of values is 1. - mCurrentPropNode.propValue_bytes = - Base64.decodeBase64(value.getBytes()); + mCurrentPropNode.propValue_bytes = Base64.decode(value.getBytes(), Base64.NO_WRAP); return value; } else if (encoding.equals("QUOTED-PRINTABLE")) { - String quotedPrintable = value - .replaceAll("= ", " ").replaceAll("=\t", "\t"); - String[] lines; - if (mStrictLineBreakParsing) { - lines = quotedPrintable.split("\r\n"); - } else { - StringBuilder builder = new StringBuilder(); - int length = quotedPrintable.length(); - ArrayList<String> list = new ArrayList<String>(); - for (int i = 0; i < length; i++) { - char ch = quotedPrintable.charAt(i); - if (ch == '\n') { - list.add(builder.toString()); - builder = new StringBuilder(); - } else if (ch == '\r') { - list.add(builder.toString()); - builder = new StringBuilder(); - if (i < length - 1) { - char nextCh = quotedPrintable.charAt(i + 1); - if (nextCh == '\n') { - i++; - } - } - } else { - builder.append(ch); - } - } - String finalLine = builder.toString(); - if (finalLine.length() > 0) { - list.add(finalLine); - } - lines = list.toArray(new String[0]); - } - StringBuilder builder = new StringBuilder(); - for (String line : lines) { - if (line.endsWith("=")) { - line = line.substring(0, line.length() - 1); - } - builder.append(line); - } - byte[] bytes; - try { - bytes = builder.toString().getBytes(mSourceCharset); - } catch (UnsupportedEncodingException e1) { - Log.e(LOG_TAG, "Failed to encode: charset=" + mSourceCharset); - bytes = builder.toString().getBytes(); - } - - try { - bytes = QuotedPrintableCodec.decodeQuotedPrintable(bytes); - } catch (DecoderException e) { - Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e); - return ""; - } - - try { - return new String(bytes, targetCharset); - } catch (UnsupportedEncodingException e) { - Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); - return new String(bytes); - } + return VCardUtils.parseQuotedPrintable( + value, mStrictLineBreakParsing, mSourceCharset, targetCharset); } // Unknown encoding. Fall back to default. } @@ -309,6 +235,6 @@ public class VNodeBuilder implements VCardInterpreter { } public String getResult(){ - return null; + throw new RuntimeException("Not supported"); } } diff --git a/core/tests/coretests/src/android/text/HtmlTest.java b/core/tests/coretests/src/android/text/HtmlTest.java index c07d212bedc3..b2298f7bfa83 100644 --- a/core/tests/coretests/src/android/text/HtmlTest.java +++ b/core/tests/coretests/src/android/text/HtmlTest.java @@ -19,7 +19,6 @@ package android.text; import android.content.res.ColorStateList; import android.content.res.Resources; import android.graphics.Typeface; -import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; import android.text.style.ForegroundColorSpan; import android.text.style.QuoteSpan; @@ -36,7 +35,7 @@ import junit.framework.TestCase; public class HtmlTest extends TestCase { - @MediumTest + @SmallTest public void testSingleTagOnWhileString() { Spanned spanned = Html.fromHtml("<b>hello</b>"); Object[] spans = spanned.getSpans(-1, 100, Object.class); @@ -46,7 +45,7 @@ public class HtmlTest extends TestCase { assertEquals(5, spanned.getSpanEnd(span)); } - @MediumTest + @SmallTest public void testEmptyFontTag() { Spanned spanned = Html.fromHtml("Hello <font color=\"#ff00ff00\"></font>"); Object[] spans = spanned.getSpans(0, 100, Object.class); @@ -54,7 +53,7 @@ public class HtmlTest extends TestCase { } /** Tests that the parser can handle mal-formed HTML. */ - @MediumTest + @SmallTest public void testBadHtml() { Spanned spanned = Html.fromHtml("Hello <b>b<i>bi</b>i</i>"); Object[] spans = spanned.getSpans(0, 100, Object.class); @@ -69,13 +68,13 @@ public class HtmlTest extends TestCase { assertEquals(10, spanned.getSpanEnd(spans[2])); } - @MediumTest + @SmallTest public void testSymbols() { String spanned = Html.fromHtml("© > <").toString(); assertEquals("\u00a9 > <", spanned); } - @MediumTest + @SmallTest public void testColor() throws Exception { Spanned s; ForegroundColorSpan[] colors; @@ -95,7 +94,7 @@ public class HtmlTest extends TestCase { assertEquals(0, colors.length); } - @MediumTest + @SmallTest public void testResourceColor() throws Exception { ColorStateList c = Resources.getSystem().getColorStateList(android.R.color.primary_text_dark); diff --git a/core/tests/coretests/src/android/text/PackedIntVectorTest.java b/core/tests/coretests/src/android/text/PackedIntVectorTest.java new file mode 100644 index 000000000000..1dc683e87937 --- /dev/null +++ b/core/tests/coretests/src/android/text/PackedIntVectorTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2007 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.text; + +import junit.framework.TestCase; + +/** + * PackedIntVectorTest tests the features of android.util.PackedIntVector. + */ +public class PackedIntVectorTest extends TestCase { + + public void testBasic() throws Exception { + for (int width = 0; width < 10; width++) { + PackedIntVector p = new PackedIntVector(width); + int[] ins = new int[width]; + + for (int height = width * 2; height < width * 4; height++) { + assertEquals(p.width(), width); + + // Test adding rows. + + for (int i = 0; i < height; i++) { + int at; + + if (i % 2 == 0) { + at = i; + } else { + at = p.size() - i; + } + + for (int j = 0; j < width; j++) { + ins[j] = i + j; + } + + if (i == height / 2) { + p.insertAt(at, null); + } else { + p.insertAt(at, ins); + } + + assertEquals(p.size(), i + 1); + + for (int j = 0; j < width; j++) { + if (i == height / 2) { + assertEquals(0, p.getValue(at, j)); + } else { + assertEquals(p.getValue(at, j), i + j); + } + } + } + + // Test setting values. + + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + p.setValue(i, j, i * j); + + assertEquals(p.getValue(i, j), i * j); + } + } + + // Test offsetting values. + + for (int j = 0; j < width; j++) { + p.adjustValuesBelow(j * 2, j, j + 27); + } + + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + int expect = i * j; + + if (i >= j * 2) { + expect += j + 27; + } + + assertEquals(p.getValue(i, j), expect); + } + } + + for (int j = 0; j < width; j++) { + p.adjustValuesBelow(j, j, j * j + 14); + } + + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + int expect = i * j; + + if (i >= j * 2) { + expect += j + 27; + } + if (i >= j) { + expect += j * j + 14; + } + + assertEquals(p.getValue(i, j), expect); + } + } + + // Test undoing offsets. + + for (int j = 0; j < width; j++) { + p.adjustValuesBelow(j * 2, j, -(j + 27)); + p.adjustValuesBelow(j, j, -(j * j + 14)); + } + + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + assertEquals(p.getValue(i, j), i * j); + } + } + + // Test deleting rows. + + while (p.size() > 0) { + int osize = p.size(); + int del = osize / 3; + + if (del == 0) { + del = 1; + } + + int at = (osize - del) / 2; + p.deleteAt(at, del); + + assertEquals(p.size(), osize - del); + + for (int i = 0; i < at; i++) { + for (int j = 0; j < width; j++) { + assertEquals(p.getValue(i, j), i * j); + } + } + + for (int i = at; i < p.size(); i++) { + for (int j = 0; j < width; j++) { + assertEquals(p.getValue(i, j), (i + height - p.size()) * j); + } + } + } + + assertEquals(0, p.size()); + } + } + } +} diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java index 5b427be23dc7..1beba53a1e29 100644 --- a/core/tests/coretests/src/android/text/TextUtilsTest.java +++ b/core/tests/coretests/src/android/text/TextUtilsTest.java @@ -26,6 +26,8 @@ import android.text.SpannedString; import android.text.TextPaint; import android.text.TextUtils; import android.text.style.StyleSpan; +import android.text.util.Rfc822Token; +import android.text.util.Rfc822Tokenizer; import android.test.MoreAsserts; import com.android.common.Rfc822Validator; @@ -269,6 +271,24 @@ public class TextUtilsTest extends TestCase { } } + @SmallTest + public void testRfc822TokenizerFullAddress() { + Rfc822Token[] tokens = Rfc822Tokenizer.tokenize("Foo Bar (something) <foo@google.com>"); + assertNotNull(tokens); + assertEquals(1, tokens.length); + assertEquals("foo@google.com", tokens[0].getAddress()); + assertEquals("Foo Bar", tokens[0].getName()); + assertEquals("something",tokens[0].getComment()); + } + + @SmallTest + public void testRfc822TokenizeItemWithError() { + Rfc822Token[] tokens = Rfc822Tokenizer.tokenize("\"Foo Bar\\"); + assertNotNull(tokens); + assertEquals(1, tokens.length); + assertEquals("Foo Bar", tokens[0].getAddress()); + } + @LargeTest public void testEllipsize() { CharSequence s1 = "The quick brown fox jumps over \u00FEhe lazy dog."; @@ -329,6 +349,26 @@ public class TextUtilsTest extends TestCase { } } + @SmallTest + public void testDelimitedStringContains() { + assertFalse(TextUtils.delimitedStringContains("", ',', null)); + assertFalse(TextUtils.delimitedStringContains(null, ',', "")); + // Whole match + assertTrue(TextUtils.delimitedStringContains("gps", ',', "gps")); + // At beginning. + assertTrue(TextUtils.delimitedStringContains("gps,gpsx,network,mock", ',', "gps")); + assertTrue(TextUtils.delimitedStringContains("gps,network,mock", ',', "gps")); + // In middle, both without, before & after a false match. + assertTrue(TextUtils.delimitedStringContains("network,gps,mock", ',', "gps")); + assertTrue(TextUtils.delimitedStringContains("network,gps,gpsx,mock", ',', "gps")); + assertTrue(TextUtils.delimitedStringContains("network,gpsx,gps,mock", ',', "gps")); + // At the end. + assertTrue(TextUtils.delimitedStringContains("network,mock,gps", ',', "gps")); + assertTrue(TextUtils.delimitedStringContains("network,mock,gpsx,gps", ',', "gps")); + // Not present (but with a false match) + assertFalse(TextUtils.delimitedStringContains("network,mock,gpsx", ',', "gps")); + } + /** * CharSequence wrapper for testing the cases where text is copied into * a char array instead of working from a String or a Spanned. diff --git a/core/tests/coretests/src/android/text/util/LinkifyTest.java b/core/tests/coretests/src/android/text/util/LinkifyTest.java index 99c6501f817d..444eb0c6a8d8 100644 --- a/core/tests/coretests/src/android/text/util/LinkifyTest.java +++ b/core/tests/coretests/src/android/text/util/LinkifyTest.java @@ -17,10 +17,8 @@ package android.text.util; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; import android.text.method.LinkMovementMethod; -import android.text.util.Linkify; import android.widget.TextView; /** @@ -39,7 +37,7 @@ public class LinkifyTest extends AndroidTestCase { assertTrue(tv.getUrls().length == 0); } - @MediumTest + @SmallTest public void testNormal() throws Exception { TextView tv; diff --git a/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java b/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java index 5207ad918943..55da665c7b34 100644 --- a/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java +++ b/core/tests/coretests/src/android/util/MonthDisplayHelperTest.java @@ -16,13 +16,11 @@ package android.util; -import junit.framework.TestCase; -import junit.framework.Test; -import junit.framework.TestSuite; +import android.test.suitebuilder.annotation.SmallTest; import java.util.Calendar; -import android.test.suitebuilder.annotation.SmallTest; -import android.test.suitebuilder.annotation.MediumTest; + +import junit.framework.TestCase; /** * Unit tests for {@link MonthDisplayHelper}. @@ -42,7 +40,7 @@ public class MonthDisplayHelperTest extends TestCase { new MonthDisplayHelper(2007, Calendar.SEPTEMBER).getFirstDayOfMonth()); } - @MediumTest + @SmallTest public void testNumberOfDaysInCurrentMonth() { assertEquals(30, new MonthDisplayHelper(2007, Calendar.SEPTEMBER).getNumberOfDaysInMonth()); diff --git a/core/tests/coretests/src/android/view/FocusFinderTest.java b/core/tests/coretests/src/android/view/FocusFinderTest.java index 186689fb57b1..6f1dd7c40184 100644 --- a/core/tests/coretests/src/android/view/FocusFinderTest.java +++ b/core/tests/coretests/src/android/view/FocusFinderTest.java @@ -18,7 +18,6 @@ package android.view; import android.graphics.Rect; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; public class FocusFinderTest extends AndroidTestCase { @@ -239,7 +238,7 @@ public class FocusFinderTest extends AndroidTestCase { * A non-candidate (even a much closer one) is always a worse choice * than a real candidate. */ - @MediumTest + @SmallTest public void testSomeCandidateBetterThanNonCandidate() { Rect src = new Rect(0, 0, 50, 50); // (left, top, right, bottom) diff --git a/core/tests/coretests/src/android/view/VelocityTest.java b/core/tests/coretests/src/android/view/VelocityTest.java new file mode 100644 index 000000000000..fb28e1e30f0a --- /dev/null +++ b/core/tests/coretests/src/android/view/VelocityTest.java @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2010 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.frameworktest.view; + +import junit.framework.Assert; + +import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.MediumTest; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; + +/** + * Exercises {@link android.view.VelocityTracker} to compute correct velocity.<br> + * To launch this test, use :<br> + * <code>./development/testrunner/runtest.py framework -c com.android.frameworktest.view.VelocityTest</code> + */ +public class VelocityTest extends InstrumentationTestCase { + + @MediumTest + public void testInitialCondiditions() { + VelocityTracker vt = VelocityTracker.obtain(); + assertNotNull(vt); + vt.recycle(); + } + + /** + * Test that {@link android.view.VelocityTracker}.clear() clears + * the previous values after a call to computeCurrentVelocity() + */ + @MediumTest + public void testClear() { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + drag(vt, 100, 200, 100, 200, 10, t, 300); + vt.computeCurrentVelocity(1); + assertFalse("Velocity should not be null", vt.getXVelocity() == 0.0f); + assertFalse("Velocity should not be null", vt.getYVelocity() == 0.0f); + vt.clear(); + vt.computeCurrentVelocity(1); + assertEquals(0.0f, vt.getXVelocity()); + assertEquals(0.0f, vt.getYVelocity()); + vt.recycle(); + } + + @MediumTest + public void testDragAcceleration () { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + drag(vt, 100, 200, 100, 200, 15, t, 400, new AccelerateInterpolator()); + vt.computeCurrentVelocity(1000); + assertGreater(250.0f, vt.getXVelocity()); + assertGreater(250.0f, vt.getYVelocity()); + vt.recycle(); + } + + @MediumTest + public void testDragDeceleration () { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + drag(vt, 100, 200, 100, 200, 15, t, 400, new DecelerateInterpolator()); + vt.computeCurrentVelocity(1000); + assertLower(250.0f, vt.getXVelocity()); + assertLower(250.0f, vt.getYVelocity()); + vt.recycle(); + } + + @MediumTest + public void testDragLinearHorizontal() { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + // 100px in 400ms => 250px/s + drag(vt, 100, 200, 200, 200, 15, t, 400); + vt.computeCurrentVelocity(1000); + assertEquals(0.0f, vt.getYVelocity()); + assertEqualFuzzy(250.0f, vt.getXVelocity(), 4f); + vt.recycle(); + } + + @MediumTest + public void testDragLinearVertical() { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + // 100px in 400ms => 250px/s + drag(vt, 200, 200, 100, 200, 15, t, 400); + vt.computeCurrentVelocity(1000); + assertEquals(0.0f, vt.getXVelocity()); + assertEqualFuzzy(250.0f, vt.getYVelocity(), 4f); + vt.recycle(); + } + + /** + * Test dragging with two points only + * (velocity must be an exact value) + */ + @MediumTest + public void testDragWith2Points () { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + // 100px, 2 steps, 100ms => 1000px/s + drag(vt, 100, 200, 100, 200, 2, t, 100); + vt.computeCurrentVelocity(1000); + assertEquals(1000.0f, vt.getXVelocity()); + assertEquals(1000.0f, vt.getYVelocity()); + vt.recycle(); + } + + /** + * Velocity is independent of the number of points used during + * the same interval + */ + @MediumTest + public void testStabilityInNbPoints () { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + drag(vt, 100, 200, 100, 200, 10, t, 400); // 10 steps over 400ms + vt.computeCurrentVelocity(1); + float firstX = vt.getXVelocity(); + float firstY = vt.getYVelocity(); + vt.clear(); + drag(vt, 100, 200, 100, 200, 20, t, 400); // 20 steps over 400ms + vt.computeCurrentVelocity(1); + float secondX = vt.getXVelocity(); + float secondY = vt.getYVelocity(); + assertEqualFuzzy(firstX, secondX, 0.1f); + assertEqualFuzzy(firstY, secondY, 0.1f); + vt.recycle(); + } + + /** + * Velocity is independent of the time when the events occurs, + * it only depends on delays between the events. + */ + @MediumTest + public void testStabilityInTime () { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + drag(vt, 100, 200, 100, 200, 10, t, 400); + vt.computeCurrentVelocity(1); + float firstX = vt.getXVelocity(); + float firstY = vt.getYVelocity(); + vt.clear(); + drag(vt, 100, 200, 100, 200, 10, t + 3600*1000, 400); // on hour later + vt.computeCurrentVelocity(1); + float secondX = vt.getXVelocity(); + float secondY = vt.getYVelocity(); + assertEqualFuzzy(firstX, secondX, 0.1f); + assertEqualFuzzy(firstY, secondY, 0.1f); + vt.recycle(); + } + + /** + * Velocity is independent of the position of the events, + * it only depends on their relative distance. + */ + @MediumTest + public void testStabilityInSpace () { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + drag(vt, 100, 200, 100, 200, 10, t, 400); + vt.computeCurrentVelocity(1); + float firstX = vt.getXVelocity(); + float firstY = vt.getYVelocity(); + vt.clear(); + drag(vt, 200, 300, 200, 300, 10, t, 400); // 100px further + vt.computeCurrentVelocity(1); + float secondX = vt.getXVelocity(); + float secondY = vt.getYVelocity(); + assertEqualFuzzy(firstX, secondX, 0.1f); + assertEqualFuzzy(firstY, secondY, 0.1f); + vt.recycle(); + } + + /** + * Test that calls to {@link android.view.VelocityTracker}.computeCurrentVelocity() + * will output same values when using the same data. + */ + @MediumTest + public void testStabilityOfComputation() { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + drag(vt, 100, 200, 100, 200, 10, t, 300); + vt.computeCurrentVelocity(1); + float firstX = vt.getXVelocity(); + float firstY = vt.getYVelocity(); + vt.computeCurrentVelocity(1); + float secondX = vt.getXVelocity(); + float secondY = vt.getYVelocity(); + assertEquals(firstX, secondX); + assertEquals(firstY, secondY); + vt.recycle(); + } + + /** + * Test the units parameter of {@link android.view.VelocityTracker}.computeCurrentVelocity() + */ + @MediumTest + public void testStabilityOfUnits() { + long t = System.currentTimeMillis(); + VelocityTracker vt = VelocityTracker.obtain(); + drag(vt, 100, 200, 100, 200, 10, t, 300); + vt.computeCurrentVelocity(1); + float firstX = vt.getXVelocity(); + float firstY = vt.getYVelocity(); + vt.computeCurrentVelocity(1000); + float secondX = vt.getXVelocity(); + float secondY = vt.getYVelocity(); + assertEqualFuzzy(firstX, secondX / 1000.0f, 0.1f); + assertEqualFuzzy(firstY, secondY / 1000.0f, 0.1f); + vt.recycle(); + } + + /** + * Simulate a drag by giving directly MotionEvents to + * the VelocityTracker using a linear interpolator + */ + private void drag(VelocityTracker vt, int startX, int endX, int startY, int endY, int steps, + long startime, int duration) { + drag(vt, startX, endX, startY, endY, steps, startime, duration, new LinearInterpolator()); + } + + /** + * Simulate a drag by giving directly MotionEvents to + * the VelocityTracker using a given interpolator + */ + private void drag(VelocityTracker vt, int startX, int endX, int startY, int endY, int steps, + long startime, int duration, Interpolator interpolator) { + addMotionEvent(vt, startX, startY, startime, MotionEvent.ACTION_DOWN); + float dt = duration / (float)steps; + int distX = endX - startX; + int distY = endY - startY; + for (int i=1; i<steps-1; i++) { + float ii = interpolator.getInterpolation(i / (float)steps); + int x = (int) (startX + distX * ii); + int y = (int) (startY + distY * ii); + long time = startime + (int) (i * dt); + addMotionEvent(vt, x, y, time, MotionEvent.ACTION_MOVE); + } + addMotionEvent(vt, endX, endY, startime + duration, MotionEvent.ACTION_UP); + } + + private void addMotionEvent(VelocityTracker vt, int x, int y, long time, int action) { + MotionEvent me = MotionEvent.obtain(time, time, action, x, y, 0); + vt.addMovement(me); + me.recycle(); + } + + /** + * Float imprecision of the average computations and filtering + * (removing last MotionEvent for N > 3) implies that tests + * accepts some approximated values. + */ + private void assertEqualFuzzy(float expected, float actual, float threshold) { + boolean fuzzyEqual = actual >= expected - threshold && actual <= expected + threshold; + Assert.assertTrue("Expected: <"+expected+"> but was: <"+actual+ + "> while accepting a variation of: <"+threshold+">", fuzzyEqual); + } + + private void assertGreater(float minExpected, float actual) { + Assert.assertTrue("Expected: minimum <"+minExpected+"> but was: <"+actual+">", + actual > minExpected); + } + + private void assertLower(float maxExpected, float actual) { + Assert.assertTrue("Expected: maximum <"+maxExpected+"> but was: <"+actual+">", + actual < maxExpected); + } +} diff --git a/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java index df8d8362a6b2..bbf1696eb25b 100644 --- a/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java +++ b/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java @@ -14,8 +14,7 @@ package android.view.accessibility; -import android.test.suitebuilder.annotation.MediumTest; -import android.view.accessibility.AccessibilityEvent; +import android.test.suitebuilder.annotation.SmallTest; import junit.framework.TestCase; @@ -39,7 +38,7 @@ public class RecycleAccessibilityEventTest extends TestCase { /** * If an {@link AccessibilityEvent} is marshaled/unmarshaled correctly */ - @MediumTest + @SmallTest public void testAccessibilityEventViewTextChangedType() { AccessibilityEvent first = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); diff --git a/core/tests/coretests/src/android/widget/RadioGroupPreCheckedTest.java b/core/tests/coretests/src/android/widget/RadioGroupPreCheckedTest.java index 855caaec95a8..1ab362835bbe 100644 --- a/core/tests/coretests/src/android/widget/RadioGroupPreCheckedTest.java +++ b/core/tests/coretests/src/android/widget/RadioGroupPreCheckedTest.java @@ -16,23 +16,22 @@ package android.widget; -import android.test.TouchUtils; -import android.widget.RadioButton; -import android.widget.RadioGroup; import com.android.frameworks.coretests.R; import android.test.ActivityInstrumentationTestCase2; +import android.test.TouchUtils; import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.MediumTest; /** * Exercises {@link android.widget.RadioGroup}'s check feature. */ public class RadioGroupPreCheckedTest extends ActivityInstrumentationTestCase2<RadioGroupActivity> { public RadioGroupPreCheckedTest() { - super("com.android.frameworks.coretests", RadioGroupActivity.class); + super(RadioGroupActivity.class); } - @LargeTest + @MediumTest public void testRadioButtonPreChecked() throws Exception { final RadioGroupActivity activity = getActivity(); diff --git a/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListWithHeadersTest.java b/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListWithHeadersTest.java index 64a0fff8e63b..c74c8539659f 100644 --- a/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListWithHeadersTest.java +++ b/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListWithHeadersTest.java @@ -62,20 +62,20 @@ public class ExpandableListWithHeadersTest extends assertTrue(mExpandableListView.isGroupExpanded(0)); } - @MediumTest + @LargeTest public void testContextMenus() { ExpandableListTester tester = new ExpandableListTester(mExpandableListView, this); tester.testContextMenus(); } - @MediumTest + @LargeTest public void testConvertionBetweenFlatAndPacked() { ExpandableListTester tester = new ExpandableListTester(mExpandableListView, this); tester.testConvertionBetweenFlatAndPackedOnGroups(); tester.testConvertionBetweenFlatAndPackedOnChildren(); } - @MediumTest + @LargeTest public void testSelectedPosition() { ExpandableListTester tester = new ExpandableListTester(mExpandableListView, this); tester.testSelectedPositionOnGroups(); diff --git a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomManyTest.java b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomManyTest.java index f8e6ae7a0317..9a8d3074890b 100644 --- a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomManyTest.java +++ b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchStackFromBottomManyTest.java @@ -67,7 +67,7 @@ public class GridTouchStackFromBottomManyTest extends ActivityInstrumentationTes mGridView.getListPaddingTop(), firstChild.getTop()); } - @MediumTest + @LargeTest public void testScrollToBottom() { TouchUtils.scrollToBottom(this, mGridView); diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListInterleaveFocusablesTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListInterleaveFocusablesTest.java index 6238dab6a2fd..ec8ab7e77357 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListInterleaveFocusablesTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListInterleaveFocusablesTest.java @@ -16,22 +16,20 @@ package android.widget.listview.arrowscroll; -import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; +import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.MediumTest; -import android.widget.ListView; +import android.util.ListUtil; import android.view.KeyEvent; import android.view.View; - +import android.widget.ListView; import android.widget.listview.ListInterleaveFocusables; -import android.util.ListUtil; -public class ListInterleaveFocusablesTest extends ActivityInstrumentationTestCase<ListInterleaveFocusables> { +public class ListInterleaveFocusablesTest extends ActivityInstrumentationTestCase2<ListInterleaveFocusables> { private ListView mListView; private ListUtil mListUtil; public ListInterleaveFocusablesTest() { - super("com.android.frameworks.coretests", ListInterleaveFocusables.class); + super(ListInterleaveFocusables.class); } @Override @@ -42,7 +40,7 @@ public class ListInterleaveFocusablesTest extends ActivityInstrumentationTestCas mListUtil = new ListUtil(mListView, getInstrumentation()); } - @LargeTest + @MediumTest public void testPreconditions() { assertEquals(7, mListView.getChildCount()); assertTrue(mListView.getChildAt(1).isFocusable()); diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsTallerThanScreenTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsTallerThanScreenTest.java index 5960942282a9..6805b728f540 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsTallerThanScreenTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfItemsTallerThanScreenTest.java @@ -16,16 +16,15 @@ package android.widget.listview.arrowscroll; -import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; +import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.MediumTest; -import android.widget.ListView; -import android.view.View; import android.view.KeyEvent; +import android.view.View; +import android.widget.ListView; import android.widget.listview.ListOfItemsTallerThanScreen; public class ListOfItemsTallerThanScreenTest - extends ActivityInstrumentationTestCase<ListOfItemsTallerThanScreen> { + extends ActivityInstrumentationTestCase2<ListOfItemsTallerThanScreen> { private ListView mListView; private ListOfItemsTallerThanScreen mActivity; @@ -38,7 +37,7 @@ public class ListOfItemsTallerThanScreenTest } public ListOfItemsTallerThanScreenTest() { - super("com.android.frameworks.coretests", ListOfItemsTallerThanScreen.class); + super(ListOfItemsTallerThanScreen.class); } @MediumTest @@ -126,7 +125,7 @@ public class ListOfItemsTallerThanScreenTest 1, mListView.getChildCount()); } - @LargeTest + @MediumTest public void testScrollDownToLastItem() { final int numItems = mListView.getAdapter().getCount(); diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortShortTallShortShortTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortShortTallShortShortTest.java index a5d4906b0c7d..5aa27b267025 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortShortTallShortShortTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListOfShortShortTallShortShortTest.java @@ -16,20 +16,19 @@ package android.widget.listview.arrowscroll; -import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; +import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.MediumTest; +import android.util.ListUtil; import android.view.KeyEvent; import android.widget.ListView; import android.widget.listview.ListOfShortShortTallShortShort; -import android.util.ListUtil; -public class ListOfShortShortTallShortShortTest extends ActivityInstrumentationTestCase<ListOfShortShortTallShortShort> { +public class ListOfShortShortTallShortShortTest extends ActivityInstrumentationTestCase2<ListOfShortShortTallShortShort> { private ListView mListView; private ListUtil mListUtil; public ListOfShortShortTallShortShortTest() { - super("com.android.frameworks.coretests", ListOfShortShortTallShortShort.class); + super(ListOfShortShortTallShortShort.class); } @Override @@ -68,7 +67,7 @@ public class ListOfShortShortTallShortShortTest extends ActivityInstrumentationT 1, mListView.getChildCount()); } - @LargeTest + @MediumTest public void testFadeInTwoBottomItems() { // put 2nd item selected sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); @@ -89,7 +88,7 @@ public class ListOfShortShortTallShortShortTest extends ActivityInstrumentationT mListView.getChildAt(2).getBottom() >= mListUtil.getListBottom()); } - @LargeTest + @MediumTest public void testFadeOutBottomTwoItems() throws Exception { mListUtil.arrowScrollToSelectedPosition(4); @@ -110,7 +109,7 @@ public class ListOfShortShortTallShortShortTest extends ActivityInstrumentationT 1, mListView.getChildCount()); } - @LargeTest + @MediumTest public void testFadeInTopTwoItems() throws Exception { mListUtil.arrowScrollToSelectedPosition(4); diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithScreenOfNoSelectablesTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithScreenOfNoSelectablesTest.java index 80716501d503..b68631a84b1f 100644 --- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithScreenOfNoSelectablesTest.java +++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithScreenOfNoSelectablesTest.java @@ -16,21 +16,19 @@ package android.widget.listview.arrowscroll; -import android.widget.listview.ListWithScreenOfNoSelectables; - -import android.test.ActivityInstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; +import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.MediumTest; import android.view.KeyEvent; import android.view.View; import android.widget.ListView; +import android.widget.listview.ListWithScreenOfNoSelectables; -public class ListWithScreenOfNoSelectablesTest extends ActivityInstrumentationTestCase<ListWithScreenOfNoSelectables> { +public class ListWithScreenOfNoSelectablesTest extends ActivityInstrumentationTestCase2<ListWithScreenOfNoSelectables> { private ListView mListView; public ListWithScreenOfNoSelectablesTest() { - super("com.android.frameworks.coretests", ListWithScreenOfNoSelectables.class); + super(ListWithScreenOfNoSelectables.class); } @Override @@ -56,7 +54,7 @@ public class ListWithScreenOfNoSelectablesTest extends ActivityInstrumentationTe @MediumTest public void testGoFromSelectedViewExistsToNoSelectedViewExists() { - // go down untile first (and only selectable) item is off screen + // go down until first (and only selectable) item is off screen View first = mListView.getChildAt(0); while (first.getParent() != null) { sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); @@ -67,7 +65,7 @@ public class ListWithScreenOfNoSelectablesTest extends ActivityInstrumentationTe assertNull("selected view", mListView.getSelectedView()); } - @LargeTest + @MediumTest public void testPanDownAcrossUnselectableChildrenToBottom() { final int lastPosition = mListView.getCount() - 1; final int maxDowns = 20; diff --git a/core/tests/coretests/src/com/android/internal/http/multipart/MultipartTest.java b/core/tests/coretests/src/com/android/internal/http/multipart/MultipartTest.java new file mode 100644 index 000000000000..32e13a7ac7f8 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/http/multipart/MultipartTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2008 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.internal.http.multipart; + +import junit.framework.TestCase; +import org.apache.http.Header; +import org.apache.http.util.EncodingUtils; + +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileWriter; + +public class MultipartTest extends TestCase { + + public void testParts() throws Exception { + StringBuffer filebuffer = new StringBuffer(); + String filepartStr = "this is file part"; + filebuffer.append(filepartStr); + File upload = File.createTempFile("Multipart", "test"); + + FileWriter outFile = new FileWriter(upload); + BufferedWriter out = new BufferedWriter(outFile); + try { + out.write(filebuffer.toString()); + out.flush(); + } finally { + out.close(); + } + + Part[] parts = new Part[3]; + parts[0] = new StringPart("stringpart", "PART1!!"); + parts[1] = new FilePart(upload.getName(), upload); + parts[2] = new StringPart("stringpart", "PART2!!"); + + MultipartEntity me = new MultipartEntity(parts); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + me.writeTo(os); + Header h = me.getContentType(); + String boundry = EncodingUtils.getAsciiString(me.getMultipartBoundary()); + StringBuffer contentType = new StringBuffer("multipart/form-data"); + contentType.append("; boundary="); + contentType.append(boundry); + assertEquals("Multipart content type error", contentType.toString(), h.getValue()); + final String CRLF = "\r\n"; + StringBuffer output = new StringBuffer(); + + output.append("--"); + output.append(boundry); + output.append(CRLF); + + output.append("Content-Disposition: form-data; name=\"stringpart\""); + output.append(CRLF); + output.append("Content-Type: text/plain; charset=US-ASCII"); + output.append(CRLF); + output.append("Content-Transfer-Encoding: 8bit"); + output.append(CRLF); + output.append(CRLF); + output.append("PART1!!"); + output.append(CRLF); + + output.append("--"); + output.append(boundry); + output.append(CRLF); + + output.append("Content-Disposition: form-data; name=\""); + output.append(upload.getName()); + output.append("\"; filename=\""); + output.append(upload.getName()); + output.append("\""); + + output.append(CRLF); + output.append("Content-Type: application/octet-stream; charset=ISO-8859-1"); + output.append(CRLF); + output.append("Content-Transfer-Encoding: binary"); + output.append(CRLF); + output.append(CRLF); + output.append(filepartStr); + output.append(CRLF); + + output.append("--"); + output.append(boundry); + output.append(CRLF); + + output.append("Content-Disposition: form-data; name=\"stringpart\""); + output.append(CRLF); + output.append("Content-Type: text/plain; charset=US-ASCII"); + output.append(CRLF); + output.append("Content-Transfer-Encoding: 8bit"); + output.append(CRLF); + output.append(CRLF); + output.append("PART2!!"); + output.append(CRLF); + + output.append("--"); + output.append(boundry); + output.append("--"); + output.append(CRLF); + // System.out.print(output.toString()); + assertEquals("Multipart content error", output.toString(), os.toString()); + + // System.out.print(os.toString()); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java b/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java new file mode 100644 index 000000000000..4d016d14c6e4 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2008 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.internal.os; + +import junit.framework.TestCase; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class LoggingPrintStreamTest extends TestCase { + + TestPrintStream out = new TestPrintStream(); + + public void testPrintException() { + @SuppressWarnings("ThrowableInstanceNeverThrown") + Throwable t = new Throwable("Ignore me."); + + StringWriter sout = new StringWriter(); + t.printStackTrace(new PrintWriter(sout)); + + t.printStackTrace(out); + // t.printStackTrace(); + + String[] lines = sout.toString().split("\\n"); + assertEquals(Arrays.asList(lines), out.lines); + } + + public void testPrintObject() { + Object o = new Object(); + out.print(4); + out.print(o); + out.print(2); + out.flush(); + assertEquals(Arrays.asList("4" + o + "2"), out.lines); + } + + public void testPrintlnObject() { + Object o = new Object(); + out.print(4); + out.println(o); + out.print(2); + out.flush(); + assertEquals(Arrays.asList("4" + o, "2"), out.lines); + } + + public void testPrintf() { + out.printf("Name: %s\nEmployer: %s", "Bob", "Google"); + assertEquals(Arrays.asList("Name: Bob"), out.lines); + out.flush(); + assertEquals(Arrays.asList("Name: Bob", "Employer: Google"), out.lines); + } + + public void testPrintInt() { + out.print(4); + out.print(2); + assertTrue(out.lines.isEmpty()); + out.flush(); + assertEquals(Collections.singletonList("42"), out.lines); + } + + public void testPrintlnInt() { + out.println(4); + out.println(2); + assertEquals(Arrays.asList("4", "2"), out.lines); + } + + public void testPrintCharArray() { + out.print("Foo\nBar\nTee".toCharArray()); + assertEquals(Arrays.asList("Foo", "Bar"), out.lines); + out.flush(); + assertEquals(Arrays.asList("Foo", "Bar", "Tee"), out.lines); + } + + public void testPrintString() { + out.print("Foo\nBar\nTee"); + assertEquals(Arrays.asList("Foo", "Bar"), out.lines); + out.flush(); + assertEquals(Arrays.asList("Foo", "Bar", "Tee"), out.lines); + } + + public void testPrintlnCharArray() { + out.println("Foo\nBar\nTee".toCharArray()); + assertEquals(Arrays.asList("Foo", "Bar", "Tee"), out.lines); + } + + public void testPrintlnString() { + out.println("Foo\nBar\nTee"); + assertEquals(Arrays.asList("Foo", "Bar", "Tee"), out.lines); + } + + public void testPrintlnStringWithBufferedData() { + out.print(5); + out.println("Foo\nBar\nTee"); + assertEquals(Arrays.asList("5Foo", "Bar", "Tee"), out.lines); + } + + public void testAppend() { + out.append("Foo\n") + .append('4') + .append('\n') + .append("Bar", 1, 2) + .append('\n'); + assertEquals(Arrays.asList("Foo", "4", "a"), out.lines); + } + + public void testMultiByteCharactersSpanningBuffers() throws Exception { + // assume 3*1000 bytes won't fit in LoggingPrintStream's internal buffer + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < 1000; i++) { + builder.append("\u20AC"); // a Euro character; 3 bytes in UTF-8 + } + String expected = builder.toString(); + + out.write(expected.getBytes("UTF-8")); + out.flush(); + assertEquals(Arrays.asList(expected), out.lines); + } + + public void testWriteOneByteAtATimeMultibyteCharacters() throws Exception { + String expected = " \u20AC \u20AC \u20AC \u20AC "; + for (byte b : expected.getBytes()) { + out.write(b); + } + out.flush(); + assertEquals(Arrays.asList(expected), out.lines); + } + + public void testWriteByteArrayAtATimeMultibyteCharacters() throws Exception { + String expected = " \u20AC \u20AC \u20AC \u20AC "; + out.write(expected.getBytes()); + out.flush(); + assertEquals(Arrays.asList(expected), out.lines); + } + + public void testWriteWithOffsetsMultibyteCharacters() throws Exception { + String expected = " \u20AC \u20AC \u20AC \u20AC "; + byte[] bytes = expected.getBytes(); + int i = 0; + while (i < bytes.length - 5) { + out.write(bytes, i, 5); + i += 5; + } + out.write(bytes, i, bytes.length - i); + out.flush(); + assertEquals(Arrays.asList(expected), out.lines); + } + + public void testWriteFlushesOnNewlines() throws Exception { + String a = " \u20AC \u20AC "; + String b = " \u20AC \u20AC "; + String c = " "; + String toWrite = a + "\n" + b + "\n" + c; + out.write(toWrite.getBytes()); + out.flush(); + assertEquals(Arrays.asList(a, b, c), out.lines); + } + + static class TestPrintStream extends LoggingPrintStream { + + final List<String> lines = new ArrayList<String>(); + + protected void log(String line) { + lines.add(line); + } + } +} diff --git a/core/tests/hosttests/src/android/content/pm/PackageManagerHostTestUtils.java b/core/tests/hosttests/src/android/content/pm/PackageManagerHostTestUtils.java index 91cbe2fc328f..fed39c949523 100644 --- a/core/tests/hosttests/src/android/content/pm/PackageManagerHostTestUtils.java +++ b/core/tests/hosttests/src/android/content/pm/PackageManagerHostTestUtils.java @@ -37,6 +37,9 @@ import java.io.IOException; import java.io.StringReader; import java.lang.Runtime; import java.lang.Process; +import java.util.Hashtable; +import java.util.Map; +import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -117,12 +120,33 @@ public class PackageManagerHostTestUtils extends Assert { /** * Helper method to run tests and return the listener that collected the results. + * + * For the optional params, pass null to use the default values. + * @param pkgName Android application package for tests + * @param className (optional) The class containing the method to test + * @param methodName (optional) The method in the class of which to test + * @param runnerName (optional) The name of the TestRunner of the test on the device to be run + * @param params (optional) Any additional parameters to pass into the Test Runner * @return the {@link CollectingTestRunListener} */ - private CollectingTestRunListener doRunTests(String pkgName) throws IOException { - RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner( - pkgName, mDevice); + private CollectingTestRunListener doRunTests(String pkgName, String className, String + methodName, String runnerName, Map<String, String> params) throws IOException { + + RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(pkgName, runnerName, + mDevice); + + if (className != null && methodName != null) { + testRunner.setMethodName(className, methodName); + } + + // Add in any additional args to pass into the test + if (params != null) { + for (Entry<String, String> argPair : params.entrySet()) { + testRunner.addInstrumentationArg(argPair.getKey(), argPair.getValue()); + } + } + CollectingTestRunListener listener = new CollectingTestRunListener(); testRunner.run(listener); return listener; @@ -132,10 +156,27 @@ public class PackageManagerHostTestUtils extends Assert { * Runs the specified packages tests, and returns whether all tests passed or not. * * @param pkgName Android application package for tests + * @param className The class containing the method to test + * @param methodName The method in the class of which to test + * @param runnerName The name of the TestRunner of the test on the device to be run + * @param params Any additional parameters to pass into the Test Runner + * @return true if test passed, false otherwise. + */ + public boolean runDeviceTestsDidAllTestsPass(String pkgName, String className, + String methodName, String runnerName, Map<String, String> params) throws IOException { + CollectingTestRunListener listener = doRunTests(pkgName, className, methodName, + runnerName, params); + return listener.didAllTestsPass(); + } + + /** + * Runs the specified packages tests, and returns whether all tests passed or not. + * + * @param pkgName Android application package for tests * @return true if every test passed, false otherwise. */ public boolean runDeviceTestsDidAllTestsPass(String pkgName) throws IOException { - CollectingTestRunListener listener = doRunTests(pkgName); + CollectingTestRunListener listener = doRunTests(pkgName, null, null, null, null); return listener.didAllTestsPass(); } @@ -452,7 +493,7 @@ public class PackageManagerHostTestUtils extends Assert { } // For collecting results from running device tests - private static class CollectingTestRunListener implements ITestRunListener { + public static class CollectingTestRunListener implements ITestRunListener { private boolean mAllTestsPassed = true; private String mTestRunErrorMessage = null; diff --git a/core/tests/hosttests/src/android/net/DownloadManagerHostTests.java b/core/tests/hosttests/src/android/net/DownloadManagerHostTests.java new file mode 100644 index 000000000000..ed280c916ec4 --- /dev/null +++ b/core/tests/hosttests/src/android/net/DownloadManagerHostTests.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2010 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.net; + +import android.content.pm.PackageManagerHostTestUtils; +import android.content.pm.PackageManagerHostTestUtils.CollectingTestRunListener; + +import com.android.ddmlib.IDevice; +import com.android.ddmlib.IShellOutputReceiver; +import com.android.ddmlib.Log; +import com.android.ddmlib.MultiLineReceiver; +import com.android.ddmlib.SyncService; +import com.android.ddmlib.SyncService.ISyncProgressMonitor; +import com.android.ddmlib.SyncService.SyncResult; +import com.android.hosttest.DeviceTestCase; +import com.android.hosttest.DeviceTestSuite; + +import java.io.File; +import java.io.IOException; +import java.util.Hashtable; + +import junit.framework.Test; + +/** + * Host-based tests of the DownloadManager API. (Uses a device-based app to actually invoke the + * various tests.) + */ +public class DownloadManagerHostTests extends DeviceTestCase { + protected PackageManagerHostTestUtils mPMUtils = null; + + private static final String LOG_TAG = "android.net.DownloadManagerHostTests"; + private static final String FILE_DOWNLOAD_APK = "DownloadManagerTestApp.apk"; + private static final String FILE_DOWNLOAD_PKG = "com.android.frameworks.downloadmanagertests"; + private static final String FILE_DOWNLOAD_CLASS = + "com.android.frameworks.downloadmanagertests.DownloadManagerTestApp"; + private static final String DOWNLOAD_TEST_RUNNER_NAME = + "com.android.frameworks.downloadmanagertests.DownloadManagerTestRunner"; + + // Extra parameters to pass to the TestRunner + private static final String EXTERNAL_DOWNLOAD_URI_KEY = "external_download_uri"; + // Note: External environment variable ANDROID_TEST_EXTERNAL_URI must be set to point to the + // external URI under which the files downloaded by the tests can be found. Note that the Uri + // must be accessible by the device during a test run. + private static String EXTERNAL_DOWNLOAD_URI_VALUE = null; + + Hashtable<String, String> mExtraParams = null; + + public static Test suite() { + return new DeviceTestSuite(DownloadManagerHostTests.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + // ensure apk path has been set before test is run + assertNotNull(getTestAppPath()); + mPMUtils = new PackageManagerHostTestUtils(getDevice()); + EXTERNAL_DOWNLOAD_URI_VALUE = System.getenv("ANDROID_TEST_EXTERNAL_URI"); + assertNotNull(EXTERNAL_DOWNLOAD_URI_VALUE); + mExtraParams = getExtraParams(); + } + + /** + * Helper function to get extra params that can be used to pass into the helper app. + */ + protected Hashtable<String, String> getExtraParams() { + Hashtable<String, String> extraParams = new Hashtable<String, String>(); + extraParams.put(EXTERNAL_DOWNLOAD_URI_KEY, EXTERNAL_DOWNLOAD_URI_VALUE); + return extraParams; + } + + /** + * Tests that a large download over WiFi + * @throws Exception if the test failed at any point + */ + public void testLargeDownloadOverWiFi() throws Exception { + mPMUtils.installAppAndVerifyExistsOnDevice(String.format("%s%s%s", getTestAppPath(), + File.separator, FILE_DOWNLOAD_APK), FILE_DOWNLOAD_PKG, true); + + boolean testPassed = mPMUtils.runDeviceTestsDidAllTestsPass(FILE_DOWNLOAD_PKG, + FILE_DOWNLOAD_CLASS, "runLargeDownloadOverWiFi", DOWNLOAD_TEST_RUNNER_NAME, + mExtraParams); + + assertTrue("Failed to install large file over WiFi in < 10 minutes!", testPassed); + } + + /** + * Spawns a device-based function to initiate a download on the device, reboots the device, + * then waits and verifies the download succeeded. + * + * @throws Exception if the test failed at any point + */ + public void testDownloadManagerSingleReboot() throws Exception { + mPMUtils.installAppAndVerifyExistsOnDevice(String.format("%s%s%s", getTestAppPath(), + File.separator, FILE_DOWNLOAD_APK), FILE_DOWNLOAD_PKG, true); + + boolean testPassed = mPMUtils.runDeviceTestsDidAllTestsPass(FILE_DOWNLOAD_PKG, + FILE_DOWNLOAD_CLASS, "initiateDownload", DOWNLOAD_TEST_RUNNER_NAME, + mExtraParams); + + assertTrue("Failed to initiate download properly!", testPassed); + mPMUtils.rebootDevice(); + testPassed = mPMUtils.runDeviceTestsDidAllTestsPass(FILE_DOWNLOAD_PKG, + FILE_DOWNLOAD_CLASS, "verifyFileDownloadSucceeded", DOWNLOAD_TEST_RUNNER_NAME, + mExtraParams); + assertTrue("Failed to verify initiated download completed properyly!", testPassed); + } + + /** + * Spawns a device-based function to initiate a download on the device, reboots the device three + * times (using different intervals), then waits and verifies the download succeeded. + * + * @throws Exception if the test failed at any point + */ + public void testDownloadManagerMultipleReboots() throws Exception { + mPMUtils.installAppAndVerifyExistsOnDevice(String.format("%s%s%s", getTestAppPath(), + File.separator, FILE_DOWNLOAD_APK), FILE_DOWNLOAD_PKG, true); + + boolean testPassed = mPMUtils.runDeviceTestsDidAllTestsPass(FILE_DOWNLOAD_PKG, + FILE_DOWNLOAD_CLASS, "initiateDownload", DOWNLOAD_TEST_RUNNER_NAME, + mExtraParams); + + assertTrue("Failed to initiate download properly!", testPassed); + Thread.sleep(5000); + + // Do 3 random reboots - after 13, 9, and 19 seconds + Log.i(LOG_TAG, "First reboot..."); + mPMUtils.rebootDevice(); + Thread.sleep(13000); + Log.i(LOG_TAG, "Second reboot..."); + mPMUtils.rebootDevice(); + Thread.sleep(9000); + Log.i(LOG_TAG, "Third reboot..."); + mPMUtils.rebootDevice(); + Thread.sleep(19000); + testPassed = mPMUtils.runDeviceTestsDidAllTestsPass(FILE_DOWNLOAD_PKG, + FILE_DOWNLOAD_CLASS, "verifyFileDownloadSucceeded", DOWNLOAD_TEST_RUNNER_NAME, + mExtraParams); + assertTrue("Failed to verify initiated download completed properyly!", testPassed); + } + + /** + * Spawns a device-based function to test download while WiFi is enabled/disabled multiple times + * during the download. + * + * @throws Exception if the test failed at any point + */ + public void testDownloadMultipleWiFiEnableDisable() throws Exception { + mPMUtils.installAppAndVerifyExistsOnDevice(String.format("%s%s%s", getTestAppPath(), + File.separator, FILE_DOWNLOAD_APK), FILE_DOWNLOAD_PKG, true); + + boolean testPassed = mPMUtils.runDeviceTestsDidAllTestsPass(FILE_DOWNLOAD_PKG, + FILE_DOWNLOAD_CLASS, "runDownloadMultipleWiFiEnableDisable", + DOWNLOAD_TEST_RUNNER_NAME, mExtraParams); + assertTrue(testPassed); + } + + /** + * Spawns a device-based function to test switching on/off both airplane mode and WiFi + * + * @throws Exception if the test failed at any point + */ + public void testDownloadMultipleSwitching() throws Exception { + mPMUtils.installAppAndVerifyExistsOnDevice(String.format("%s%s%s", getTestAppPath(), + File.separator, FILE_DOWNLOAD_APK), FILE_DOWNLOAD_PKG, true); + + boolean testPassed = mPMUtils.runDeviceTestsDidAllTestsPass(FILE_DOWNLOAD_PKG, + FILE_DOWNLOAD_CLASS, "runDownloadMultipleSwitching", + DOWNLOAD_TEST_RUNNER_NAME, mExtraParams); + assertTrue(testPassed); + } + + /** + * Spawns a device-based function to test switching on/off airplane mode multiple times + * + * @throws Exception if the test failed at any point + */ + public void testDownloadMultipleAirplaneModeEnableDisable() throws Exception { + mPMUtils.installAppAndVerifyExistsOnDevice(String.format("%s%s%s", getTestAppPath(), + File.separator, FILE_DOWNLOAD_APK), FILE_DOWNLOAD_PKG, true); + + boolean testPassed = mPMUtils.runDeviceTestsDidAllTestsPass(FILE_DOWNLOAD_PKG, + FILE_DOWNLOAD_CLASS, "runDownloadMultipleAirplaneModeEnableDisable", + DOWNLOAD_TEST_RUNNER_NAME, mExtraParams); + assertTrue(testPassed); + } +} diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk new file mode 100644 index 000000000000..576765c742b0 --- /dev/null +++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk @@ -0,0 +1,29 @@ +# Copyright (C) 2010 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-java-files-under, src) \ + ../../../coretests/src/android/net/DownloadManagerBaseTest.java + +LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib +LOCAL_SDK_VERSION := current + +LOCAL_PACKAGE_NAME := DownloadManagerTestApp + +include $(BUILD_PACKAGE) diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/AndroidManifest.xml b/core/tests/hosttests/test-apps/DownloadManagerTestApp/AndroidManifest.xml new file mode 100644 index 000000000000..3f2be3cab6e6 --- /dev/null +++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/AndroidManifest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.downloadmanagertests"> + + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> + <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> + <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.WRITE_SETTINGS" /> + + <application android:label="DownloadManagerTestApp"> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name=".DownloadManagerTestRunner" + android:targetPackage="com.android.frameworks.downloadmanagertests" + android:label="Frameworks Download Manager Test App" /> + +</manifest> diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java new file mode 100644 index 000000000000..ef8135398583 --- /dev/null +++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestApp.java @@ -0,0 +1,463 @@ +/* + * Copyright (C) 2010 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.frameworks.downloadmanagertests; + +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.net.DownloadManager; +import android.net.DownloadManager.Query; +import android.net.DownloadManager.Request; +import android.net.DownloadManagerBaseTest; +import android.net.Uri; +import android.os.Environment; +import android.os.ParcelFileDescriptor; +import android.provider.Settings; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; + +import coretestutils.http.MockResponse; +import coretestutils.http.MockWebServer; +import coretestutils.http.RecordedRequest; + +/** + * Class to test downloading files from a real (not mock) external server. + */ +public class DownloadManagerTestApp extends DownloadManagerBaseTest { + protected static String DOWNLOAD_STARTED_FLAG = "DMTEST_DOWNLOAD_STARTED"; + protected static String LOG_TAG = + "com.android.frameworks.downloadmanagertests.DownloadManagerTestApp"; + + protected static String DOWNLOAD_500K_FILENAME = "External541kb.apk"; + protected static long DOWNLOAD_500K_FILESIZE = 570927; + protected static String DOWNLOAD_1MB_FILENAME = "External1mb.apk"; + protected static long DOWNLOAD_1MB_FILESIZE = 1041262; + protected static String DOWNLOAD_10MB_FILENAME = "External10mb.apk"; + protected static long DOWNLOAD_10MB_FILESIZE = 10258741; + + // Values to be obtained from TestRunner + private String externalDownloadUriValue = null; + + /** + * {@inheritDoc } + */ + @Override + public void setUp() throws Exception { + super.setUp(); + DownloadManagerTestRunner mRunner = (DownloadManagerTestRunner)getInstrumentation(); + externalDownloadUriValue = mRunner.externalDownloadUriValue; + assertNotNull(externalDownloadUriValue); + + if (!externalDownloadUriValue.endsWith("/")) { + externalDownloadUriValue += "/"; + } + } + + /** + * Gets the external URL of the file to download + * + * @return the Uri of the external file to download + */ + private Uri getExternalFileUri(String file) { + return Uri.parse(externalDownloadUriValue + file); + } + + /** + * Gets the path to the file that flags that a download has started. The file contains the + * DownloadManager id of the download being trackted between reboot sessions. + * + * @return The path of the file tracking that a download has started + * @throws InterruptedException if interrupted + * @throws Exception if timed out while waiting for SD card to mount + */ + protected String getDownloadStartedFilePath() { + String path = Environment.getExternalStorageDirectory().getPath(); + return path + File.separatorChar + DOWNLOAD_STARTED_FLAG; + } + + /** + * Common setup steps for downloads. + * + * Note that these are not included in setUp, so that individual tests can control their own + * state between reboots, etc. + */ + protected void doCommonDownloadSetup() throws Exception { + setWiFiStateOn(true); + setAirplaneModeOn(false); + waitForExternalStoreMount(); + removeAllCurrentDownloads(); + } + + /** + * Initiates a download. + * + * Queues up a download to the download manager, and saves the DownloadManager's assigned + * download ID for this download to a file. + * + * @throws Exception if unsuccessful + */ + public void initiateDownload() throws Exception { + String filename = DOWNLOAD_1MB_FILENAME; + mContext.deleteFile(DOWNLOAD_STARTED_FLAG); + FileOutputStream fileOutput = mContext.openFileOutput(DOWNLOAD_STARTED_FLAG, 0); + DataOutputStream outputFile = null; + doCommonDownloadSetup(); + + try { + long dlRequest = -1; + + // Make sure there are no pending downloads currently going on + removeAllCurrentDownloads(); + + Uri remoteUri = getExternalFileUri(filename); + Request request = new Request(remoteUri); + + dlRequest = mDownloadManager.enqueue(request); + waitForDownloadToStart(dlRequest); + assertTrue(dlRequest != -1); + + // Store ID of download for later retrieval + outputFile = new DataOutputStream(fileOutput); + outputFile.writeLong(dlRequest); + } finally { + if (outputFile != null) { + outputFile.flush(); + outputFile.close(); + } + } + } + + /** + * Waits for a previously-initiated download and verifies it has completed successfully. + * + * @throws Exception if unsuccessful + */ + public void verifyFileDownloadSucceeded() throws Exception { + String filename = DOWNLOAD_1MB_FILENAME; + long filesize = DOWNLOAD_1MB_FILESIZE; + long dlRequest = -1; + boolean rebootMarkerValid = false; + DataInputStream dataInputFile = null; + + setWiFiStateOn(true); + setAirplaneModeOn(false); + + try { + FileInputStream inFile = mContext.openFileInput(DOWNLOAD_STARTED_FLAG); + dataInputFile = new DataInputStream(inFile); + dlRequest = dataInputFile.readLong(); + } catch (Exception e) { + // The file was't valid so we just leave the flag false + Log.i(LOG_TAG, "Unable to determine initial download id."); + throw e; + } finally { + if (dataInputFile != null) { + dataInputFile.close(); + } + mContext.deleteFile(DOWNLOAD_STARTED_FLAG); + } + + assertTrue(dlRequest != -1); + Cursor cursor = getCursor(dlRequest); + ParcelFileDescriptor pfd = null; + try { + assertTrue("Unable to query last initiated download!", cursor.moveToFirst()); + + int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS); + int status = cursor.getInt(columnIndex); + int currentWaitTime = 0; + + // Wait until the download finishes + waitForDownloadOrTimeout(dlRequest); + + Log.i(LOG_TAG, "Verifying download information..."); + // Verify specific info about the file (size, name, etc)... + pfd = mDownloadManager.openDownloadedFile(dlRequest); + verifyFileSize(pfd, filesize); + } catch (Exception e) { + Log.i(LOG_TAG, "error: " + e.toString()); + throw e; + } finally { + // Clean up... + cursor.close(); + mDownloadManager.remove(dlRequest); + if (pfd != null) { + pfd.close(); + } + } + } + + /** + * Tests downloading a large file over WiFi (~10 Mb). + * + * @throws Exception if unsuccessful + */ + public void runLargeDownloadOverWiFi() throws Exception { + String filename = DOWNLOAD_10MB_FILENAME; + long filesize = DOWNLOAD_10MB_FILESIZE; + long dlRequest = -1; + doCommonDownloadSetup(); + + // Make sure there are no pending downloads currently going on + removeAllCurrentDownloads(); + + Uri remoteUri = getExternalFileUri(filename); + Request request = new Request(remoteUri); + request.setMediaType(getMimeMapping(DownloadFileType.APK)); + + dlRequest = mDownloadManager.enqueue(request); + + // Rather large file, so wait up to 15 mins... + waitForDownloadOrTimeout(dlRequest, WAIT_FOR_DOWNLOAD_POLL_TIME, 15 * 60 * 1000); + + Cursor cursor = getCursor(dlRequest); + ParcelFileDescriptor pfd = null; + try { + Log.i(LOG_TAG, "Verifying download information..."); + // Verify specific info about the file (size, name, etc)... + pfd = mDownloadManager.openDownloadedFile(dlRequest); + verifyFileSize(pfd, filesize); + } finally { + if (pfd != null) { + pfd.close(); + } + mDownloadManager.remove(dlRequest); + cursor.close(); + } + } + + /** + * Tests that downloads resume when switching back and forth from having connectivity to + * having no connectivity using both WiFi and airplane mode. + * + * Note: Device has no mobile access when running this test. + * + * @throws Exception if unsuccessful + */ + public void runDownloadMultipleSwitching() throws Exception { + String filename = DOWNLOAD_500K_FILENAME; + long filesize = DOWNLOAD_500K_FILESIZE; + doCommonDownloadSetup(); + + String localDownloadDirectory = Environment.getExternalStorageDirectory().getPath(); + File downloadedFile = new File(localDownloadDirectory, filename); + + long dlRequest = -1; + try { + downloadedFile.delete(); + + // Make sure there are no pending downloads currently going on + removeAllCurrentDownloads(); + + Uri remoteUri = getExternalFileUri(filename); + Request request = new Request(remoteUri); + + // Local destination of downloaded file + Uri localUri = Uri.fromFile(downloadedFile); + Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath()); + request.setDestinationUri(localUri); + + request.setAllowedNetworkTypes(Request.NETWORK_MOBILE | Request.NETWORK_WIFI); + + dlRequest = mDownloadManager.enqueue(request); + waitForDownloadToStart(dlRequest); + // make sure we're starting to download some data... + waitForFileToGrow(downloadedFile); + + // download disable + setWiFiStateOn(false); + + // download disable + Log.i(LOG_TAG, "Turning on airplane mode..."); + setAirplaneModeOn(true); + Thread.sleep(30 * 1000); // wait 30 secs + + // download disable + setWiFiStateOn(true); + Thread.sleep(30 * 1000); // wait 30 secs + + // download enable + Log.i(LOG_TAG, "Turning off airplane mode..."); + setAirplaneModeOn(false); + Thread.sleep(5 * 1000); // wait 5 seconds + + // download disable + Log.i(LOG_TAG, "Turning off WiFi..."); + setWiFiStateOn(false); + Thread.sleep(30 * 1000); // wait 30 secs + + // finally, turn WiFi back on and finish up the download + Log.i(LOG_TAG, "Turning on WiFi..."); + setWiFiStateOn(true); + Log.i(LOG_TAG, "Waiting up to 3 minutes for download to complete..."); + waitForDownloadsOrTimeout(dlRequest, 3 * 60 * 1000); + ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(dlRequest); + verifyFileSize(pfd, filesize); + } finally { + Log.i(LOG_TAG, "Cleaning up files..."); + if (dlRequest != -1) { + mDownloadManager.remove(dlRequest); + } + downloadedFile.delete(); + } + } + + /** + * Tests that downloads resume when switching on/off WiFi at various intervals. + * + * Note: Device has no mobile access when running this test. + * + * @throws Exception if unsuccessful + */ + public void runDownloadMultipleWiFiEnableDisable() throws Exception { + String filename = DOWNLOAD_500K_FILENAME; + long filesize = DOWNLOAD_500K_FILESIZE; + doCommonDownloadSetup(); + + String localDownloadDirectory = Environment.getExternalStorageDirectory().getPath(); + File downloadedFile = new File(localDownloadDirectory, filename); + long dlRequest = -1; + try { + downloadedFile.delete(); + + // Make sure there are no pending downloads currently going on + removeAllCurrentDownloads(); + + Uri remoteUri = getExternalFileUri(filename); + Request request = new Request(remoteUri); + + // Local destination of downloaded file + Uri localUri = Uri.fromFile(downloadedFile); + Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath()); + request.setDestinationUri(localUri); + + request.setAllowedNetworkTypes(Request.NETWORK_WIFI); + + dlRequest = mDownloadManager.enqueue(request); + waitForDownloadToStart(dlRequest); + // are we making any progress? + waitForFileToGrow(downloadedFile); + + // download disable + Log.i(LOG_TAG, "Turning off WiFi..."); + setWiFiStateOn(false); + Thread.sleep(40 * 1000); // wait 40 seconds + + // enable download... + Log.i(LOG_TAG, "Turning on WiFi again..."); + setWiFiStateOn(true); + waitForFileToGrow(downloadedFile); + + // download disable + Log.i(LOG_TAG, "Turning off WiFi..."); + setWiFiStateOn(false); + Thread.sleep(20 * 1000); // wait 20 seconds + + // enable download... + Log.i(LOG_TAG, "Turning on WiFi again..."); + setWiFiStateOn(true); + + Log.i(LOG_TAG, "Waiting up to 3 minutes for download to complete..."); + waitForDownloadsOrTimeout(dlRequest, 3 * 60 * 1000); + ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(dlRequest); + verifyFileSize(pfd, filesize); + } finally { + Log.i(LOG_TAG, "Cleaning up files..."); + if (dlRequest != -1) { + mDownloadManager.remove(dlRequest); + } + downloadedFile.delete(); + } + } + + /** + * Tests that downloads resume when switching on/off Airplane mode numerous times at + * various intervals. + * + * Note: Device has no mobile access when running this test. + * + * @throws Exception if unsuccessful + */ + public void runDownloadMultipleAirplaneModeEnableDisable() throws Exception { + String filename = DOWNLOAD_500K_FILENAME; + long filesize = DOWNLOAD_500K_FILESIZE; + // make sure WiFi is enabled, and airplane mode is not on + doCommonDownloadSetup(); + + String localDownloadDirectory = Environment.getExternalStorageDirectory().getPath(); + File downloadedFile = new File(localDownloadDirectory, filename); + long dlRequest = -1; + try { + downloadedFile.delete(); + + // Make sure there are no pending downloads currently going on + removeAllCurrentDownloads(); + + Uri remoteUri = getExternalFileUri(filename); + Request request = new Request(remoteUri); + + // Local destination of downloaded file + Uri localUri = Uri.fromFile(downloadedFile); + Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath()); + request.setDestinationUri(localUri); + + request.setAllowedNetworkTypes(Request.NETWORK_WIFI); + + dlRequest = mDownloadManager.enqueue(request); + waitForDownloadToStart(dlRequest); + // are we making any progress? + waitForFileToGrow(downloadedFile); + + // download disable + Log.i(LOG_TAG, "Turning on Airplane mode..."); + setAirplaneModeOn(true); + Thread.sleep(60 * 1000); // wait 1 minute + + // download enable + Log.i(LOG_TAG, "Turning off Airplane mode..."); + setAirplaneModeOn(false); + // make sure we're starting to download some data... + waitForFileToGrow(downloadedFile); + + // reenable the connection to start up the download again + Log.i(LOG_TAG, "Turning on Airplane mode again..."); + setAirplaneModeOn(true); + Thread.sleep(20 * 1000); // wait 20 seconds + + // Finish up the download... + Log.i(LOG_TAG, "Turning off Airplane mode again..."); + setAirplaneModeOn(false); + + Log.i(LOG_TAG, "Waiting up to 3 minutes for donwload to complete..."); + waitForDownloadsOrTimeout(dlRequest, 180 * 1000); // wait up to 3 mins before timeout + ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(dlRequest); + verifyFileSize(pfd, filesize); + } finally { + Log.i(LOG_TAG, "Cleaning up files..."); + if (dlRequest != -1) { + mDownloadManager.remove(dlRequest); + } + downloadedFile.delete(); + } + } +} diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestRunner.java b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestRunner.java new file mode 100644 index 000000000000..0f166195b4be --- /dev/null +++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/DownloadManagerTestRunner.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010, 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.frameworks.downloadmanagertests; + +import android.os.Bundle; +import android.test.InstrumentationTestRunner; +import android.test.InstrumentationTestSuite; +import android.util.Log; + +import com.android.frameworks.downloadmanagertests.DownloadManagerTestApp; + +import junit.framework.TestSuite; + +/** + * Instrumentation Test Runner for all download manager tests. + * + * To run the download manager tests: + * + * adb shell am instrument -e external_download_1mb_uri <uri> external_download_500k_uri <uri> \ + * -w com.android.frameworks.downloadmanagertests/.DownloadManagerTestRunner + */ + +public class DownloadManagerTestRunner extends InstrumentationTestRunner { + private static final String EXTERNAL_DOWNLOAD_URI_KEY = "external_download_uri"; + public String externalDownloadUriValue = null; + + @Override + public TestSuite getAllTests() { + TestSuite suite = new InstrumentationTestSuite(this); + suite.addTestSuite(DownloadManagerTestApp.class); + return suite; + } + + @Override + public ClassLoader getLoader() { + return DownloadManagerTestRunner.class.getClassLoader(); + } + + @Override + public void onCreate(Bundle icicle) { + // Extract the extra params passed in from the bundle... + String externalDownloadUri = (String) icicle.get(EXTERNAL_DOWNLOAD_URI_KEY); + if (externalDownloadUri != null) { + externalDownloadUriValue = externalDownloadUri; + } + super.onCreate(icicle); + } + +} diff --git a/core/tests/utillib/Android.mk b/core/tests/utillib/Android.mk new file mode 100644 index 000000000000..299ea5a2aeec --- /dev/null +++ b/core/tests/utillib/Android.mk @@ -0,0 +1,27 @@ +# Copyright (C) 2010 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_MODULE := frameworks-core-util-lib + +include $(BUILD_STATIC_JAVA_LIBRARY) + +# Build the test APKs using their own makefiles +include $(call all-makefiles-under,$(LOCAL_PATH)) + diff --git a/core/tests/utillib/src/coretestutils/http/MockResponse.java b/core/tests/utillib/src/coretestutils/http/MockResponse.java new file mode 100644 index 000000000000..5b03e5fe9019 --- /dev/null +++ b/core/tests/utillib/src/coretestutils/http/MockResponse.java @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2010 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 coretestutils.http; + +import static coretestutils.http.MockWebServer.ASCII; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import android.util.Log; + +/** + * A scripted response to be replayed by the mock web server. + */ +public class MockResponse { + private static final byte[] EMPTY_BODY = new byte[0]; + static final String LOG_TAG = "coretestutils.http.MockResponse"; + + private String status = "HTTP/1.1 200 OK"; + private Map<String, String> headers = new HashMap<String, String>(); + private byte[] body = EMPTY_BODY; + private boolean closeConnectionAfter = false; + private String closeConnectionAfterHeader = null; + private int closeConnectionAfterXBytes = -1; + private int pauseConnectionAfterXBytes = -1; + private File bodyExternalFile = null; + + public MockResponse() { + addHeader("Content-Length", 0); + } + + /** + * Returns the HTTP response line, such as "HTTP/1.1 200 OK". + */ + public String getStatus() { + return status; + } + + public MockResponse setResponseCode(int code) { + this.status = "HTTP/1.1 " + code + " OK"; + return this; + } + + /** + * Returns the HTTP headers, such as "Content-Length: 0". + */ + public List<String> getHeaders() { + List<String> headerStrings = new ArrayList<String>(); + for (String header : headers.keySet()) { + headerStrings.add(header + ": " + headers.get(header)); + } + return headerStrings; + } + + public MockResponse addHeader(String header, String value) { + headers.put(header.toLowerCase(), value); + return this; + } + + public MockResponse addHeader(String header, long value) { + return addHeader(header, Long.toString(value)); + } + + public MockResponse removeHeader(String header) { + headers.remove(header.toLowerCase()); + return this; + } + + /** + * Returns true if the body should come from an external file, false otherwise. + */ + private boolean bodyIsExternal() { + return bodyExternalFile != null; + } + + /** + * Returns an input stream containing the raw HTTP payload. + */ + public InputStream getBody() { + if (bodyIsExternal()) { + try { + return new FileInputStream(bodyExternalFile); + } catch (FileNotFoundException e) { + Log.e(LOG_TAG, "File not found: " + bodyExternalFile.getAbsolutePath()); + } + } + return new ByteArrayInputStream(this.body); + } + + public MockResponse setBody(File body) { + addHeader("Content-Length", body.length()); + this.bodyExternalFile = body; + return this; + } + + public MockResponse setBody(byte[] body) { + addHeader("Content-Length", body.length); + this.body = body; + return this; + } + + public MockResponse setBody(String body) { + try { + return setBody(body.getBytes(ASCII)); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(); + } + } + + /** + * Sets the body as chunked. + * + * Currently chunked body is not supported for external files as bodies. + */ + public MockResponse setChunkedBody(byte[] body, int maxChunkSize) throws IOException { + addHeader("Transfer-encoding", "chunked"); + + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + int pos = 0; + while (pos < body.length) { + int chunkSize = Math.min(body.length - pos, maxChunkSize); + bytesOut.write(Integer.toHexString(chunkSize).getBytes(ASCII)); + bytesOut.write("\r\n".getBytes(ASCII)); + bytesOut.write(body, pos, chunkSize); + bytesOut.write("\r\n".getBytes(ASCII)); + pos += chunkSize; + } + bytesOut.write("0\r\n".getBytes(ASCII)); + this.body = bytesOut.toByteArray(); + return this; + } + + public MockResponse setChunkedBody(String body, int maxChunkSize) throws IOException { + return setChunkedBody(body.getBytes(ASCII), maxChunkSize); + } + + @Override public String toString() { + return status; + } + + public boolean shouldCloseConnectionAfter() { + return closeConnectionAfter; + } + + public MockResponse setCloseConnectionAfter(boolean closeConnectionAfter) { + this.closeConnectionAfter = closeConnectionAfter; + return this; + } + + /** + * Sets the header after which sending the server should close the connection. + */ + public MockResponse setCloseConnectionAfterHeader(String header) { + closeConnectionAfterHeader = header; + setCloseConnectionAfter(true); + return this; + } + + /** + * Returns the header after which sending the server should close the connection. + */ + public String getCloseConnectionAfterHeader() { + return closeConnectionAfterHeader; + } + + /** + * Sets the number of bytes in the body to send before which the server should close the + * connection. Set to -1 to unset and send the entire body (default). + */ + public MockResponse setCloseConnectionAfterXBytes(int position) { + closeConnectionAfterXBytes = position; + setCloseConnectionAfter(true); + return this; + } + + /** + * Returns the number of bytes in the body to send before which the server should close the + * connection. Returns -1 if the entire body should be sent (default). + */ + public int getCloseConnectionAfterXBytes() { + return closeConnectionAfterXBytes; + } + + /** + * Sets the number of bytes in the body to send before which the server should pause the + * connection (stalls in sending data). Only one pause per response is supported. + * Set to -1 to unset pausing (default). + */ + public MockResponse setPauseConnectionAfterXBytes(int position) { + pauseConnectionAfterXBytes = position; + return this; + } + + /** + * Returns the number of bytes in the body to send before which the server should pause the + * connection (stalls in sending data). (Returns -1 if it should not pause). + */ + public int getPauseConnectionAfterXBytes() { + return pauseConnectionAfterXBytes; + } + + /** + * Returns true if this response is flagged to pause the connection mid-stream, false otherwise + */ + public boolean getShouldPause() { + return (pauseConnectionAfterXBytes != -1); + } + + /** + * Returns true if this response is flagged to close the connection mid-stream, false otherwise + */ + public boolean getShouldClose() { + return (closeConnectionAfterXBytes != -1); + } +} diff --git a/core/tests/utillib/src/coretestutils/http/MockWebServer.java b/core/tests/utillib/src/coretestutils/http/MockWebServer.java new file mode 100644 index 000000000000..c329ffa518c1 --- /dev/null +++ b/core/tests/utillib/src/coretestutils/http/MockWebServer.java @@ -0,0 +1,426 @@ +/* + * Copyright (C) 2010 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 coretestutils.http; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URL; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import android.util.Log; + +/** + * A scriptable web server. Callers supply canned responses and the server + * replays them upon request in sequence. + * + * TODO: merge with the version from libcore/support/src/tests/java once it's in. + */ +public final class MockWebServer { + static final String ASCII = "US-ASCII"; + static final String LOG_TAG = "coretestutils.http.MockWebServer"; + + private final BlockingQueue<RecordedRequest> requestQueue + = new LinkedBlockingQueue<RecordedRequest>(); + private final BlockingQueue<MockResponse> responseQueue + = new LinkedBlockingQueue<MockResponse>(); + private int bodyLimit = Integer.MAX_VALUE; + private final ExecutorService executor = Executors.newCachedThreadPool(); + // keep Futures around so we can rethrow any exceptions thrown by Callables + private final Queue<Future<?>> futures = new LinkedList<Future<?>>(); + private final Object downloadPauseLock = new Object(); + // global flag to signal when downloads should resume on the server + private volatile boolean downloadResume = false; + + private int port = -1; + + public int getPort() { + if (port == -1) { + throw new IllegalStateException("Cannot retrieve port before calling play()"); + } + return port; + } + + /** + * Returns a URL for connecting to this server. + * + * @param path the request path, such as "/". + */ + public URL getUrl(String path) throws MalformedURLException { + return new URL("http://localhost:" + getPort() + path); + } + + /** + * Sets the number of bytes of the POST body to keep in memory to the given + * limit. + */ + public void setBodyLimit(int maxBodyLength) { + this.bodyLimit = maxBodyLength; + } + + public void enqueue(MockResponse response) { + responseQueue.add(response); + } + + /** + * Awaits the next HTTP request, removes it, and returns it. Callers should + * use this to verify the request sent was as intended. + */ + public RecordedRequest takeRequest() throws InterruptedException { + return requestQueue.take(); + } + + public RecordedRequest takeRequestWithTimeout(long timeoutMillis) throws InterruptedException { + return requestQueue.poll(timeoutMillis, TimeUnit.MILLISECONDS); + } + + public List<RecordedRequest> drainRequests() { + List<RecordedRequest> requests = new ArrayList<RecordedRequest>(); + requestQueue.drainTo(requests); + return requests; + } + + /** + * Starts the server, serves all enqueued requests, and shuts the server + * down using the default (server-assigned) port. + */ + public void play() throws IOException { + play(0); + } + + /** + * Starts the server, serves all enqueued requests, and shuts the server + * down. + * + * @param port The port number to use to listen to connections on; pass in 0 to have the + * server automatically assign a free port + */ + public void play(int portNumber) throws IOException { + final ServerSocket ss = new ServerSocket(portNumber); + ss.setReuseAddress(true); + port = ss.getLocalPort(); + submitCallable(new Callable<Void>() { + public Void call() throws Exception { + int count = 0; + while (true) { + if (count > 0 && responseQueue.isEmpty()) { + ss.close(); + executor.shutdown(); + return null; + } + + serveConnection(ss.accept()); + count++; + } + } + }); + } + + private void serveConnection(final Socket s) { + submitCallable(new Callable<Void>() { + public Void call() throws Exception { + InputStream in = new BufferedInputStream(s.getInputStream()); + OutputStream out = new BufferedOutputStream(s.getOutputStream()); + + int sequenceNumber = 0; + while (true) { + RecordedRequest request = readRequest(in, sequenceNumber); + if (request == null) { + if (sequenceNumber == 0) { + throw new IllegalStateException("Connection without any request!"); + } else { + break; + } + } + requestQueue.add(request); + MockResponse response = computeResponse(request); + writeResponse(out, response); + if (response.shouldCloseConnectionAfter()) { + break; + } + sequenceNumber++; + } + + in.close(); + out.close(); + return null; + } + }); + } + + private void submitCallable(Callable<?> callable) { + Future<?> future = executor.submit(callable); + futures.add(future); + } + + /** + * Check for and raise any exceptions that have been thrown by child threads. Will not block on + * children still running. + * @throws ExecutionException for the first child thread that threw an exception + */ + public void checkForExceptions() throws ExecutionException, InterruptedException { + final int originalSize = futures.size(); + for (int i = 0; i < originalSize; i++) { + Future<?> future = futures.remove(); + try { + future.get(0, TimeUnit.SECONDS); + } catch (TimeoutException e) { + futures.add(future); // still running + } + } + } + + /** + * @param sequenceNumber the index of this request on this connection. + */ + private RecordedRequest readRequest(InputStream in, int sequenceNumber) throws IOException { + String request = readAsciiUntilCrlf(in); + if (request.equals("")) { + return null; // end of data; no more requests + } + + List<String> headers = new ArrayList<String>(); + int contentLength = -1; + boolean chunked = false; + String header; + while (!(header = readAsciiUntilCrlf(in)).equals("")) { + headers.add(header); + String lowercaseHeader = header.toLowerCase(); + if (contentLength == -1 && lowercaseHeader.startsWith("content-length:")) { + contentLength = Integer.parseInt(header.substring(15).trim()); + } + if (lowercaseHeader.startsWith("transfer-encoding:") && + lowercaseHeader.substring(18).trim().equals("chunked")) { + chunked = true; + } + } + + boolean hasBody = false; + TruncatingOutputStream requestBody = new TruncatingOutputStream(); + List<Integer> chunkSizes = new ArrayList<Integer>(); + if (contentLength != -1) { + hasBody = true; + transfer(contentLength, in, requestBody); + } else if (chunked) { + hasBody = true; + while (true) { + int chunkSize = Integer.parseInt(readAsciiUntilCrlf(in).trim(), 16); + if (chunkSize == 0) { + readEmptyLine(in); + break; + } + chunkSizes.add(chunkSize); + transfer(chunkSize, in, requestBody); + readEmptyLine(in); + } + } + + if (request.startsWith("GET ")) { + if (hasBody) { + throw new IllegalArgumentException("GET requests should not have a body!"); + } + } else if (request.startsWith("POST ")) { + if (!hasBody) { + throw new IllegalArgumentException("POST requests must have a body!"); + } + } else { + throw new UnsupportedOperationException("Unexpected method: " + request); + } + + return new RecordedRequest(request, headers, chunkSizes, + requestBody.numBytesReceived, requestBody.toByteArray(), sequenceNumber); + } + + /** + * Returns a response to satisfy {@code request}. + */ + private MockResponse computeResponse(RecordedRequest request) throws InterruptedException { + if (responseQueue.isEmpty()) { + throw new IllegalStateException("Unexpected request: " + request); + } + return responseQueue.take(); + } + + private void writeResponse(OutputStream out, MockResponse response) throws IOException { + out.write((response.getStatus() + "\r\n").getBytes(ASCII)); + boolean doCloseConnectionAfterHeader = (response.getCloseConnectionAfterHeader() != null); + + // Send headers + String closeConnectionAfterHeader = response.getCloseConnectionAfterHeader(); + for (String header : response.getHeaders()) { + out.write((header + "\r\n").getBytes(ASCII)); + + if (doCloseConnectionAfterHeader && header.startsWith(closeConnectionAfterHeader)) { + Log.i(LOG_TAG, "Closing connection after header" + header); + break; + } + } + + // Send actual body data + if (!doCloseConnectionAfterHeader) { + out.write(("\r\n").getBytes(ASCII)); + + InputStream body = response.getBody(); + final int READ_BLOCK_SIZE = 10000; // process blocks this size + byte[] currentBlock = new byte[READ_BLOCK_SIZE]; + int currentBlockSize = 0; + int writtenSoFar = 0; + + boolean shouldPause = response.getShouldPause(); + boolean shouldClose = response.getShouldClose(); + int pause = response.getPauseConnectionAfterXBytes(); + int close = response.getCloseConnectionAfterXBytes(); + + // Don't bother pausing if it's set to pause -after- the connection should be dropped + if (shouldPause && shouldClose && (pause > close)) { + shouldPause = false; + } + + // Process each block we read in... + while ((currentBlockSize = body.read(currentBlock)) != -1) { + int startIndex = 0; + int writeLength = currentBlockSize; + + // handle the case of pausing + if (shouldPause && (writtenSoFar + currentBlockSize >= pause)) { + writeLength = pause - writtenSoFar; + out.write(currentBlock, 0, writeLength); + out.flush(); + writtenSoFar += writeLength; + + // now pause... + try { + Log.i(LOG_TAG, "Pausing connection after " + pause + " bytes"); + // Wait until someone tells us to resume sending... + synchronized(downloadPauseLock) { + while (!downloadResume) { + downloadPauseLock.wait(); + } + // reset resume back to false + downloadResume = false; + } + } catch (InterruptedException e) { + Log.e(LOG_TAG, "Server was interrupted during pause in download."); + } + + startIndex = writeLength; + writeLength = currentBlockSize - writeLength; + } + + // handle the case of closing the connection + if (shouldClose && (writtenSoFar + writeLength > close)) { + writeLength = close - writtenSoFar; + out.write(currentBlock, startIndex, writeLength); + writtenSoFar += writeLength; + Log.i(LOG_TAG, "Closing connection after " + close + " bytes"); + break; + } + out.write(currentBlock, startIndex, writeLength); + writtenSoFar += writeLength; + } + } + out.flush(); + } + + /** + * Transfer bytes from {@code in} to {@code out} until either {@code length} + * bytes have been transferred or {@code in} is exhausted. + */ + private void transfer(int length, InputStream in, OutputStream out) throws IOException { + byte[] buffer = new byte[1024]; + while (length > 0) { + int count = in.read(buffer, 0, Math.min(buffer.length, length)); + if (count == -1) { + return; + } + out.write(buffer, 0, count); + length -= count; + } + } + + /** + * Returns the text from {@code in} until the next "\r\n", or null if + * {@code in} is exhausted. + */ + private String readAsciiUntilCrlf(InputStream in) throws IOException { + StringBuilder builder = new StringBuilder(); + while (true) { + int c = in.read(); + if (c == '\n' && builder.length() > 0 && builder.charAt(builder.length() - 1) == '\r') { + builder.deleteCharAt(builder.length() - 1); + return builder.toString(); + } else if (c == -1) { + return builder.toString(); + } else { + builder.append((char) c); + } + } + } + + private void readEmptyLine(InputStream in) throws IOException { + String line = readAsciiUntilCrlf(in); + if (!line.equals("")) { + throw new IllegalStateException("Expected empty but was: " + line); + } + } + + /** + * An output stream that drops data after bodyLimit bytes. + */ + private class TruncatingOutputStream extends ByteArrayOutputStream { + private int numBytesReceived = 0; + @Override public void write(byte[] buffer, int offset, int len) { + numBytesReceived += len; + super.write(buffer, offset, Math.min(len, bodyLimit - count)); + } + @Override public void write(int oneByte) { + numBytesReceived++; + if (count < bodyLimit) { + super.write(oneByte); + } + } + } + + /** + * Trigger the server to resume sending the download + */ + public void doResumeDownload() { + synchronized (downloadPauseLock) { + downloadResume = true; + downloadPauseLock.notifyAll(); + } + } +} diff --git a/core/tests/utillib/src/coretestutils/http/RecordedRequest.java b/core/tests/utillib/src/coretestutils/http/RecordedRequest.java new file mode 100644 index 000000000000..293ff80dc6e9 --- /dev/null +++ b/core/tests/utillib/src/coretestutils/http/RecordedRequest.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2010 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 coretestutils.http; + +import java.util.List; + +/** + * An HTTP request that came into the mock web server. + */ +public final class RecordedRequest { + private final String requestLine; + private final List<String> headers; + private final List<Integer> chunkSizes; + private final int bodySize; + private final byte[] body; + private final int sequenceNumber; + + RecordedRequest(String requestLine, List<String> headers, List<Integer> chunkSizes, + int bodySize, byte[] body, int sequenceNumber) { + this.requestLine = requestLine; + this.headers = headers; + this.chunkSizes = chunkSizes; + this.bodySize = bodySize; + this.body = body; + this.sequenceNumber = sequenceNumber; + } + + public String getRequestLine() { + return requestLine; + } + + public List<String> getHeaders() { + return headers; + } + + /** + * Returns the sizes of the chunks of this request's body, or an empty list + * if the request's body was empty or unchunked. + */ + public List<Integer> getChunkSizes() { + return chunkSizes; + } + + /** + * Returns the total size of the body of this POST request (before + * truncation). + */ + public int getBodySize() { + return bodySize; + } + + /** + * Returns the body of this POST request. This may be truncated. + */ + public byte[] getBody() { + return body; + } + + /** + * Returns the index of this request on its HTTP connection. Since a single + * HTTP connection may serve multiple requests, each request is assigned its + * own sequence number. + */ + public int getSequenceNumber() { + return sequenceNumber; + } + + @Override public String toString() { + return requestLine; + } + + public String getMethod() { + return getRequestLine().split(" ")[0]; + } + + public String getPath() { + return getRequestLine().split(" ")[1]; + } +} |
