summaryrefslogtreecommitdiffstats
path: root/core/tests/coretests/src
diff options
context:
space:
mode:
Diffstat (limited to 'core/tests/coretests/src')
-rw-r--r--core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java178
-rw-r--r--core/tests/coretests/src/android/app/SearchManagerTest.java195
-rw-r--r--core/tests/coretests/src/android/app/SearchablesTest.java477
-rw-r--r--core/tests/coretests/src/android/app/SuggestionProvider.java110
-rw-r--r--core/tests/coretests/src/android/app/activity/AbortReceiver.java49
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityManagerTest.java135
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityTests.java40
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityTestsBase.java212
-rw-r--r--core/tests/coretests/src/android/app/activity/BroadcastTest.java536
-rw-r--r--core/tests/coretests/src/android/app/activity/ClearTop.java51
-rw-r--r--core/tests/coretests/src/android/app/activity/IntentSenderTest.java86
-rw-r--r--core/tests/coretests/src/android/app/activity/LaunchTest.java75
-rw-r--r--core/tests/coretests/src/android/app/activity/LaunchpadActivity.java588
-rw-r--r--core/tests/coretests/src/android/app/activity/LaunchpadTabActivity.java43
-rw-r--r--core/tests/coretests/src/android/app/activity/LifecycleTest.java109
-rw-r--r--core/tests/coretests/src/android/app/activity/LocalActivity.java33
-rw-r--r--core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java42
-rw-r--r--core/tests/coretests/src/android/app/activity/LocalDeniedService.java22
-rw-r--r--core/tests/coretests/src/android/app/activity/LocalDialog.java33
-rw-r--r--core/tests/coretests/src/android/app/activity/LocalGrantedReceiver.java42
-rw-r--r--core/tests/coretests/src/android/app/activity/LocalGrantedService.java22
-rw-r--r--core/tests/coretests/src/android/app/activity/LocalProvider.java163
-rw-r--r--core/tests/coretests/src/android/app/activity/LocalReceiver.java77
-rw-r--r--core/tests/coretests/src/android/app/activity/LocalScreen.java33
-rw-r--r--core/tests/coretests/src/android/app/activity/LocalService.java122
-rw-r--r--core/tests/coretests/src/android/app/activity/MetaDataTest.java166
-rw-r--r--core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java42
-rw-r--r--core/tests/coretests/src/android/app/activity/RemoteGrantedReceiver.java42
-rw-r--r--core/tests/coretests/src/android/app/activity/RemoteReceiver.java50
-rw-r--r--core/tests/coretests/src/android/app/activity/RemoteSubActivityScreen.java59
-rw-r--r--core/tests/coretests/src/android/app/activity/ResultReceiver.java43
-rw-r--r--core/tests/coretests/src/android/app/activity/SearchableActivity.java30
-rw-r--r--core/tests/coretests/src/android/app/activity/ServiceTest.java469
-rw-r--r--core/tests/coretests/src/android/app/activity/SetTimeZonePermissionsTest.java70
-rw-r--r--core/tests/coretests/src/android/app/activity/SubActivityScreen.java168
-rw-r--r--core/tests/coretests/src/android/app/activity/SubActivityTest.java92
-rw-r--r--core/tests/coretests/src/android/app/activity/TestedActivity.java77
-rw-r--r--core/tests/coretests/src/android/app/activity/TestedScreen.java128
-rw-r--r--core/tests/coretests/src/android/content/AssetTest.java74
-rw-r--r--core/tests/coretests/src/android/content/ContentQueryMapTest.java104
-rw-r--r--core/tests/coretests/src/android/content/ContentTests.java28
-rw-r--r--core/tests/coretests/src/android/content/MemoryFileProvider.java211
-rw-r--r--core/tests/coretests/src/android/content/MemoryFileProviderTest.java82
-rw-r--r--core/tests/coretests/src/android/database/CursorWindowTest.java173
-rw-r--r--core/tests/coretests/src/android/database/DatabaseCursorTest.java630
-rw-r--r--core/tests/coretests/src/android/database/DatabaseGeneralTest.java1008
-rw-r--r--core/tests/coretests/src/android/database/DatabaseLocaleTest.java129
-rw-r--r--core/tests/coretests/src/android/database/DatabaseLockTest.java175
-rw-r--r--core/tests/coretests/src/android/database/DatabasePerformanceTests.java1353
-rw-r--r--core/tests/coretests/src/android/database/DatabaseStatementTest.java324
-rw-r--r--core/tests/coretests/src/android/database/DatabaseStressTest.java99
-rw-r--r--core/tests/coretests/src/android/net/LocalSocketTest.java171
-rw-r--r--core/tests/coretests/src/android/net/SSLTest.java52
-rw-r--r--core/tests/coretests/src/android/net/UriMatcherTest.java95
-rw-r--r--core/tests/coretests/src/android/net/UriTest.java578
-rw-r--r--core/tests/coretests/src/android/os/AidlTest.aidl20
-rw-r--r--core/tests/coretests/src/android/os/AidlTest.java422
-rw-r--r--core/tests/coretests/src/android/os/BroadcasterTest.java232
-rw-r--r--core/tests/coretests/src/android/os/BuildTest.java76
-rw-r--r--core/tests/coretests/src/android/os/FileObserverTest.java152
-rw-r--r--core/tests/coretests/src/android/os/FileUtilsTest.java171
-rw-r--r--core/tests/coretests/src/android/os/HandlerTester.java89
-rw-r--r--core/tests/coretests/src/android/os/HandlerThreadTest.java102
-rw-r--r--core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java1392
-rw-r--r--core/tests/coretests/src/android/os/IAidlTest.aidl47
-rw-r--r--core/tests/coretests/src/android/os/IdleHandlerTest.java199
-rw-r--r--core/tests/coretests/src/android/os/MemoryFileTest.java427
-rw-r--r--core/tests/coretests/src/android/os/MessageQueueTest.java103
-rw-r--r--core/tests/coretests/src/android/os/MessengerService.java50
-rw-r--r--core/tests/coretests/src/android/os/MessengerTest.java119
-rw-r--r--core/tests/coretests/src/android/os/OsTests.java39
-rw-r--r--core/tests/coretests/src/android/os/PerformanceCollectorTest.java528
-rw-r--r--core/tests/coretests/src/android/os/PowerManagerTest.java142
-rw-r--r--core/tests/coretests/src/android/os/SystemPropertiesTest.java51
-rw-r--r--core/tests/coretests/src/android/os/TestHandlerThread.java104
-rw-r--r--core/tests/coretests/src/android/os/TraceTest.java215
-rw-r--r--core/tests/coretests/src/android/pim/vcard/ContentValuesBuilder.java81
-rw-r--r--core/tests/coretests/src/android/pim/vcard/ContentValuesVerifier.java101
-rw-r--r--core/tests/coretests/src/android/pim/vcard/ContentValuesVerifierElem.java95
-rw-r--r--core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java213
-rw-r--r--core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java299
-rw-r--r--core/tests/coretests/src/android/pim/vcard/LineVerifier.java65
-rw-r--r--core/tests/coretests/src/android/pim/vcard/LineVerifierElem.java105
-rw-r--r--core/tests/coretests/src/android/pim/vcard/PropertyNode.java198
-rw-r--r--core/tests/coretests/src/android/pim/vcard/PropertyNodesVerifier.java386
-rw-r--r--core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java959
-rw-r--r--core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java1011
-rw-r--r--core/tests/coretests/src/android/pim/vcard/VCardJapanizationTests.java434
-rw-r--r--core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java169
-rw-r--r--core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java85
-rw-r--r--core/tests/coretests/src/android/pim/vcard/VCardVerifier.java306
-rw-r--r--core/tests/coretests/src/android/pim/vcard/VNode.java30
-rw-r--r--core/tests/coretests/src/android/pim/vcard/VNodeBuilder.java314
-rw-r--r--core/tests/coretests/src/android/provider/SettingsProviderTest.java147
-rw-r--r--core/tests/coretests/src/android/provider/SmsProviderTest.java76
-rw-r--r--core/tests/coretests/src/android/text/SpannedTest.java187
-rw-r--r--core/tests/coretests/src/android/text/TextLayoutTest.java51
-rw-r--r--core/tests/coretests/src/android/text/TextUtilsTest.java359
-rw-r--r--core/tests/coretests/src/android/text/format/TimeTest.java608
-rw-r--r--core/tests/coretests/src/android/util/LogTest.java152
-rw-r--r--core/tests/coretests/src/android/util/TimeUtilsTest.java432
-rw-r--r--core/tests/coretests/src/android/view/CreateViewTest.java123
-rw-r--r--core/tests/coretests/src/android/view/InflateTest.java155
-rw-r--r--core/tests/coretests/src/android/view/MenuTest.java279
-rw-r--r--core/tests/coretests/src/android/webkit/WebkitTest.java59
-rw-r--r--core/tests/coretests/src/android/widget/LabelView.java191
-rw-r--r--core/tests/coretests/src/android/widget/TextViewPerformanceTest.java132
-rw-r--r--core/tests/coretests/src/android/widget/TextViewTest.java61
-rw-r--r--core/tests/coretests/src/com/android/internal/util/BitwiseStreamsTest.java136
-rw-r--r--core/tests/coretests/src/com/android/internal/util/CharSequencesTest.java58
-rw-r--r--core/tests/coretests/src/com/google/android/net/ParentalControlTest.java57
111 files changed, 22989 insertions, 0 deletions
diff --git a/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java b/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java
new file mode 100644
index 0000000..394b9f2
--- /dev/null
+++ b/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java
@@ -0,0 +1,178 @@
+/*
+ * 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.accounts;
+
+import android.test.AndroidTestCase;
+import android.test.RenamingDelegatingContext;
+import android.test.IsolatedContext;
+import android.test.mock.MockContext;
+import android.test.mock.MockContentResolver;
+import android.content.*;
+import android.accounts.Account;
+import android.accounts.AccountManagerService;
+import android.os.Bundle;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+public class AccountManagerServiceTest extends AndroidTestCase {
+ @Override
+ protected void setUp() throws Exception {
+ final String filenamePrefix = "test.";
+ MockContentResolver resolver = new MockContentResolver();
+ RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(
+ new MockContext(), // The context that most methods are delegated to
+ getContext(), // The context that file methods are delegated to
+ filenamePrefix);
+ Context context = new IsolatedContext(resolver, targetContextWrapper);
+ setContext(context);
+ }
+
+ public class AccountSorter implements Comparator<Account> {
+ public int compare(Account object1, Account object2) {
+ if (object1 == object2) return 0;
+ if (object1 == null) return 1;
+ if (object2 == null) return -1;
+ int result = object1.type.compareTo(object2.type);
+ if (result != 0) return result;
+ return object1.name.compareTo(object2.name);
+ }
+ }
+
+ public void testCheckAddAccount() throws Exception {
+ AccountManagerService ams = new AccountManagerService(getContext());
+ Account a11 = new Account("account1", "type1");
+ Account a21 = new Account("account2", "type1");
+ Account a31 = new Account("account3", "type1");
+ Account a12 = new Account("account1", "type2");
+ Account a22 = new Account("account2", "type2");
+ Account a32 = new Account("account3", "type2");
+ ams.addAccount(a11, "p11", null);
+ ams.addAccount(a12, "p12", null);
+ ams.addAccount(a21, "p21", null);
+ ams.addAccount(a22, "p22", null);
+ ams.addAccount(a31, "p31", null);
+ ams.addAccount(a32, "p32", null);
+
+ Account[] accounts = ams.getAccounts(null);
+ Arrays.sort(accounts, new AccountSorter());
+ assertEquals(6, accounts.length);
+ assertEquals(a11, accounts[0]);
+ assertEquals(a21, accounts[1]);
+ assertEquals(a31, accounts[2]);
+ assertEquals(a12, accounts[3]);
+ assertEquals(a22, accounts[4]);
+ assertEquals(a32, accounts[5]);
+
+ accounts = ams.getAccountsByType("type1" );
+ Arrays.sort(accounts, new AccountSorter());
+ assertEquals(3, accounts.length);
+ assertEquals(a11, accounts[0]);
+ assertEquals(a21, accounts[1]);
+ assertEquals(a31, accounts[2]);
+
+ ams.removeAccount(null, a21);
+
+ accounts = ams.getAccountsByType("type1" );
+ Arrays.sort(accounts, new AccountSorter());
+ assertEquals(2, accounts.length);
+ assertEquals(a11, accounts[0]);
+ assertEquals(a31, accounts[1]);
+ }
+
+ public void testPasswords() throws Exception {
+ AccountManagerService ams = new AccountManagerService(getContext());
+ Account a11 = new Account("account1", "type1");
+ Account a12 = new Account("account1", "type2");
+ ams.addAccount(a11, "p11", null);
+ ams.addAccount(a12, "p12", null);
+
+ assertEquals("p11", ams.getPassword(a11));
+ assertEquals("p12", ams.getPassword(a12));
+
+ ams.setPassword(a11, "p11b");
+
+ assertEquals("p11b", ams.getPassword(a11));
+ assertEquals("p12", ams.getPassword(a12));
+ }
+
+ public void testUserdata() throws Exception {
+ AccountManagerService ams = new AccountManagerService(getContext());
+ Account a11 = new Account("account1", "type1");
+ Bundle u11 = new Bundle();
+ u11.putString("a", "a_a11");
+ u11.putString("b", "b_a11");
+ u11.putString("c", "c_a11");
+ Account a12 = new Account("account1", "type2");
+ Bundle u12 = new Bundle();
+ u12.putString("a", "a_a12");
+ u12.putString("b", "b_a12");
+ u12.putString("c", "c_a12");
+ ams.addAccount(a11, "p11", u11);
+ ams.addAccount(a12, "p12", u12);
+
+ assertEquals("a_a11", ams.getUserData(a11, "a"));
+ assertEquals("b_a11", ams.getUserData(a11, "b"));
+ assertEquals("c_a11", ams.getUserData(a11, "c"));
+ assertEquals("a_a12", ams.getUserData(a12, "a"));
+ assertEquals("b_a12", ams.getUserData(a12, "b"));
+ assertEquals("c_a12", ams.getUserData(a12, "c"));
+
+ ams.setUserData(a11, "b", "b_a11b");
+
+ assertEquals("a_a11", ams.getUserData(a11, "a"));
+ assertEquals("b_a11b", ams.getUserData(a11, "b"));
+ assertEquals("c_a11", ams.getUserData(a11, "c"));
+ assertEquals("a_a12", ams.getUserData(a12, "a"));
+ assertEquals("b_a12", ams.getUserData(a12, "b"));
+ assertEquals("c_a12", ams.getUserData(a12, "c"));
+ }
+
+ public void testAuthtokens() throws Exception {
+ AccountManagerService ams = new AccountManagerService(getContext());
+ Account a11 = new Account("account1", "type1");
+ Account a12 = new Account("account1", "type2");
+ ams.addAccount(a11, "p11", null);
+ ams.addAccount(a12, "p12", null);
+
+ ams.setAuthToken(a11, "att1", "a11_att1");
+ ams.setAuthToken(a11, "att2", "a11_att2");
+ ams.setAuthToken(a11, "att3", "a11_att3");
+ ams.setAuthToken(a12, "att1", "a12_att1");
+ ams.setAuthToken(a12, "att2", "a12_att2");
+ ams.setAuthToken(a12, "att3", "a12_att3");
+
+ assertEquals("a11_att1", ams.peekAuthToken(a11, "att1"));
+ assertEquals("a11_att2", ams.peekAuthToken(a11, "att2"));
+ assertEquals("a11_att3", ams.peekAuthToken(a11, "att3"));
+ assertEquals("a12_att1", ams.peekAuthToken(a12, "att1"));
+ assertEquals("a12_att2", ams.peekAuthToken(a12, "att2"));
+ assertEquals("a12_att3", ams.peekAuthToken(a12, "att3"));
+
+ ams.setAuthToken(a11, "att3", "a11_att3b");
+ ams.invalidateAuthToken(a12.type, "a12_att2");
+
+ assertEquals("a11_att1", ams.peekAuthToken(a11, "att1"));
+ assertEquals("a11_att2", ams.peekAuthToken(a11, "att2"));
+ assertEquals("a11_att3b", ams.peekAuthToken(a11, "att3"));
+ assertEquals("a12_att1", ams.peekAuthToken(a12, "att1"));
+ assertNull(ams.peekAuthToken(a12, "att2"));
+ assertEquals("a12_att3", ams.peekAuthToken(a12, "att3"));
+
+ assertNull(ams.readAuthTokenFromDatabase(a12, "att2"));
+ }
+}
diff --git a/core/tests/coretests/src/android/app/SearchManagerTest.java b/core/tests/coretests/src/android/app/SearchManagerTest.java
new file mode 100644
index 0000000..21ed4c5
--- /dev/null
+++ b/core/tests/coretests/src/android/app/SearchManagerTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.app.activity.LocalActivity;
+
+import android.app.Activity;
+import android.app.ISearchManager;
+import android.app.SearchManager;
+import android.app.SearchableInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.ServiceManager;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+
+/**
+ * To launch this test from the command line:
+ *
+ * adb shell am instrument -w \
+ * -e class com.android.unit_tests.SearchManagerTest \
+ * com.android.unit_tests/android.test.InstrumentationTestRunner
+ */
+public class SearchManagerTest extends ActivityInstrumentationTestCase2<LocalActivity> {
+
+ private ComponentName SEARCHABLE_ACTIVITY =
+ new ComponentName("com.android.frameworks.coretests",
+ "android.app.activity.SearchableActivity");
+
+ /*
+ * Bug list of test ideas.
+ *
+ * testSearchManagerInterfaceAvailable()
+ * Exercise the interface obtained
+ *
+ * testSearchManagerAvailable()
+ * Exercise the interface obtained
+ *
+ * testSearchManagerInvocations()
+ * FIX - make it work again
+ * stress test with a very long string
+ *
+ * SearchManager tests
+ * confirm proper identification of "default" activity based on policy, not hardcoded contacts
+ *
+ * SearchBar tests
+ * Maybe have to do with framework / unittest runner - need instrumented activity?
+ * How can we unit test the suggestions content providers?
+ * Should I write unit tests for any of them?
+ * Test scenarios:
+ * type-BACK (cancel)
+ * type-GO (send)
+ * type-navigate-click (suggestion)
+ * type-action
+ * type-navigate-action (suggestion)
+ */
+
+ /**
+ * Local copy of activity context
+ */
+ Context mContext;
+
+ public SearchManagerTest() {
+ super("com.android.frameworks.coretests", LocalActivity.class);
+ }
+
+ /**
+ * Setup any common data for the upcoming tests.
+ */
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ Activity testActivity = getActivity();
+ mContext = testActivity;
+ }
+
+ private ISearchManager getSearchManagerService() {
+ return ISearchManager.Stub.asInterface(
+ ServiceManager.getService(Context.SEARCH_SERVICE));
+ }
+
+ /**
+ * The goal of this test is to confirm that we can obtain
+ * a search manager interface.
+ */
+ @MediumTest
+ public void testSearchManagerInterfaceAvailable() {
+ assertNotNull(getSearchManagerService());
+ }
+
+ /**
+ * The goal of this test is to confirm that we can obtain
+ * a search manager at any time, and that for any given context,
+ * it is a singleton.
+ */
+ @LargeTest
+ public void testSearchManagerAvailable() {
+ SearchManager searchManager1 = (SearchManager)
+ mContext.getSystemService(Context.SEARCH_SERVICE);
+ assertNotNull(searchManager1);
+ SearchManager searchManager2 = (SearchManager)
+ mContext.getSystemService(Context.SEARCH_SERVICE);
+ assertNotNull(searchManager2);
+ assertSame(searchManager1, searchManager2 );
+ }
+
+ @MediumTest
+ public void testSearchables() {
+ SearchManager searchManager = (SearchManager)
+ mContext.getSystemService(Context.SEARCH_SERVICE);
+ SearchableInfo si;
+
+ si = searchManager.getSearchableInfo(SEARCHABLE_ACTIVITY, false);
+ assertNotNull(si);
+ assertFalse(searchManager.isDefaultSearchable(si));
+ si = searchManager.getSearchableInfo(SEARCHABLE_ACTIVITY, true);
+ assertNotNull(si);
+ assertTrue(searchManager.isDefaultSearchable(si));
+ si = searchManager.getSearchableInfo(null, true);
+ assertNotNull(si);
+ assertTrue(searchManager.isDefaultSearchable(si));
+ }
+
+ /**
+ * Tests that startSearch() can be called multiple times without stopSearch()
+ * in between.
+ */
+ @MediumTest
+ public void testStartSearchIdempotent() throws Exception {
+ SearchManager searchManager = (SearchManager)
+ mContext.getSystemService(Context.SEARCH_SERVICE);
+ assertNotNull(searchManager);
+
+ searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false);
+ searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false);
+ searchManager.stopSearch();
+ }
+
+ /**
+ * Tests that stopSearch() can be called when the search UI is not visible and can be
+ * called multiple times without startSearch() in between.
+ */
+ @MediumTest
+ public void testStopSearchIdempotent() throws Exception {
+ SearchManager searchManager = (SearchManager)
+ mContext.getSystemService(Context.SEARCH_SERVICE);
+ assertNotNull(searchManager);
+ searchManager.stopSearch();
+
+ searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false);
+ searchManager.stopSearch();
+ searchManager.stopSearch();
+ }
+
+ /**
+ * The goal of this test is to confirm that we can start and then
+ * stop a simple search.
+ */
+ @MediumTest
+ public void testSearchManagerInvocations() throws Exception {
+ SearchManager searchManager = (SearchManager)
+ mContext.getSystemService(Context.SEARCH_SERVICE);
+ assertNotNull(searchManager);
+
+ // These tests should simply run to completion w/o exceptions
+ searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false);
+ searchManager.stopSearch();
+
+ searchManager.startSearch("", false, SEARCHABLE_ACTIVITY, null, false);
+ searchManager.stopSearch();
+
+ searchManager.startSearch("test search string", false, SEARCHABLE_ACTIVITY, null, false);
+ searchManager.stopSearch();
+
+ searchManager.startSearch("test search string", true, SEARCHABLE_ACTIVITY, null, false);
+ searchManager.stopSearch();
+ }
+
+}
diff --git a/core/tests/coretests/src/android/app/SearchablesTest.java b/core/tests/coretests/src/android/app/SearchablesTest.java
new file mode 100644
index 0000000..9b4520e
--- /dev/null
+++ b/core/tests/coretests/src/android/app/SearchablesTest.java
@@ -0,0 +1,477 @@
+/*
+ * 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.app;
+
+import android.app.SearchManager;
+import android.app.SearchableInfo;
+import android.app.SearchableInfo.ActionKeyInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.os.RemoteException;
+import android.server.search.Searchables;
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+import android.test.mock.MockContext;
+import android.test.mock.MockPackageManager;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.KeyEvent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * To launch this test from the command line:
+ *
+ * adb shell am instrument -w \
+ * -e class com.android.unit_tests.SearchablesTest \
+ * com.android.unit_tests/android.test.InstrumentationTestRunner
+ */
+@SmallTest
+public class SearchablesTest extends AndroidTestCase {
+
+ /*
+ * SearchableInfo tests
+ * Mock the context so I can provide very specific input data
+ * Confirm OK with "zero" searchables
+ * Confirm "good" metadata read properly
+ * Confirm "bad" metadata skipped properly
+ * Confirm ordering of searchables
+ * Confirm "good" actionkeys
+ * confirm "bad" actionkeys are rejected
+ * confirm XML ordering enforced (will fail today - bug in SearchableInfo)
+ * findActionKey works
+ * getIcon works
+ */
+
+ /**
+ * The goal of this test is to confirm proper operation of the
+ * SearchableInfo helper class.
+ *
+ * TODO: The metadata source needs to be mocked out because adding
+ * searchability metadata via this test is causing it to leak into the
+ * real system. So for now I'm just going to test for existence of the
+ * GlobalSearch app (which is searchable).
+ */
+ public void testSearchableGlobalSearch() {
+ // test basic array & hashmap
+ Searchables searchables = new Searchables(mContext);
+ searchables.buildSearchableList();
+
+ // test linkage from another activity
+ // TODO inject this via mocking into the package manager.
+ // TODO for now, just check for searchable GlobalSearch app (this isn't really a unit test)
+ ComponentName thisActivity = new ComponentName(
+ "com.android.globalsearch",
+ "com.android.globalsearch.GlobalSearch");
+
+ SearchableInfo si = searchables.getSearchableInfo(thisActivity);
+ assertNotNull(si);
+ assertEquals(thisActivity, si.getSearchActivity());
+
+ Context appContext = si.getActivityContext(mContext);
+ assertNotNull(appContext);
+ MoreAsserts.assertNotEqual(appContext, mContext);
+ assertEquals("Quick Search Box", appContext.getString(si.getHintId()));
+ assertEquals("Quick Search Box", appContext.getString(si.getLabelId()));
+ }
+
+ /**
+ * Test that non-searchable activities return no searchable info (this would typically
+ * trigger the use of the default searchable e.g. contacts)
+ */
+ public void testNonSearchable() {
+ // test basic array & hashmap
+ Searchables searchables = new Searchables(mContext);
+ searchables.buildSearchableList();
+
+ // confirm that we return null for non-searchy activities
+ ComponentName nonActivity = new ComponentName(
+ "com.android.frameworks.coretests",
+ "com.android.frameworks.coretests.activity.NO_SEARCH_ACTIVITY");
+ SearchableInfo si = searchables.getSearchableInfo(nonActivity);
+ assertNull(si);
+ }
+
+ /**
+ * Test that there is a default searchable (aka global search provider).
+ */
+ public void testDefaultSearchable() {
+ Searchables searchables = new Searchables(mContext);
+ searchables.buildSearchableList();
+ SearchableInfo si = searchables.getDefaultSearchable();
+ checkSearchable(si);
+ assertTrue(searchables.isDefaultSearchable(si));
+ }
+
+ /**
+ * This is an attempt to run the searchable info list with a mocked context. Here are some
+ * things I'd like to test.
+ *
+ * Confirm OK with "zero" searchables
+ * Confirm "good" metadata read properly
+ * Confirm "bad" metadata skipped properly
+ * Confirm ordering of searchables
+ * Confirm "good" actionkeys
+ * confirm "bad" actionkeys are rejected
+ * confirm XML ordering enforced (will fail today - bug in SearchableInfo)
+ * findActionKey works
+ * getIcon works
+
+ */
+ public void testSearchablesListReal() {
+ MyMockPackageManager mockPM = new MyMockPackageManager(mContext.getPackageManager());
+ MyMockContext mockContext = new MyMockContext(mContext, mockPM);
+
+ // build item list with real-world source data
+ mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_PASSTHROUGH);
+ Searchables searchables = new Searchables(mockContext);
+ searchables.buildSearchableList();
+ // tests with "real" searchables (deprecate, this should be a unit test)
+ ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
+ int count = searchablesList.size();
+ assertTrue(count >= 1); // this isn't really a unit test
+ checkSearchables(searchablesList);
+ ArrayList<SearchableInfo> global = searchables.getSearchablesInGlobalSearchList();
+ checkSearchables(global);
+ }
+
+ /**
+ * This round of tests confirms good operations with "zero" searchables found
+ */
+ public void testSearchablesListEmpty() {
+ MyMockPackageManager mockPM = new MyMockPackageManager(mContext.getPackageManager());
+ MyMockContext mockContext = new MyMockContext(mContext, mockPM);
+
+ mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_MOCK_ZERO);
+ Searchables searchables = new Searchables(mockContext);
+ searchables.buildSearchableList();
+ ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
+ assertNotNull(searchablesList);
+ MoreAsserts.assertEmpty(searchablesList);
+ ArrayList<SearchableInfo> global = searchables.getSearchablesInGlobalSearchList();
+ MoreAsserts.assertEmpty(global);
+ }
+
+ /**
+ * Generic health checker for an array of searchables.
+ *
+ * This is designed to pass for any semi-legal searchable, without knowing much about
+ * the format of the underlying data. It's fairly easy for a non-compliant application
+ * to provide meta-data that will pass here (e.g. a non-existent suggestions authority).
+ *
+ * @param searchables The list of searchables to examine.
+ */
+ private void checkSearchables(ArrayList<SearchableInfo> searchablesList) {
+ assertNotNull(searchablesList);
+ int count = searchablesList.size();
+ for (int ii = 0; ii < count; ii++) {
+ SearchableInfo si = searchablesList.get(ii);
+ checkSearchable(si);
+ }
+ }
+
+ private void checkSearchable(SearchableInfo si) {
+ assertNotNull(si);
+ assertTrue(si.getLabelId() != 0); // This must be a useable string
+ assertNotEmpty(si.getSearchActivity().getClassName());
+ assertNotEmpty(si.getSearchActivity().getPackageName());
+ if (si.getSuggestAuthority() != null) {
+ // The suggestion fields are largely optional, so we'll just confirm basic health
+ assertNotEmpty(si.getSuggestAuthority());
+ assertNullOrNotEmpty(si.getSuggestPath());
+ assertNullOrNotEmpty(si.getSuggestSelection());
+ assertNullOrNotEmpty(si.getSuggestIntentAction());
+ assertNullOrNotEmpty(si.getSuggestIntentData());
+ }
+ /* Add a way to get the entire action key list, then explicitly test its elements */
+ /* For now, test the most common action key (CALL) */
+ ActionKeyInfo ai = si.findActionKey(KeyEvent.KEYCODE_CALL);
+ if (ai != null) {
+ assertEquals(ai.getKeyCode(), KeyEvent.KEYCODE_CALL);
+ // one of these three fields must be non-null & non-empty
+ boolean m1 = (ai.getQueryActionMsg() != null) && (ai.getQueryActionMsg().length() > 0);
+ boolean m2 = (ai.getSuggestActionMsg() != null) && (ai.getSuggestActionMsg().length() > 0);
+ boolean m3 = (ai.getSuggestActionMsgColumn() != null) &&
+ (ai.getSuggestActionMsgColumn().length() > 0);
+ assertTrue(m1 || m2 || m3);
+ }
+
+ /*
+ * Find ways to test these:
+ *
+ * private int mSearchMode
+ * private Drawable mIcon
+ */
+
+ /*
+ * Explicitly not tested here:
+ *
+ * Can be null, so not much to see:
+ * public String mSearchHint
+ * private String mZeroQueryBanner
+ *
+ * To be deprecated/removed, so don't bother:
+ * public boolean mFilterMode
+ * public boolean mQuickStart
+ * private boolean mIconResized
+ * private int mIconResizeWidth
+ * private int mIconResizeHeight
+ *
+ * All of these are "internal" working variables, not part of any contract
+ * private ActivityInfo mActivityInfo
+ * private Rect mTempRect
+ * private String mSuggestProviderPackage
+ * private String mCacheActivityContext
+ */
+ }
+
+ /**
+ * Combo assert for "string not null and not empty"
+ */
+ private void assertNotEmpty(final String s) {
+ assertNotNull(s);
+ MoreAsserts.assertNotEqual(s, "");
+ }
+
+ /**
+ * Combo assert for "string null or (not null and not empty)"
+ */
+ private void assertNullOrNotEmpty(final String s) {
+ if (s != null) {
+ MoreAsserts.assertNotEqual(s, "");
+ }
+ }
+
+ /**
+ * This is a mock for context. Used to perform a true unit test on SearchableInfo.
+ *
+ */
+ private class MyMockContext extends MockContext {
+
+ protected Context mRealContext;
+ protected PackageManager mPackageManager;
+
+ /**
+ * Constructor.
+ *
+ * @param realContext Please pass in a real context for some pass-throughs to function.
+ */
+ MyMockContext(Context realContext, PackageManager packageManager) {
+ mRealContext = realContext;
+ mPackageManager = packageManager;
+ }
+
+ /**
+ * Resources. Pass through for now.
+ */
+ @Override
+ public Resources getResources() {
+ return mRealContext.getResources();
+ }
+
+ /**
+ * Package manager. Pass through for now.
+ */
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ /**
+ * Package manager. Pass through for now.
+ */
+ @Override
+ public Context createPackageContext(String packageName, int flags)
+ throws PackageManager.NameNotFoundException {
+ return mRealContext.createPackageContext(packageName, flags);
+ }
+
+ /**
+ * Message broadcast. Pass through for now.
+ */
+ @Override
+ public void sendBroadcast(Intent intent) {
+ mRealContext.sendBroadcast(intent);
+ }
+ }
+
+/**
+ * This is a mock for package manager. Used to perform a true unit test on SearchableInfo.
+ *
+ */
+ private class MyMockPackageManager extends MockPackageManager {
+
+ public final static int SEARCHABLES_PASSTHROUGH = 0;
+ public final static int SEARCHABLES_MOCK_ZERO = 1;
+ public final static int SEARCHABLES_MOCK_ONEGOOD = 2;
+ public final static int SEARCHABLES_MOCK_ONEGOOD_ONEBAD = 3;
+
+ protected PackageManager mRealPackageManager;
+ protected int mSearchablesMode;
+
+ public MyMockPackageManager(PackageManager realPM) {
+ mRealPackageManager = realPM;
+ mSearchablesMode = SEARCHABLES_PASSTHROUGH;
+ }
+
+ /**
+ * Set the mode for various tests.
+ */
+ public void setSearchablesMode(int newMode) {
+ switch (newMode) {
+ case SEARCHABLES_PASSTHROUGH:
+ case SEARCHABLES_MOCK_ZERO:
+ mSearchablesMode = newMode;
+ break;
+
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Find activities that support a given intent.
+ *
+ * Retrieve all activities that can be performed for the given intent.
+ *
+ * @param intent The desired intent as per resolveActivity().
+ * @param flags Additional option flags. The most important is
+ * MATCH_DEFAULT_ONLY, to limit the resolution to only
+ * those activities that support the CATEGORY_DEFAULT.
+ *
+ * @return A List<ResolveInfo> containing one entry for each matching
+ * Activity. These are ordered from best to worst match -- that
+ * is, the first item in the list is what is returned by
+ * resolveActivity(). If there are no matching activities, an empty
+ * list is returned.
+ */
+ @Override
+ public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
+ assertNotNull(intent);
+ assertTrue(intent.getAction().equals(Intent.ACTION_SEARCH)
+ || intent.getAction().equals(Intent.ACTION_WEB_SEARCH));
+ switch (mSearchablesMode) {
+ case SEARCHABLES_PASSTHROUGH:
+ return mRealPackageManager.queryIntentActivities(intent, flags);
+ case SEARCHABLES_MOCK_ZERO:
+ return null;
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Override
+ public ResolveInfo resolveActivity(Intent intent, int flags) {
+ assertNotNull(intent);
+ assertTrue(intent.getAction().equals(Intent.ACTION_WEB_SEARCH)
+ || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH));
+ switch (mSearchablesMode) {
+ case SEARCHABLES_PASSTHROUGH:
+ return mRealPackageManager.resolveActivity(intent, flags);
+ case SEARCHABLES_MOCK_ZERO:
+ return null;
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Retrieve an XML file from a package. This is a low-level API used to
+ * retrieve XML meta data.
+ *
+ * @param packageName The name of the package that this xml is coming from.
+ * Can not be null.
+ * @param resid The resource identifier of the desired xml. Can not be 0.
+ * @param appInfo Overall information about <var>packageName</var>. This
+ * may be null, in which case the application information will be retrieved
+ * for you if needed; if you already have this information around, it can
+ * be much more efficient to supply it here.
+ *
+ * @return Returns an XmlPullParser allowing you to parse out the XML
+ * data. Returns null if the xml resource could not be found for any
+ * reason.
+ */
+ @Override
+ public XmlResourceParser getXml(String packageName, int resid, ApplicationInfo appInfo) {
+ assertNotNull(packageName);
+ MoreAsserts.assertNotEqual(packageName, "");
+ MoreAsserts.assertNotEqual(resid, 0);
+ switch (mSearchablesMode) {
+ case SEARCHABLES_PASSTHROUGH:
+ return mRealPackageManager.getXml(packageName, resid, appInfo);
+ case SEARCHABLES_MOCK_ZERO:
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Find a single content provider by its base path name.
+ *
+ * @param name The name of the provider to find.
+ * @param flags Additional option flags. Currently should always be 0.
+ *
+ * @return ContentProviderInfo Information about the provider, if found,
+ * else null.
+ */
+ @Override
+ public ProviderInfo resolveContentProvider(String name, int flags) {
+ assertNotNull(name);
+ MoreAsserts.assertNotEqual(name, "");
+ assertEquals(flags, 0);
+ switch (mSearchablesMode) {
+ case SEARCHABLES_PASSTHROUGH:
+ return mRealPackageManager.resolveContentProvider(name, flags);
+ case SEARCHABLES_MOCK_ZERO:
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Get the activity information for a particular activity.
+ *
+ * @param name The name of the activity to find.
+ * @param flags Additional option flags.
+ *
+ * @return ActivityInfo Information about the activity, if found, else null.
+ */
+ @Override
+ public ActivityInfo getActivityInfo(ComponentName name, int flags)
+ throws NameNotFoundException {
+ assertNotNull(name);
+ MoreAsserts.assertNotEqual(name, "");
+ switch (mSearchablesMode) {
+ case SEARCHABLES_PASSTHROUGH:
+ return mRealPackageManager.getActivityInfo(name, flags);
+ case SEARCHABLES_MOCK_ZERO:
+ throw new NameNotFoundException();
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+ }
+}
+
diff --git a/core/tests/coretests/src/android/app/SuggestionProvider.java b/core/tests/coretests/src/android/app/SuggestionProvider.java
new file mode 100644
index 0000000..9fb7dcf
--- /dev/null
+++ b/core/tests/coretests/src/android/app/SuggestionProvider.java
@@ -0,0 +1,110 @@
+/*
+ * 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.app;
+
+import android.app.SearchManager;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+
+/** Simple test provider that runs in the local process.
+ *
+ * Used by {@link SearchManagerTest}.
+ */
+public class SuggestionProvider extends ContentProvider {
+ private static final String TAG = "SuggestionProvider";
+
+ private static final int SEARCH_SUGGESTIONS = 1;
+
+ private static final UriMatcher sURLMatcher = new UriMatcher(
+ UriMatcher.NO_MATCH);
+
+ static {
+ sURLMatcher.addURI("*", SearchManager.SUGGEST_URI_PATH_QUERY,
+ SEARCH_SUGGESTIONS);
+ sURLMatcher.addURI("*", SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
+ SEARCH_SUGGESTIONS);
+ }
+
+ private static final String[] COLUMNS = new String[] {
+ "_id",
+ SearchManager.SUGGEST_COLUMN_TEXT_1,
+ SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
+ SearchManager.SUGGEST_COLUMN_QUERY
+ };
+
+ public SuggestionProvider() {
+ }
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri url, String[] projectionIn, String selection,
+ String[] selectionArgs, String sort) {
+ int match = sURLMatcher.match(url);
+ switch (match) {
+ case SEARCH_SUGGESTIONS:
+ String query = url.getLastPathSegment();
+ MatrixCursor cursor = new MatrixCursor(COLUMNS);
+ String[] suffixes = { "", "a", " foo", "XXXXXXXXXXXXXXXXX" };
+ for (String suffix : suffixes) {
+ addRow(cursor, query + suffix);
+ }
+ return cursor;
+ default:
+ throw new IllegalArgumentException("Unknown URL: " + url);
+ }
+ }
+
+ private void addRow(MatrixCursor cursor, String string) {
+ long id = cursor.getCount();
+ cursor.newRow().add(id).add(string).add(Intent.ACTION_SEARCH).add(string);
+ }
+
+ @Override
+ public String getType(Uri url) {
+ int match = sURLMatcher.match(url);
+ switch (match) {
+ case SEARCH_SUGGESTIONS:
+ return SearchManager.SUGGEST_MIME_TYPE;
+ default:
+ throw new IllegalArgumentException("Unknown URL: " + url);
+ }
+ }
+
+ @Override
+ public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
+ throw new UnsupportedOperationException("update not supported");
+ }
+
+ @Override
+ public Uri insert(Uri url, ContentValues initialValues) {
+ throw new UnsupportedOperationException("insert not supported");
+ }
+
+ @Override
+ public int delete(Uri url, String where, String[] whereArgs) {
+ throw new UnsupportedOperationException("delete not supported");
+ }
+}
diff --git a/core/tests/coretests/src/android/app/activity/AbortReceiver.java b/core/tests/coretests/src/android/app/activity/AbortReceiver.java
new file mode 100644
index 0000000..fef1775
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/AbortReceiver.java
@@ -0,0 +1,49 @@
+/*
+ * 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.app.activity;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.util.Log;
+
+public class AbortReceiver extends BroadcastReceiver
+{
+ public AbortReceiver()
+ {
+ }
+
+ public void onReceive(Context context, Intent intent)
+ {
+ //Log.i("AbortReceiver", "onReceiveIntent!");
+ try {
+ IBinder caller = intent.getIBinderExtra("caller");
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(LaunchpadActivity.LAUNCH);
+ data.writeString(LaunchpadActivity.RECEIVER_ABORT);
+ caller.transact(LaunchpadActivity.GOT_RECEIVE_TRANSACTION, data, null, 0);
+ data.recycle();
+ } catch (RemoteException ex) {
+ }
+
+ // abort the broadcast!!!
+ abortBroadcast();
+ }
+}
diff --git a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
new file mode 100644
index 0000000..61d73bc
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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.app.activity;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.pm.ConfigurationInfo;
+import android.content.res.Configuration;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
+
+import java.util.Iterator;
+import java.util.List;
+
+public class ActivityManagerTest extends AndroidTestCase {
+
+ protected Context mContext;
+ protected ActivityManager mActivityManager;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mContext = getContext();
+ mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ }
+
+ // TODO should write a test for getRecentTasks()
+ // TODO should write a test for getRunningTasks()
+ // TODO should write a test for getMemoryInfo()
+
+ // TODO: Find a way to re-enable this. It fails if any other app has failed during startup.
+ // This is probably an OK assumption given the desired system status when we run unit tests,
+ // but it's not necessarily the right assumption for a unit test.
+ @Suppress
+ public void disabledTestErrorTasksEmpty() throws Exception {
+
+ List<ActivityManager.ProcessErrorStateInfo> errList;
+
+ errList = mActivityManager.getProcessesInErrorState();
+
+ // test: confirm list is empty
+ assertNull(errList);
+ }
+
+ // TODO: Force an activity into an error state - then see if we can catch it here?
+ @SmallTest
+ public void testErrorTasksWithError() throws Exception {
+
+ List<ActivityManager.ProcessErrorStateInfo> errList;
+
+ // TODO force another process into an error condition. How?
+
+ // test: confirm error list length is at least 1 under varying query lengths
+// checkErrorListMax(1,-1);
+
+ errList = mActivityManager.getProcessesInErrorState();
+
+ // test: the list itself is healthy
+ checkErrorListSanity(errList);
+
+ // test: confirm our application shows up in the list
+ }
+
+ // TODO: Force an activity into an ANR state - then see if we can catch it here?
+ @SmallTest
+ public void testErrorTasksWithANR() throws Exception {
+
+ List<ActivityManager.ProcessErrorStateInfo> errList;
+
+ // TODO: force an application into an ANR state
+
+ errList = mActivityManager.getProcessesInErrorState();
+
+ // test: the list itself is healthy
+ checkErrorListSanity(errList);
+
+ // test: confirm our ANR'ing application shows up in the list
+ }
+
+ @SmallTest
+ public void testGetDeviceConfigurationInfo() throws Exception {
+ ConfigurationInfo config = mActivityManager.getDeviceConfigurationInfo();
+ assertNotNull(config);
+ // Validate values against configuration retrieved from resources
+ Configuration vconfig = mContext.getResources().getConfiguration();
+ assertNotNull(vconfig);
+ assertEquals(config.reqKeyboardType, vconfig.keyboard);
+ assertEquals(config.reqTouchScreen, vconfig.touchscreen);
+ assertEquals(config.reqNavigation, vconfig.navigation);
+ if (vconfig.navigation == Configuration.NAVIGATION_NONAV) {
+ assertNotNull(config.reqInputFeatures & ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV);
+ }
+ if (vconfig.keyboard != Configuration.KEYBOARD_UNDEFINED) {
+ assertNotNull(config.reqInputFeatures & ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD);
+ }
+ }
+
+ // If any entries in appear in the list, sanity check them against all running applications
+ private void checkErrorListSanity(List<ActivityManager.ProcessErrorStateInfo> errList) {
+ if (errList == null) return;
+
+ Iterator<ActivityManager.ProcessErrorStateInfo> iter = errList.iterator();
+ while (iter.hasNext()) {
+ ActivityManager.ProcessErrorStateInfo info = iter.next();
+ assertNotNull(info);
+ // sanity checks
+ assertTrue((info.condition == ActivityManager.ProcessErrorStateInfo.CRASHED) ||
+ (info.condition == ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING));
+ // TODO look at each of these and consider a stronger test
+ // TODO can we cross-check at the process name via some other API?
+ // TODO is there a better test for strings, e.g. "assertIsLegalString")
+ assertNotNull(info.processName);
+ // reasonableness test for info.pid ?
+ assertNotNull(info.longMsg);
+ assertNotNull(info.shortMsg);
+ // is there any reasonable test for the crashData? Probably not.
+ }
+ }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/ActivityTests.java b/core/tests/coretests/src/android/app/activity/ActivityTests.java
new file mode 100644
index 0000000..c57fe98
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/ActivityTests.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import junit.framework.TestSuite;
+
+public class ActivityTests {
+ public static final boolean DEBUG_LIFECYCLE = false;
+
+ public static TestSuite suite() {
+ TestSuite suite = new TestSuite(ActivityTests.class.getName());
+
+ suite.addTestSuite(BroadcastTest.class);
+ suite.addTestSuite(IntentSenderTest.class);
+ suite.addTestSuite(ActivityManagerTest.class);
+ suite.addTestSuite(LaunchTest.class);
+ suite.addTestSuite(LifecycleTest.class);
+ suite.addTestSuite(ServiceTest.class);
+ suite.addTestSuite(MetaDataTest.class);
+ // Remove temporarily until bug 1171309 is fixed.
+ //suite.addTestSuite(SubActivityTest.class);
+ suite.addTestSuite(SetTimeZonePermissionsTest.class);
+
+ return suite;
+ }
+}
diff --git a/core/tests/coretests/src/android/app/activity/ActivityTestsBase.java b/core/tests/coretests/src/android/app/activity/ActivityTestsBase.java
new file mode 100644
index 0000000..232abe2
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/ActivityTestsBase.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.test.AndroidTestCase;
+import android.test.PerformanceTestCase;
+
+public class ActivityTestsBase extends AndroidTestCase
+ implements PerformanceTestCase, LaunchpadActivity.CallingTest {
+ public static final String PERMISSION_GRANTED =
+ "com.android.frameworks.coretests.permission.TEST_GRANTED";
+ public static final String PERMISSION_DENIED =
+ "com.android.frameworks.coretests.permission.TEST_DENIED";
+
+ protected Intent mIntent;
+
+ private PerformanceTestCase.Intermediates mIntermediates;
+ private String mExpecting;
+
+ // Synchronization of activity result.
+ private boolean mFinished;
+ private int mResultCode = 0;
+ private Intent mData;
+ private RuntimeException mResultStack = null;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mIntent = new Intent(mContext, LaunchpadActivity.class);
+ mIntermediates = null;
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mIntermediates = null;
+ super.tearDown();
+ }
+
+ public boolean isPerformanceOnly() {
+ return false;
+ }
+
+ public void setInternalIterations(int count) {
+ }
+
+ public void startTiming(boolean realTime) {
+ if (mIntermediates != null) {
+ mIntermediates.startTiming(realTime);
+ }
+ }
+
+ public void addIntermediate(String name) {
+ if (mIntermediates != null) {
+ mIntermediates.addIntermediate(name);
+ }
+ }
+
+ public void addIntermediate(String name, long timeInNS) {
+ if (mIntermediates != null) {
+ mIntermediates.addIntermediate(name, timeInNS);
+ }
+ }
+
+ public void finishTiming(boolean realTime) {
+ if (mIntermediates != null) {
+ mIntermediates.finishTiming(realTime);
+ }
+ }
+
+ public void activityFinished(int resultCode, Intent data, RuntimeException where) {
+ finishWithResult(resultCode, data, where);
+ }
+
+ public Intent editIntent() {
+ return mIntent;
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public int startPerformance(Intermediates intermediates) {
+ mIntermediates = intermediates;
+ return 1;
+ }
+
+ public void finishGood() {
+ finishWithResult(Activity.RESULT_OK, null);
+ }
+
+ public void finishBad(String error) {
+ finishWithResult(Activity.RESULT_CANCELED, (new Intent()).setAction(error));
+ }
+
+ public void finishWithResult(int resultCode, Intent data) {
+ RuntimeException where = new RuntimeException("Original error was here");
+ where.fillInStackTrace();
+ finishWithResult(resultCode, data, where);
+ }
+
+ public void finishWithResult(int resultCode, Intent data, RuntimeException where) {
+ synchronized (this) {
+ //System.out.println("*** Activity finished!!");
+ mResultCode = resultCode;
+ mData = data;
+ mResultStack = where;
+ mFinished = true;
+ notifyAll();
+ }
+ }
+
+ public int runLaunchpad(String action) {
+ LaunchpadActivity.setCallingTest(this);
+
+ synchronized (this) {
+ mIntent.setAction(action);
+ mFinished = false;
+ //System.out.println("*** Starting: " + mIntent);
+ mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(mIntent);
+ }
+
+ return waitForResultOrThrow(60 * 1000);
+ }
+
+ public int waitForResultOrThrow(int timeoutMs) {
+ return waitForResultOrThrow(timeoutMs, null);
+ }
+
+ public int waitForResultOrThrow(int timeoutMs, String expected) {
+ int res = waitForResult(timeoutMs, expected);
+
+ if (res == Activity.RESULT_CANCELED) {
+ if (mResultStack != null) {
+ throw new RuntimeException(
+ mData != null ? mData.toString() : "Unable to launch",
+ mResultStack);
+ } else {
+ throw new RuntimeException(
+ mData != null ? mData.toString() : "Unable to launch");
+ }
+ }
+ return res;
+ }
+
+ public int waitForResult(int timeoutMs, String expected) {
+ mExpecting = expected;
+
+ long endTime = System.currentTimeMillis() + timeoutMs;
+
+ boolean timeout = false;
+ synchronized (this) {
+ while (!mFinished) {
+ long delay = endTime - System.currentTimeMillis();
+ if (delay < 0) {
+ timeout = true;
+ break;
+ }
+
+ try {
+ wait(delay);
+ } catch (java.lang.InterruptedException e) {
+ // do nothing
+ }
+ }
+ }
+
+ mFinished = false;
+
+ if (timeout) {
+ mResultCode = Activity.RESULT_CANCELED;
+ onTimeout();
+ }
+ return mResultCode;
+ }
+
+ public int getResultCode() {
+ return mResultCode;
+ }
+
+ public Intent getResultData() {
+ return mData;
+ }
+
+ public RuntimeException getResultStack() {
+ return mResultStack;
+ }
+
+ public void onTimeout() {
+ String msg = mExpecting == null
+ ? "Timeout" : ("Timeout while expecting " + mExpecting);
+ finishWithResult(Activity.RESULT_CANCELED, (new Intent()).setAction(msg));
+ }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/BroadcastTest.java b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
new file mode 100644
index 0000000..4b1f9fd
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.app.Activity;
+import android.app.ActivityManagerNative;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.test.FlakyTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.util.Log;
+
+import java.util.Arrays;
+
+public class BroadcastTest extends ActivityTestsBase {
+ public static final int BROADCAST_TIMEOUT = 5 * 1000;
+
+ public static final String BROADCAST_REGISTERED =
+ "com.android.frameworks.coretests.activity.BROADCAST_REGISTERED";
+ public static final String BROADCAST_LOCAL =
+ "com.android.frameworks.coretests.activity.BROADCAST_LOCAL";
+ public static final String BROADCAST_LOCAL_GRANTED =
+ "com.android.frameworks.coretests.activity.BROADCAST_LOCAL_GRANTED";
+ public static final String BROADCAST_LOCAL_DENIED =
+ "com.android.frameworks.coretests.activity.BROADCAST_LOCAL_DENIED";
+ public static final String BROADCAST_REMOTE =
+ "com.android.frameworks.coretests.activity.BROADCAST_REMOTE";
+ public static final String BROADCAST_REMOTE_GRANTED =
+ "com.android.frameworks.coretests.activity.BROADCAST_REMOTE_GRANTED";
+ public static final String BROADCAST_REMOTE_DENIED =
+ "com.android.frameworks.coretests.activity.BROADCAST_REMOTE_DENIED";
+ public static final String BROADCAST_ALL =
+ "com.android.frameworks.coretests.activity.BROADCAST_ALL";
+ public static final String BROADCAST_MULTI =
+ "com.android.frameworks.coretests.activity.BROADCAST_MULTI";
+ public static final String BROADCAST_ABORT =
+ "com.android.frameworks.coretests.activity.BROADCAST_ABORT";
+
+ public static final String BROADCAST_STICKY1 =
+ "com.android.frameworks.coretests.activity.BROADCAST_STICKY1";
+ public static final String BROADCAST_STICKY2 =
+ "com.android.frameworks.coretests.activity.BROADCAST_STICKY2";
+
+ public static final String BROADCAST_FAIL_REGISTER =
+ "com.android.frameworks.coretests.activity.BROADCAST_FAIL_REGISTER";
+ public static final String BROADCAST_FAIL_BIND =
+ "com.android.frameworks.coretests.activity.BROADCAST_FAIL_BIND";
+
+ public static final String RECEIVER_REG = "receiver-reg";
+ public static final String RECEIVER_LOCAL = "receiver-local";
+ public static final String RECEIVER_REMOTE = "receiver-remote";
+ public static final String RECEIVER_ABORT = "receiver-abort";
+ public static final String RECEIVER_RESULTS = "receiver-results";
+
+ public static final String DATA_1 = "one";
+ public static final String DATA_2 = "two";
+
+ public static final int GOT_RECEIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
+ public static final int ERROR_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 1;
+
+ private String[] mExpectedReceivers = null;
+ private int mNextReceiver;
+
+ private String[] mExpectedData = null;
+ private boolean[] mReceivedData = null;
+
+ boolean mReceiverRegistered = false;
+
+ public void setExpectedReceivers(String[] receivers) {
+ mExpectedReceivers = receivers;
+ mNextReceiver = 0;
+ }
+
+ public void setExpectedData(String[] data) {
+ mExpectedData = data;
+ mReceivedData = new boolean[data.length];
+ }
+
+ public void onTimeout() {
+ String msg = "Timeout";
+ if (mExpectedReceivers != null && mNextReceiver < mExpectedReceivers.length) {
+ msg = msg + " waiting for " + mExpectedReceivers[mNextReceiver];
+ }
+ finishBad(msg);
+ }
+
+ public Intent makeBroadcastIntent(String action) {
+ Intent intent = new Intent(action, null);
+ intent.putExtra("caller", mCallTarget);
+ return intent;
+ }
+
+ public void finishWithResult(int resultCode, Intent data) {
+ unregisterMyReceiver();
+ super.finishWithResult(resultCode, data);
+ }
+
+ public final void gotReceive(String name, Intent intent) {
+ synchronized (this) {
+
+ //System.out.println("Got receive: " + name);
+ //System.out.println(mNextReceiver + " in " + mExpectedReceivers);
+ //new RuntimeException("stack").printStackTrace();
+
+ addIntermediate(name);
+
+ if (mExpectedData != null) {
+ int n = mExpectedData.length;
+ int i;
+ boolean prev = false;
+ for (i = 0; i < n; i++) {
+ if (mExpectedData[i].equals(intent.getStringExtra("test"))) {
+ if (mReceivedData[i]) {
+ prev = true;
+ continue;
+ }
+ mReceivedData[i] = true;
+ break;
+ }
+ }
+ if (i >= n) {
+ if (prev) {
+ finishBad("Receive got data too many times: "
+ + intent.getStringExtra("test"));
+ } else {
+ finishBad("Receive got unexpected data: "
+ + intent.getStringExtra("test"));
+ }
+ new RuntimeException("stack").printStackTrace();
+ return;
+ }
+ }
+
+ if (mNextReceiver >= mExpectedReceivers.length) {
+ finishBad("Got too many onReceiveIntent() calls!");
+// System.out.println("Too many intents received: now at "
+// + mNextReceiver + ", expect list: "
+// + Arrays.toString(mExpectedReceivers));
+ fail("Got too many onReceiveIntent() calls!");
+ } else if (!mExpectedReceivers[mNextReceiver].equals(name)) {
+ finishBad("Receive out of order: got " + name
+ + " but expected "
+ + mExpectedReceivers[mNextReceiver]);
+ fail("Receive out of order: got " + name
+ + " but expected "
+ + mExpectedReceivers[mNextReceiver]);
+ } else {
+ mNextReceiver++;
+ if (mNextReceiver == mExpectedReceivers.length) {
+ finishTest();
+ }
+ }
+ }
+ }
+
+ public void registerMyReceiver(IntentFilter filter, String permission) {
+ mReceiverRegistered = true;
+ //System.out.println("Registering: " + mReceiver);
+ getContext().registerReceiver(mReceiver, filter, permission, null);
+ }
+
+ public void unregisterMyReceiver() {
+ if (mReceiverRegistered) {
+ unregisterMyReceiverNoCheck();
+ }
+ }
+
+ public void unregisterMyReceiverNoCheck() {
+ mReceiverRegistered = false;
+ //System.out.println("Unregistering: " + mReceiver);
+ getContext().unregisterReceiver(mReceiver);
+ }
+
+ public void onRegisteredReceiver(Intent intent) {
+ gotReceive(RECEIVER_REG, intent);
+ }
+
+ private Binder mCallTarget = new Binder() {
+ public boolean onTransact(int code, Parcel data, Parcel reply,
+ int flags) {
+ data.setDataPosition(0);
+ data.enforceInterface(LaunchpadActivity.LAUNCH);
+ if (code == GOT_RECEIVE_TRANSACTION) {
+ String name = data.readString();
+ gotReceive(name, null);
+ return true;
+ } else if (code == ERROR_TRANSACTION) {
+ finishBad(data.readString());
+ return true;
+ }
+ return false;
+ }
+ };
+
+ private void finishTest() {
+ if (mReceiverRegistered) {
+ addIntermediate("before-unregister");
+ unregisterMyReceiver();
+ }
+ finishTiming(true);
+ finishGood();
+ }
+
+ private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ //System.out.println("Receive in: " + this + ": " + intent);
+ onRegisteredReceiver(intent);
+ }
+ };
+
+ // Mark flaky until http://b/issue?id=1191607 is resolved.
+ @FlakyTest(tolerance=2)
+ public void testRegistered() throws Exception {
+ runLaunchpad(LaunchpadActivity.BROADCAST_REGISTERED);
+ }
+
+ public void testLocal() throws Exception {
+ runLaunchpad(LaunchpadActivity.BROADCAST_LOCAL);
+ }
+
+ public void testRemote() throws Exception {
+ runLaunchpad(LaunchpadActivity.BROADCAST_REMOTE);
+ }
+
+ public void testAbort() throws Exception {
+ runLaunchpad(LaunchpadActivity.BROADCAST_ABORT);
+ }
+
+ @FlakyTest(tolerance=2)
+ public void testAll() throws Exception {
+ runLaunchpad(LaunchpadActivity.BROADCAST_ALL);
+ }
+
+ @FlakyTest(tolerance=2)
+ public void testMulti() throws Exception {
+ runLaunchpad(LaunchpadActivity.BROADCAST_MULTI);
+ }
+
+ private class TestBroadcastReceiver extends BroadcastReceiver {
+ public boolean mHaveResult = false;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (BroadcastTest.this) {
+ mHaveResult = true;
+ BroadcastTest.this.notifyAll();
+ }
+ }
+ }
+
+ public void testResult() throws Exception {
+ TestBroadcastReceiver broadcastReceiver = new TestBroadcastReceiver();
+
+ synchronized (this) {
+ Bundle map = new Bundle();
+ map.putString("foo", "you");
+ map.putString("remove", "me");
+ getContext().sendOrderedBroadcast(
+ new Intent("com.android.frameworks.coretests.activity.BROADCAST_RESULT"),
+ null, broadcastReceiver, null, 1, "foo", map);
+ while (!broadcastReceiver.mHaveResult) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+
+ //System.out.println("Code: " + mResultCode + ", data: " + mResultData);
+ //System.out.println("Extras: " + mResultExtras);
+
+ assertEquals("Incorrect code: " + broadcastReceiver.getResultCode(),
+ 3, broadcastReceiver.getResultCode());
+
+ assertEquals("bar", broadcastReceiver.getResultData());
+
+ Bundle resultExtras = broadcastReceiver.getResultExtras(false);
+ assertEquals("them", resultExtras.getString("bar"));
+ assertEquals("you", resultExtras.getString("foo"));
+ assertNull(resultExtras.getString("remove"));
+ }
+ }
+
+ public void testSetSticky() throws Exception {
+ Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
+ intent.putExtra("test", LaunchpadActivity.DATA_1);
+ ActivityManagerNative.getDefault().unbroadcastIntent(null, intent);
+
+ ActivityManagerNative.broadcastStickyIntent(intent, null);
+ addIntermediate("finished-broadcast");
+
+ IntentFilter filter = new IntentFilter(LaunchpadActivity.BROADCAST_STICKY1);
+ Intent sticky = getContext().registerReceiver(null, filter);
+ assertNotNull("Sticky not found", sticky);
+ assertEquals(LaunchpadActivity.DATA_1, sticky.getStringExtra("test"));
+ }
+
+ public void testClearSticky() throws Exception {
+ Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
+ intent.putExtra("test", LaunchpadActivity.DATA_1);
+ ActivityManagerNative.broadcastStickyIntent(intent, null);
+
+ ActivityManagerNative.getDefault().unbroadcastIntent(
+ null, new Intent(LaunchpadActivity.BROADCAST_STICKY1, null));
+ addIntermediate("finished-unbroadcast");
+
+ IntentFilter filter = new IntentFilter(LaunchpadActivity.BROADCAST_STICKY1);
+ Intent sticky = getContext().registerReceiver(null, filter);
+ assertNull("Sticky not found", sticky);
+ }
+
+ public void testReplaceSticky() throws Exception {
+ Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
+ intent.putExtra("test", LaunchpadActivity.DATA_1);
+ ActivityManagerNative.broadcastStickyIntent(intent, null);
+ intent.putExtra("test", LaunchpadActivity.DATA_2);
+
+ ActivityManagerNative.broadcastStickyIntent(intent, null);
+ addIntermediate("finished-broadcast");
+
+ IntentFilter filter = new IntentFilter(LaunchpadActivity.BROADCAST_STICKY1);
+ Intent sticky = getContext().registerReceiver(null, filter);
+ assertNotNull("Sticky not found", sticky);
+ assertEquals(LaunchpadActivity.DATA_2, sticky.getStringExtra("test"));
+ }
+
+ // Marking flaky until http://b/issue?id=1191337 is resolved
+ @FlakyTest(tolerance=2)
+ public void testReceiveSticky() throws Exception {
+ Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
+ intent.putExtra("test", LaunchpadActivity.DATA_1);
+ ActivityManagerNative.broadcastStickyIntent(intent, null);
+
+ runLaunchpad(LaunchpadActivity.BROADCAST_STICKY1);
+ }
+
+ // Marking flaky until http://b/issue?id=1191337 is resolved
+ @FlakyTest(tolerance=2)
+ public void testReceive2Sticky() throws Exception {
+ Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
+ intent.putExtra("test", LaunchpadActivity.DATA_1);
+ ActivityManagerNative.broadcastStickyIntent(intent, null);
+ intent = new Intent(LaunchpadActivity.BROADCAST_STICKY2, null);
+ intent.putExtra("test", LaunchpadActivity.DATA_2);
+ ActivityManagerNative.broadcastStickyIntent(intent, null);
+
+ runLaunchpad(LaunchpadActivity.BROADCAST_STICKY2);
+ }
+
+ public void testRegisteredReceivePermissionGranted() throws Exception {
+ setExpectedReceivers(new String[]{RECEIVER_REG});
+ registerMyReceiver(new IntentFilter(BROADCAST_REGISTERED), PERMISSION_GRANTED);
+ addIntermediate("after-register");
+ getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_REGISTERED));
+ waitForResultOrThrow(BROADCAST_TIMEOUT);
+ }
+
+ public void testRegisteredReceivePermissionDenied() throws Exception {
+ setExpectedReceivers(new String[]{RECEIVER_RESULTS});
+ registerMyReceiver(new IntentFilter(BROADCAST_REGISTERED), PERMISSION_DENIED);
+ addIntermediate("after-register");
+
+ BroadcastReceiver finish = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ gotReceive(RECEIVER_RESULTS, intent);
+ }
+ };
+
+ getContext().sendOrderedBroadcast(
+ makeBroadcastIntent(BROADCAST_REGISTERED),
+ null, finish, null, Activity.RESULT_CANCELED, null, null);
+ waitForResultOrThrow(BROADCAST_TIMEOUT);
+ }
+
+ public void testRegisteredBroadcastPermissionGranted() throws Exception {
+ setExpectedReceivers(new String[]{RECEIVER_REG});
+ registerMyReceiver(new IntentFilter(BROADCAST_REGISTERED), null);
+ addIntermediate("after-register");
+ getContext().sendBroadcast(
+ makeBroadcastIntent(BROADCAST_REGISTERED),
+ PERMISSION_GRANTED);
+ waitForResultOrThrow(BROADCAST_TIMEOUT);
+ }
+
+ public void testRegisteredBroadcastPermissionDenied() throws Exception {
+ setExpectedReceivers(new String[]{RECEIVER_RESULTS});
+ registerMyReceiver(new IntentFilter(BROADCAST_REGISTERED), null);
+ addIntermediate("after-register");
+
+ BroadcastReceiver finish = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ gotReceive(RECEIVER_RESULTS, intent);
+ }
+ };
+
+ getContext().sendOrderedBroadcast(
+ makeBroadcastIntent(BROADCAST_REGISTERED),
+ PERMISSION_DENIED, finish, null, Activity.RESULT_CANCELED,
+ null, null);
+ waitForResultOrThrow(BROADCAST_TIMEOUT);
+ }
+
+ public void testLocalReceivePermissionGranted() throws Exception {
+ setExpectedReceivers(new String[]{RECEIVER_LOCAL});
+ getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_LOCAL_GRANTED));
+ waitForResultOrThrow(BROADCAST_TIMEOUT);
+ }
+
+ public void testLocalReceivePermissionDenied() throws Exception {
+ setExpectedReceivers(new String[]{RECEIVER_RESULTS});
+
+ BroadcastReceiver finish = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ gotReceive(RECEIVER_RESULTS, intent);
+ }
+ };
+
+ getContext().sendOrderedBroadcast(
+ makeBroadcastIntent(BROADCAST_LOCAL_DENIED),
+ null, finish, null, Activity.RESULT_CANCELED,
+ null, null);
+ waitForResultOrThrow(BROADCAST_TIMEOUT);
+ }
+
+ public void testLocalBroadcastPermissionGranted() throws Exception {
+ setExpectedReceivers(new String[]{RECEIVER_LOCAL});
+ getContext().sendBroadcast(
+ makeBroadcastIntent(BROADCAST_LOCAL),
+ PERMISSION_GRANTED);
+ waitForResultOrThrow(BROADCAST_TIMEOUT);
+ }
+
+ public void testLocalBroadcastPermissionDenied() throws Exception {
+ setExpectedReceivers(new String[]{RECEIVER_RESULTS});
+
+ BroadcastReceiver finish = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ gotReceive(RECEIVER_RESULTS, intent);
+ }
+ };
+
+ getContext().sendOrderedBroadcast(
+ makeBroadcastIntent(BROADCAST_LOCAL),
+ PERMISSION_DENIED, finish, null, Activity.RESULT_CANCELED,
+ null, null);
+ waitForResultOrThrow(BROADCAST_TIMEOUT);
+ }
+
+ public void testRemoteReceivePermissionGranted() throws Exception {
+ setExpectedReceivers(new String[]{RECEIVER_REMOTE});
+ getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_REMOTE_GRANTED));
+ waitForResultOrThrow(BROADCAST_TIMEOUT);
+ }
+
+ public void testRemoteReceivePermissionDenied() throws Exception {
+ setExpectedReceivers(new String[]{RECEIVER_RESULTS});
+
+ BroadcastReceiver finish = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ gotReceive(RECEIVER_RESULTS, intent);
+ }
+ };
+
+ getContext().sendOrderedBroadcast(
+ makeBroadcastIntent(BROADCAST_REMOTE_DENIED),
+ null, finish, null, Activity.RESULT_CANCELED,
+ null, null);
+ waitForResultOrThrow(BROADCAST_TIMEOUT);
+ }
+
+ public void testRemoteBroadcastPermissionGranted() throws Exception {
+ setExpectedReceivers(new String[]{RECEIVER_REMOTE});
+ getContext().sendBroadcast(
+ makeBroadcastIntent(BROADCAST_REMOTE),
+ PERMISSION_GRANTED);
+ waitForResultOrThrow(BROADCAST_TIMEOUT);
+ }
+
+ public void testRemoteBroadcastPermissionDenied() throws Exception {
+ setExpectedReceivers(new String[]{RECEIVER_RESULTS});
+
+ BroadcastReceiver finish = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ gotReceive(RECEIVER_RESULTS, intent);
+ }
+ };
+
+ getContext().sendOrderedBroadcast(
+ makeBroadcastIntent(BROADCAST_REMOTE),
+ PERMISSION_DENIED, finish, null, Activity.RESULT_CANCELED,
+ null, null);
+ waitForResultOrThrow(BROADCAST_TIMEOUT);
+ }
+
+ public void testReceiverCanNotRegister() throws Exception {
+ setExpectedReceivers(new String[]{RECEIVER_LOCAL});
+ getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_REGISTER));
+ waitForResultOrThrow(BROADCAST_TIMEOUT);
+ }
+
+ public void testReceiverCanNotBind() throws Exception {
+ setExpectedReceivers(new String[]{RECEIVER_LOCAL});
+ getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_BIND));
+ waitForResultOrThrow(BROADCAST_TIMEOUT);
+ }
+
+ public void testLocalUnregisterTwice() throws Exception {
+ registerMyReceiver(new IntentFilter(BROADCAST_REGISTERED), null);
+ unregisterMyReceiverNoCheck();
+ try {
+ unregisterMyReceiverNoCheck();
+ fail("No exception thrown on second unregister");
+ } catch (IllegalArgumentException e) {
+ Log.i("foo", "Unregister exception", e);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/app/activity/ClearTop.java b/core/tests/coretests/src/android/app/activity/ClearTop.java
new file mode 100644
index 0000000..a5ee2ce
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/ClearTop.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+public class ClearTop extends Activity {
+ public static final String WAIT_CLEAR_TASK = "waitClearTask";
+
+ public ClearTop() {
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ //Log.i("foo", "Creating: " + this);
+ Intent intent = new Intent(getIntent()).setAction(LocalScreen.CLEAR_TASK)
+ .setClass(this, LocalScreen.class);
+ startActivity(intent);
+ }
+
+ @Override
+ public void onNewIntent(Intent intent) {
+ //Log.i("foo", "New intent in " + this + ": " + intent);
+ if (LocalScreen.CLEAR_TASK.equals(intent.getAction())) {
+ setResult(RESULT_OK);
+ } else {
+ setResult(RESULT_CANCELED, new Intent().setAction(
+ "New intent received " + intent + ", expecting action "
+ + TestedScreen.CLEAR_TASK));
+ }
+ finish();
+ }
+}
diff --git a/core/tests/coretests/src/android/app/activity/IntentSenderTest.java b/core/tests/coretests/src/android/app/activity/IntentSenderTest.java
new file mode 100644
index 0000000..3c30915
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/IntentSenderTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.test.suitebuilder.annotation.Suppress;
+import android.os.Bundle;
+import android.test.suitebuilder.annotation.Suppress;
+
+public class IntentSenderTest extends BroadcastTest {
+
+ public void testRegisteredReceivePermissionGranted() throws Exception {
+ setExpectedReceivers(new String[]{RECEIVER_REG});
+ registerMyReceiver(new IntentFilter(BROADCAST_REGISTERED), PERMISSION_GRANTED);
+ addIntermediate("after-register");
+ PendingIntent is = PendingIntent.getBroadcast(getContext(), 0,
+ makeBroadcastIntent(BROADCAST_REGISTERED), 0);
+ is.send();
+ waitForResultOrThrow(BROADCAST_TIMEOUT);
+ is.cancel();
+ }
+
+ public void testRegisteredReceivePermissionDenied() throws Exception {
+ final Intent intent = makeBroadcastIntent(BROADCAST_REGISTERED);
+
+ setExpectedReceivers(new String[]{RECEIVER_RESULTS});
+ registerMyReceiver(new IntentFilter(BROADCAST_REGISTERED), PERMISSION_DENIED);
+ addIntermediate("after-register");
+
+ PendingIntent.OnFinished finish = new PendingIntent.OnFinished() {
+ public void onSendFinished(PendingIntent pi, Intent intent,
+ int resultCode, String resultData, Bundle resultExtras) {
+ gotReceive(RECEIVER_RESULTS, intent);
+ }
+ };
+
+ PendingIntent is = PendingIntent.getBroadcast(getContext(), 0, intent, 0);
+ is.send(Activity.RESULT_CANCELED, finish, null);
+ waitForResultOrThrow(BROADCAST_TIMEOUT);
+ is.cancel();
+ }
+
+ public void testLocalReceivePermissionGranted() throws Exception {
+ setExpectedReceivers(new String[]{RECEIVER_LOCAL});
+ PendingIntent is = PendingIntent.getBroadcast(getContext(), 0,
+ makeBroadcastIntent(BROADCAST_LOCAL_GRANTED), 0);
+ is.send();
+ waitForResultOrThrow(BROADCAST_TIMEOUT);
+ is.cancel();
+ }
+
+ public void testLocalReceivePermissionDenied() throws Exception {
+ final Intent intent = makeBroadcastIntent(BROADCAST_LOCAL_DENIED);
+
+ setExpectedReceivers(new String[]{RECEIVER_RESULTS});
+
+ PendingIntent.OnFinished finish = new PendingIntent.OnFinished() {
+ public void onSendFinished(PendingIntent pi, Intent intent,
+ int resultCode, String resultData, Bundle resultExtras) {
+ gotReceive(RECEIVER_RESULTS, intent);
+ }
+ };
+
+ PendingIntent is = PendingIntent.getBroadcast(getContext(), 0, intent, 0);
+ is.send(Activity.RESULT_CANCELED, finish, null);
+ waitForResultOrThrow(BROADCAST_TIMEOUT);
+ is.cancel();
+ }
+}
diff --git a/core/tests/coretests/src/android/app/activity/LaunchTest.java b/core/tests/coretests/src/android/app/activity/LaunchTest.java
new file mode 100644
index 0000000..5893fd0
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LaunchTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.content.ComponentName;
+import android.test.suitebuilder.annotation.LargeTest;
+
+public class LaunchTest extends ActivityTestsBase {
+
+ @LargeTest
+ public void testColdActivity() throws Exception {
+ mIntent.putExtra("component", new ComponentName(getContext(), TestedActivity.class));
+ runLaunchpad(LaunchpadActivity.LAUNCH);
+ }
+
+ @LargeTest
+ public void testLocalActivity() throws Exception {
+ mIntent.putExtra("component", new ComponentName(getContext(), LocalActivity.class));
+ runLaunchpad(LaunchpadActivity.LAUNCH);
+ }
+
+ @LargeTest
+ public void testColdScreen() throws Exception {
+ mIntent.putExtra("component", new ComponentName(getContext(), TestedScreen.class));
+ runLaunchpad(LaunchpadActivity.LAUNCH);
+ }
+
+ @LargeTest
+ public void testLocalScreen() throws Exception {
+ mIntent.putExtra("component", new ComponentName(getContext(), LocalScreen.class));
+ runLaunchpad(LaunchpadActivity.LAUNCH);
+ }
+
+ @LargeTest
+ public void testForwardResult() throws Exception {
+ runLaunchpad(LaunchpadActivity.FORWARD_RESULT);
+ }
+
+ // The following is disabled until we can catch and recover from
+ // application errors.
+ public void xxtestBadParcelable() throws Exception {
+ // All we really care about for this test is that the system
+ // doesn't crash.
+ runLaunchpad(LaunchpadActivity.BAD_PARCELABLE);
+ }
+
+ @LargeTest
+ public void testClearTopInCreate() throws Exception {
+ mIntent.putExtra("component", new ComponentName(getContext(), ClearTop.class));
+ runLaunchpad(LaunchpadActivity.LAUNCH);
+ }
+
+ @LargeTest
+ public void testClearTopWhileResumed() throws Exception {
+ mIntent.putExtra("component", new ComponentName(getContext(), ClearTop.class));
+ mIntent.putExtra(ClearTop.WAIT_CLEAR_TASK, true);
+ runLaunchpad(LaunchpadActivity.LAUNCH);
+ }
+}
+
+
diff --git a/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java b/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java
new file mode 100644
index 0000000..7662456
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java
@@ -0,0 +1,588 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.test.PerformanceTestCase;
+import android.util.Log;
+
+class MyBadParcelable implements Parcelable {
+ public MyBadParcelable() {
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString("I am bad");
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<MyBadParcelable> CREATOR
+ = new Parcelable.Creator<MyBadParcelable>() {
+ public MyBadParcelable createFromParcel(Parcel in) {
+ return new MyBadParcelable(in);
+ }
+
+ public MyBadParcelable[] newArray(int size) {
+ return new MyBadParcelable[size];
+ }
+ };
+
+ public MyBadParcelable(Parcel in) {
+ String nm = in.readString();
+ }
+}
+
+public class LaunchpadActivity extends Activity {
+ public interface CallingTest extends PerformanceTestCase.Intermediates {
+ public void startTiming(boolean realTime);
+ public void addIntermediate(String name);
+ public void addIntermediate(String name, long timeInNS);
+ public void finishTiming(boolean realTime);
+ public void activityFinished(int resultCode, Intent data,
+ RuntimeException where);
+ }
+
+ // Also used as the Binder interface descriptor string in these tests
+ public static final String LAUNCH = "com.android.frameworks.coretests.activity.LAUNCH";
+
+ public static final String FORWARD_RESULT =
+ "com.android.frameworks.coretests.activity.FORWARD_RESULT";
+ public static final String RETURNED_RESULT =
+ "com.android.frameworks.coretests.activity.RETURNED_RESULT";
+
+ public static final String BAD_PARCELABLE =
+ "comcom.android.frameworks.coretests.activity.BAD_PARCELABLE";
+
+ public static final int LAUNCHED_RESULT = 1;
+ public static final int FORWARDED_RESULT = 2;
+
+ public static final String LIFECYCLE_BASIC =
+ "com.android.frameworks.coretests.activity.LIFECYCLE_BASIC";
+ public static final String LIFECYCLE_SCREEN =
+ "com.android.frameworks.coretests.activity.LIFECYCLE_SCREEN";
+ public static final String LIFECYCLE_DIALOG =
+ "com.android.frameworks.coretests.activity.LIFECYCLE_DIALOG";
+ public static final String LIFECYCLE_FINISH_CREATE =
+ "com.android.frameworks.coretests.activity.LIFECYCLE_FINISH_CREATE";
+ public static final String LIFECYCLE_FINISH_START =
+ "com.android.frameworks.coretests.activity.LIFECYCLE_FINISH_START";
+
+ public static final String BROADCAST_REGISTERED =
+ "com.android.frameworks.coretests.activity.BROADCAST_REGISTERED";
+ public static final String BROADCAST_LOCAL =
+ "com.android.frameworks.coretests.activity.BROADCAST_LOCAL";
+ public static final String BROADCAST_REMOTE =
+ "com.android.frameworks.coretests.activity.BROADCAST_REMOTE";
+ public static final String BROADCAST_ALL =
+ "com.android.frameworks.coretests.activity.BROADCAST_ALL";
+ public static final String BROADCAST_REPEAT =
+ "com.android.frameworks.coretests.activity.BROADCAST_REPEAT";
+ public static final String BROADCAST_MULTI =
+ "com.android.frameworks.coretests.activity.BROADCAST_MULTI";
+ public static final String BROADCAST_ABORT =
+ "com.android.frameworks.coretests.activity.BROADCAST_ABORT";
+
+ public static final String BROADCAST_STICKY1 =
+ "com.android.frameworks.coretests.activity.BROADCAST_STICKY1";
+ public static final String BROADCAST_STICKY2 =
+ "com.android.frameworks.coretests.activity.BROADCAST_STICKY2";
+
+ public static final String RECEIVER_REG = "receiver-reg";
+ public static final String RECEIVER_LOCAL = "receiver-local";
+ public static final String RECEIVER_REMOTE = "receiver-remote";
+ public static final String RECEIVER_ABORT = "receiver-abort";
+
+ public static final String DATA_1 = "one";
+ public static final String DATA_2 = "two";
+
+ public static final String ON_START = "onStart";
+ public static final String ON_RESTART = "onRestart";
+ public static final String ON_RESUME = "onResume";
+ public static final String ON_FREEZE = "onSaveInstanceState";
+ public static final String ON_PAUSE = "onPause";
+ public static final String ON_STOP = "onStop";
+ public static final String ON_DESTROY = "onDestroy";
+
+ public static final String DO_FINISH = "finish";
+ public static final String DO_LOCAL_SCREEN = "local-screen";
+ public static final String DO_LOCAL_DIALOG = "local-dialog";
+
+ private boolean mBadParcelable = false;
+
+ private boolean mStarted = false;
+ private long mStartTime;
+
+ private int mResultCode = RESULT_CANCELED;
+ private Intent mData = (new Intent()).setAction("No result received");
+ private RuntimeException mResultStack = null;
+
+ private String[] mExpectedLifecycle = null;
+ private int mNextLifecycle;
+
+ private String[] mExpectedReceivers = null;
+ private int mNextReceiver;
+
+ private String[] mExpectedData = null;
+ private boolean[] mReceivedData = null;
+
+ boolean mReceiverRegistered = false;
+
+ private static CallingTest sCallingTest = null;
+
+ public static void setCallingTest(CallingTest ct) {
+ sCallingTest = ct;
+ }
+
+ public LaunchpadActivity() {
+ mStartTime = System.currentTimeMillis();
+ }
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ String action = getIntent().getAction();
+ if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "CREATE lauchpad "
+ + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+ if (LIFECYCLE_BASIC.equals(action)) {
+ setExpectedLifecycle(new String[]{ON_START, ON_RESUME,
+ DO_FINISH, ON_PAUSE, ON_STOP, ON_DESTROY});
+ } else if (LIFECYCLE_SCREEN.equals(action)) {
+ setExpectedLifecycle(new String[]{ON_START, ON_RESUME,
+ DO_LOCAL_SCREEN, ON_FREEZE, ON_PAUSE, ON_STOP,
+ ON_RESTART, ON_START, ON_RESUME,
+ DO_FINISH, ON_PAUSE, ON_STOP, ON_DESTROY});
+ } else if (LIFECYCLE_DIALOG.equals(action)) {
+ setExpectedLifecycle(new String[]{ON_START, ON_RESUME,
+ DO_LOCAL_DIALOG, ON_FREEZE, ON_PAUSE, ON_RESUME,
+ DO_FINISH, ON_PAUSE, ON_STOP, ON_DESTROY});
+ } else if (LIFECYCLE_FINISH_CREATE.equals(action)) {
+ // This one behaves a little differently when running in a group.
+ if (getParent() == null) {
+ setExpectedLifecycle(new String[]{ON_DESTROY});
+ } else {
+ setExpectedLifecycle(new String[]{ON_START, ON_STOP, ON_DESTROY});
+ }
+ finish();
+ } else if (LIFECYCLE_FINISH_START.equals(action)) {
+ setExpectedLifecycle(new String[]{ON_START, DO_FINISH,
+ ON_STOP, ON_DESTROY});
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "START lauchpad "
+ + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+ checkLifecycle(ON_START);
+ }
+
+ @Override
+ protected void onRestart() {
+ super.onStart();
+ checkLifecycle(ON_RESTART);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "RESUME lauchpad "
+ + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+ checkLifecycle(ON_RESUME);
+
+ if (!mStarted) {
+ mStarted = true;
+
+ mHandler.postDelayed(mTimeout, 5 * 1000);
+
+ String action = getIntent().getAction();
+
+ sCallingTest.startTiming(true);
+
+ if (LAUNCH.equals(action)) {
+ Intent intent = getIntent();
+ intent.setFlags(0);
+ intent.setComponent((ComponentName)
+ intent.getParcelableExtra("component"));
+ //System.out.println("*** Launchpad is starting: comp=" + intent.component);
+ startActivityForResult(intent, LAUNCHED_RESULT);
+ } else if (FORWARD_RESULT.equals(action)) {
+ Intent intent = getIntent();
+ intent.setFlags(0);
+ intent.setClass(this, LocalScreen.class);
+ startActivityForResult(intent, FORWARDED_RESULT);
+ } else if (BAD_PARCELABLE.equals(action)) {
+ mBadParcelable = true;
+ Intent intent = getIntent();
+ intent.setFlags(0);
+ intent.setClass(this, LocalScreen.class);
+ startActivityForResult(intent, LAUNCHED_RESULT);
+ } else if (BROADCAST_REGISTERED.equals(action)) {
+ setExpectedReceivers(new String[]{RECEIVER_REG});
+ registerMyReceiver(new IntentFilter(BROADCAST_REGISTERED));
+ sCallingTest.addIntermediate("after-register");
+ sendBroadcast(makeBroadcastIntent(BROADCAST_REGISTERED));
+ } else if (BROADCAST_LOCAL.equals(action)) {
+ setExpectedReceivers(new String[]{RECEIVER_LOCAL});
+ sendBroadcast(makeBroadcastIntent(BROADCAST_LOCAL));
+ } else if (BROADCAST_REMOTE.equals(action)) {
+ setExpectedReceivers(new String[]{RECEIVER_REMOTE});
+ sendBroadcast(makeBroadcastIntent(BROADCAST_REMOTE));
+ } else if (BROADCAST_ALL.equals(action)) {
+ setExpectedReceivers(new String[]{
+ RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL});
+ registerMyReceiver(new IntentFilter(BROADCAST_ALL));
+ sCallingTest.addIntermediate("after-register");
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+ } else if (BROADCAST_MULTI.equals(action)) {
+ setExpectedReceivers(new String[]{
+ RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL,
+ RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL,
+ RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL,
+ RECEIVER_LOCAL, RECEIVER_REMOTE,
+ RECEIVER_LOCAL, RECEIVER_REMOTE,
+ RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL,
+ RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL,
+ RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL,
+ RECEIVER_REMOTE, RECEIVER_LOCAL,
+ RECEIVER_REMOTE, RECEIVER_LOCAL});
+ registerMyReceiver(new IntentFilter(BROADCAST_ALL));
+ sCallingTest.addIntermediate("after-register");
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_LOCAL), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REMOTE), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_LOCAL), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REMOTE), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REPEAT), null);
+ } else if (BROADCAST_ABORT.equals(action)) {
+ setExpectedReceivers(new String[]{
+ RECEIVER_REMOTE, RECEIVER_ABORT});
+ registerMyReceiver(new IntentFilter(BROADCAST_ABORT));
+ sCallingTest.addIntermediate("after-register");
+ sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ABORT), null);
+ } else if (BROADCAST_STICKY1.equals(action)) {
+ setExpectedReceivers(new String[]{RECEIVER_REG});
+ setExpectedData(new String[]{DATA_1});
+ registerMyReceiver(new IntentFilter(BROADCAST_STICKY1));
+ sCallingTest.addIntermediate("after-register");
+ } else if (BROADCAST_STICKY2.equals(action)) {
+ setExpectedReceivers(new String[]{RECEIVER_REG, RECEIVER_REG});
+ setExpectedData(new String[]{DATA_1, DATA_2});
+ IntentFilter filter = new IntentFilter(BROADCAST_STICKY1);
+ filter.addAction(BROADCAST_STICKY2);
+ registerMyReceiver(filter);
+ sCallingTest.addIntermediate("after-register");
+ }
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle icicle) {
+ super.onSaveInstanceState(icicle);
+ checkLifecycle(ON_FREEZE);
+ if (mBadParcelable) {
+ icicle.putParcelable("baddy", new MyBadParcelable());
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "PAUSE lauchpad "
+ + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+ checkLifecycle(ON_PAUSE);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "STOP lauchpad "
+ + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+ checkLifecycle(ON_STOP);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode,
+ Intent data) {
+ switch (requestCode) {
+ case LAUNCHED_RESULT:
+ sCallingTest.finishTiming(true);
+ finishWithResult(resultCode, data);
+ break;
+ case FORWARDED_RESULT:
+ sCallingTest.finishTiming(true);
+ if (RETURNED_RESULT.equals(data.getAction())) {
+ finishWithResult(resultCode, data);
+ } else {
+ finishWithResult(RESULT_CANCELED, (new Intent()).setAction(
+ "Bad data returned: " + data));
+ }
+ break;
+ default:
+ sCallingTest.finishTiming(true);
+ finishWithResult(RESULT_CANCELED, (new Intent()).setAction(
+ "Unexpected request code: " + requestCode));
+ break;
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "DESTROY lauchpad "
+ + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+ checkLifecycle(ON_DESTROY);
+ sCallingTest.activityFinished(mResultCode, mData, mResultStack);
+ }
+
+ private void setExpectedLifecycle(String[] lifecycle) {
+ mExpectedLifecycle = lifecycle;
+ mNextLifecycle = 0;
+ }
+
+ private void checkLifecycle(String where) {
+ if (mExpectedLifecycle == null) return;
+
+ if (mNextLifecycle >= mExpectedLifecycle.length) {
+ finishBad("Activity lifecycle incorrect: received " + where
+ + " but don't expect any more calls");
+ mExpectedLifecycle = null;
+ return;
+ }
+ if (!mExpectedLifecycle[mNextLifecycle].equals(where)) {
+ finishBad("Activity lifecycle incorrect: received " + where
+ + " but expected " + mExpectedLifecycle[mNextLifecycle]
+ + " at " + mNextLifecycle);
+ mExpectedLifecycle = null;
+ return;
+ }
+
+ mNextLifecycle++;
+
+ if (mNextLifecycle >= mExpectedLifecycle.length) {
+ setTestResult(RESULT_OK, null);
+ return;
+ }
+
+ String next = mExpectedLifecycle[mNextLifecycle];
+ if (where.equals(ON_DESTROY)) {
+ finishBad("Activity lifecycle incorrect: received " + where
+ + " but expected more actions (next is " + next + ")");
+ mExpectedLifecycle = null;
+ return;
+ } else if (next.equals(DO_FINISH)) {
+ mNextLifecycle++;
+ if (mNextLifecycle >= mExpectedLifecycle.length) {
+ setTestResult(RESULT_OK, null);
+ }
+ if (!isFinishing()) {
+ finish();
+ }
+ } else if (next.equals(DO_LOCAL_SCREEN)) {
+ mNextLifecycle++;
+ Intent intent = new Intent(TestedScreen.WAIT_BEFORE_FINISH);
+ intent.setClass(this, LocalScreen.class);
+ startActivity(intent);
+ } else if (next.equals(DO_LOCAL_DIALOG)) {
+ mNextLifecycle++;
+ Intent intent = new Intent(TestedScreen.WAIT_BEFORE_FINISH);
+ intent.setClass(this, LocalDialog.class);
+ startActivity(intent);
+ }
+ }
+
+ private void setExpectedReceivers(String[] receivers) {
+ mExpectedReceivers = receivers;
+ mNextReceiver = 0;
+ }
+
+ private void setExpectedData(String[] data) {
+ mExpectedData = data;
+ mReceivedData = new boolean[data.length];
+ }
+
+ private Intent makeBroadcastIntent(String action) {
+ Intent intent = new Intent(action, null);
+ intent.putExtra("caller", mCallTarget);
+ return intent;
+ }
+
+ private void finishGood() {
+ finishWithResult(RESULT_OK, null);
+ }
+
+ private void finishBad(String error) {
+ finishWithResult(RESULT_CANCELED, (new Intent()).setAction(error));
+ }
+
+ private void finishWithResult(int resultCode, Intent data) {
+ setTestResult(resultCode, data);
+ finish();
+ }
+
+ private void setTestResult(int resultCode, Intent data) {
+ mHandler.removeCallbacks(mTimeout);
+ unregisterMyReceiver();
+ mResultCode = resultCode;
+ mData = data;
+ mResultStack = new RuntimeException("Original error was here");
+ mResultStack.fillInStackTrace();
+ }
+
+ private void registerMyReceiver(IntentFilter filter) {
+ mReceiverRegistered = true;
+ //System.out.println("Registering: " + mReceiver);
+ registerReceiver(mReceiver, filter);
+ }
+
+ private void unregisterMyReceiver() {
+ if (mReceiverRegistered) {
+ mReceiverRegistered = false;
+ //System.out.println("Unregistering: " + mReceiver);
+ unregisterReceiver(mReceiver);
+ }
+ }
+
+ private Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ }
+ };
+
+ static final int GOT_RECEIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
+ static final int ERROR_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 1;
+
+ private Binder mCallTarget = new Binder() {
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
+ data.setDataPosition(0);
+ data.enforceInterface(LaunchpadActivity.LAUNCH);
+ if (code == GOT_RECEIVE_TRANSACTION) {
+ String name = data.readString();
+ gotReceive(name, null);
+ return true;
+ } else if (code == ERROR_TRANSACTION) {
+ finishBad(data.readString());
+ return true;
+ }
+ return false;
+ }
+ };
+
+ private final void gotReceive(String name, Intent intent) {
+ synchronized (this) {
+
+ //System.out.println("Got receive: " + name);
+ //System.out.println(mNextReceiver + " in " + mExpectedReceivers);
+ //new RuntimeException("stack").printStackTrace();
+
+ sCallingTest.addIntermediate(mNextReceiver + "-" + name);
+
+ if (mExpectedData != null) {
+ int n = mExpectedData.length;
+ int i;
+ boolean prev = false;
+ for (i = 0; i < n; i++) {
+ if (mExpectedData[i].equals(intent.getStringExtra("test"))) {
+ if (mReceivedData[i]) {
+ prev = true;
+ continue;
+ }
+ mReceivedData[i] = true;
+ break;
+ }
+ }
+ if (i >= n) {
+ if (prev) {
+ finishBad("Receive got data too many times: "
+ + intent.getStringExtra("test"));
+ } else {
+ finishBad("Receive got unexpected data: "
+ + intent.getStringExtra("test"));
+ }
+ return;
+ }
+ }
+
+ if (mNextReceiver >= mExpectedReceivers.length) {
+ finishBad("Got too many onReceiveIntent() calls!");
+// System.out.println("Too many intents received: now at "
+// + mNextReceiver + ", expect list: "
+// + Arrays.toString(mExpectedReceivers));
+ } else if (!mExpectedReceivers[mNextReceiver].equals(name)) {
+ finishBad("Receive out of order: got " + name + " but expected "
+ + mExpectedReceivers[mNextReceiver] + " at "
+ + mNextReceiver);
+ } else {
+ mNextReceiver++;
+ if (mNextReceiver == mExpectedReceivers.length) {
+ mHandler.post(mUnregister);
+ }
+ }
+
+ }
+ }
+
+ private Runnable mUnregister = new Runnable() {
+ public void run() {
+ if (mReceiverRegistered) {
+ sCallingTest.addIntermediate("before-unregister");
+ unregisterMyReceiver();
+ }
+ sCallingTest.finishTiming(true);
+ finishGood();
+ }
+ };
+
+ private Runnable mTimeout = new Runnable() {
+ public void run() {
+ Log.i("foo", "**** TIMEOUT");
+ String msg = "Timeout";
+ if (mExpectedReceivers != null
+ && mNextReceiver < mExpectedReceivers.length) {
+ msg = msg + " waiting for " + mExpectedReceivers[mNextReceiver];
+ }
+ finishBad(msg);
+ }
+ };
+
+ private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ //System.out.println("Receive in: " + this + ": " + intent);
+ gotReceive(RECEIVER_REG, intent);
+ }
+ };
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/LaunchpadTabActivity.java b/core/tests/coretests/src/android/app/activity/LaunchpadTabActivity.java
new file mode 100644
index 0000000..79b860f
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LaunchpadTabActivity.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.app.TabActivity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.TabHost;
+
+public class LaunchpadTabActivity extends TabActivity {
+ public LaunchpadTabActivity() {
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ Intent tabIntent = new Intent(getIntent());
+ tabIntent.setComponent((ComponentName)tabIntent.getParcelableExtra("tab"));
+
+ TabHost th = getTabHost();
+ TabHost.TabSpec ts = th.newTabSpec("1");
+ ts.setIndicator("One");
+ ts.setContent(tabIntent);
+ th.addTab(ts);
+ }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/LifecycleTest.java b/core/tests/coretests/src/android/app/activity/LifecycleTest.java
new file mode 100644
index 0000000..768a9a4
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LifecycleTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.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 {
+ private Intent mTopIntent;
+ private Intent mTabIntent;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mTopIntent = mIntent;
+ mTabIntent = new Intent(mContext, LaunchpadTabActivity.class);
+ mTabIntent.putExtra("tab", new ComponentName(mContext,
+ LaunchpadActivity.class));
+ }
+
+ @LargeTest
+ public void testBasic() throws Exception {
+ mIntent = mTopIntent;
+ runLaunchpad(LaunchpadActivity.LIFECYCLE_BASIC);
+ }
+
+ //Suppressing until 1285425 is fixed.
+ @Suppress
+ public void testTabBasic() throws Exception {
+ mIntent = mTabIntent;
+ runLaunchpad(LaunchpadActivity.LIFECYCLE_BASIC);
+ }
+
+ //Marking flaky until bug 1164344 is fixed.
+ // @FlakyTest(tolerance=2)
+ // @LargeTest
+ public void testScreen() throws Exception {
+ mIntent = mTopIntent;
+ runLaunchpad(LaunchpadActivity.LIFECYCLE_SCREEN);
+ }
+
+ //Marking flaky until bug 1164344 is fixed.
+ //@FlakyTest(tolerance=2)
+ //Suppressing until 1285425 is fixed.
+ @Suppress
+ public void testTabScreen() throws Exception {
+ mIntent = mTabIntent;
+ runLaunchpad(LaunchpadActivity.LIFECYCLE_SCREEN);
+ }
+
+ //flaky test, removing from large suite until 1866891 is fixed
+ //@LargeTest
+ public void testDialog() throws Exception {
+ mIntent = mTopIntent;
+ runLaunchpad(LaunchpadActivity.LIFECYCLE_DIALOG);
+ }
+
+ //Suppressing until 1285425 is fixed.
+ @Suppress
+ public void testTabDialog() throws Exception {
+ mIntent = mTabIntent;
+ runLaunchpad(LaunchpadActivity.LIFECYCLE_DIALOG);
+ }
+
+ @MediumTest
+ public void testFinishCreate() throws Exception {
+ mIntent = mTopIntent;
+ runLaunchpad(LaunchpadActivity.LIFECYCLE_FINISH_CREATE);
+ }
+
+ //Suppressing until 1285425 is fixed.
+ @Suppress
+ public void testTabFinishCreate() throws Exception {
+ mIntent = mTabIntent;
+ runLaunchpad(LaunchpadActivity.LIFECYCLE_FINISH_CREATE);
+ }
+
+ @MediumTest
+ public void testFinishStart() throws Exception {
+ mIntent = mTopIntent;
+ runLaunchpad(LaunchpadActivity.LIFECYCLE_FINISH_START);
+ }
+
+ //Suppressing until 1285425 is fixed.
+ @Suppress
+ public void testTabFinishStart() throws Exception {
+ mIntent = mTabIntent;
+ runLaunchpad(LaunchpadActivity.LIFECYCLE_FINISH_START);
+ }
+}
diff --git a/core/tests/coretests/src/android/app/activity/LocalActivity.java b/core/tests/coretests/src/android/app/activity/LocalActivity.java
new file mode 100644
index 0000000..01f1fb6
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LocalActivity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import java.util.Map;
+
+import android.app.Activity;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+
+public class LocalActivity extends TestedActivity
+{
+ public LocalActivity()
+ {
+ }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java b/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java
new file mode 100644
index 0000000..2120a1d
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Parcel;
+
+class LocalDeniedReceiver extends BroadcastReceiver {
+ public LocalDeniedReceiver() {
+ }
+
+ public void onReceive(Context context, Intent intent) {
+ try {
+ IBinder caller = intent.getIBinderExtra("caller");
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(LaunchpadActivity.LAUNCH);
+ data.writeString(BroadcastTest.RECEIVER_LOCAL);
+ caller.transact(BroadcastTest.GOT_RECEIVE_TRANSACTION, data, null, 0);
+ data.recycle();
+ } catch (RemoteException ex) {
+ }
+ }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/LocalDeniedService.java b/core/tests/coretests/src/android/app/activity/LocalDeniedService.java
new file mode 100644
index 0000000..3bdac22
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LocalDeniedService.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+public class LocalDeniedService extends LocalService
+{
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/LocalDialog.java b/core/tests/coretests/src/android/app/activity/LocalDialog.java
new file mode 100644
index 0000000..c92fa43
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LocalDialog.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import java.util.Map;
+
+import android.app.Activity;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+
+public class LocalDialog extends TestedScreen
+{
+ public LocalDialog()
+ {
+ }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/LocalGrantedReceiver.java b/core/tests/coretests/src/android/app/activity/LocalGrantedReceiver.java
new file mode 100644
index 0000000..c9e6ab4
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LocalGrantedReceiver.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Parcel;
+
+public class LocalGrantedReceiver extends BroadcastReceiver {
+ public LocalGrantedReceiver() {
+ }
+
+ public void onReceive(Context context, Intent intent) {
+ try {
+ IBinder caller = intent.getIBinderExtra("caller");
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(LaunchpadActivity.LAUNCH);
+ data.writeString(BroadcastTest.RECEIVER_LOCAL);
+ caller.transact(BroadcastTest.GOT_RECEIVE_TRANSACTION, data, null, 0);
+ data.recycle();
+ } catch (RemoteException ex) {
+ }
+ }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/LocalGrantedService.java b/core/tests/coretests/src/android/app/activity/LocalGrantedService.java
new file mode 100644
index 0000000..7ab0fb4
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LocalGrantedService.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+public class LocalGrantedService extends LocalService
+{
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/LocalProvider.java b/core/tests/coretests/src/android/app/activity/LocalProvider.java
new file mode 100644
index 0000000..085e622
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LocalProvider.java
@@ -0,0 +1,163 @@
+/*
+ * 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.app.activity;
+
+import android.content.UriMatcher;
+import android.content.*;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.util.Config;
+import android.util.Log;
+
+/** Simple test provider that runs in the local process. */
+public class LocalProvider extends ContentProvider {
+ private static final String TAG = "LocalProvider";
+
+ private SQLiteOpenHelper mOpenHelper;
+
+ private static final int DATA = 1;
+ private static final int DATA_ID = 2;
+ private static final UriMatcher sURLMatcher = new UriMatcher(
+ UriMatcher.NO_MATCH);
+
+ static {
+ sURLMatcher.addURI("*", "data", DATA);
+ sURLMatcher.addURI("*", "data/#", DATA_ID);
+ }
+
+ private static class DatabaseHelper extends SQLiteOpenHelper {
+ private static final String DATABASE_NAME = "local.db";
+ private static final int DATABASE_VERSION = 1;
+
+ public DatabaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE data (" +
+ "_id INTEGER PRIMARY KEY," +
+ "text TEXT, " +
+ "integer INTEGER);");
+
+ // insert alarms
+ db.execSQL("INSERT INTO data (text, integer) VALUES ('first data', 100);");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) {
+ Log.w(TAG, "Upgrading test database from version " +
+ oldVersion + " to " + currentVersion +
+ ", which will destroy all old data");
+ db.execSQL("DROP TABLE IF EXISTS data");
+ onCreate(db);
+ }
+ }
+
+
+ public LocalProvider() {
+ }
+
+ @Override
+ public boolean onCreate() {
+ mOpenHelper = new DatabaseHelper(getContext());
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri url, String[] projectionIn, String selection,
+ String[] selectionArgs, String sort) {
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+
+ // Generate the body of the query
+ int match = sURLMatcher.match(url);
+ switch (match) {
+ case DATA:
+ qb.setTables("data");
+ break;
+ case DATA_ID:
+ qb.setTables("data");
+ qb.appendWhere("_id=");
+ qb.appendWhere(url.getPathSegments().get(1));
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown URL " + url);
+ }
+
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ Cursor ret = qb.query(db, projectionIn, selection, selectionArgs,
+ null, null, sort);
+
+ if (ret == null) {
+ if (Config.LOGD) Log.d(TAG, "Alarms.query: failed");
+ } else {
+ ret.setNotificationUri(getContext().getContentResolver(), url);
+ }
+
+ return ret;
+ }
+
+ @Override
+ public String getType(Uri url) {
+ int match = sURLMatcher.match(url);
+ switch (match) {
+ case DATA:
+ return "vnd.android.cursor.dir/vnd.google.unit_tests.local";
+ case DATA_ID:
+ return "vnd.android.cursor.item/vnd.google.unit_tests.local";
+ default:
+ throw new IllegalArgumentException("Unknown URL");
+ }
+ }
+
+ @Override
+ public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
+ int count;
+ long rowId = 0;
+ int match = sURLMatcher.match(url);
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ switch (match) {
+ case DATA_ID: {
+ String segment = url.getPathSegments().get(1);
+ rowId = Long.parseLong(segment);
+ count = db.update("data", values, "_id=" + rowId, null);
+ break;
+ }
+ default: {
+ throw new UnsupportedOperationException(
+ "Cannot update URL: " + url);
+ }
+ }
+ if (Config.LOGD) Log.d(TAG, "*** notifyChange() rowId: " + rowId);
+ getContext().getContentResolver().notifyChange(url, null);
+ return count;
+ }
+
+
+ @Override
+ public Uri insert(Uri url, ContentValues initialValues) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri url, String where, String[] whereArgs) {
+ throw new UnsupportedOperationException("delete not supported");
+ }
+}
diff --git a/core/tests/coretests/src/android/app/activity/LocalReceiver.java b/core/tests/coretests/src/android/app/activity/LocalReceiver.java
new file mode 100644
index 0000000..bfd543f
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LocalReceiver.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ReceiverCallNotAllowedException;
+import android.content.ServiceConnection;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Parcel;
+
+public class LocalReceiver extends BroadcastReceiver {
+ public LocalReceiver() {
+ }
+
+ public void onReceive(Context context, Intent intent) {
+ String resultString = LaunchpadActivity.RECEIVER_LOCAL;
+ if (BroadcastTest.BROADCAST_FAIL_REGISTER.equals(intent.getAction())) {
+ resultString = "Successfully registered, but expected it to fail";
+ try {
+ context.registerReceiver(this, new IntentFilter("foo.bar"));
+ context.unregisterReceiver(this);
+ } catch (ReceiverCallNotAllowedException e) {
+ //resultString = "This is the correct behavior but not yet implemented";
+ resultString = LaunchpadActivity.RECEIVER_LOCAL;
+ }
+ } else if (BroadcastTest.BROADCAST_FAIL_BIND.equals(intent.getAction())) {
+ resultString = "Successfully bound to service, but expected it to fail";
+ try {
+ ServiceConnection sc = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ }
+ };
+ context.bindService(new Intent(context, LocalService.class), sc, 0);
+ context.unbindService(sc);
+ } catch (ReceiverCallNotAllowedException e) {
+ //resultString = "This is the correct behavior but not yet implemented";
+ resultString = LaunchpadActivity.RECEIVER_LOCAL;
+ }
+ } else if (LaunchpadActivity.BROADCAST_REPEAT.equals(intent.getAction())) {
+ Intent newIntent = new Intent(intent);
+ newIntent.setAction(LaunchpadActivity.BROADCAST_LOCAL);
+ context.sendOrderedBroadcast(newIntent, null);
+ }
+ try {
+ IBinder caller = intent.getIBinderExtra("caller");
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(LaunchpadActivity.LAUNCH);
+ data.writeString(resultString);
+ caller.transact(LaunchpadActivity.GOT_RECEIVE_TRANSACTION, data, null, 0);
+ data.recycle();
+ } catch (RemoteException ex) {
+ }
+ }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/LocalScreen.java b/core/tests/coretests/src/android/app/activity/LocalScreen.java
new file mode 100644
index 0000000..f7c8c33
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LocalScreen.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import java.util.Map;
+
+import android.app.Activity;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+
+public class LocalScreen extends TestedScreen
+{
+ public LocalScreen()
+ {
+ }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/LocalService.java b/core/tests/coretests/src/android/app/activity/LocalService.java
new file mode 100644
index 0000000..c31ca4b
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LocalService.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.util.Log;
+
+public class LocalService extends Service {
+ private final IBinder mBinder = new Binder() {
+
+ @Override
+ protected boolean onTransact(int code, Parcel data, Parcel reply,
+ int flags) throws RemoteException {
+ if (code == ServiceTest.SET_REPORTER_CODE) {
+ data.enforceInterface(ServiceTest.SERVICE_LOCAL);
+ mReportObject = data.readStrongBinder();
+ return true;
+ } else {
+ return super.onTransact(code, data, reply, flags);
+ }
+ }
+
+ };
+
+ private IBinder mReportObject;
+ private int mStartCount = 1;
+
+ public LocalService() {
+ }
+
+ @Override
+ public void onStart(Intent intent, int startId) {
+ //Log.i("LocalService", "onStart: " + intent);
+ if (intent.getExtras() != null) {
+ mReportObject = intent.getExtras().getIBinder(ServiceTest.REPORT_OBJ_NAME);
+ if (mReportObject != null) {
+ try {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(ServiceTest.SERVICE_LOCAL);
+ data.writeInt(mStartCount);
+ mStartCount++;
+ mReportObject.transact(
+ ServiceTest.STARTED_CODE, data, null, 0);
+ data.recycle();
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.i("LocalService", "onDestroy: mReportObject=" + mReportObject);
+ if (mReportObject != null) {
+ try {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(ServiceTest.SERVICE_LOCAL);
+ mReportObject.transact(
+ ServiceTest.DESTROYED_CODE, data, null, 0);
+ data.recycle();
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ Log.i("LocalService", "onBind: " + intent);
+ return mBinder;
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ Log.i("LocalService", "onUnbind: " + intent);
+ if (mReportObject != null) {
+ try {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(ServiceTest.SERVICE_LOCAL);
+ mReportObject.transact(
+ ServiceTest.UNBIND_CODE, data, null, 0);
+ data.recycle();
+ } catch (RemoteException e) {
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void onRebind(Intent intent) {
+ Log.i("LocalService", "onUnbind: " + intent);
+ if (mReportObject != null) {
+ try {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(ServiceTest.SERVICE_LOCAL);
+ mReportObject.transact(
+ ServiceTest.REBIND_CODE, data, null, 0);
+ data.recycle();
+ } catch (RemoteException e) {
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/app/activity/MetaDataTest.java b/core/tests/coretests/src/android/app/activity/MetaDataTest.java
new file mode 100644
index 0000000..214bc91
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/MetaDataTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ServiceInfo;
+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;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * Tests for meta-data associated with application components.
+ */
+public class MetaDataTest extends AndroidTestCase {
+
+ private void checkMetaData(ComponentName cn, PackageItemInfo ci)
+ throws IOException, XmlPullParserException {
+ assertNotNull("Unable to find component " + cn, ci);
+
+ Bundle md = ci.metaData;
+ assertNotNull("No meta data found", md);
+
+ assertEquals("foo", md.getString("com.android.frameworks.coretests.string"));
+ assertTrue(md.getBoolean("com.android.frameworks.coretests.boolean"));
+ assertEquals(100, md.getInt("com.android.frameworks.coretests.integer"));
+ assertEquals(0xff000000, md.getInt("com.android.frameworks.coretests.color"));
+
+ assertEquals((double) 1001,
+ Math.floor(md.getFloat("com.android.frameworks.coretests.float") * 10 + .5));
+
+ assertEquals(R.xml.metadata, md.getInt("com.android.frameworks.coretests.reference"));
+
+ XmlResourceParser xml = ci.loadXmlMetaData(mContext.getPackageManager(),
+ "com.android.frameworks.coretests.reference");
+ assertNotNull(xml);
+
+ int type;
+ while ((type = xml.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ }
+ assertEquals(XmlPullParser.START_TAG, type);
+ assertEquals("thedata", xml.getName());
+
+ // method 1: direct access
+ final String rawAttr = xml.getAttributeValue(null, "rawText");
+ assertEquals("some raw text", rawAttr);
+
+ // method 2: direct access of typed value
+ final int rawColorIntAttr = xml.getAttributeIntValue(null, "rawColor", 0);
+ assertEquals(0xffffff00, rawColorIntAttr);
+ final String rawColorStrAttr = xml.getAttributeValue(null, "rawColor");
+ assertEquals("#ffffff00", rawColorStrAttr);
+
+ // method 2: direct access of resource attribute
+ final String nameSpace = "http://schemas.android.com/apk/res/android";
+ final int colorIntAttr = xml.getAttributeIntValue(nameSpace, "color", 0);
+ assertEquals(0xffff0000, colorIntAttr);
+ final String colorStrAttr = xml.getAttributeValue(nameSpace, "color");
+ assertEquals("#ffff0000", colorStrAttr);
+
+ // method 3: styled access (borrowing an attr from view system here)
+ TypedArray a = mContext.obtainStyledAttributes(xml,
+ android.R.styleable.TextView);
+ String styledAttr = a.getString(android.R.styleable.TextView_text);
+ assertEquals("text", styledAttr);
+ a.recycle();
+
+ xml.close();
+ }
+
+ @SmallTest
+ public void testActivityWithData() throws Exception {
+ ComponentName cn = new ComponentName(mContext, LocalActivity.class);
+ ActivityInfo ai = mContext.getPackageManager().getActivityInfo(
+ cn, PackageManager.GET_META_DATA);
+
+ checkMetaData(cn, ai);
+
+ ai = mContext.getPackageManager().getActivityInfo(cn, 0);
+
+ assertNull("Meta data returned when not requested", ai.metaData);
+ }
+
+ @SmallTest
+ public void testReceiverWithData() throws Exception {
+ ComponentName cn = new ComponentName(mContext, LocalReceiver.class);
+ ActivityInfo ai = mContext.getPackageManager().getReceiverInfo(
+ cn, PackageManager.GET_META_DATA);
+
+ checkMetaData(cn, ai);
+
+ ai = mContext.getPackageManager().getReceiverInfo(cn, 0);
+
+ assertNull("Meta data returned when not requested", ai.metaData);
+ }
+
+ @SmallTest
+ public void testServiceWithData() throws Exception {
+ ComponentName cn = new ComponentName(mContext, LocalService.class);
+ ServiceInfo si = mContext.getPackageManager().getServiceInfo(
+ cn, PackageManager.GET_META_DATA);
+
+ checkMetaData(cn, si);
+
+ si = mContext.getPackageManager().getServiceInfo(cn, 0);
+
+ assertNull("Meta data returned when not requested", si.metaData);
+ }
+
+ @MediumTest
+ public void testProviderWithData() throws Exception {
+ ComponentName cn = new ComponentName(mContext, LocalProvider.class);
+ ProviderInfo pi = mContext.getPackageManager().resolveContentProvider(
+ "com.android.frameworks.coretests.LocalProvider",
+ PackageManager.GET_META_DATA);
+ checkMetaData(cn, pi);
+
+ pi = mContext.getPackageManager().resolveContentProvider(
+ "com.android.frameworks.coretests.LocalProvider", 0);
+
+ assertNull("Meta data returned when not requested", pi.metaData);
+ }
+
+ @SmallTest
+ public void testPermissionWithData() throws Exception {
+ ComponentName cn = new ComponentName("foo",
+ "com.android.frameworks.coretests.permission.TEST_GRANTED");
+ PermissionInfo pi = mContext.getPackageManager().getPermissionInfo(
+ cn.getClassName(), PackageManager.GET_META_DATA);
+ checkMetaData(cn, pi);
+
+ pi = mContext.getPackageManager().getPermissionInfo(
+ cn.getClassName(), 0);
+
+ assertNull("Meta data returned when not requested", pi.metaData);
+ }
+}
+
+
diff --git a/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java b/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java
new file mode 100644
index 0000000..7c89346
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Parcel;
+
+class RemoteDeniedReceiver extends BroadcastReceiver {
+ public RemoteDeniedReceiver() {
+ }
+
+ public void onReceive(Context context, Intent intent) {
+ try {
+ IBinder caller = intent.getIBinderExtra("caller");
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(LaunchpadActivity.LAUNCH);
+ data.writeString(BroadcastTest.RECEIVER_REMOTE);
+ caller.transact(BroadcastTest.GOT_RECEIVE_TRANSACTION, data, null, 0);
+ data.recycle();
+ } catch (RemoteException ex) {
+ }
+ }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/RemoteGrantedReceiver.java b/core/tests/coretests/src/android/app/activity/RemoteGrantedReceiver.java
new file mode 100644
index 0000000..0eca8f7
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/RemoteGrantedReceiver.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Parcel;
+
+public class RemoteGrantedReceiver extends BroadcastReceiver {
+ public RemoteGrantedReceiver() {
+ }
+
+ public void onReceive(Context context, Intent intent) {
+ try {
+ IBinder caller = intent.getIBinderExtra("caller");
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(LaunchpadActivity.LAUNCH);
+ data.writeString(BroadcastTest.RECEIVER_REMOTE);
+ caller.transact(BroadcastTest.GOT_RECEIVE_TRANSACTION, data, null, 0);
+ data.recycle();
+ } catch (RemoteException ex) {
+ }
+ }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/RemoteReceiver.java b/core/tests/coretests/src/android/app/activity/RemoteReceiver.java
new file mode 100644
index 0000000..9608fc4
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/RemoteReceiver.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Parcel;
+
+public class RemoteReceiver extends BroadcastReceiver
+{
+ public RemoteReceiver()
+ {
+ }
+
+ public void onReceive(Context context, Intent intent)
+ {
+ if (LaunchpadActivity.BROADCAST_REPEAT.equals(intent.getAction())) {
+ Intent newIntent = new Intent(intent);
+ newIntent.setAction(LaunchpadActivity.BROADCAST_REMOTE);
+ context.sendOrderedBroadcast(newIntent, null);
+ }
+ try {
+ IBinder caller = intent.getIBinderExtra("caller");
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(LaunchpadActivity.LAUNCH);
+ data.writeString(LaunchpadActivity.RECEIVER_REMOTE);
+ caller.transact(LaunchpadActivity.GOT_RECEIVE_TRANSACTION, data, null, 0);
+ data.recycle();
+ } catch (RemoteException ex) {
+ }
+ }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/RemoteSubActivityScreen.java b/core/tests/coretests/src/android/app/activity/RemoteSubActivityScreen.java
new file mode 100644
index 0000000..e969d10
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/RemoteSubActivityScreen.java
@@ -0,0 +1,59 @@
+/* //device/apps/AndroidTests/src/com.android.unit_tests/activity/TestedScreen.java
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.app.activity;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Process;
+import android.util.Log;
+
+public class RemoteSubActivityScreen extends SubActivityScreen {
+ Handler mHandler = new Handler();
+ boolean mFirst = false;
+
+ public RemoteSubActivityScreen() {
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ // We are running in a remote process, so want to have the sub-activity
+ // sending the result back in the original process.
+ Intent intent = getIntent();
+ intent.setClass(this, SubActivityScreen.class);
+
+ super.onCreate(icicle);
+
+ boolean kill = intent.getBooleanExtra("kill", false);
+ //Log.i("foo", "RemoteSubActivityScreen pid=" + Process.myPid()
+ // + " kill=" + kill);
+
+ if (kill) {
+ // After finishing initialization, kill the process! But only if
+ // this is the first time...
+ if (icicle == null) {
+ mHandler.post(new Runnable() {
+ public void run() {
+ handleBeforeStopping();
+ Process.killProcess(Process.myPid());
+ }
+ });
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/app/activity/ResultReceiver.java b/core/tests/coretests/src/android/app/activity/ResultReceiver.java
new file mode 100644
index 0000000..f7daf2c
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/ResultReceiver.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Bundle;
+
+import java.util.Map;
+
+public class ResultReceiver extends BroadcastReceiver
+{
+ public ResultReceiver()
+ {
+ }
+
+ public void onReceive(Context context, Intent intent)
+ {
+ setResultCode(3);
+ setResultData("bar");
+ Bundle map = getResultExtras(false);
+ map.remove("remove");
+ map.putString("bar", "them");
+ }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/SearchableActivity.java b/core/tests/coretests/src/android/app/activity/SearchableActivity.java
new file mode 100644
index 0000000..e238572
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/SearchableActivity.java
@@ -0,0 +1,30 @@
+/*
+ * 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.app.activity;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class SearchableActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ finish();
+ }
+
+}
diff --git a/core/tests/coretests/src/android/app/activity/ServiceTest.java b/core/tests/coretests/src/android/app/activity/ServiceTest.java
new file mode 100644
index 0000000..d3ae415
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/ServiceTest.java
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.util.Log;
+
+// These test binders purport to support an interface whose canonical
+// interface name is ServiceTest.SERVICE_LOCAL
+// Temporarily suppress, this test is causing unit test suite run to fail
+// TODO: remove this suppress
+@Suppress
+public class ServiceTest extends ActivityTestsBase {
+
+ public static final String SERVICE_LOCAL =
+ "com.android.frameworks.coretests.activity.SERVICE_LOCAL";
+ public static final String SERVICE_LOCAL_GRANTED =
+ "com.android.frameworks.coretests.activity.SERVICE_LOCAL_GRANTED";
+ public static final String SERVICE_LOCAL_DENIED =
+ "com.android.frameworks.coretests.activity.SERVICE_LOCAL_DENIED";
+
+ public static final String REPORT_OBJ_NAME = "report";
+
+ public static final int STARTED_CODE = 1;
+ public static final int DESTROYED_CODE = 2;
+ public static final int SET_REPORTER_CODE = 3;
+ public static final int UNBIND_CODE = 4;
+ public static final int REBIND_CODE = 5;
+
+ public static final int STATE_START_1 = 0;
+ public static final int STATE_START_2 = 1;
+ public static final int STATE_UNBIND = 2;
+ public static final int STATE_DESTROY = 3;
+ public static final int STATE_REBIND = 4;
+ public static final int STATE_UNBIND_ONLY = 5;
+ public int mStartState;
+
+ public IBinder mStartReceiver = new Binder() {
+ @Override
+ protected boolean onTransact(int code, Parcel data, Parcel reply,
+ int flags) throws RemoteException {
+ //Log.i("ServiceTest", "Received code " + code + " in state " + mStartState);
+ if (code == STARTED_CODE) {
+ data.enforceInterface(SERVICE_LOCAL);
+ int count = data.readInt();
+ if (mStartState == STATE_START_1) {
+ if (count == 1) {
+ finishGood();
+ } else {
+ finishBad("onStart() again on an object when it should have been the first time");
+ }
+ } else if (mStartState == STATE_START_2) {
+ if (count == 2) {
+ finishGood();
+ } else {
+ finishBad("onStart() the first time on an object when it should have been the second time");
+ }
+ } else {
+ finishBad("onStart() was called when not expected (state="+mStartState+")");
+ }
+ return true;
+ } else if (code == DESTROYED_CODE) {
+ data.enforceInterface(SERVICE_LOCAL);
+ if (mStartState == STATE_DESTROY) {
+ finishGood();
+ } else {
+ finishBad("onDestroy() was called when not expected (state="+mStartState+")");
+ }
+ return true;
+ } else if (code == UNBIND_CODE) {
+ data.enforceInterface(SERVICE_LOCAL);
+ if (mStartState == STATE_UNBIND) {
+ mStartState = STATE_DESTROY;
+ } else if (mStartState == STATE_UNBIND_ONLY) {
+ finishGood();
+ } else {
+ finishBad("onUnbind() was called when not expected (state="+mStartState+")");
+ }
+ return true;
+ } else if (code == REBIND_CODE) {
+ data.enforceInterface(SERVICE_LOCAL);
+ if (mStartState == STATE_REBIND) {
+ finishGood();
+ } else {
+ finishBad("onRebind() was called when not expected (state="+mStartState+")");
+ }
+ return true;
+ } else {
+ return super.onTransact(code, data, reply, flags);
+ }
+ }
+ };
+
+ public class EmptyConnection implements ServiceConnection {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ }
+ }
+
+ public class TestConnection implements ServiceConnection {
+ private final boolean mExpectDisconnect;
+ private final boolean mSetReporter;
+ private boolean mMonitor;
+ private int mCount;
+
+ public TestConnection(boolean expectDisconnect, boolean setReporter) {
+ mExpectDisconnect = expectDisconnect;
+ mSetReporter = setReporter;
+ mMonitor = !setReporter;
+ }
+
+ void setMonitor(boolean v) {
+ mMonitor = v;
+ }
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (mSetReporter) {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(SERVICE_LOCAL);
+ data.writeStrongBinder(mStartReceiver);
+ try {
+ service.transact(SET_REPORTER_CODE, data, null, 0);
+ } catch (RemoteException e) {
+ finishBad("DeadObjectException when sending reporting object");
+ }
+ data.recycle();
+ }
+
+ if (mMonitor) {
+ mCount++;
+ if (mStartState == STATE_START_1) {
+ if (mCount == 1) {
+ finishGood();
+ } else {
+ finishBad("onServiceConnected() again on an object when it should have been the first time");
+ }
+ } else if (mStartState == STATE_START_2) {
+ if (mCount == 2) {
+ finishGood();
+ } else {
+ finishBad("onServiceConnected() the first time on an object when it should have been the second time");
+ }
+ } else {
+ finishBad("onServiceConnected() called unexpectedly");
+ }
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ if (mMonitor) {
+ if (mStartState == STATE_DESTROY) {
+ if (mExpectDisconnect) {
+ finishGood();
+ } else {
+ finishBad("onServiceDisconnected() when it shouldn't have been");
+ }
+ } else {
+ finishBad("onServiceDisconnected() called unexpectedly");
+ }
+ }
+ }
+ }
+
+ void startExpectResult(Intent service) {
+ startExpectResult(service, new Bundle());
+ }
+
+ void startExpectResult(Intent service, Bundle bundle) {
+ bundle.putIBinder(REPORT_OBJ_NAME, mStartReceiver);
+ boolean success = false;
+ try {
+ //Log.i("foo", "STATE_START_1");
+ mStartState = STATE_START_1;
+ getContext().startService(new Intent(service).putExtras(bundle));
+ waitForResultOrThrow(5 * 1000, "service to start first time");
+ //Log.i("foo", "STATE_START_2");
+ mStartState = STATE_START_2;
+ getContext().startService(new Intent(service).putExtras(bundle));
+ waitForResultOrThrow(5 * 1000, "service to start second time");
+ success = true;
+ } finally {
+ if (!success) {
+ try {
+ getContext().stopService(service);
+ } catch (Exception e) {
+ // eat
+ }
+ }
+ }
+ //Log.i("foo", "STATE_DESTROY");
+ mStartState = STATE_DESTROY;
+ getContext().stopService(service);
+ waitForResultOrThrow(5 * 1000, "service to be destroyed");
+ }
+
+ void startExpectNoPermission(Intent service) {
+ try {
+ getContext().startService(service);
+ fail("Expected security exception when starting " + service);
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ void bindExpectResult(Intent service) {
+ TestConnection conn = new TestConnection(true, false);
+ TestConnection conn2 = new TestConnection(false, false);
+ boolean success = false;
+ try {
+ // Expect to see the TestConnection connected.
+ mStartState = STATE_START_1;
+ getContext().bindService(service, conn, 0);
+ getContext().startService(service);
+ waitForResultOrThrow(5 * 1000, "existing connection to receive service");
+
+ // Expect to see the second TestConnection connected.
+ getContext().bindService(service, conn2, 0);
+ waitForResultOrThrow(5 * 1000, "new connection to receive service");
+
+ getContext().unbindService(conn2);
+ success = true;
+ } finally {
+ if (!success) {
+ try {
+ getContext().stopService(service);
+ getContext().unbindService(conn);
+ getContext().unbindService(conn2);
+ } catch (Exception e) {
+ // eat
+ }
+ }
+ }
+
+ // Expect to see the TestConnection disconnected.
+ mStartState = STATE_DESTROY;
+ getContext().stopService(service);
+ waitForResultOrThrow(5 * 1000, "existing connection to lose service");
+
+ getContext().unbindService(conn);
+
+ conn = new TestConnection(true, true);
+ success = false;
+ try {
+ // Expect to see the TestConnection connected.
+ conn.setMonitor(true);
+ mStartState = STATE_START_1;
+ getContext().bindService(service, conn, 0);
+ getContext().startService(service);
+ waitForResultOrThrow(5 * 1000, "existing connection to receive service");
+
+ success = true;
+ } finally {
+ if (!success) {
+ try {
+ getContext().stopService(service);
+ getContext().unbindService(conn);
+ } catch (Exception e) {
+ // eat
+ }
+ }
+ }
+
+ // Expect to see the service unbind and then destroyed.
+ conn.setMonitor(false);
+ mStartState = STATE_UNBIND;
+ getContext().stopService(service);
+ waitForResultOrThrow(5 * 1000, "existing connection to lose service");
+
+ getContext().unbindService(conn);
+
+ conn = new TestConnection(true, true);
+ success = false;
+ try {
+ // Expect to see the TestConnection connected.
+ conn.setMonitor(true);
+ mStartState = STATE_START_1;
+ getContext().bindService(service, conn, 0);
+ getContext().startService(service);
+ waitForResultOrThrow(5 * 1000, "existing connection to receive service");
+
+ success = true;
+ } finally {
+ if (!success) {
+ try {
+ getContext().stopService(service);
+ getContext().unbindService(conn);
+ } catch (Exception e) {
+ // eat
+ }
+ }
+ }
+
+ // Expect to see the service unbind but not destroyed.
+ conn.setMonitor(false);
+ mStartState = STATE_UNBIND_ONLY;
+ getContext().unbindService(conn);
+ waitForResultOrThrow(5 * 1000, "existing connection to unbind service");
+
+ // Expect to see the service rebound.
+ mStartState = STATE_REBIND;
+ getContext().bindService(service, conn, 0);
+ waitForResultOrThrow(5 * 1000, "existing connection to rebind service");
+
+ // Expect to see the service unbind and then destroyed.
+ mStartState = STATE_UNBIND;
+ getContext().stopService(service);
+ waitForResultOrThrow(5 * 1000, "existing connection to lose service");
+
+ getContext().unbindService(conn);
+ }
+
+ void bindAutoExpectResult(Intent service) {
+ TestConnection conn = new TestConnection(false, true);
+ boolean success = false;
+ try {
+ conn.setMonitor(true);
+ mStartState = STATE_START_1;
+ getContext().bindService(
+ service, conn, Context.BIND_AUTO_CREATE);
+ waitForResultOrThrow(5 * 1000, "connection to start and receive service");
+ success = true;
+ } finally {
+ if (!success) {
+ try {
+ getContext().unbindService(conn);
+ } catch (Exception e) {
+ // eat
+ }
+ }
+ }
+ mStartState = STATE_UNBIND;
+ getContext().unbindService(conn);
+ waitForResultOrThrow(5 * 1000, "disconnecting from service");
+ }
+
+ void bindExpectNoPermission(Intent service) {
+ TestConnection conn = new TestConnection(false, false);
+ try {
+ getContext().bindService(service, conn, Context.BIND_AUTO_CREATE);
+ fail("Expected security exception when binding " + service);
+ } catch (SecurityException e) {
+ // expected
+ } finally {
+ getContext().unbindService(conn);
+ }
+ }
+
+
+ @MediumTest
+ public void testLocalStartClass() throws Exception {
+ startExpectResult(new Intent(getContext(), LocalService.class));
+ }
+
+ @MediumTest
+ public void testLocalStartAction() throws Exception {
+ startExpectResult(new Intent(SERVICE_LOCAL));
+ }
+
+ @MediumTest
+ public void testLocalBindClass() throws Exception {
+ bindExpectResult(new Intent(getContext(), LocalService.class));
+ }
+
+ @MediumTest
+ public void testLocalBindAction() throws Exception {
+ bindExpectResult(new Intent(SERVICE_LOCAL));
+ }
+
+ @MediumTest
+ public void testLocalBindAutoClass() throws Exception {
+ bindAutoExpectResult(new Intent(getContext(), LocalService.class));
+ }
+
+ @MediumTest
+ public void testLocalBindAutoAction() throws Exception {
+ bindAutoExpectResult(new Intent(SERVICE_LOCAL));
+ }
+
+ @MediumTest
+ public void testLocalStartClassPermissionGranted() throws Exception {
+ startExpectResult(new Intent(getContext(), LocalGrantedService.class));
+ }
+
+ @MediumTest
+ public void testLocalStartActionPermissionGranted() throws Exception {
+ startExpectResult(new Intent(SERVICE_LOCAL_GRANTED));
+ }
+
+ @MediumTest
+ public void testLocalBindClassPermissionGranted() throws Exception {
+ bindExpectResult(new Intent(getContext(), LocalGrantedService.class));
+ }
+
+ @MediumTest
+ public void testLocalBindActionPermissionGranted() throws Exception {
+ bindExpectResult(new Intent(SERVICE_LOCAL_GRANTED));
+ }
+
+ @MediumTest
+ public void testLocalBindAutoClassPermissionGranted() throws Exception {
+ bindAutoExpectResult(new Intent(getContext(), LocalGrantedService.class));
+ }
+
+ @MediumTest
+ public void testLocalBindAutoActionPermissionGranted() throws Exception {
+ bindAutoExpectResult(new Intent(SERVICE_LOCAL_GRANTED));
+ }
+
+ @MediumTest
+ public void testLocalStartClassPermissionDenied() throws Exception {
+ startExpectNoPermission(new Intent(getContext(), LocalDeniedService.class));
+ }
+
+ @MediumTest
+ public void testLocalStartActionPermissionDenied() throws Exception {
+ startExpectNoPermission(new Intent(SERVICE_LOCAL_DENIED));
+ }
+
+ @MediumTest
+ public void testLocalBindClassPermissionDenied() throws Exception {
+ bindExpectNoPermission(new Intent(getContext(), LocalDeniedService.class));
+ }
+
+ @MediumTest
+ public void testLocalBindActionPermissionDenied() throws Exception {
+ bindExpectNoPermission(new Intent(SERVICE_LOCAL_DENIED));
+ }
+
+ @MediumTest
+ public void testLocalUnbindTwice() throws Exception {
+ EmptyConnection conn = new EmptyConnection();
+ getContext().bindService(
+ new Intent(SERVICE_LOCAL_GRANTED), conn, 0);
+ getContext().unbindService(conn);
+ try {
+ getContext().unbindService(conn);
+ fail("No exception thrown on second unbind");
+ } catch (IllegalArgumentException e) {
+ //Log.i("foo", "Unbind exception", e);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/app/activity/SetTimeZonePermissionsTest.java b/core/tests/coretests/src/android/app/activity/SetTimeZonePermissionsTest.java
new file mode 100644
index 0000000..41b9547
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/SetTimeZonePermissionsTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.app.activity;
+
+import android.app.AlarmManager;
+import android.content.Context;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import java.util.TimeZone;
+
+public class SetTimeZonePermissionsTest extends AndroidTestCase {
+
+ private String[] mZones;
+ private String mCurrentZone;
+ private AlarmManager mAlarm;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mZones = TimeZone.getAvailableIDs();
+ mCurrentZone = TimeZone.getDefault().getID();
+ mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ }
+
+ /**
+ * Verify that non-system processes cannot set the time zone.
+ */
+ @LargeTest
+ public void testSetTimeZonePermissions() {
+ /**
+ * Attempt to set several predefined time zones, verifying that the system
+ * system default time zone has not actually changed from its prior state
+ * after each attempt.
+ */
+ int max = (mZones.length > 10) ? mZones.length : 10;
+ assertTrue("No system-defined time zones - test invalid", max > 0);
+
+ for (int i = 0; i < max; i++) {
+ String tz = mZones[i];
+ try {
+ mAlarm.setTimeZone(tz);
+ } catch (SecurityException se) {
+ // Expected failure; no need to handle specially since we're
+ // about to assert that the test invariant holds: no change
+ // to the system time zone.
+ }
+
+ String newZone = TimeZone.getDefault().getID();
+ assertEquals("AlarmManager.setTimeZone() succeeded despite lack of permission",
+ mCurrentZone,
+ newZone);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/app/activity/SubActivityScreen.java b/core/tests/coretests/src/android/app/activity/SubActivityScreen.java
new file mode 100644
index 0000000..919c591
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/SubActivityScreen.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class SubActivityScreen extends Activity {
+ static final int NO_RESULT_MODE = 0;
+ static final int RESULT_MODE = 1;
+ static final int PENDING_RESULT_MODE = 2;
+ static final int FINISH_SUB_MODE = 3;
+
+ static final int CHILD_OFFSET = 1000;
+
+ int mMode;
+
+ public SubActivityScreen() {
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ mMode = getIntent().getIntExtra("mode", mMode);
+ //Log.i("foo", "SubActivityScreen pid=" + Process.myPid()
+ // + " mode=" + mMode);
+
+ // Move on to the next thing that will generate a result... but only
+ // if we are being launched for the first time.
+ if (icicle == null) {
+ if (mMode == PENDING_RESULT_MODE) {
+ PendingIntent apr = createPendingResult(1, null,
+ Intent.FILL_IN_ACTION);
+ Intent res = new Intent();
+ res.putExtra("tkey", "tval");
+ res.setAction("test");
+ try {
+ apr.send(this, RESULT_OK, res);
+ } catch (PendingIntent.CanceledException e) {
+ }
+ } else if (mMode < CHILD_OFFSET) {
+ Intent intent = new Intent();
+ intent.setClass(this, SubActivityScreen.class);
+ intent.putExtra("mode", CHILD_OFFSET+mMode);
+ //System.out.println("*** Starting from onStart: " + intent);
+ startActivityForResult(intent, 1);
+ return;
+ }
+ }
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle state) {
+ super.onRestoreInstanceState(state);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ //Log.i("foo", "SubActivityScreen pid=" + Process.myPid() + " onResume");
+
+ if (mMode >= CHILD_OFFSET) {
+ // Wait a little bit, to give our parent time to kill itself
+ // if that is something it is into.
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ setResult(RESULT_CANCELED, (new Intent()).setAction("Interrupted!"));
+ finish();
+ return;
+ }
+ //System.out.println("Resuming sub-activity: mode=" + mMode);
+ switch (mMode-CHILD_OFFSET) {
+ case NO_RESULT_MODE:
+ finish();
+ break;
+ case RESULT_MODE:
+ Intent res = new Intent();
+ res.putExtra("tkey", "tval");
+ res.setAction("test");
+ setResult(RESULT_OK, res);
+ finish();
+ break;
+ case FINISH_SUB_MODE:
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode,
+ Intent data) {
+ //Log.i("foo", "SubActivityScreen pid=" + Process.myPid()
+ // + " onActivityResult: req=" + requestCode
+ // + " res=" + resultCode);
+
+ // Assume success.
+ setResult(RESULT_OK);
+
+ if (requestCode == 1) {
+ switch (mMode) {
+ case NO_RESULT_MODE:
+ case FINISH_SUB_MODE:
+ if (resultCode != RESULT_CANCELED) {
+ setResult(RESULT_CANCELED, (new Intent()).setAction(
+ "Incorrect result code returned: " + resultCode));
+ }
+ break;
+ case RESULT_MODE:
+ case PENDING_RESULT_MODE:
+ if (resultCode != RESULT_OK) {
+ setResult(RESULT_CANCELED, (new Intent()).setAction(
+ "Incorrect result code returned: " + resultCode));
+ } else if (data == null) {
+ setResult(RESULT_CANCELED, (new Intent()).setAction(
+ "null data returned"));
+ } else if (!("test".equals(data.getAction()))) {
+ setResult(RESULT_CANCELED, (new Intent()).setAction(
+ "Incorrect action returned: " + data));
+ } else if (!("tval".equals(data.getStringExtra("tkey")))) {
+ setResult(RESULT_CANCELED, (new Intent()).setAction(
+ "Incorrect extras returned: " + data.getExtras()));
+ }
+ break;
+ }
+ } else {
+ setResult(RESULT_CANCELED, (new Intent()).setAction(
+ "Incorrect request code returned: " + requestCode));
+ }
+
+ finish();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ handleBeforeStopping();
+ }
+
+ public void handleBeforeStopping() {
+ if (mMode == FINISH_SUB_MODE) {
+ finishActivity(1);
+ }
+ }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/SubActivityTest.java b/core/tests/coretests/src/android/app/activity/SubActivityTest.java
new file mode 100644
index 0000000..35dde8a
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/SubActivityTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.test.suitebuilder.annotation.Suppress;
+import android.content.ComponentName;
+
+@Suppress
+public class SubActivityTest extends ActivityTestsBase {
+
+ public void testPendingResult() throws Exception {
+ mIntent.putExtra("component", new ComponentName(getContext(), SubActivityScreen.class));
+ mIntent.putExtra("mode", SubActivityScreen.PENDING_RESULT_MODE);
+ runLaunchpad(LaunchpadActivity.LAUNCH);
+ }
+
+ public void testNoResult() throws Exception {
+ mIntent.putExtra("component", new ComponentName(getContext(), SubActivityScreen.class));
+ mIntent.putExtra("mode", SubActivityScreen.NO_RESULT_MODE);
+ runLaunchpad(LaunchpadActivity.LAUNCH);
+ }
+
+ public void testResult() throws Exception {
+ mIntent.putExtra("component", new ComponentName(getContext(), SubActivityScreen.class));
+ mIntent.putExtra("mode", SubActivityScreen.RESULT_MODE);
+ runLaunchpad(LaunchpadActivity.LAUNCH);
+ }
+
+ public void testFinishSub() throws Exception {
+ mIntent.putExtra("component",
+ new ComponentName(getContext(), RemoteSubActivityScreen.class));
+ mIntent.putExtra("mode", SubActivityScreen.FINISH_SUB_MODE);
+ runLaunchpad(LaunchpadActivity.LAUNCH);
+ }
+
+ public void testRemoteNoResult() throws Exception {
+ mIntent.putExtra("component",
+ new ComponentName(getContext(), RemoteSubActivityScreen.class));
+ mIntent.putExtra("mode", SubActivityScreen.NO_RESULT_MODE);
+ runLaunchpad(LaunchpadActivity.LAUNCH);
+ }
+
+ public void testRemoteResult() throws Exception {
+ mIntent.putExtra("component",
+ new ComponentName(getContext(), RemoteSubActivityScreen.class));
+ mIntent.putExtra("mode", SubActivityScreen.RESULT_MODE);
+ runLaunchpad(LaunchpadActivity.LAUNCH);
+ }
+
+ public void testRemoteFinishSub() throws Exception {
+ mIntent.putExtra("component", new ComponentName(getContext(), SubActivityScreen.class));
+ mIntent.putExtra("mode", SubActivityScreen.FINISH_SUB_MODE);
+ runLaunchpad(LaunchpadActivity.LAUNCH);
+ }
+
+ public void testRemoteRestartNoResult() throws Exception {
+ mIntent.putExtra("component",
+ new ComponentName(getContext(), RemoteSubActivityScreen.class));
+ mIntent.putExtra("mode", SubActivityScreen.NO_RESULT_MODE);
+ mIntent.putExtra("kill", true);
+ runLaunchpad(LaunchpadActivity.LAUNCH);
+ }
+
+ public void testRemoteRestartResult() throws Exception {
+ mIntent.putExtra("component",
+ new ComponentName(getContext(), RemoteSubActivityScreen.class));
+ mIntent.putExtra("mode", SubActivityScreen.RESULT_MODE);
+ mIntent.putExtra("kill", true);
+ runLaunchpad(LaunchpadActivity.LAUNCH);
+ }
+
+ public void testRemoteRestartFinishSub() throws Exception {
+ mIntent.putExtra("component", new ComponentName(getContext(), SubActivityScreen.class));
+ mIntent.putExtra("mode", SubActivityScreen.FINISH_SUB_MODE);
+ mIntent.putExtra("kill", true);
+ runLaunchpad(LaunchpadActivity.LAUNCH);
+ }
+}
diff --git a/core/tests/coretests/src/android/app/activity/TestedActivity.java b/core/tests/coretests/src/android/app/activity/TestedActivity.java
new file mode 100644
index 0000000..3a1c15f
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/TestedActivity.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.app.Activity;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+import android.os.Bundle;
+
+public class TestedActivity extends Activity
+{
+ public TestedActivity()
+ {
+ }
+
+ public void onCreate(Bundle icicle)
+ {
+ super.onCreate(icicle);
+ }
+
+ protected void onRestoreInstanceState(Bundle state)
+ {
+ super.onRestoreInstanceState(state);
+ }
+
+ protected void onResume()
+ {
+ super.onResume();
+ Looper.myLooper().myQueue().addIdleHandler(new Idler());
+ }
+
+ protected void onSaveInstanceState(Bundle outState)
+ {
+ super.onSaveInstanceState(outState);
+ }
+
+ protected void onStop()
+ {
+ super.onStop();
+ }
+
+ private Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ setResult(RESULT_OK);
+ finish();
+ }
+ };
+
+ private class Idler implements MessageQueue.IdleHandler
+ {
+ public final boolean queueIdle()
+ {
+ //Message m = Message.obtain();
+ //mHandler.sendMessageAtTime(m, SystemClock.uptimeMillis()+1000);
+ setResult(RESULT_OK);
+ finish();
+ return false;
+ }
+ }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/TestedScreen.java b/core/tests/coretests/src/android/app/activity/TestedScreen.java
new file mode 100644
index 0000000..1682d1a
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/TestedScreen.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.activity;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+import android.os.SystemClock;
+import android.os.Bundle;
+import android.util.Log;
+
+public class TestedScreen extends Activity
+{
+ public static final String WAIT_BEFORE_FINISH = "TestedScreen.WAIT_BEFORE_FINISH";
+ public static final String DELIVER_RESULT = "TestedScreen.DELIVER_RESULT";
+ public static final String CLEAR_TASK = "TestedScreen.CLEAR_TASK";
+
+ public TestedScreen() {
+ }
+
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "CREATE tested "
+ + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+ if (LaunchpadActivity.FORWARD_RESULT.equals(getIntent().getAction())) {
+ Intent intent = new Intent(getIntent());
+ intent.setAction(DELIVER_RESULT);
+ intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+ startActivity(intent);
+ if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "Finishing tested "
+ + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+ finish();
+ } else if (DELIVER_RESULT.equals(getIntent().getAction())) {
+ setResult(RESULT_OK, (new Intent()).setAction(
+ LaunchpadActivity.RETURNED_RESULT));
+ if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "Finishing tested "
+ + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+ finish();
+ } else if (CLEAR_TASK.equals(getIntent().getAction())) {
+ if (!getIntent().getBooleanExtra(ClearTop.WAIT_CLEAR_TASK, false)) {
+ launchClearTask();
+ }
+ }
+ }
+
+ protected void onRestoreInstanceState(Bundle state) {
+ super.onRestoreInstanceState(state);
+ }
+
+ protected void onResume() {
+ super.onResume();
+ if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "RESUME tested "
+ + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+ if (CLEAR_TASK.equals(getIntent().getAction())) {
+ if (getIntent().getBooleanExtra(ClearTop.WAIT_CLEAR_TASK, false)) {
+ Looper.myLooper().myQueue().addIdleHandler(new Idler());
+ }
+ } else {
+ Looper.myLooper().myQueue().addIdleHandler(new Idler());
+ }
+ }
+
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ }
+
+ protected void onStop() {
+ super.onStop();
+ if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "STOP tested "
+ + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+ }
+
+ private void launchClearTask() {
+ Intent intent = new Intent(getIntent()).
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP).
+ setClass(this, ClearTop.class);
+ startActivity(intent);
+ }
+
+ private Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ if (CLEAR_TASK.equals(getIntent().getAction())) {
+ launchClearTask();
+ } else {
+ if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "Finishing tested "
+ + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+ setResult(RESULT_OK);
+ finish();
+ }
+ }
+ };
+
+ private class Idler implements MessageQueue.IdleHandler {
+ public final boolean queueIdle() {
+ if (WAIT_BEFORE_FINISH.equals(getIntent().getAction())) {
+ Message m = Message.obtain();
+ mHandler.sendMessageAtTime(m, SystemClock.uptimeMillis()+1000);
+ } else if (CLEAR_TASK.equals(getIntent().getAction())) {
+ Message m = Message.obtain();
+ mHandler.sendMessageAtTime(m, SystemClock.uptimeMillis()+1000);
+ } else {
+ if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "Finishing tested "
+ + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+ setResult(RESULT_OK);
+ finish();
+ }
+ return false;
+ }
+ }
+}
+
diff --git a/core/tests/coretests/src/android/content/AssetTest.java b/core/tests/coretests/src/android/content/AssetTest.java
new file mode 100644
index 0000000..b66574c
--- /dev/null
+++ b/core/tests/coretests/src/android/content/AssetTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.content;
+
+import android.content.res.AssetManager;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class AssetTest extends AndroidTestCase {
+ private AssetManager mAssets;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mAssets = mContext.getAssets();
+ }
+
+ public static void verifyTextAsset(InputStream is) throws IOException {
+ String expectedString = "OneTwoThreeFourFiveSixSevenEightNineTen";
+ byte[] buffer = new byte[10];
+
+ int readCount;
+ int curIndex = 0;
+ while ((readCount = is.read(buffer, 0, buffer.length)) > 0) {
+ for (int i = 0; i < readCount; i++) {
+ assertEquals("At index " + curIndex
+ + " expected " + expectedString.charAt(curIndex)
+ + " but found " + ((char) buffer[i]),
+ buffer[i], expectedString.charAt(curIndex));
+ curIndex++;
+ }
+ }
+
+ readCount = is.read(buffer, 0, buffer.length);
+ assertEquals("Reading end of buffer: expected readCount=-1 but got " + readCount,
+ -1, readCount);
+
+ readCount = is.read(buffer, buffer.length, 0);
+ assertEquals("Reading end of buffer length 0: expected readCount=0 but got " + readCount,
+ 0, readCount);
+
+ is.close();
+ }
+
+ @SmallTest
+ public void testReadToEnd() throws Exception {
+ InputStream is = mAssets.open("text.txt");
+ verifyTextAsset(is);
+ }
+
+ // XXX failing
+ public void xxtestListDir() throws Exception {
+ String[] files = mAssets.list("");
+ assertEquals(1, files.length);
+ assertEquals("test.txt", files[0]);
+ }
+}
diff --git a/core/tests/coretests/src/android/content/ContentQueryMapTest.java b/core/tests/coretests/src/android/content/ContentQueryMapTest.java
new file mode 100644
index 0000000..d1b8c24
--- /dev/null
+++ b/core/tests/coretests/src/android/content/ContentQueryMapTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.content;
+
+import android.content.ContentQueryMap;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import java.util.Observable;
+import java.util.Observer;
+
+/** Test of {@link ContentQueryMap} */
+public class ContentQueryMapTest extends AndroidTestCase {
+ /** Helper class to run test code in a new thread with a Looper. */
+ private abstract class LooperThread extends Thread {
+ public Throwable mError = null;
+ public boolean mSuccess = false;
+
+ abstract void go();
+
+ public void run() {
+ try {
+ Looper.prepare();
+ go();
+ Looper.loop();
+ } catch (Throwable e) {
+ mError = e;
+ }
+ }
+ }
+
+ @MediumTest
+ public void testContentQueryMap() throws Throwable {
+ LooperThread thread = new LooperThread() {
+ void go() {
+ ContentResolver r = getContext().getContentResolver();
+ Settings.System.putString(r, "test", "Value");
+ Cursor cursor = r.query(
+ Settings.System.CONTENT_URI,
+ new String[] {
+ Settings.System.NAME,
+ Settings.System.VALUE,
+ }, null, null, null);
+
+ final ContentQueryMap cqm = new ContentQueryMap(
+ cursor, Settings.System.NAME, true, null);
+ // Get the current state of the CQM. This forces a requery and means that the
+ // call to getValues() below won't do a requery().
+ cqm.getRows();
+
+ // The cache won't notice changes until the loop runs.
+ Settings.System.putString(r, "test", "New Value");
+ ContentValues v = cqm.getValues("test");
+ String value = v.getAsString(Settings.System.VALUE);
+ assertEquals("Value", value);
+
+ // Use an Observer to find out when the cache does update.
+ cqm.addObserver(new Observer() {
+ public void update(Observable o, Object arg) {
+ // Should have the new values by now.
+ ContentValues v = cqm.getValues("test");
+ String value = v.getAsString(Settings.System.VALUE);
+ assertEquals("New Value", value);
+ Looper.myLooper().quit();
+ cqm.close();
+ mSuccess = true;
+ }
+ });
+
+ // Give up after a few seconds, if it doesn't.
+ new Handler().postDelayed(new Runnable() {
+ public void run() {
+ fail("Timed out");
+ }
+ }, 5000);
+ }
+ };
+
+ thread.start();
+ thread.join();
+ if (thread.mError != null) throw thread.mError;
+ assertTrue(thread.mSuccess);
+ }
+}
diff --git a/core/tests/coretests/src/android/content/ContentTests.java b/core/tests/coretests/src/android/content/ContentTests.java
new file mode 100644
index 0000000..a1299e3
--- /dev/null
+++ b/core/tests/coretests/src/android/content/ContentTests.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2006 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.content;
+
+import junit.framework.TestSuite;
+
+public class ContentTests {
+ public static TestSuite suite() {
+ TestSuite suite = new TestSuite(ContentTests.class.getName());
+
+ suite.addTestSuite(AssetTest.class);
+ return suite;
+ }
+}
diff --git a/core/tests/coretests/src/android/content/MemoryFileProvider.java b/core/tests/coretests/src/android/content/MemoryFileProvider.java
new file mode 100644
index 0000000..c4bc767
--- /dev/null
+++ b/core/tests/coretests/src/android/content/MemoryFileProvider.java
@@ -0,0 +1,211 @@
+/*
+ * 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.content;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.UriMatcher;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.net.Uri;
+import android.os.MemoryFile;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/** Simple test provider that runs in the local process. */
+public class MemoryFileProvider extends ContentProvider {
+ private static final String TAG = "MemoryFileProvider";
+
+ private static final String DATA_FILE = "data.bin";
+
+ // some random data
+ public static final byte[] TEST_BLOB = new byte[] {
+ -12, 127, 0, 3, 1, 2, 3, 4, 5, 6, 1, -128, -1, -54, -65, 35,
+ -53, -96, -74, -74, -55, -43, -69, 3, 52, -58,
+ -121, 127, 87, -73, 16, -13, -103, -65, -128, -36,
+ 107, 24, 118, -17, 97, 97, -88, 19, -94, -54,
+ 53, 43, 44, -27, -124, 28, -74, 26, 35, -36,
+ 16, -124, -31, -31, -128, -79, 108, 116, 43, -17 };
+
+ private SQLiteOpenHelper mOpenHelper;
+
+ private static final int DATA_ID_BLOB = 1;
+ private static final int HUGE = 2;
+ private static final int FILE = 3;
+
+ private static final UriMatcher sURLMatcher = new UriMatcher(
+ UriMatcher.NO_MATCH);
+
+ static {
+ sURLMatcher.addURI("*", "data/#/blob", DATA_ID_BLOB);
+ sURLMatcher.addURI("*", "huge", HUGE);
+ sURLMatcher.addURI("*", "file", FILE);
+ }
+
+ private static class DatabaseHelper extends SQLiteOpenHelper {
+ private static final String DATABASE_NAME = "local.db";
+ private static final int DATABASE_VERSION = 1;
+
+ public DatabaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE data (" +
+ "_id INTEGER PRIMARY KEY," +
+ "_blob TEXT, " +
+ "integer INTEGER);");
+
+ // insert alarms
+ ContentValues values = new ContentValues();
+ values.put("_id", 1);
+ values.put("_blob", TEST_BLOB);
+ values.put("integer", 100);
+ db.insert("data", null, values);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) {
+ Log.w(TAG, "Upgrading test database from version " +
+ oldVersion + " to " + currentVersion +
+ ", which will destroy all old data");
+ db.execSQL("DROP TABLE IF EXISTS data");
+ onCreate(db);
+ }
+ }
+
+
+ public MemoryFileProvider() {
+ }
+
+ @Override
+ public boolean onCreate() {
+ mOpenHelper = new DatabaseHelper(getContext());
+ try {
+ OutputStream out = getContext().openFileOutput(DATA_FILE, Context.MODE_PRIVATE);
+ out.write(TEST_BLOB);
+ out.close();
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri url, String[] projectionIn, String selection,
+ String[] selectionArgs, String sort) {
+ throw new UnsupportedOperationException("query not supported");
+ }
+
+ @Override
+ public String getType(Uri url) {
+ int match = sURLMatcher.match(url);
+ switch (match) {
+ case DATA_ID_BLOB:
+ return "application/octet-stream";
+ case FILE:
+ return "application/octet-stream";
+ default:
+ throw new IllegalArgumentException("Unknown URL");
+ }
+ }
+
+ @Override
+ public AssetFileDescriptor openAssetFile(Uri url, String mode) throws FileNotFoundException {
+ int match = sURLMatcher.match(url);
+ switch (match) {
+ case DATA_ID_BLOB:
+ String sql = "SELECT _blob FROM data WHERE _id=" + url.getPathSegments().get(1);
+ return getBlobColumnAsAssetFile(url, mode, sql);
+ case HUGE:
+ try {
+ MemoryFile memoryFile = new MemoryFile(null, 5000000);
+ memoryFile.writeBytes(TEST_BLOB, 0, 1000000, TEST_BLOB.length);
+ memoryFile.deactivate();
+ return AssetFileDescriptor.fromMemoryFile(memoryFile);
+ } catch (IOException ex) {
+ throw new FileNotFoundException("Error reading " + url + ":" + ex.toString());
+ }
+ case FILE:
+ File file = getContext().getFileStreamPath(DATA_FILE);
+ ParcelFileDescriptor fd =
+ ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
+ return new AssetFileDescriptor(fd, 0, AssetFileDescriptor.UNKNOWN_LENGTH);
+ default:
+ throw new FileNotFoundException("No files supported by provider at " + url);
+ }
+ }
+
+ private AssetFileDescriptor getBlobColumnAsAssetFile(Uri url, String mode, String sql)
+ throws FileNotFoundException {
+ if (!"r".equals(mode)) {
+ throw new FileNotFoundException("Mode " + mode + " not supported for " + url);
+ }
+ try {
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ MemoryFile file = simpleQueryForBlobMemoryFile(db, sql);
+ if (file == null) throw new FileNotFoundException("No such entry: " + url);
+ AssetFileDescriptor afd = AssetFileDescriptor.fromMemoryFile(file);
+ file.deactivate();
+ // need to dup and then close? openFileHelper() doesn't do that though
+ return afd;
+ } catch (IOException ex) {
+ throw new FileNotFoundException("Error reading " + url + ":" + ex.toString());
+ }
+ }
+
+ private MemoryFile simpleQueryForBlobMemoryFile(SQLiteDatabase db, String sql) throws IOException {
+ Cursor cursor = db.rawQuery(sql, null);
+ try {
+ if (!cursor.moveToFirst()) {
+ return null;
+ }
+ byte[] bytes = cursor.getBlob(0);
+ MemoryFile file = new MemoryFile(null, bytes.length);
+ file.writeBytes(bytes, 0, 0, bytes.length);
+ return file;
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ @Override
+ public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
+ throw new UnsupportedOperationException("update not supported");
+ }
+
+ @Override
+ public Uri insert(Uri url, ContentValues initialValues) {
+ throw new UnsupportedOperationException("insert not supported");
+ }
+
+ @Override
+ public int delete(Uri url, String where, String[] whereArgs) {
+ throw new UnsupportedOperationException("delete not supported");
+ }
+}
diff --git a/core/tests/coretests/src/android/content/MemoryFileProviderTest.java b/core/tests/coretests/src/android/content/MemoryFileProviderTest.java
new file mode 100644
index 0000000..6708af6
--- /dev/null
+++ b/core/tests/coretests/src/android/content/MemoryFileProviderTest.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.content;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import java.io.InputStream;
+import java.util.Arrays;
+
+/**
+ * Tests reading a MemoryFile-based AssestFile from a ContentProvider running
+ * in a different process.
+ */
+public class MemoryFileProviderTest extends AndroidTestCase {
+
+ // reads from a cross-process AssetFileDescriptor for a MemoryFile
+ @MediumTest
+ public void testRead() throws Exception {
+ ContentResolver resolver = getContext().getContentResolver();
+ Uri uri = Uri.parse("content://android.content.MemoryFileProvider/data/1/blob");
+ byte[] buf = new byte[MemoryFileProvider.TEST_BLOB.length];
+ InputStream in = resolver.openInputStream(uri);
+ assertNotNull(in);
+ int count = in.read(buf);
+ assertEquals(buf.length, count);
+ assertEquals(-1, in.read());
+ in.close();
+ assertTrue(Arrays.equals(MemoryFileProvider.TEST_BLOB, buf));
+ }
+
+ // tests that we don't leak file descriptors or virtual address space
+ @MediumTest
+ public void testClose() throws Exception {
+ ContentResolver resolver = getContext().getContentResolver();
+ // open enough file descriptors that we will crash something if we leak FDs
+ // or address space
+ for (int i = 0; i < 1025; i++) {
+ Uri uri = Uri.parse("content://android.content.MemoryFileProvider/huge");
+ InputStream in = resolver.openInputStream(uri);
+ assertNotNull("Failed to open stream number " + i, in);
+ assertEquals(1000000, in.skip(1000000));
+ byte[] buf = new byte[MemoryFileProvider.TEST_BLOB.length];
+ int count = in.read(buf);
+ assertEquals(buf.length, count);
+ assertTrue(Arrays.equals(MemoryFileProvider.TEST_BLOB, buf));
+ in.close();
+ }
+ }
+
+ // tests that we haven't broken AssestFileDescriptors for normal files.
+ @MediumTest
+ public void testFile() throws Exception {
+ ContentResolver resolver = getContext().getContentResolver();
+ Uri uri = Uri.parse("content://android.content.MemoryFileProvider/file");
+ byte[] buf = new byte[MemoryFileProvider.TEST_BLOB.length];
+ InputStream in = resolver.openInputStream(uri);
+ assertNotNull(in);
+ int count = in.read(buf);
+ assertEquals(buf.length, count);
+ assertEquals(-1, in.read());
+ in.close();
+ assertTrue(Arrays.equals(MemoryFileProvider.TEST_BLOB, buf));
+ }
+
+}
diff --git a/core/tests/coretests/src/android/database/CursorWindowTest.java b/core/tests/coretests/src/android/database/CursorWindowTest.java
new file mode 100644
index 0000000..07e75cb
--- /dev/null
+++ b/core/tests/coretests/src/android/database/CursorWindowTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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;
+
+import android.database.AbstractCursor;
+import android.test.suitebuilder.annotation.SmallTest;
+import com.android.common.ArrayListCursor;
+import android.database.CursorWindow;
+import android.test.PerformanceTestCase;
+
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Random;
+
+import junit.framework.TestCase;
+
+public class CursorWindowTest extends TestCase implements PerformanceTestCase {
+ public boolean isPerformanceOnly() {
+ return false;
+ }
+
+ // These test can only be run once.
+ public int startPerformance(Intermediates intermediates) {
+ return 1;
+ }
+
+ @SmallTest
+ public void testWriteCursorToWindow() throws Exception {
+ // create cursor
+ String[] colNames = new String[]{"name", "number", "profit"};
+ int colsize = colNames.length;
+ ArrayList<ArrayList> list = createTestList(10, colsize);
+ AbstractCursor cursor = new ArrayListCursor(colNames, (ArrayList<ArrayList>) list);
+
+ // fill window
+ CursorWindow window = new CursorWindow(false);
+ cursor.fillWindow(0, window);
+
+ // read from cursor window
+ for (int i = 0; i < list.size(); i++) {
+ ArrayList<Integer> col = list.get(i);
+ for (int j = 0; j < colsize; j++) {
+ String s = window.getString(i, j);
+ int r2 = col.get(j);
+ int r1 = Integer.parseInt(s);
+ assertEquals(r2, r1);
+ }
+ }
+
+ // test cursor window handle startpos != 0
+ window.clear();
+ cursor.fillWindow(1, window);
+ // read from cursor from window
+ for (int i = 1; i < list.size(); i++) {
+ ArrayList<Integer> col = list.get(i);
+ for (int j = 0; j < colsize; j++) {
+ String s = window.getString(i, j);
+ int r2 = col.get(j);
+ int r1 = Integer.parseInt(s);
+ assertEquals(r2, r1);
+ }
+ }
+
+ // Clear the window and make sure it's empty
+ window.clear();
+ assertEquals(0, window.getNumRows());
+ }
+
+ @SmallTest
+ public void testValuesLocalWindow() {
+ doTestValues(new CursorWindow(true));
+ }
+
+ @SmallTest
+ public void testValuesRemoteWindow() {
+ doTestValues(new CursorWindow(false));
+ }
+
+ private void doTestValues(CursorWindow window) {
+ assertTrue(window.setNumColumns(7));
+ assertTrue(window.allocRow());
+ double db1 = 1.26;
+ assertTrue(window.putDouble(db1, 0, 0));
+ double db2 = window.getDouble(0, 0);
+ assertEquals(db1, db2);
+
+ long int1 = Long.MAX_VALUE;
+ assertTrue(window.putLong(int1, 0, 1));
+ long int2 = window.getLong(0, 1);
+ assertEquals(int1, int2);
+
+ assertTrue(window.putString("1198032740000", 0, 3));
+ assertEquals("1198032740000", window.getString(0, 3));
+ assertEquals(1198032740000L, window.getLong(0, 3));
+
+ assertTrue(window.putString(Long.toString(1198032740000L), 0, 3));
+ assertEquals(Long.toString(1198032740000L), window.getString(0, 3));
+ assertEquals(1198032740000L, window.getLong(0, 3));
+
+ assertTrue(window.putString(Double.toString(42.0), 0, 4));
+ assertEquals(Double.toString(42.0), window.getString(0, 4));
+ assertEquals(42.0, window.getDouble(0, 4));
+
+ // put blob
+ byte[] blob = new byte[1000];
+ byte value = 99;
+ Arrays.fill(blob, value);
+ assertTrue(window.putBlob(blob, 0, 6));
+ assertTrue(Arrays.equals(blob, window.getBlob(0, 6)));
+ }
+
+ @SmallTest
+ public void testNull() {
+ CursorWindow window = getOneByOneWindow();
+
+ // Put in a null value and read it back as various types
+ assertTrue(window.putNull(0, 0));
+ assertNull(window.getString(0, 0));
+ assertEquals(0, window.getLong(0, 0));
+ assertEquals(0.0, window.getDouble(0, 0));
+ assertNull(window.getBlob(0, 0));
+ }
+
+ @SmallTest
+ public void testEmptyString() {
+ CursorWindow window = getOneByOneWindow();
+
+ // put size 0 string and read it back as various types
+ assertTrue(window.putString("", 0, 0));
+ assertEquals("", window.getString(0, 0));
+ assertEquals(0, window.getLong(0, 0));
+ assertEquals(0.0, window.getDouble(0, 0));
+ }
+
+ private CursorWindow getOneByOneWindow() {
+ CursorWindow window = new CursorWindow(false);
+ assertTrue(window.setNumColumns(1));
+ assertTrue(window.allocRow());
+ return window;
+ }
+
+ private static ArrayList<ArrayList> createTestList(int rows, int cols) {
+ ArrayList<ArrayList> list = Lists.newArrayList();
+ Random generator = new Random();
+
+ for (int i = 0; i < rows; i++) {
+ ArrayList<Integer> col = Lists.newArrayList();
+ list.add(col);
+ for (int j = 0; j < cols; j++) {
+ // generate random number
+ Integer r = generator.nextInt();
+ col.add(r);
+ }
+ }
+ return list;
+ }
+}
diff --git a/core/tests/coretests/src/android/database/DatabaseCursorTest.java b/core/tests/coretests/src/android/database/DatabaseCursorTest.java
new file mode 100644
index 0000000..fad4349
--- /dev/null
+++ b/core/tests/coretests/src/android/database/DatabaseCursorTest.java
@@ -0,0 +1,630 @@
+/*
+ * 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.database;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.CursorIndexOutOfBoundsException;
+import android.database.DataSetObserver;
+import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteCursor;
+import android.database.sqlite.SQLiteCursorDriver;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQuery;
+import android.database.sqlite.SQLiteStatement;
+import android.os.Looper;
+import android.test.AndroidTestCase;
+import android.test.PerformanceTestCase;
+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 java.io.File;
+import java.util.Arrays;
+import java.util.Random;
+
+import junit.framework.TestCase;
+
+public class DatabaseCursorTest extends AndroidTestCase implements PerformanceTestCase {
+
+ private static final String sString1 = "this is a test";
+ private static final String sString2 = "and yet another test";
+ private static final String sString3 = "this string is a little longer, but still a test";
+
+ private static final int CURRENT_DATABASE_VERSION = 42;
+ private SQLiteDatabase mDatabase;
+ private File mDatabaseFile;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ File dbDir = getContext().getDir("tests", Context.MODE_PRIVATE);
+ mDatabaseFile = new File(dbDir, "database_test.db");
+
+ if (mDatabaseFile.exists()) {
+ mDatabaseFile.delete();
+ }
+ mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
+ assertNotNull(mDatabase);
+ mDatabase.setVersion(CURRENT_DATABASE_VERSION);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mDatabase.close();
+ mDatabaseFile.delete();
+ super.tearDown();
+ }
+
+ public boolean isPerformanceOnly() {
+ return false;
+ }
+
+ // These test can only be run once.
+ public int startPerformance(Intermediates intermediates) {
+ return 1;
+ }
+
+ private void populateDefaultTable() {
+ mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);");
+
+ mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString1 + "');");
+ mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString2 + "');");
+ mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString3 + "');");
+ }
+
+ @MediumTest
+ public void testCursorUpdate() {
+ mDatabase.execSQL(
+ "CREATE TABLE test (_id INTEGER PRIMARY KEY, d INTEGER, s INTEGER);");
+ for(int i = 0; i < 20; i++) {
+ mDatabase.execSQL("INSERT INTO test (d, s) VALUES (" + i +
+ "," + i%2 + ");");
+ }
+
+ Cursor c = mDatabase.query("test", null, "s = 0", null, null, null, null);
+ int dCol = c.getColumnIndexOrThrow("d");
+ int sCol = c.getColumnIndexOrThrow("s");
+
+ int count = 0;
+ while (c.moveToNext()) {
+ assertTrue(c.updateInt(dCol, 3));
+ count++;
+ }
+ assertEquals(10, count);
+
+ assertTrue(c.commitUpdates());
+
+ assertTrue(c.requery());
+
+ count = 0;
+ while (c.moveToNext()) {
+ assertEquals(3, c.getInt(dCol));
+ count++;
+ }
+
+ assertEquals(10, count);
+ assertTrue(c.moveToFirst());
+ assertTrue(c.deleteRow());
+ assertEquals(9, c.getCount());
+ c.close();
+ }
+
+ @MediumTest
+ public void testBlob() throws Exception {
+ // create table
+ mDatabase.execSQL(
+ "CREATE TABLE test (_id INTEGER PRIMARY KEY, s TEXT, d REAL, l INTEGER, b BLOB);");
+ // insert blob
+ Object[] args = new Object[4];
+
+ byte[] blob = new byte[1000];
+ byte value = 99;
+ Arrays.fill(blob, value);
+ args[3] = blob;
+
+ String s = new String("text");
+ args[0] = s;
+ Double d = 99.9;
+ args[1] = d;
+ Long l = (long)1000;
+ args[2] = l;
+
+ String sql = "INSERT INTO test (s, d, l, b) VALUES (?,?,?,?)";
+ mDatabase.execSQL(sql, args);
+ // use cursor to access blob
+ Cursor c = mDatabase.query("test", null, null, null, null, null, null);
+ c.moveToNext();
+ ContentValues cv = new ContentValues();
+ DatabaseUtils.cursorRowToContentValues(c, cv);
+
+ int bCol = c.getColumnIndexOrThrow("b");
+ int sCol = c.getColumnIndexOrThrow("s");
+ int dCol = c.getColumnIndexOrThrow("d");
+ int lCol = c.getColumnIndexOrThrow("l");
+ byte[] cBlob = c.getBlob(bCol);
+ assertTrue(Arrays.equals(blob, cBlob));
+ assertEquals(s, c.getString(sCol));
+ assertEquals((double)d, c.getDouble(dCol));
+ assertEquals((long)l, c.getLong(lCol));
+
+ // new byte[]
+ byte[] newblob = new byte[1000];
+ value = 98;
+ Arrays.fill(blob, value);
+
+ c.updateBlob(bCol, newblob);
+ cBlob = c.getBlob(bCol);
+ assertTrue(Arrays.equals(newblob, cBlob));
+
+ // commit
+ assertTrue(c.commitUpdates());
+ assertTrue(c.requery());
+ c.moveToNext();
+ cBlob = c.getBlob(bCol);
+ assertTrue(Arrays.equals(newblob, cBlob));
+ c.close();
+ }
+
+ @MediumTest
+ public void testRealColumns() throws Exception {
+ mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data REAL);");
+ ContentValues values = new ContentValues();
+ values.put("data", 42.11);
+ long id = mDatabase.insert("test", "data", values);
+ assertTrue(id > 0);
+ Cursor c = mDatabase.rawQuery("SELECT data FROM test", null);
+ assertNotNull(c);
+ assertTrue(c.moveToFirst());
+ assertEquals(42.11, c.getDouble(0));
+ c.close();
+ }
+
+ @MediumTest
+ public void testCursor1() throws Exception {
+ populateDefaultTable();
+
+ Cursor c = mDatabase.query("test", null, null, null, null, null, null);
+
+ int dataColumn = c.getColumnIndexOrThrow("data");
+
+ // The cursor should ignore text before the last period when looking for a column. (This
+ // is a temporary hack in all implementations of getColumnIndex.)
+ int dataColumn2 = c.getColumnIndexOrThrow("junk.data");
+ assertEquals(dataColumn, dataColumn2);
+
+ assertSame(3, c.getCount());
+
+ assertTrue(c.isBeforeFirst());
+
+ try {
+ c.getInt(0);
+ fail("CursorIndexOutOfBoundsException expected");
+ } catch (CursorIndexOutOfBoundsException ex) {
+ // expected
+ }
+
+ c.moveToNext();
+ assertEquals(1, c.getInt(0));
+
+ String s = c.getString(dataColumn);
+ assertEquals(sString1, s);
+
+ c.moveToNext();
+ s = c.getString(dataColumn);
+ assertEquals(sString2, s);
+
+ c.moveToNext();
+ s = c.getString(dataColumn);
+ assertEquals(sString3, s);
+
+ c.moveToPosition(-1);
+ c.moveToNext();
+ s = c.getString(dataColumn);
+ assertEquals(sString1, s);
+
+ c.moveToPosition(2);
+ s = c.getString(dataColumn);
+ assertEquals(sString3, s);
+
+ int i;
+
+ for (c.moveToFirst(), i = 0; !c.isAfterLast(); c.moveToNext(), i++) {
+ c.getInt(0);
+ }
+
+ assertEquals(3, i);
+
+ try {
+ c.getInt(0);
+ fail("CursorIndexOutOfBoundsException expected");
+ } catch (CursorIndexOutOfBoundsException ex) {
+ // expected
+ }
+ c.close();
+ }
+
+ @MediumTest
+ public void testCursor2() throws Exception {
+ populateDefaultTable();
+
+ Cursor c = mDatabase.query("test", null, "_id > 1000", null, null, null, null);
+ assertEquals(0, c.getCount());
+ assertTrue(c.isBeforeFirst());
+
+ try {
+ c.getInt(0);
+ fail("CursorIndexOutOfBoundsException expected");
+ } catch (CursorIndexOutOfBoundsException ex) {
+ // expected
+ }
+
+ int i;
+ for (c.moveToFirst(), i = 0; !c.isAfterLast(); c.moveToNext(), i++) {
+ c.getInt(0);
+ }
+ assertEquals(0, i);
+ try {
+ c.getInt(0);
+ fail("CursorIndexOutOfBoundsException expected");
+ } catch (CursorIndexOutOfBoundsException ex) {
+ // expected
+ }
+ c.close();
+ }
+
+ @MediumTest
+ public void testLargeField() throws Exception {
+ mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);");
+
+ StringBuilder sql = new StringBuilder(2100);
+ sql.append("INSERT INTO test (data) VALUES ('");
+ Random random = new Random(System.currentTimeMillis());
+ StringBuilder randomString = new StringBuilder(1979);
+ for (int i = 0; i < 1979; i++) {
+ randomString.append((random.nextInt() & 0xf) % 10);
+ }
+ sql.append(randomString);
+ sql.append("');");
+ mDatabase.execSQL(sql.toString());
+
+ Cursor c = mDatabase.query("test", null, null, null, null, null, null);
+ assertNotNull(c);
+ assertEquals(1, c.getCount());
+
+ assertTrue(c.moveToFirst());
+ assertEquals(0, c.getPosition());
+ String largeString = c.getString(c.getColumnIndexOrThrow("data"));
+ assertNotNull(largeString);
+ assertEquals(randomString.toString(), largeString);
+ c.close();
+ }
+
+ class TestObserver extends DataSetObserver {
+ int total;
+ SQLiteCursor c;
+ boolean quit = false;
+ public TestObserver(int total_, SQLiteCursor cursor) {
+ c = cursor;
+ total = total_;
+ }
+
+ @Override
+ public void onChanged() {
+ int count = c.getCount();
+ if (total == count) {
+ int i = 0;
+ while (c.moveToNext()) {
+ assertEquals(i, c.getInt(1));
+ i++;
+ }
+ assertEquals(count, i);
+ quit = true;
+ Looper.myLooper().quit();
+ }
+ }
+
+ @Override
+ public void onInvalidated() {
+ }
+ }
+
+ //@Large
+ @Suppress
+ public void testLoadingThreadDelayRegisterData() throws Exception {
+ mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
+
+ final int count = 505;
+ String sql = "INSERT INTO test (data) VALUES (?);";
+ SQLiteStatement s = mDatabase.compileStatement(sql);
+ for (int i = 0; i < count; i++) {
+ s.bindLong(1, i);
+ s.execute();
+ }
+
+ int maxRead = 500;
+ int initialRead = 5;
+ SQLiteCursor c = (SQLiteCursor)mDatabase.rawQuery("select * from test;",
+ null, initialRead, maxRead);
+
+ TestObserver observer = new TestObserver(count, c);
+ c.getCount();
+ c.registerDataSetObserver(observer);
+ if (!observer.quit) {
+ Looper.loop();
+ }
+ c.close();
+ }
+
+ @LargeTest
+ public void testLoadingThread() throws Exception {
+ mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
+
+ final int count = 50000;
+ String sql = "INSERT INTO test (data) VALUES (?);";
+ SQLiteStatement s = mDatabase.compileStatement(sql);
+ for (int i = 0; i < count; i++) {
+ s.bindLong(1, i);
+ s.execute();
+ }
+
+ int maxRead = 1000;
+ int initialRead = 5;
+ SQLiteCursor c = (SQLiteCursor)mDatabase.rawQuery("select * from test;",
+ null, initialRead, maxRead);
+
+ TestObserver observer = new TestObserver(count, c);
+ c.registerDataSetObserver(observer);
+ c.getCount();
+
+ Looper.loop();
+ c.close();
+ }
+
+ @LargeTest
+ public void testLoadingThreadClose() throws Exception {
+ mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
+
+ final int count = 1000;
+ String sql = "INSERT INTO test (data) VALUES (?);";
+ SQLiteStatement s = mDatabase.compileStatement(sql);
+ for (int i = 0; i < count; i++) {
+ s.bindLong(1, i);
+ s.execute();
+ }
+
+ int maxRead = 11;
+ int initialRead = 5;
+ SQLiteCursor c = (SQLiteCursor)mDatabase.rawQuery("select * from test;",
+ null, initialRead, maxRead);
+
+ TestObserver observer = new TestObserver(count, c);
+ c.registerDataSetObserver(observer);
+ c.getCount();
+ c.close();
+ }
+
+ @LargeTest
+ public void testLoadingThreadDeactivate() throws Exception {
+ mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
+
+ final int count = 1000;
+ String sql = "INSERT INTO test (data) VALUES (?);";
+ SQLiteStatement s = mDatabase.compileStatement(sql);
+ for (int i = 0; i < count; i++) {
+ s.bindLong(1, i);
+ s.execute();
+ }
+
+ int maxRead = 11;
+ int initialRead = 5;
+ SQLiteCursor c = (SQLiteCursor)mDatabase.rawQuery("select * from test;",
+ null, initialRead, maxRead);
+
+ TestObserver observer = new TestObserver(count, c);
+ c.registerDataSetObserver(observer);
+ c.getCount();
+ c.deactivate();
+ c.close();
+ }
+
+ @LargeTest
+ public void testManyRowsLong() throws Exception {
+ mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
+
+ final int count = 36799;
+ for (int i = 0; i < count; i++) {
+ mDatabase.execSQL("INSERT INTO test (data) VALUES (" + i + ");");
+ }
+
+ Cursor c = mDatabase.query("test", new String[]{"data"}, null, null, null, null, null);
+ assertNotNull(c);
+
+ int i = 0;
+ while (c.moveToNext()) {
+ assertEquals(i, c.getInt(0));
+ i++;
+ }
+ assertEquals(count, i);
+ assertEquals(count, c.getCount());
+
+ Log.d("testManyRows", "count " + Integer.toString(i));
+ c.close();
+ }
+
+ @LargeTest
+ public void testManyRowsTxt() throws Exception {
+ mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);");
+ StringBuilder sql = new StringBuilder(2100);
+ sql.append("INSERT INTO test (data) VALUES ('");
+ Random random = new Random(System.currentTimeMillis());
+ StringBuilder randomString = new StringBuilder(1979);
+ for (int i = 0; i < 1979; i++) {
+ randomString.append((random.nextInt() & 0xf) % 10);
+ }
+ sql.append(randomString);
+ sql.append("');");
+
+ // if cursor window size changed, adjust this value too
+ final int count = 600; // more than two fillWindow needed
+ for (int i = 0; i < count; i++) {
+ mDatabase.execSQL(sql.toString());
+ }
+
+ Cursor c = mDatabase.query("test", new String[]{"data"}, null, null, null, null, null);
+ assertNotNull(c);
+
+ int i = 0;
+ while (c.moveToNext()) {
+ assertEquals(randomString.toString(), c.getString(0));
+ i++;
+ }
+ assertEquals(count, i);
+ assertEquals(count, c.getCount());
+ c.close();
+ }
+
+ @LargeTest
+ public void testManyRowsTxtLong() throws Exception {
+ mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, txt TEXT, data INT);");
+
+ Random random = new Random(System.currentTimeMillis());
+ StringBuilder randomString = new StringBuilder(1979);
+ for (int i = 0; i < 1979; i++) {
+ randomString.append((random.nextInt() & 0xf) % 10);
+ }
+
+ // if cursor window size changed, adjust this value too
+ final int count = 600;
+ for (int i = 0; i < count; i++) {
+ StringBuilder sql = new StringBuilder(2100);
+ sql.append("INSERT INTO test (txt, data) VALUES ('");
+ sql.append(randomString);
+ sql.append("','");
+ sql.append(i);
+ sql.append("');");
+ mDatabase.execSQL(sql.toString());
+ }
+
+ Cursor c = mDatabase.query("test", new String[]{"txt", "data"}, null, null, null, null, null);
+ assertNotNull(c);
+
+ int i = 0;
+ while (c.moveToNext()) {
+ assertEquals(randomString.toString(), c.getString(0));
+ assertEquals(i, c.getInt(1));
+ i++;
+ }
+ assertEquals(count, i);
+ assertEquals(count, c.getCount());
+ c.close();
+ }
+
+ @MediumTest
+ public void testRequery() throws Exception {
+ populateDefaultTable();
+
+ Cursor c = mDatabase.rawQuery("SELECT * FROM test", null);
+ assertNotNull(c);
+ assertEquals(3, c.getCount());
+ c.deactivate();
+ c.requery();
+ assertEquals(3, c.getCount());
+ c.close();
+ }
+
+ @MediumTest
+ public void testRequeryWithSelection() throws Exception {
+ populateDefaultTable();
+
+ Cursor c = mDatabase.rawQuery("SELECT data FROM test WHERE data = '" + sString1 + "'",
+ null);
+ assertNotNull(c);
+ assertEquals(1, c.getCount());
+ assertTrue(c.moveToFirst());
+ assertEquals(sString1, c.getString(0));
+ c.deactivate();
+ c.requery();
+ assertEquals(1, c.getCount());
+ assertTrue(c.moveToFirst());
+ assertEquals(sString1, c.getString(0));
+ c.close();
+ }
+
+ @MediumTest
+ public void testRequeryWithSelectionArgs() throws Exception {
+ populateDefaultTable();
+
+ Cursor c = mDatabase.rawQuery("SELECT data FROM test WHERE data = ?",
+ new String[]{sString1});
+ assertNotNull(c);
+ assertEquals(1, c.getCount());
+ assertTrue(c.moveToFirst());
+ assertEquals(sString1, c.getString(0));
+ c.deactivate();
+ c.requery();
+ assertEquals(1, c.getCount());
+ assertTrue(c.moveToFirst());
+ assertEquals(sString1, c.getString(0));
+ c.close();
+ }
+
+ @MediumTest
+ public void testRequeryWithAlteredSelectionArgs() throws Exception {
+ /**
+ * Test the ability of a subclass of SQLiteCursor to change its query arguments.
+ */
+ populateDefaultTable();
+
+ SQLiteDatabase.CursorFactory factory = new SQLiteDatabase.CursorFactory() {
+ public Cursor newCursor(
+ SQLiteDatabase db, SQLiteCursorDriver masterQuery, String editTable,
+ SQLiteQuery query) {
+ return new SQLiteCursor(db, masterQuery, editTable, query) {
+ @Override
+ public boolean requery() {
+ setSelectionArguments(new String[]{"2"});
+ return super.requery();
+ }
+ };
+ }
+ };
+ Cursor c = mDatabase.rawQueryWithFactory(
+ factory, "SELECT data FROM test WHERE _id <= ?", new String[]{"1"},
+ null);
+ assertNotNull(c);
+ assertEquals(1, c.getCount());
+ assertTrue(c.moveToFirst());
+ assertEquals(sString1, c.getString(0));
+
+ // Our hacked requery() changes the query arguments in the cursor.
+ c.requery();
+
+ assertEquals(2, c.getCount());
+ assertTrue(c.moveToFirst());
+ assertEquals(sString1, c.getString(0));
+ assertTrue(c.moveToNext());
+ assertEquals(sString2, c.getString(0));
+
+ // Test that setting query args on a deactivated cursor also works.
+ c.deactivate();
+ c.requery();
+ }
+}
diff --git a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
new file mode 100644
index 0000000..ca650e0
--- /dev/null
+++ b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
@@ -0,0 +1,1008 @@
+/*
+ * Copyright (C) 2006 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;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.CharArrayBuffer;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteStatement;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Parcel;
+import android.test.AndroidTestCase;
+import android.test.PerformanceTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.text.Collator;
+import java.util.Arrays;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import static android.database.DatabaseUtils.InsertHelper.TABLE_INFO_PRAGMA_COLUMNNAME_INDEX;
+import static android.database.DatabaseUtils.InsertHelper.TABLE_INFO_PRAGMA_DEFAULT_INDEX;
+
+public class DatabaseGeneralTest extends AndroidTestCase implements PerformanceTestCase {
+
+ private static final String sString1 = "this is a test";
+ private static final String sString2 = "and yet another test";
+ private static final String sString3 = "this string is a little longer, but still a test";
+ private static final String PHONE_NUMBER = "16175551212";
+
+ private static final int CURRENT_DATABASE_VERSION = 42;
+ private SQLiteDatabase mDatabase;
+ private File mDatabaseFile;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ File dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
+ mDatabaseFile = new File(dbDir, "database_test.db");
+ if (mDatabaseFile.exists()) {
+ mDatabaseFile.delete();
+ }
+ mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
+ assertNotNull(mDatabase);
+ mDatabase.setVersion(CURRENT_DATABASE_VERSION);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mDatabase.close();
+ mDatabaseFile.delete();
+ super.tearDown();
+ }
+
+ public boolean isPerformanceOnly() {
+ return false;
+ }
+
+ // These test can only be run once.
+ public int startPerformance(Intermediates intermediates) {
+ return 1;
+ }
+
+ private void populateDefaultTable() {
+ mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);");
+
+ mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString1 + "');");
+ mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString2 + "');");
+ mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString3 + "');");
+ }
+
+ @MediumTest
+ public void testVersion() throws Exception {
+ assertEquals(CURRENT_DATABASE_VERSION, mDatabase.getVersion());
+ mDatabase.setVersion(11);
+ assertEquals(11, mDatabase.getVersion());
+ }
+
+ @MediumTest
+ public void testUpdate() throws Exception {
+ populateDefaultTable();
+
+ ContentValues values = new ContentValues(1);
+ values.put("data", "this is an updated test");
+ assertEquals(1, mDatabase.update("test", values, "_id=1", null));
+ Cursor c = mDatabase.query("test", null, "_id=1", null, null, null, null);
+ assertNotNull(c);
+ assertEquals(1, c.getCount());
+ c.moveToFirst();
+ String value = c.getString(c.getColumnIndexOrThrow("data"));
+ assertEquals("this is an updated test", value);
+ }
+
+ @MediumTest
+ public void testPhoneNumbersEqual() throws Exception {
+ mDatabase.execSQL("CREATE TABLE phones (num TEXT);");
+ mDatabase.execSQL("INSERT INTO phones (num) VALUES ('911');");
+ mDatabase.execSQL("INSERT INTO phones (num) VALUES ('5555');");
+ mDatabase.execSQL("INSERT INTO phones (num) VALUES ('+" + PHONE_NUMBER + "');");
+
+ String number;
+ Cursor c;
+
+ c = mDatabase.query("phones", null,
+ "PHONE_NUMBERS_EQUAL(num, '504-555-7683')", null, null, null, null);
+ assertTrue(c == null || c.getCount() == 0);
+ c.close();
+
+ c = mDatabase.query("phones", null,
+ "PHONE_NUMBERS_EQUAL(num, '911')", null, null, null, null);
+ assertNotNull(c);
+ assertEquals(1, c.getCount());
+ c.moveToFirst();
+ number = c.getString(c.getColumnIndexOrThrow("num"));
+ assertEquals("911", number);
+ c.close();
+
+ c = mDatabase.query("phones", null,
+ "PHONE_NUMBERS_EQUAL(num, '5555')", null, null, null, null);
+ assertNotNull(c);
+ assertEquals(1, c.getCount());
+ c.moveToFirst();
+ number = c.getString(c.getColumnIndexOrThrow("num"));
+ assertEquals("5555", number);
+ c.close();
+
+ c = mDatabase.query("phones", null,
+ "PHONE_NUMBERS_EQUAL(num, '180055555555')", null, null, null, null);
+ assertTrue(c == null || c.getCount() == 0);
+ c.close();
+
+ c = mDatabase.query("phones", null,
+ "PHONE_NUMBERS_EQUAL(num, '+" + PHONE_NUMBER + "')", null, null, null, null);
+ assertNotNull(c);
+ assertEquals(1, c.getCount());
+ c.moveToFirst();
+ number = c.getString(c.getColumnIndexOrThrow("num"));
+ assertEquals("+" + PHONE_NUMBER, number);
+ c.close();
+
+ c = mDatabase.query("phones", null,
+ "PHONE_NUMBERS_EQUAL(num, '+1 (617).555-1212')", null, null, null, null);
+ assertNotNull(c);
+ assertEquals(1, c.getCount());
+ c.moveToFirst();
+ number = c.getString(c.getColumnIndexOrThrow("num"));
+ assertEquals("+" + PHONE_NUMBER, number);
+ c.close();
+
+ c = mDatabase.query("phones", null,
+ "PHONE_NUMBERS_EQUAL(num, '" + PHONE_NUMBER + "')", null, null, null, null);
+ assertNotNull(c);
+ assertEquals(1, c.getCount());
+ c.moveToFirst();
+ number = c.getString(c.getColumnIndexOrThrow("num"));
+ assertEquals("+" + PHONE_NUMBER, number);
+ c.close();
+
+ /*
+ c = mDatabase.query("phones", null,
+ "PHONE_NUMBERS_EQUAL(num, '5551212')", null, null, null, null);
+ assertNotNull(c);
+ assertEquals(1, c.getCount());
+ c.moveToFirst();
+ number = c.getString(c.getColumnIndexOrThrow("num"));
+ assertEquals("+" + PHONE_NUMBER, number);
+ c.close();
+ */
+
+ c = mDatabase.query("phones", null,
+ "PHONE_NUMBERS_EQUAL(num, '011" + PHONE_NUMBER + "')", null, null, null, null);
+ assertNotNull(c);
+ assertEquals(1, c.getCount());
+ c.moveToFirst();
+ number = c.getString(c.getColumnIndexOrThrow("num"));
+ assertEquals("+" + PHONE_NUMBER, number);
+ c.close();
+
+ c = mDatabase.query("phones", null,
+ "PHONE_NUMBERS_EQUAL(num, '00" + PHONE_NUMBER + "')", null, null, null, null);
+ assertNotNull(c);
+ assertEquals(1, c.getCount());
+ c.moveToFirst();
+ number = c.getString(c.getColumnIndexOrThrow("num"));
+ assertEquals("+" + PHONE_NUMBER, number);
+ c.close();
+ }
+
+ private void phoneNumberCompare(String phone1, String phone2, boolean equal,
+ boolean useStrictComparation) {
+ String[] temporalPhoneNumbers = new String[2];
+ temporalPhoneNumbers[0] = phone1;
+ temporalPhoneNumbers[1] = phone2;
+
+ Cursor cursor = mDatabase.rawQuery(
+ String.format(
+ "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?, %d) " +
+ "THEN 'equal' ELSE 'not equal' END",
+ (useStrictComparation ? 1 : 0)),
+ temporalPhoneNumbers);
+ try {
+ assertNotNull(cursor);
+ assertTrue(cursor.moveToFirst());
+ if (equal) {
+ assertEquals(String.format("Unexpectedly, \"%s != %s\".", phone1, phone2),
+ "equal", cursor.getString(0));
+ } else {
+ assertEquals(String.format("Unexpectedly, \"%s\" == \"%s\".", phone1, phone2),
+ "not equal", cursor.getString(0));
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ private void assertPhoneNumberEqual(String phone1, String phone2) throws Exception {
+ assertPhoneNumberEqual(phone1, phone2, true);
+ assertPhoneNumberEqual(phone1, phone2, false);
+ }
+
+ private void assertPhoneNumberEqual(String phone1, String phone2, boolean useStrict)
+ throws Exception {
+ phoneNumberCompare(phone1, phone2, true, useStrict);
+ }
+
+ private void assertPhoneNumberNotEqual(String phone1, String phone2) throws Exception {
+ assertPhoneNumberNotEqual(phone1, phone2, true);
+ assertPhoneNumberNotEqual(phone1, phone2, false);
+ }
+
+ private void assertPhoneNumberNotEqual(String phone1, String phone2, boolean useStrict)
+ throws Exception {
+ phoneNumberCompare(phone1, phone2, false, useStrict);
+ }
+
+ /**
+ * Tests international matching issues for the PHONE_NUMBERS_EQUAL function.
+ *
+ * @throws Exception
+ */
+ @SmallTest
+ public void testPhoneNumbersEqualInternationl() throws Exception {
+ assertPhoneNumberEqual("1", "1");
+ assertPhoneNumberEqual("123123", "123123");
+ assertPhoneNumberNotEqual("123123", "923123");
+ assertPhoneNumberNotEqual("123123", "123129");
+ assertPhoneNumberNotEqual("123123", "1231234");
+ assertPhoneNumberNotEqual("123123", "0123123", false);
+ assertPhoneNumberNotEqual("123123", "0123123", true);
+ assertPhoneNumberEqual("650-253-0000", "6502530000");
+ assertPhoneNumberEqual("650-253-0000", "650 253 0000");
+ assertPhoneNumberEqual("650 253 0000", "6502530000");
+ assertPhoneNumberEqual("+1 650-253-0000", "6502530000");
+ assertPhoneNumberEqual("001 650-253-0000", "6502530000");
+ assertPhoneNumberEqual("0111 650-253-0000", "6502530000");
+
+ // Russian trunk digit
+ assertPhoneNumberEqual("+79161234567", "89161234567");
+
+ // French trunk digit
+ assertPhoneNumberEqual("+33123456789", "0123456789");
+
+ // Trunk digit for city codes in the Netherlands
+ assertPhoneNumberEqual("+31771234567", "0771234567");
+
+ // Test broken caller ID seen on call from Thailand to the US
+ assertPhoneNumberEqual("+66811234567", "166811234567");
+
+ // Test the same in-country number with different country codes
+ assertPhoneNumberNotEqual("+33123456789", "+1123456789");
+
+ // Test one number with country code and the other without
+ assertPhoneNumberEqual("5125551212", "+15125551212");
+
+ // Test two NANP numbers that only differ in the area code
+ assertPhoneNumberNotEqual("5125551212", "6505551212");
+
+ // Japanese phone numbers
+ assertPhoneNumberEqual("090-1234-5678", "+819012345678");
+ assertPhoneNumberEqual("090(1234)5678", "+819012345678");
+ assertPhoneNumberEqual("090-1234-5678", "+81-90-1234-5678");
+
+ // Equador
+ assertPhoneNumberEqual("+593(800)123-1234", "8001231234");
+ assertPhoneNumberEqual("+593-2-1234-123", "21234123");
+
+ // Two continuous 0 at the beginning of the phone string should not be
+ // treated as trunk prefix in the strict comparation.
+ assertPhoneNumberEqual("008001231234", "8001231234", false);
+ assertPhoneNumberNotEqual("008001231234", "8001231234", true);
+
+ // Confirm that the bug found before does not re-appear in the strict compalation
+ assertPhoneNumberEqual("080-1234-5678", "+819012345678", false);
+ assertPhoneNumberNotEqual("080-1234-5678", "+819012345678", true);
+ }
+
+ @MediumTest
+ public void testCopyString() throws Exception {
+ mDatabase.execSQL("CREATE TABLE guess (numi INTEGER, numf FLOAT, str TEXT);");
+ mDatabase.execSQL(
+ "INSERT INTO guess (numi,numf,str) VALUES (0,0.0,'ZoomZoomZoomZoom');");
+ mDatabase.execSQL("INSERT INTO guess (numi,numf,str) VALUES (2000000000,3.1415926535,'');");
+ String chinese = "\u4eac\u4ec5 \u5c3d\u5f84\u60ca";
+ String[] arr = new String[1];
+ arr[0] = chinese;
+ mDatabase.execSQL("INSERT INTO guess (numi,numf,str) VALUES (-32768,-1.0,?)", arr);
+
+ Cursor c;
+
+ c = mDatabase.rawQuery("SELECT * FROM guess", null);
+
+ c.moveToFirst();
+
+ CharArrayBuffer buf = new CharArrayBuffer(14);
+
+ String compareTo = c.getString(c.getColumnIndexOrThrow("numi"));
+ int numiIdx = c.getColumnIndexOrThrow("numi");
+ int numfIdx = c.getColumnIndexOrThrow("numf");
+ int strIdx = c.getColumnIndexOrThrow("str");
+
+ c.copyStringToBuffer(numiIdx, buf);
+ assertEquals(1, buf.sizeCopied);
+ assertEquals(compareTo, new String(buf.data, 0, buf.sizeCopied));
+
+ c.copyStringToBuffer(strIdx, buf);
+ assertEquals("ZoomZoomZoomZoom", new String(buf.data, 0, buf.sizeCopied));
+
+ c.moveToNext();
+ compareTo = c.getString(numfIdx);
+
+ c.copyStringToBuffer(numfIdx, buf);
+ assertEquals(compareTo, new String(buf.data, 0, buf.sizeCopied));
+ c.copyStringToBuffer(strIdx, buf);
+ assertEquals(0, buf.sizeCopied);
+
+ c.moveToNext();
+ c.copyStringToBuffer(numfIdx, buf);
+ assertEquals(-1.0, Double.valueOf(
+ new String(buf.data, 0, buf.sizeCopied)).doubleValue());
+
+ c.copyStringToBuffer(strIdx, buf);
+ compareTo = c.getString(strIdx);
+ assertEquals(chinese, compareTo);
+
+ assertEquals(chinese, new String(buf.data, 0, buf.sizeCopied));
+ c.close();
+ }
+
+ @MediumTest
+ public void testSchemaChange1() throws Exception {
+ SQLiteDatabase db1 = mDatabase;
+ Cursor cursor;
+
+ db1.execSQL("CREATE TABLE db1 (_id INTEGER PRIMARY KEY, data TEXT);");
+
+ cursor = db1.query("db1", null, null, null, null, null, null);
+ assertNotNull("Cursor is null", cursor);
+
+ db1.execSQL("CREATE TABLE db2 (_id INTEGER PRIMARY KEY, data TEXT);");
+
+ assertEquals(0, cursor.getCount());
+ cursor.deactivate();
+ }
+
+ @MediumTest
+ public void testSchemaChange2() throws Exception {
+ SQLiteDatabase db1 = mDatabase;
+ SQLiteDatabase db2 = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile, null);
+ Cursor cursor;
+
+ db1.execSQL("CREATE TABLE db1 (_id INTEGER PRIMARY KEY, data TEXT);");
+
+ cursor = db1.query("db1", null, null, null, null, null, null);
+ assertNotNull("Cursor is null", cursor);
+ assertEquals(0, cursor.getCount());
+ cursor.deactivate();
+ // this cause exception because we're still using sqlite_prepate16 and not
+ // sqlite_prepare16_v2. The v2 variant added the ability to check the
+ // schema version and handle the case when the schema has changed
+ // Marco Nelissen claim it was 2x slower to compile SQL statements so
+ // I reverted back to the v1 variant.
+ /* db2.execSQL("CREATE TABLE db2 (_id INTEGER PRIMARY KEY, data TEXT);");
+
+ cursor = db1.query("db1", null, null, null, null, null, null);
+ assertNotNull("Cursor is null", cursor);
+ assertEquals(0, cursor.count());
+ cursor.deactivate();
+ */
+ }
+
+ @MediumTest
+ public void testSchemaChange3() throws Exception {
+ SQLiteDatabase db1 = mDatabase;
+ SQLiteDatabase db2 = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile, null);
+ Cursor cursor;
+
+
+ db1.execSQL("CREATE TABLE db1 (_id INTEGER PRIMARY KEY, data TEXT);");
+ db1.execSQL("INSERT INTO db1 (data) VALUES ('test');");
+
+ cursor = db1.query("db1", null, null, null, null, null, null);
+ // this cause exception because we're still using sqlite_prepate16 and not
+ // sqlite_prepare16_v2. The v2 variant added the ability to check the
+ // schema version and handle the case when the schema has changed
+ // Marco Nelissen claim it was 2x slower to compile SQL statements so
+ // I reverted back to the v1 variant.
+ /* db2.execSQL("CREATE TABLE db2 (_id INTEGER PRIMARY KEY, data TEXT);");
+
+ assertNotNull("Cursor is null", cursor);
+ assertEquals(1, cursor.count());
+ assertTrue(cursor.first());
+ assertEquals("test", cursor.getString(cursor.getColumnIndexOrThrow("data")));
+ cursor.deactivate();
+ */
+ }
+
+ private class ChangeObserver extends ContentObserver {
+ private int mCursorNotificationCount = 0;
+ private int mNotificationCount = 0;
+
+ public int getCursorNotificationCount() {
+ return mCursorNotificationCount;
+ }
+
+ public int getNotificationCount() {
+ return mNotificationCount;
+ }
+
+ public ChangeObserver(boolean cursor) {
+ super(new Handler());
+ mCursor = cursor;
+ }
+
+ @Override
+ public boolean deliverSelfNotifications() {
+ return true;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ if (mCursor) {
+ mCursorNotificationCount++;
+ } else {
+ mNotificationCount++;
+ }
+ }
+
+ boolean mCursor;
+ }
+
+ @MediumTest
+ public void testNotificationTest1() throws Exception {
+ /*
+ Cursor c = mContentResolver.query(Notes.CONTENT_URI,
+ new String[] {Notes._ID, Notes.NOTE},
+ null, null);
+ c.registerContentObserver(new MyContentObserver(true));
+ int count = c.count();
+
+ MyContentObserver observer = new MyContentObserver(false);
+ mContentResolver.registerContentObserver(Notes.CONTENT_URI, true, observer);
+
+ Uri uri;
+
+ HashMap<String, String> values = new HashMap<String, String>();
+ values.put(Notes.NOTE, "test note1");
+ uri = mContentResolver.insert(Notes.CONTENT_URI, values);
+ assertEquals(1, mCursorNotificationCount);
+ assertEquals(1, mNotificationCount);
+
+ c.requery();
+ assertEquals(count + 1, c.count());
+ c.first();
+ assertEquals("test note1", c.getString(c.getColumnIndex(Notes.NOTE)));
+ c.updateString(c.getColumnIndex(Notes.NOTE), "test note2");
+ c.commitUpdates();
+
+ assertEquals(2, mCursorNotificationCount);
+ assertEquals(2, mNotificationCount);
+
+ mContentResolver.delete(uri, null);
+
+ assertEquals(3, mCursorNotificationCount);
+ assertEquals(3, mNotificationCount);
+
+ mContentResolver.unregisterContentObserver(observer);
+ */
+ }
+
+ @MediumTest
+ public void testSelectionArgs() throws Exception {
+ mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);");
+ ContentValues values = new ContentValues(1);
+ values.put("data", "don't forget to handled 's");
+ mDatabase.insert("test", "data", values);
+ values.clear();
+ values.put("data", "no apostrophes here");
+ mDatabase.insert("test", "data", values);
+ Cursor c = mDatabase.query(
+ "test", null, "data GLOB ?", new String[]{"*'*"}, null, null, null);
+ assertEquals(1, c.getCount());
+ assertTrue(c.moveToFirst());
+ assertEquals("don't forget to handled 's", c.getString(1));
+ c.deactivate();
+
+ // make sure code should checking null string properly so that
+ // it won't crash
+ try {
+ mDatabase.query("test", new String[]{"_id"},
+ "_id=?", new String[]{null}, null, null, null);
+ fail("expected exception not thrown");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ @MediumTest
+ public void testTokenize() throws Exception {
+ Cursor c;
+ mDatabase.execSQL("CREATE TABLE tokens (" +
+ "token TEXT COLLATE unicode," +
+ "source INTEGER," +
+ "token_index INTEGER," +
+ "tag TEXT" +
+ ");");
+ mDatabase.execSQL("CREATE TABLE tokens_no_index (" +
+ "token TEXT COLLATE unicode," +
+ "source INTEGER" +
+ ");");
+
+ Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT _TOKENIZE(NULL, NULL, NULL, NULL)", null));
+ Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT _TOKENIZE('tokens', NULL, NULL, NULL)", null));
+ Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT _TOKENIZE('tokens', 10, NULL, NULL)", null));
+ Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT _TOKENIZE('tokens', 10, 'some string', NULL)", null));
+
+ Assert.assertEquals(3, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT _TOKENIZE('tokens', 11, 'some string ok', ' ', 1, 'foo')", null));
+ Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT _TOKENIZE('tokens', 11, 'second field', ' ', 1, 'bar')", null));
+
+ Assert.assertEquals(3, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT _TOKENIZE('tokens_no_index', 20, 'some string ok', ' ')", null));
+ Assert.assertEquals(3, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT _TOKENIZE('tokens_no_index', 21, 'foo bar baz', ' ', 0)", null));
+
+ // test Chinese
+ String chinese = new String("\u4eac\u4ec5 \u5c3d\u5f84\u60ca");
+ Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT _TOKENIZE('tokens', 12,'" + chinese + "', ' ', 1)", null));
+
+ String icustr = new String("Fr\u00e9d\u00e9ric Hj\u00f8nnev\u00e5g");
+
+ Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT _TOKENIZE('tokens', 13, '" + icustr + "', ' ', 1)", null));
+
+ Assert.assertEquals(9, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT count(*) from tokens;", null));
+
+ String key = DatabaseUtils.getHexCollationKey("Frederic Hjonneva");
+ Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(13, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT source from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
+ key = DatabaseUtils.getHexCollationKey("Hjonneva");
+ Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(13, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT source from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
+
+ key = DatabaseUtils.getHexCollationKey("some string ok");
+ Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT source from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals("foo", DatabaseUtils.stringForQuery(mDatabase,
+ "SELECT tag from tokens where token GLOB '" + key + "*'", null));
+ key = DatabaseUtils.getHexCollationKey("string");
+ Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT source from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals("foo", DatabaseUtils.stringForQuery(mDatabase,
+ "SELECT tag from tokens where token GLOB '" + key + "*'", null));
+ key = DatabaseUtils.getHexCollationKey("ok");
+ Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT source from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals("foo", DatabaseUtils.stringForQuery(mDatabase,
+ "SELECT tag from tokens where token GLOB '" + key + "*'", null));
+
+ key = DatabaseUtils.getHexCollationKey("second field");
+ Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT source from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals("bar", DatabaseUtils.stringForQuery(mDatabase,
+ "SELECT tag from tokens where token GLOB '" + key + "*'", null));
+ key = DatabaseUtils.getHexCollationKey("field");
+ Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT source from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals("bar", DatabaseUtils.stringForQuery(mDatabase,
+ "SELECT tag from tokens where token GLOB '" + key + "*'", null));
+
+ key = DatabaseUtils.getHexCollationKey(chinese);
+ String[] a = new String[1];
+ a[0] = key;
+ Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT count(*) from tokens where token= ?", a));
+ Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT source from tokens where token= ?", a));
+ Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT token_index from tokens where token= ?", a));
+ a[0] += "*";
+ Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT count(*) from tokens where token GLOB ?", a));
+ Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT source from tokens where token GLOB ?", a));
+ Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT token_index from tokens where token GLOB ?", a));
+
+ Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT count(*) from tokens where token= '" + key + "'", null));
+ Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT source from tokens where token= '" + key + "'", null));
+ Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT token_index from tokens where token= '" + key + "'", null));
+
+ Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT source from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
+
+ key = DatabaseUtils.getHexCollationKey("\u4eac\u4ec5");
+ Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT source from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
+
+ key = DatabaseUtils.getHexCollationKey("\u5c3d\u5f84\u60ca");
+ Log.d("DatabaseGeneralTest", "key = " + key);
+ Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT source from tokens where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
+
+ Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT count(*) from tokens where token GLOB 'ab*'", null));
+
+ key = DatabaseUtils.getHexCollationKey("some string ok");
+ Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT count(*) from tokens_no_index where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(20, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT source from tokens_no_index where token GLOB '" + key + "*'", null));
+
+ key = DatabaseUtils.getHexCollationKey("bar");
+ Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT count(*) from tokens_no_index where token GLOB '" + key + "*'", null));
+ Assert.assertEquals(21, DatabaseUtils.longForQuery(mDatabase,
+ "SELECT source from tokens_no_index where token GLOB '" + key + "*'", null));
+ }
+
+ @MediumTest
+ public void testTransactions() throws Exception {
+ mDatabase.execSQL("CREATE TABLE test (num INTEGER);");
+ mDatabase.execSQL("INSERT INTO test (num) VALUES (0)");
+
+ // Make sure that things work outside an explicit transaction.
+ setNum(1);
+ checkNum(1);
+
+ // Test a single-level transaction.
+ setNum(0);
+ mDatabase.beginTransaction();
+ setNum(1);
+ mDatabase.setTransactionSuccessful();
+ mDatabase.endTransaction();
+ checkNum(1);
+ Assert.assertFalse(mDatabase.isDbLockedByCurrentThread());
+
+ // Test a rolled-back transaction.
+ setNum(0);
+ mDatabase.beginTransaction();
+ setNum(1);
+ mDatabase.endTransaction();
+ checkNum(0);
+ Assert.assertFalse(mDatabase.isDbLockedByCurrentThread());
+
+ // We should get an error if we end a non-existent transaction.
+ assertThrowsIllegalState(new Runnable() { public void run() {
+ mDatabase.endTransaction();
+ }});
+
+ // We should get an error if a set a non-existent transaction as clean.
+ assertThrowsIllegalState(new Runnable() { public void run() {
+ mDatabase.setTransactionSuccessful();
+ }});
+
+ mDatabase.beginTransaction();
+ mDatabase.setTransactionSuccessful();
+ // We should get an error if we mark a transaction as clean twice.
+ assertThrowsIllegalState(new Runnable() { public void run() {
+ mDatabase.setTransactionSuccessful();
+ }});
+ // We should get an error if we begin a transaction after marking the parent as clean.
+ assertThrowsIllegalState(new Runnable() { public void run() {
+ mDatabase.beginTransaction();
+ }});
+ mDatabase.endTransaction();
+ Assert.assertFalse(mDatabase.isDbLockedByCurrentThread());
+
+ // Test a two-level transaction.
+ setNum(0);
+ mDatabase.beginTransaction();
+ mDatabase.beginTransaction();
+ setNum(1);
+ mDatabase.setTransactionSuccessful();
+ mDatabase.endTransaction();
+ mDatabase.setTransactionSuccessful();
+ mDatabase.endTransaction();
+ checkNum(1);
+ Assert.assertFalse(mDatabase.isDbLockedByCurrentThread());
+
+ // Test rolling back an inner transaction.
+ setNum(0);
+ mDatabase.beginTransaction();
+ mDatabase.beginTransaction();
+ setNum(1);
+ mDatabase.endTransaction();
+ mDatabase.setTransactionSuccessful();
+ mDatabase.endTransaction();
+ checkNum(0);
+ Assert.assertFalse(mDatabase.isDbLockedByCurrentThread());
+
+ // Test rolling back an outer transaction.
+ setNum(0);
+ mDatabase.beginTransaction();
+ mDatabase.beginTransaction();
+ setNum(1);
+ mDatabase.setTransactionSuccessful();
+ mDatabase.endTransaction();
+ mDatabase.endTransaction();
+ checkNum(0);
+ Assert.assertFalse(mDatabase.isDbLockedByCurrentThread());
+ }
+
+ private void setNum(int num) {
+ mDatabase.execSQL("UPDATE test SET num = " + num);
+ }
+
+ private void checkNum(int num) {
+ Assert.assertEquals(
+ num, DatabaseUtils.longForQuery(mDatabase, "SELECT num FROM test", null));
+ }
+
+ private void assertThrowsIllegalState(Runnable r) {
+ boolean ok = false;
+ try {
+ r.run();
+ } catch (IllegalStateException e) {
+ ok = true;
+ }
+ Assert.assertTrue(ok);
+ }
+
+ // Disable these until we can explicitly mark them as stress tests
+ public void xxtestMem1() throws Exception {
+ populateDefaultTable();
+
+ for (int i = 0; i < 50000; i++) {
+ Cursor cursor = mDatabase.query("test", null, null, null, null, null, null);
+ cursor.moveToFirst();
+ cursor.close();
+// Log.i("~~~~", "Finished round " + i);
+ }
+ }
+
+ // Disable these until we can explicitly mark them as stress tests
+ public void xxtestMem2() throws Exception {
+ populateDefaultTable();
+
+ for (int i = 0; i < 50000; i++) {
+ Cursor cursor = mDatabase.query("test", null, null, null, null, null, null);
+ cursor.close();
+// Log.i("~~~~", "Finished round " + i);
+ }
+ }
+
+ // Disable these until we can explicitly mark them as stress tests
+ public void xxtestMem3() throws Exception {
+ populateDefaultTable();
+
+ for (int i = 0; i < 50000; i++) {
+ Cursor cursor = mDatabase.query("test", null, null, null, null, null, null);
+ cursor.deactivate();
+// Log.i("~~~~", "Finished round " + i);
+ }
+ }
+
+ @MediumTest
+ public void testContentValues() throws Exception {
+ ContentValues values = new ContentValues();
+ values.put("string", "value");
+ assertEquals("value", values.getAsString("string"));
+ byte[] bytes = new byte[42];
+ Arrays.fill(bytes, (byte) 0x28);
+ values.put("byteArray", bytes);
+ assertTrue(Arrays.equals(bytes, values.getAsByteArray("byteArray")));
+
+ // Write the ContentValues to a Parcel and then read them out
+ Parcel p = Parcel.obtain();
+ values.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ values = ContentValues.CREATOR.createFromParcel(p);
+
+ // Read the values out again and make sure they're the same
+ assertTrue(Arrays.equals(bytes, values.getAsByteArray("byteArray")));
+ assertEquals("value", values.get("string"));
+ }
+
+ @MediumTest
+ public void testTableInfoPragma() throws Exception {
+ mDatabase.execSQL("CREATE TABLE pragma_test (" +
+ "i INTEGER DEFAULT 1234, " +
+ "j INTEGER, " +
+ "s TEXT DEFAULT 'hello', " +
+ "t TEXT, " +
+ "'select' TEXT DEFAULT \"hello\")");
+ try {
+ Cursor cur = mDatabase.rawQuery("PRAGMA table_info(pragma_test)", null);
+ Assert.assertEquals(5, cur.getCount());
+
+ Assert.assertTrue(cur.moveToNext());
+ Assert.assertEquals("i",
+ cur.getString(TABLE_INFO_PRAGMA_COLUMNNAME_INDEX));
+ Assert.assertEquals("1234",
+ cur.getString(TABLE_INFO_PRAGMA_DEFAULT_INDEX));
+
+ Assert.assertTrue(cur.moveToNext());
+ Assert.assertEquals("j",
+ cur.getString(TABLE_INFO_PRAGMA_COLUMNNAME_INDEX));
+ Assert.assertNull(cur.getString(TABLE_INFO_PRAGMA_DEFAULT_INDEX));
+
+ Assert.assertTrue(cur.moveToNext());
+ Assert.assertEquals("s",
+ cur.getString(TABLE_INFO_PRAGMA_COLUMNNAME_INDEX));
+ Assert.assertEquals("'hello'",
+ cur.getString(TABLE_INFO_PRAGMA_DEFAULT_INDEX));
+
+ Assert.assertTrue(cur.moveToNext());
+ Assert.assertEquals("t",
+ cur.getString(TABLE_INFO_PRAGMA_COLUMNNAME_INDEX));
+ Assert.assertNull(cur.getString(TABLE_INFO_PRAGMA_DEFAULT_INDEX));
+
+ Assert.assertTrue(cur.moveToNext());
+ Assert.assertEquals("select",
+ cur.getString(TABLE_INFO_PRAGMA_COLUMNNAME_INDEX));
+ Assert.assertEquals("\"hello\"",
+ cur.getString(TABLE_INFO_PRAGMA_DEFAULT_INDEX));
+
+ cur.close();
+ } catch (Throwable t) {
+ throw new RuntimeException(
+ "If you see this test fail, it's likely that something about " +
+ "sqlite's PRAGMA table_info(...) command has changed.", t);
+ }
+ }
+
+ @MediumTest
+ public void testInsertHelper() throws Exception {
+ Cursor cur;
+ ContentValues cv;
+ long row;
+
+ mDatabase.execSQL("CREATE TABLE insert_test (" +
+ "_id INTEGER PRIMARY KEY, " +
+ "s TEXT NOT NULL UNIQUE, " +
+ "t TEXT NOT NULL DEFAULT 'hello world', " +
+ "i INTEGER, " +
+ "j INTEGER NOT NULL DEFAULT 1234, " +
+ "'select' TEXT)");
+
+ DatabaseUtils.InsertHelper ih =
+ new DatabaseUtils.InsertHelper(mDatabase, "insert_test");
+
+ cv = new ContentValues();
+ cv.put("s", "one");
+ row = ih.insert(cv);
+ cur = mDatabase.rawQuery("SELECT * FROM insert_test WHERE _id == " + row, null);
+ Assert.assertTrue(cur.moveToFirst());
+ Assert.assertEquals("one", cur.getString(1));
+ Assert.assertEquals("hello world", cur.getString(2));
+ Assert.assertNull(cur.getString(3));
+ Assert.assertEquals(1234, cur.getLong(4));
+ Assert.assertNull(cur.getString(5));
+
+ cv = new ContentValues();
+ cv.put("s", "two");
+ cv.put("t", "goodbye world");
+ row = ih.insert(cv);
+ cur = mDatabase.rawQuery("SELECT * FROM insert_test WHERE _id == " + row, null);
+ Assert.assertTrue(cur.moveToFirst());
+ Assert.assertEquals("two", cur.getString(1));
+ Assert.assertEquals("goodbye world", cur.getString(2));
+ Assert.assertNull(cur.getString(3));
+ Assert.assertEquals(1234, cur.getLong(4));
+ Assert.assertNull(cur.getString(5));
+
+ cv = new ContentValues();
+ cv.put("t", "goodbye world");
+ row = ih.insert(cv);
+ Assert.assertEquals(-1, row);
+
+ cv = new ContentValues();
+ cv.put("s", "three");
+ cv.put("i", 2345);
+ cv.put("j", 3456);
+ cv.put("select", "tricky");
+ row = ih.insert(cv);
+ cur = mDatabase.rawQuery("SELECT * FROM insert_test WHERE _id == " + row, null);
+ Assert.assertTrue(cur.moveToFirst());
+ Assert.assertEquals("three", cur.getString(1));
+ Assert.assertEquals("hello world", cur.getString(2));
+ Assert.assertEquals(2345, cur.getLong(3));
+ Assert.assertEquals(3456, cur.getLong(4));
+ Assert.assertEquals("tricky", cur.getString(5));
+
+ cv = new ContentValues();
+ cv.put("s", "three");
+ cv.put("i", 6789);
+ row = ih.insert(cv);
+ Assert.assertEquals(-1, row);
+ row = ih.replace(cv);
+ cur = mDatabase.rawQuery("SELECT * FROM insert_test WHERE _id == " + row, null);
+ Assert.assertTrue(cur.moveToFirst());
+ Assert.assertEquals("three", cur.getString(1));
+ Assert.assertEquals("hello world", cur.getString(2));
+ Assert.assertEquals(6789, cur.getLong(3));
+
+ ih.close();
+ }
+
+ @MediumTest
+ public void testDbCloseReleasingAllCachedSql() {
+ mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, text1 TEXT, text2 TEXT, " +
+ "num1 INTEGER, num2 INTEGER, image BLOB);");
+ final String statement = "DELETE FROM test WHERE _id=?;";
+ SQLiteStatement statementDoNotClose = mDatabase.compileStatement(statement);
+ assertTrue(statementDoNotClose.getUniqueId() > 0);
+ int nStatement = statementDoNotClose.getUniqueId();
+ assertTrue(statementDoNotClose.getUniqueId() == nStatement);
+ /* do not close statementDoNotClose object.
+ * That should leave it in SQLiteDatabase.mPrograms.
+ * mDatabase.close() in tearDown() should release it.
+ */
+ }
+}
diff --git a/core/tests/coretests/src/android/database/DatabaseLocaleTest.java b/core/tests/coretests/src/android/database/DatabaseLocaleTest.java
new file mode 100644
index 0000000..b3282941
--- /dev/null
+++ b/core/tests/coretests/src/android/database/DatabaseLocaleTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.database;
+
+import android.database.sqlite.SQLiteDatabase;
+import android.database.Cursor;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+import android.test.MoreAsserts;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+import junit.framework.TestCase;
+
+public class DatabaseLocaleTest extends TestCase {
+
+ private SQLiteDatabase mDatabase;
+
+ private static final String[] STRINGS = {
+ "c\u00f4t\u00e9",
+ "cote",
+ "c\u00f4te",
+ "cot\u00e9",
+ "boy",
+ "dog",
+ "COTE",
+ };
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mDatabase = SQLiteDatabase.create(null);
+ mDatabase.execSQL(
+ "CREATE TABLE test (id INTEGER PRIMARY KEY, data TEXT COLLATE LOCALIZED);");
+ }
+
+ private void insertStrings() {
+ for (String s : STRINGS) {
+ mDatabase.execSQL("INSERT INTO test (data) VALUES('" + s + "');");
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mDatabase.close();
+ super.tearDown();
+ }
+
+ private String[] query(String sql) {
+ Log.i("LocaleTest", "Querying: " + sql);
+ Cursor c = mDatabase.rawQuery(sql, null);
+ assertNotNull(c);
+ ArrayList<String> items = new ArrayList<String>();
+ while (c.moveToNext()) {
+ items.add(c.getString(0));
+ Log.i("LocaleTest", "...." + c.getString(0));
+ }
+ String[] result = items.toArray(new String[items.size()]);
+ assertEquals(STRINGS.length, result.length);
+ c.close();
+ return result;
+ }
+
+ @MediumTest
+ public void testLocaleInsertOrder() throws Exception {
+ insertStrings();
+ String[] results = query("SELECT data FROM test");
+ MoreAsserts.assertEquals(STRINGS, results);
+ }
+
+ @MediumTest
+ public void testLocaleenUS() throws Exception {
+ insertStrings();
+ Log.i("LocaleTest", "about to call setLocale en_US");
+ mDatabase.setLocale(new Locale("en", "US"));
+ String[] results;
+ results = query("SELECT data FROM test ORDER BY data COLLATE LOCALIZED ASC");
+
+ // The database code currently uses PRIMARY collation strength,
+ // meaning that all versions of a character compare equal (regardless
+ // of case or accents), leaving the "cote" flavors in database order.
+ MoreAsserts.assertEquals(results, new String[] {
+ STRINGS[4], // "boy"
+ STRINGS[0], // sundry forms of "cote"
+ STRINGS[1],
+ STRINGS[2],
+ STRINGS[3],
+ STRINGS[6], // "COTE"
+ STRINGS[5], // "dog"
+ });
+ }
+
+ @SmallTest
+ public void testHoge() throws Exception {
+ Cursor cursor = null;
+ try {
+ String expectedString = new String(new int[] {0xFE000}, 0, 1);
+ mDatabase.execSQL("INSERT INTO test(id, data) VALUES(1, '" + expectedString + "')");
+ cursor = mDatabase.rawQuery("SELECT data FROM test WHERE id = 1", null);
+
+ assertNotNull(cursor);
+ assertTrue(cursor.moveToFirst());
+ String actualString = cursor.getString(0);
+ assertEquals(expectedString.length(), actualString.length());
+ for (int i = 0; i < expectedString.length(); i++) {
+ assertEquals((int)expectedString.charAt(i), (int)actualString.charAt(i));
+ }
+ assertEquals(expectedString, actualString);
+ } finally {
+ if (cursor != null) cursor.close();
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/database/DatabaseLockTest.java b/core/tests/coretests/src/android/database/DatabaseLockTest.java
new file mode 100644
index 0000000..f7a9f8a
--- /dev/null
+++ b/core/tests/coretests/src/android/database/DatabaseLockTest.java
@@ -0,0 +1,175 @@
+/*
+ * 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.database;
+
+import android.app.Activity;
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.util.Log;
+import java.io.File;
+import java.util.concurrent.atomic.AtomicInteger;
+import android.test.AndroidTestCase;
+
+import junit.framework.TestCase;
+
+/*
+ * This is a series of unit tests for database locks.
+ *
+ * Suppress these tests for now, since they have has inconsistent results.
+ * This should be turned into a performance tracking test.
+ */
+@Suppress
+public class DatabaseLockTest extends AndroidTestCase {
+
+ private static final int NUM_ITERATIONS = 100;
+ private static final int SLEEP_TIME = 30;
+ private static final int MAX_ALLOWED_LATENCY_TIME = 30;
+ private SQLiteDatabase mDatabase;
+ private File mDatabaseFile;
+ private AtomicInteger mCounter = new AtomicInteger();
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ File parentDir = getContext().getFilesDir();
+ mDatabaseFile = new File(parentDir, "database_test.db");
+
+ if (mDatabaseFile.exists()) {
+ mDatabaseFile.delete();
+ }
+ mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
+ assertNotNull(mDatabase);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mDatabase.close();
+ mDatabaseFile.delete();
+ super.tearDown();
+ }
+
+ /*
+ * testLockFairness() tests the fairness of prioritizing multiple threads
+ * attempting to access a database concurrently.
+ * This test is intended to verify that, when two threads are accessing the
+ * same database at the same time with the same prioritization, neither thread
+ * is locked out and prevented from accessing the database.
+ */
+ @Suppress
+ public void testLockFairness() {
+ startDatabaseFairnessThread();
+ int previous = 0;
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ mDatabase.beginTransaction();
+ int val = mCounter.get();
+ if (i == 0) {
+ previous = val - i;
+ }
+ assertTrue(previous == (val - i));
+ try {
+ Thread.currentThread().sleep(SLEEP_TIME);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ mDatabase.endTransaction();
+ }
+ }
+
+ /*
+ * This function is to create the second thread for testLockFairness() test.
+ */
+ private void startDatabaseFairnessThread() {
+ Thread thread = new DatabaseFairnessThread();
+ thread.start();
+ }
+
+ private class DatabaseFairnessThread extends Thread {
+ @Override
+ public void run() {
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ mDatabase.beginTransaction();
+ int val = mCounter.incrementAndGet();
+ try {
+ Thread.currentThread().sleep(SLEEP_TIME);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ mDatabase.endTransaction();
+ }
+ }
+ }
+
+ /*
+ * testLockLatency() tests the latency of database locks.
+ * This test is intended to verify that, even when two threads are accessing
+ * the same database, the locking/unlocking of the database is done within an
+ * appropriate amount of time (MAX_ALLOWED_LATENCY_TIME).
+ */
+ @Suppress
+ public void testLockLatency() {
+ startDatabaseLatencyThread();
+ int previous = 0;
+ long sumTime = 0;
+ long maxTime = 0;
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ long startTime = System.currentTimeMillis();
+ mDatabase.beginTransaction();
+ long endTime = System.currentTimeMillis();
+ long elapsedTime = endTime - startTime;
+ if (maxTime < elapsedTime) {
+ maxTime = elapsedTime;
+ }
+ sumTime += elapsedTime;
+ try {
+ Thread.currentThread().sleep(SLEEP_TIME);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ mDatabase.endTransaction();
+ }
+ long averageTime = sumTime/NUM_ITERATIONS;
+ Log.i("DatabaseLockLatency", "AverageTime: " + averageTime);
+ Log.i("DatabaseLockLatency", "MaxTime: " + maxTime);
+ assertTrue( (averageTime - SLEEP_TIME) <= MAX_ALLOWED_LATENCY_TIME);
+ }
+
+ /*
+ * This function is to create the second thread for testLockLatency() test.
+ */
+ private void startDatabaseLatencyThread() {
+ Thread thread = new DatabaseLatencyThread();
+ thread.start();
+ }
+
+ private class DatabaseLatencyThread extends Thread {
+ @Override
+ public void run() {
+ for (int i = 0; i < NUM_ITERATIONS; i++)
+ {
+ mDatabase.beginTransaction();
+ try {
+ Thread.currentThread().sleep(SLEEP_TIME);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ mDatabase.endTransaction();
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/database/DatabasePerformanceTests.java b/core/tests/coretests/src/android/database/DatabasePerformanceTests.java
new file mode 100644
index 0000000..b8ebcc4
--- /dev/null
+++ b/core/tests/coretests/src/android/database/DatabasePerformanceTests.java
@@ -0,0 +1,1353 @@
+/*
+ * 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.database;
+
+import junit.framework.Assert;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.provider.Contacts;
+import android.provider.Contacts.People;
+import android.test.PerformanceTestCase;
+import android.test.TestCase;
+
+import java.io.File;
+import java.util.Random;
+
+/**
+ * Database Performance Tests
+ *
+ */
+
+public class DatabasePerformanceTests {
+
+ public static String[] children() {
+ return new String[] {
+ ContactReadingTest1.class.getName(),
+ Perf1Test.class.getName(),
+ Perf2Test.class.getName(),
+ Perf3Test.class.getName(),
+ Perf4Test.class.getName(),
+ Perf5Test.class.getName(),
+ Perf6Test.class.getName(),
+ Perf7Test.class.getName(),
+ Perf8Test.class.getName(),
+ Perf9Test.class.getName(),
+ Perf10Test.class.getName(),
+ Perf11Test.class.getName(),
+ Perf12Test.class.getName(),
+ Perf13Test.class.getName(),
+ Perf14Test.class.getName(),
+ Perf15Test.class.getName(),
+ Perf16Test.class.getName(),
+ Perf17Test.class.getName(),
+ Perf18Test.class.getName(),
+ Perf19Test.class.getName(),
+ Perf20Test.class.getName(),
+ Perf21Test.class.getName(),
+ Perf22Test.class.getName(),
+ Perf23Test.class.getName(),
+ Perf24Test.class.getName(),
+ Perf25Test.class.getName(),
+ Perf26Test.class.getName(),
+ Perf27Test.class.getName(),
+ Perf28Test.class.getName(),
+ Perf29Test.class.getName(),
+ Perf30Test.class.getName(),
+ Perf31Test.class.getName(),
+ };
+ }
+
+ public static abstract class PerformanceBase implements TestCase,
+ PerformanceTestCase {
+ protected static final int CURRENT_DATABASE_VERSION = 42;
+ protected SQLiteDatabase mDatabase;
+ protected File mDatabaseFile;
+ protected Context mContext;
+
+ public void setUp(Context c) {
+ mContext = c;
+ mDatabaseFile = new File("/tmp", "perf_database_test.db");
+ if (mDatabaseFile.exists()) {
+ mDatabaseFile.delete();
+ }
+ mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
+ Assert.assertTrue(mDatabase != null);
+ mDatabase.setVersion(CURRENT_DATABASE_VERSION);
+ }
+
+ public void tearDown() {
+ mDatabase.close();
+ mDatabaseFile.delete();
+ }
+
+ public boolean isPerformanceOnly() {
+ return true;
+ }
+
+ // These test can only be run once.
+ public int startPerformance(Intermediates intermediates) {
+ return 0;
+ }
+
+ public void run() {
+ }
+
+ public String numberName(int number) {
+ String result = "";
+
+ if (number >= 1000) {
+ result += numberName((number / 1000)) + " thousand";
+ number = (number % 1000);
+
+ if (number > 0) result += " ";
+ }
+
+ if (number >= 100) {
+ result += ONES[(number / 100)] + " hundred";
+ number = (number % 100);
+
+ if (number > 0) result += " ";
+ }
+
+ if (number >= 20) {
+ result += TENS[(number / 10)];
+ number = (number % 10);
+
+ if (number > 0) result += " ";
+ }
+
+ if (number > 0) {
+ result += ONES[number];
+ }
+
+ return result;
+ }
+ }
+
+ /**
+ * Test reading all contact data.
+ */
+ public static class ContactReadingTest1 implements TestCase, PerformanceTestCase {
+ private static final String[] PEOPLE_PROJECTION = new String[] {
+ Contacts.People._ID, // 0
+ Contacts.People.PRIMARY_PHONE_ID, // 1
+ Contacts.People.TYPE, // 2
+ Contacts.People.NUMBER, // 3
+ Contacts.People.LABEL, // 4
+ Contacts.People.NAME, // 5
+ Contacts.People.PRESENCE_STATUS, // 6
+ };
+
+ private Cursor mCursor;
+
+ public void setUp(Context c) {
+ mCursor = c.getContentResolver().query(People.CONTENT_URI, PEOPLE_PROJECTION, null,
+ null, People.DEFAULT_SORT_ORDER);
+ }
+
+ public void tearDown() {
+ mCursor.close();
+ }
+
+ public boolean isPerformanceOnly() {
+ return true;
+ }
+
+ public int startPerformance(Intermediates intermediates) {
+ // This test can only be run once.
+ return 0;
+ }
+
+ public void run() {
+ while (mCursor.moveToNext()) {
+ // Read out all of the data
+ mCursor.getLong(0);
+ mCursor.getLong(1);
+ mCursor.getLong(2);
+ mCursor.getString(3);
+ mCursor.getString(4);
+ mCursor.getString(5);
+ mCursor.getLong(6);
+ }
+ }
+ }
+
+ /**
+ * Test 1000 inserts
+ */
+
+ public static class Perf1Test extends PerformanceBase {
+ private static final int SIZE = 1000;
+
+ private String[] statements = new String[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ statements[i] =
+ "INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')";
+ }
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.execSQL(statements[i]);
+ }
+ }
+ }
+
+ /**
+ * Test 1000 inserts into and indexed table
+ */
+
+ public static class Perf2Test extends PerformanceBase {
+ private static final int SIZE = 1000;
+
+ private String[] statements = new String[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ statements[i] =
+ "INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')";
+ }
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+ mDatabase.execSQL("CREATE INDEX i1c ON t1(c)");
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.execSQL(statements[i]);
+ }
+ }
+ }
+
+ /**
+ * 100 SELECTs without an index
+ */
+
+ public static class Perf3Test extends PerformanceBase {
+ private static final int SIZE = 100;
+ private static final String[] COLUMNS = {"count(*)", "avg(b)"};
+
+ private String[] where = new String[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+
+ for (int i = 0; i < SIZE; i++) {
+ int lower = i * 100;
+ int upper = (i + 10) * 100;
+ where[i] = "b >= " + lower + " AND b < " + upper;
+ }
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase
+ .query("t1", COLUMNS, where[i], null, null, null, null);
+ }
+ }
+ }
+
+ /**
+ * 100 SELECTs on a string comparison
+ */
+
+ public static class Perf4Test extends PerformanceBase {
+ private static final int SIZE = 100;
+ private static final String[] COLUMNS = {"count(*)", "avg(b)"};
+
+ private String[] where = new String[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+
+ for (int i = 0; i < SIZE; i++) {
+ where[i] = "c LIKE '" + numberName(i) + "'";
+ }
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase
+ .query("t1", COLUMNS, where[i], null, null, null, null);
+ }
+ }
+ }
+
+ /**
+ * 100 SELECTs with an index
+ */
+
+ public static class Perf5Test extends PerformanceBase {
+ private static final int SIZE = 100;
+ private static final String[] COLUMNS = {"count(*)", "avg(b)"};
+
+ private String[] where = new String[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+ mDatabase.execSQL("CREATE INDEX i1b ON t1(b)");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+
+ for (int i = 0; i < SIZE; i++) {
+ int lower = i * 100;
+ int upper = (i + 10) * 100;
+ where[i] = "b >= " + lower + " AND b < " + upper;
+ }
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase
+ .query("t1", COLUMNS, where[i], null, null, null, null);
+ }
+ }
+ }
+
+ /**
+ * INNER JOIN without an index
+ */
+
+ public static class Perf6Test extends PerformanceBase {
+ private static final int SIZE = 100;
+ private static final String[] COLUMNS = {"t1.a"};
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+ mDatabase
+ .execSQL("CREATE TABLE t2(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t2 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+ }
+
+ @Override
+ public void run() {
+ mDatabase.query("t1 INNER JOIN t2 ON t1.b = t2.b", COLUMNS, null,
+ null, null, null, null);
+ }
+ }
+
+ /**
+ * INNER JOIN without an index on one side
+ */
+
+ public static class Perf7Test extends PerformanceBase {
+ private static final int SIZE = 100;
+ private static final String[] COLUMNS = {"t1.a"};
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+ mDatabase
+ .execSQL("CREATE TABLE t2(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+ mDatabase.execSQL("CREATE INDEX i1b ON t1(b)");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t2 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+ }
+
+ @Override
+ public void run() {
+ mDatabase.query("t1 INNER JOIN t2 ON t1.b = t2.b", COLUMNS, null,
+ null, null, null, null);
+ }
+ }
+
+ /**
+ * INNER JOIN without an index on one side
+ */
+
+ public static class Perf8Test extends PerformanceBase {
+ private static final int SIZE = 100;
+ private static final String[] COLUMNS = {"t1.a"};
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+ mDatabase
+ .execSQL("CREATE TABLE t2(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+ mDatabase.execSQL("CREATE INDEX i1b ON t1(b)");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t2 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+ }
+
+ @Override
+ public void run() {
+ mDatabase.query("t1 INNER JOIN t2 ON t1.c = t2.c", COLUMNS, null,
+ null, null, null, null);
+ }
+ }
+
+ /**
+ * 100 SELECTs with subqueries. Subquery is using an index
+ */
+
+ public static class Perf9Test extends PerformanceBase {
+ private static final int SIZE = 100;
+ private static final String[] COLUMNS = {"t1.a"};
+
+ private String[] where = new String[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+ mDatabase
+ .execSQL("CREATE TABLE t2(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+ mDatabase.execSQL("CREATE INDEX i2b ON t2(b)");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t2 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+
+ for (int i = 0; i < SIZE; i++) {
+ int lower = i * 100;
+ int upper = (i + 10) * 100;
+ where[i] =
+ "t1.b IN (SELECT t2.b FROM t2 WHERE t2.b >= " + lower
+ + " AND t2.b < " + upper + ")";
+ }
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase
+ .query("t1", COLUMNS, where[i], null, null, null, null);
+ }
+ }
+ }
+
+ /**
+ * 100 SELECTs on string comparison with Index
+ */
+
+ public static class Perf10Test extends PerformanceBase {
+ private static final int SIZE = 100;
+ private static final String[] COLUMNS = {"count(*)", "avg(b)"};
+
+ private String[] where = new String[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+ mDatabase.execSQL("CREATE INDEX i3c ON t1(c)");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+
+ for (int i = 0; i < SIZE; i++) {
+ where[i] = "c LIKE '" + numberName(i) + "'";
+ }
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase
+ .query("t1", COLUMNS, where[i], null, null, null, null);
+ }
+ }
+ }
+
+ /**
+ * 100 SELECTs on integer
+ */
+
+ public static class Perf11Test extends PerformanceBase {
+ private static final int SIZE = 100;
+ private static final String[] COLUMNS = {"b"};
+
+ private String[] where = new String[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.query("t1", COLUMNS, null, null, null, null, null);
+ }
+ }
+ }
+
+ /**
+ * 100 SELECTs on String
+ */
+
+ public static class Perf12Test extends PerformanceBase {
+ private static final int SIZE = 100;
+ private static final String[] COLUMNS = {"c"};
+
+ private String[] where = new String[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.query("t1", COLUMNS, null, null, null, null, null);
+ }
+ }
+ }
+
+ /**
+ * 100 SELECTs on integer with index
+ */
+
+ public static class Perf13Test extends PerformanceBase {
+ private static final int SIZE = 100;
+ private static final String[] COLUMNS = {"b"};
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+ mDatabase.execSQL("CREATE INDEX i1b on t1(b)");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.query("t1", COLUMNS, null, null, null, null, null);
+ }
+ }
+ }
+
+ /**
+ * 100 SELECTs on String with index
+ */
+
+ public static class Perf14Test extends PerformanceBase {
+ private static final int SIZE = 100;
+ private static final String[] COLUMNS = {"c"};
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+ mDatabase.execSQL("CREATE INDEX i1c ON t1(c)");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.query("t1", COLUMNS, null, null, null, null, null);
+ }
+ }
+ }
+
+ /**
+ * 100 SELECTs on String with starts with
+ */
+
+ public static class Perf15Test extends PerformanceBase {
+ private static final int SIZE = 100;
+ private static final String[] COLUMNS = {"c"};
+ private String[] where = new String[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+ mDatabase.execSQL("CREATE INDEX i1c ON t1(c)");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ where[i] = "c LIKE '" + numberName(r).substring(0, 1) + "*'";
+
+ }
+
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase
+ .query("t1", COLUMNS, where[i], null, null, null, null);
+ }
+ }
+ }
+
+ /**
+ * 1000 Deletes on an indexed table
+ */
+
+ public static class Perf16Test extends PerformanceBase {
+ private static final int SIZE = 1000;
+ private static final String[] COLUMNS = {"c"};
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+ mDatabase.execSQL("CREATE INDEX i3c ON t1(c)");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.delete("t1", null, null);
+ }
+ }
+ }
+
+ /**
+ * 1000 Deletes
+ */
+
+ public static class Perf17Test extends PerformanceBase {
+ private static final int SIZE = 1000;
+ private static final String[] COLUMNS = {"c"};
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.delete("t1", null, null);
+ }
+ }
+ }
+
+ /**
+ * 1000 DELETE's without an index with where clause
+ */
+
+ public static class Perf18Test extends PerformanceBase {
+ private static final int SIZE = 1000;
+ private String[] where = new String[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+
+ for (int i = 0; i < SIZE; i++) {
+ int lower = i * 100;
+ int upper = (i + 10) * 100;
+ where[i] = "b >= " + lower + " AND b < " + upper;
+ }
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.delete("t1", where[i], null);
+ }
+ }
+ }
+
+ /**
+ * 1000 DELETE's with an index with where clause
+ */
+
+ public static class Perf19Test extends PerformanceBase {
+ private static final int SIZE = 1000;
+ private String[] where = new String[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+ mDatabase.execSQL("CREATE INDEX i1b ON t1(b)");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+
+ for (int i = 0; i < SIZE; i++) {
+ int lower = i * 100;
+ int upper = (i + 10) * 100;
+ where[i] = "b >= " + lower + " AND b < " + upper;
+ }
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.delete("t1", where[i], null);
+ }
+ }
+ }
+
+ /**
+ * 1000 update's with an index with where clause
+ */
+
+ public static class Perf20Test extends PerformanceBase {
+ private static final int SIZE = 1000;
+ private String[] where = new String[SIZE];
+ ContentValues[] mValues = new ContentValues[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+ mDatabase.execSQL("CREATE INDEX i1b ON t1(b)");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+
+ for (int i = 0; i < SIZE; i++) {
+
+ int lower = i * 100;
+ int upper = (i + 10) * 100;
+ where[i] = "b >= " + lower + " AND b < " + upper;
+ ContentValues b = new ContentValues(1);
+ b.put("b", upper);
+ mValues[i] = b;
+
+ }
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.update("t1", mValues[i], where[i], null);
+ }
+ }
+ }
+
+ /**
+ * 1000 update's without an index with where clause
+ */
+
+ public static class Perf21Test extends PerformanceBase {
+ private static final int SIZE = 1000;
+ private String[] where = new String[SIZE];
+ ContentValues[] mValues = new ContentValues[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+ + numberName(r) + "')");
+ }
+
+ for (int i = 0; i < SIZE; i++) {
+
+ int lower = i * 100;
+ int upper = (i + 10) * 100;
+ where[i] = "b >= " + lower + " AND b < " + upper;
+ ContentValues b = new ContentValues(1);
+ b.put("b", upper);
+ mValues[i] = b;
+ }
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.update("t1", mValues[i], where[i], null);
+ }
+ }
+ }
+
+ /**
+ * 10000 inserts for an integer
+ */
+
+ public static class Perf22Test extends PerformanceBase {
+ private static final int SIZE = 10000;
+ ContentValues[] mValues = new ContentValues[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER)");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ ContentValues b = new ContentValues(1);
+ b.put("a", r);
+ mValues[i] = b;
+ }
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.insert("t1", null, mValues[i]);
+ }
+ }
+ }
+
+ /**
+ * 10000 inserts for an integer -indexed table
+ */
+
+ public static class Perf23Test extends PerformanceBase {
+ private static final int SIZE = 10000;
+ ContentValues[] mValues = new ContentValues[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a INTEGER)");
+ mDatabase.execSQL("CREATE INDEX i1a ON t1(a)");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ ContentValues b = new ContentValues(1);
+ b.put("a", r);
+ mValues[i] = b;
+ }
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.insert("t1", null, mValues[i]);
+ }
+ }
+ }
+
+ /**
+ * 10000 inserts for a String
+ */
+
+ public static class Perf24Test extends PerformanceBase {
+ private static final int SIZE = 10000;
+ ContentValues[] mValues = new ContentValues[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a VARCHAR(100))");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ ContentValues b = new ContentValues(1);
+ b.put("a", numberName(r));
+ mValues[i] = b;
+ }
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.insert("t1", null, mValues[i]);
+ }
+ }
+ }
+
+ /**
+ * 10000 inserts for a String - indexed table
+ */
+
+ public static class Perf25Test extends PerformanceBase {
+ private static final int SIZE = 10000;
+ ContentValues[] mValues = new ContentValues[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t1(a VARCHAR(100))");
+ mDatabase.execSQL("CREATE INDEX i1a ON t1(a)");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ ContentValues b = new ContentValues(1);
+ b.put("a", numberName(r));
+ mValues[i] = b;
+ }
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.insert("t1", null, mValues[i]);
+ }
+ }
+ }
+
+
+ /**
+ * 10000 selects for a String -starts with
+ */
+
+ public static class Perf26Test extends PerformanceBase {
+ private static final int SIZE = 10000;
+ private static final String[] COLUMNS = {"t3.a"};
+ private String[] where = new String[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t3(a VARCHAR(100))");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t3 VALUES('"
+ + numberName(r) + "')");
+ }
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ where[i] = "a LIKE '" + numberName(r).substring(0, 1) + "*'";
+
+ }
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.query("t3", COLUMNS, where[i], null, null, null, null);
+ }
+ }
+ }
+
+ /**
+ * 10000 selects for a String - indexed table -starts with
+ */
+
+ public static class Perf27Test extends PerformanceBase {
+ private static final int SIZE = 10000;
+ private static final String[] COLUMNS = {"t3.a"};
+ private String[] where = new String[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t3(a VARCHAR(100))");
+ mDatabase.execSQL("CREATE INDEX i3a ON t3(a)");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t3 VALUES('"
+ + numberName(r) + "')");
+ }
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ where[i] = "a LIKE '" + numberName(r).substring(0, 1) + "*'";
+
+ }
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.query("t3", COLUMNS, where[i], null, null, null, null);
+ }
+ }
+ }
+
+ /**
+ * 10000 selects for an integer -
+ */
+
+ public static class Perf28Test extends PerformanceBase {
+ private static final int SIZE = 10000;
+ private static final String[] COLUMNS = {"t4.a"};
+ private String[] where = new String[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t4(a INTEGER)");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t4 VALUES(" + r + ")");
+ int lower = i * 100;
+ int upper = (i + 10) * 100;
+ where[i] = "a >= " + lower + " AND a < " + upper;
+ }
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.query("t4", COLUMNS, where[i], null, null, null, null);
+ }
+ }
+ }
+
+ /**
+ * 10000 selects for an integer -indexed table
+ */
+
+ public static class Perf29Test extends PerformanceBase {
+ private static final int SIZE = 10000;
+ private static final String[] COLUMNS = {"t4.a"};
+ private String[] where = new String[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t4(a INTEGER)");
+ mDatabase.execSQL("CREATE INDEX i4a ON t4(a)");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t4 VALUES(" + r + ")");
+
+ int lower = i * 100;
+ int upper = (i + 10) * 100;
+ where[i] = "a >= " + lower + " AND a < " + upper;
+ }
+
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.query("t4", COLUMNS, where[i], null, null, null, null);
+ }
+ }
+ }
+
+
+ /**
+ * 10000 selects for a String - contains 'e'
+ */
+
+ public static class Perf30Test extends PerformanceBase {
+ private static final int SIZE = 10000;
+ private static final String[] COLUMNS = {"t3.a"};
+ private String[] where = new String[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t3(a VARCHAR(100))");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t3 VALUES('"
+ + numberName(r) + "')");
+ }
+
+ for (int i = 0; i < SIZE; i++) {
+ where[i] = "a LIKE '*e*'";
+
+ }
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.query("t3", COLUMNS, where[i], null, null, null, null);
+ }
+ }
+ }
+
+ /**
+ * 10000 selects for a String - contains 'e'-indexed table
+ */
+
+ public static class Perf31Test extends PerformanceBase {
+ private static final int SIZE = 10000;
+ private static final String[] COLUMNS = {"t3.a"};
+ private String[] where = new String[SIZE];
+
+ @Override
+ public void setUp(Context c) {
+ super.setUp(c);
+ Random random = new Random(42);
+
+ mDatabase
+ .execSQL("CREATE TABLE t3(a VARCHAR(100))");
+ mDatabase.execSQL("CREATE INDEX i3a ON t3(a)");
+
+ for (int i = 0; i < SIZE; i++) {
+ int r = random.nextInt(100000);
+ mDatabase.execSQL("INSERT INTO t3 VALUES('"
+ + numberName(r) + "')");
+ }
+
+ for (int i = 0; i < SIZE; i++) {
+ where[i] = "a LIKE '*e*'";
+
+ }
+
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < SIZE; i++) {
+ mDatabase.query("t3", COLUMNS, where[i], null, null, null, null);
+ }
+ }
+ }
+
+ public static final String[] ONES =
+ {"zero", "one", "two", "three", "four", "five", "six", "seven",
+ "eight", "nine", "ten", "eleven", "twelve", "thirteen",
+ "fourteen", "fifteen", "sixteen", "seventeen", "eighteen",
+ "nineteen"};
+
+ public static final String[] TENS =
+ {"", "ten", "twenty", "thirty", "forty", "fifty", "sixty",
+ "seventy", "eighty", "ninety"};
+}
diff --git a/core/tests/coretests/src/android/database/DatabaseStatementTest.java b/core/tests/coretests/src/android/database/DatabaseStatementTest.java
new file mode 100644
index 0000000..71dc3ae
--- /dev/null
+++ b/core/tests/coretests/src/android/database/DatabaseStatementTest.java
@@ -0,0 +1,324 @@
+/*
+ * 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.database;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteConstraintException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDoneException;
+import android.database.sqlite.SQLiteStatement;
+import android.test.AndroidTestCase;
+import android.test.PerformanceTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+import java.io.File;
+
+public class DatabaseStatementTest extends AndroidTestCase implements PerformanceTestCase {
+
+ private static final String sString1 = "this is a test";
+ private static final String sString2 = "and yet another test";
+ private static final String sString3 = "this string is a little longer, but still a test";
+
+ private static final int CURRENT_DATABASE_VERSION = 42;
+ private SQLiteDatabase mDatabase;
+ private File mDatabaseFile;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ File dbDir = getContext().getDir("tests", Context.MODE_PRIVATE);
+ mDatabaseFile = new File(dbDir, "database_test.db");
+
+ if (mDatabaseFile.exists()) {
+ mDatabaseFile.delete();
+ }
+ mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
+ assertNotNull(mDatabase);
+ mDatabase.setVersion(CURRENT_DATABASE_VERSION);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mDatabase.close();
+ mDatabaseFile.delete();
+ super.tearDown();
+ }
+
+ public boolean isPerformanceOnly() {
+ return false;
+ }
+
+ // These test can only be run once.
+ public int startPerformance(Intermediates intermediates) {
+ return 1;
+ }
+
+ private void populateDefaultTable() {
+ mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);");
+
+ mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString1 + "');");
+ mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString2 + "');");
+ mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString3 + "');");
+ }
+
+ @MediumTest
+ public void testExecuteStatement() throws Exception {
+ populateDefaultTable();
+ SQLiteStatement statement = mDatabase.compileStatement("DELETE FROM test");
+ statement.execute();
+
+ Cursor c = mDatabase.query("test", null, null, null, null, null, null);
+ assertEquals(0, c.getCount());
+ c.deactivate();
+ statement.close();
+ }
+
+ @MediumTest
+ public void testSimpleQuery() throws Exception {
+ mDatabase.execSQL("CREATE TABLE test (num INTEGER NOT NULL, str TEXT NOT NULL);");
+ mDatabase.execSQL("INSERT INTO test VALUES (1234, 'hello');");
+ SQLiteStatement statement1 =
+ mDatabase.compileStatement("SELECT num FROM test WHERE str = ?");
+ SQLiteStatement statement2 =
+ mDatabase.compileStatement("SELECT str FROM test WHERE num = ?");
+
+ try {
+ statement1.bindString(1, "hello");
+ long value = statement1.simpleQueryForLong();
+ assertEquals(1234, value);
+
+ statement1.bindString(1, "world");
+ statement1.simpleQueryForLong();
+ fail("shouldn't get here");
+ } catch (SQLiteDoneException e) {
+ // expected
+ }
+
+ try {
+ statement2.bindLong(1, 1234);
+ String value = statement1.simpleQueryForString();
+ assertEquals("hello", value);
+
+ statement2.bindLong(1, 5678);
+ statement1.simpleQueryForString();
+ fail("shouldn't get here");
+ } catch (SQLiteDoneException e) {
+ // expected
+ }
+
+ statement1.close();
+ statement2.close();
+ }
+
+ @MediumTest
+ public void testStatementLongBinding() throws Exception {
+ mDatabase.execSQL("CREATE TABLE test (num INTEGER);");
+ SQLiteStatement statement = mDatabase.compileStatement("INSERT INTO test (num) VALUES (?)");
+
+ for (int i = 0; i < 10; i++) {
+ statement.bindLong(1, i);
+ statement.execute();
+ }
+ statement.close();
+
+ Cursor c = mDatabase.query("test", null, null, null, null, null, null);
+ int numCol = c.getColumnIndexOrThrow("num");
+ c.moveToFirst();
+ for (long i = 0; i < 10; i++) {
+ long num = c.getLong(numCol);
+ assertEquals(i, num);
+ c.moveToNext();
+ }
+ c.close();
+ }
+
+ @MediumTest
+ public void testStatementStringBinding() throws Exception {
+ mDatabase.execSQL("CREATE TABLE test (num TEXT);");
+ SQLiteStatement statement = mDatabase.compileStatement("INSERT INTO test (num) VALUES (?)");
+
+ for (long i = 0; i < 10; i++) {
+ statement.bindString(1, Long.toHexString(i));
+ statement.execute();
+ }
+ statement.close();
+
+ Cursor c = mDatabase.query("test", null, null, null, null, null, null);
+ int numCol = c.getColumnIndexOrThrow("num");
+ c.moveToFirst();
+ for (long i = 0; i < 10; i++) {
+ String num = c.getString(numCol);
+ assertEquals(Long.toHexString(i), num);
+ c.moveToNext();
+ }
+ c.close();
+ }
+
+ @MediumTest
+ public void testStatementClearBindings() throws Exception {
+ mDatabase.execSQL("CREATE TABLE test (num INTEGER);");
+ SQLiteStatement statement = mDatabase.compileStatement("INSERT INTO test (num) VALUES (?)");
+
+ for (long i = 0; i < 10; i++) {
+ statement.bindLong(1, i);
+ statement.clearBindings();
+ statement.execute();
+ }
+ statement.close();
+
+ Cursor c = mDatabase.query("test", null, null, null, null, null, "ROWID");
+ int numCol = c.getColumnIndexOrThrow("num");
+ assertTrue(c.moveToFirst());
+ for (long i = 0; i < 10; i++) {
+ assertTrue(c.isNull(numCol));
+ c.moveToNext();
+ }
+ c.close();
+ }
+
+ @MediumTest
+ public void testSimpleStringBinding() throws Exception {
+ mDatabase.execSQL("CREATE TABLE test (num TEXT, value TEXT);");
+ String statement = "INSERT INTO test (num, value) VALUES (?,?)";
+
+ String[] args = new String[2];
+ for (int i = 0; i < 2; i++) {
+ args[i] = Integer.toHexString(i);
+ }
+
+ mDatabase.execSQL(statement, args);
+
+ Cursor c = mDatabase.query("test", null, null, null, null, null, null);
+ int numCol = c.getColumnIndexOrThrow("num");
+ int valCol = c.getColumnIndexOrThrow("value");
+ c.moveToFirst();
+ String num = c.getString(numCol);
+ assertEquals(Integer.toHexString(0), num);
+
+ String val = c.getString(valCol);
+ assertEquals(Integer.toHexString(1), val);
+ c.close();
+ }
+
+ @MediumTest
+ public void testStatementMultipleBindings() throws Exception {
+ mDatabase.execSQL("CREATE TABLE test (num INTEGER, str TEXT);");
+ SQLiteStatement statement =
+ mDatabase.compileStatement("INSERT INTO test (num, str) VALUES (?, ?)");
+
+ for (long i = 0; i < 10; i++) {
+ statement.bindLong(1, i);
+ statement.bindString(2, Long.toHexString(i));
+ statement.execute();
+ }
+ statement.close();
+
+ Cursor c = mDatabase.query("test", null, null, null, null, null, "ROWID");
+ int numCol = c.getColumnIndexOrThrow("num");
+ int strCol = c.getColumnIndexOrThrow("str");
+ assertTrue(c.moveToFirst());
+ for (long i = 0; i < 10; i++) {
+ long num = c.getLong(numCol);
+ String str = c.getString(strCol);
+ assertEquals(i, num);
+ assertEquals(Long.toHexString(i), str);
+ c.moveToNext();
+ }
+ c.close();
+ }
+
+ private static class StatementTestThread extends Thread {
+ private SQLiteDatabase mDatabase;
+ private SQLiteStatement mStatement;
+
+ public StatementTestThread(SQLiteDatabase db, SQLiteStatement statement) {
+ super();
+ mDatabase = db;
+ mStatement = statement;
+ }
+
+ @Override
+ public void run() {
+ mDatabase.beginTransaction();
+ for (long i = 0; i < 10; i++) {
+ mStatement.bindLong(1, i);
+ mStatement.bindString(2, Long.toHexString(i));
+ mStatement.execute();
+ }
+ mDatabase.setTransactionSuccessful();
+ mDatabase.endTransaction();
+
+ Cursor c = mDatabase.query("test", null, null, null, null, null, "ROWID");
+ int numCol = c.getColumnIndexOrThrow("num");
+ int strCol = c.getColumnIndexOrThrow("str");
+ assertTrue(c.moveToFirst());
+ for (long i = 0; i < 10; i++) {
+ long num = c.getLong(numCol);
+ String str = c.getString(strCol);
+ assertEquals(i, num);
+ assertEquals(Long.toHexString(i), str);
+ c.moveToNext();
+ }
+ c.close();
+ }
+ }
+
+ @MediumTest
+ public void testStatementMultiThreaded() throws Exception {
+ mDatabase.execSQL("CREATE TABLE test (num INTEGER, str TEXT);");
+ SQLiteStatement statement =
+ mDatabase.compileStatement("INSERT INTO test (num, str) VALUES (?, ?)");
+
+ StatementTestThread thread = new StatementTestThread(mDatabase, statement);
+ thread.start();
+ try {
+ thread.join();
+ } finally {
+ statement.close();
+ }
+ }
+
+ @MediumTest
+ public void testStatementConstraint() throws Exception {
+ mDatabase.execSQL("CREATE TABLE test (num INTEGER NOT NULL);");
+ SQLiteStatement statement = mDatabase.compileStatement("INSERT INTO test (num) VALUES (?)");
+
+ // Try to insert NULL, which violates the constraint
+ try {
+ statement.clearBindings();
+ statement.execute();
+ fail("expected exception not thrown");
+ } catch (SQLiteConstraintException e) {
+ // expected
+ }
+
+ // Make sure the statement can still be used
+ statement.bindLong(1, 1);
+ statement.execute();
+ statement.close();
+
+ Cursor c = mDatabase.query("test", null, null, null, null, null, null);
+ int numCol = c.getColumnIndexOrThrow("num");
+ c.moveToFirst();
+ long num = c.getLong(numCol);
+ assertEquals(1, num);
+ c.close();
+ }
+}
diff --git a/core/tests/coretests/src/android/database/DatabaseStressTest.java b/core/tests/coretests/src/android/database/DatabaseStressTest.java
new file mode 100644
index 0000000..30e46e7
--- /dev/null
+++ b/core/tests/coretests/src/android/database/DatabaseStressTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.database.sqlite.*;
+import android.util.Log;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.Suppress;
+
+import java.io.File;
+
+// This test suite is too desctructive and takes too long to be included in the
+// automated suite.
+@Suppress
+public class DatabaseStressTest extends AndroidTestCase {
+ private static final String TAG = "DatabaseStressTest";
+ private static final int CURRENT_DATABASE_VERSION = 1;
+ private SQLiteDatabase mDatabase;
+ private File mDatabaseFile;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ Context c = getContext();
+
+ mDatabaseFile = c.getDatabasePath("database_test.db");
+ if (mDatabaseFile.exists()) {
+ mDatabaseFile.delete();
+ }
+
+ mDatabase = c.openOrCreateDatabase("database_test.db", 0, null);
+
+ assertNotNull(mDatabase);
+ mDatabase.setVersion(CURRENT_DATABASE_VERSION);
+
+ mDatabase.execSQL("CREATE TABLE IF NOT EXISTS test (_id INTEGER PRIMARY KEY, data TEXT);");
+
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mDatabase.close();
+ mDatabaseFile.delete();
+ super.tearDown();
+ }
+
+ public void testSingleThreadInsertDelete() {
+ int i = 0;
+ char[] ch = new char[100000];
+ String str = new String(ch);
+ String[] strArr = new String[1];
+ strArr[0] = str;
+ for (; i < 10000; ++i) {
+ try {
+ mDatabase.execSQL("INSERT INTO test (data) VALUES (?)", strArr);
+ mDatabase.execSQL("delete from test;");
+ } catch (Exception e) {
+ Log.e(TAG, "exception " + e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * use fillup -p 90 before run the test
+ * and when disk run out
+ * start delete some fillup files
+ * and see if db recover
+ */
+ public void testOutOfSpace() {
+ int i = 0;
+ char[] ch = new char[100000];
+ String str = new String(ch);
+ String[] strArr = new String[1];
+ strArr[0] = str;
+ for (; i < 10000; ++i) {
+ try {
+ mDatabase.execSQL("INSERT INTO test (data) VALUES (?)", strArr);
+ } catch (Exception e) {
+ Log.e(TAG, "exception " + e.getMessage());
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/net/LocalSocketTest.java b/core/tests/coretests/src/android/net/LocalSocketTest.java
new file mode 100644
index 0000000..1349844
--- /dev/null
+++ b/core/tests/coretests/src/android/net/LocalSocketTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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.net;
+
+import android.net.Credentials;
+import android.net.LocalServerSocket;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import android.test.MoreAsserts;
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+public class LocalSocketTest extends TestCase {
+
+ @SmallTest
+ public void testBasic() throws Exception {
+ LocalServerSocket ss;
+ LocalSocket ls;
+ LocalSocket ls1;
+
+ ss = new LocalServerSocket("android.net.LocalSocketTest");
+
+ ls = new LocalSocket();
+
+ ls.connect(new LocalSocketAddress("android.net.LocalSocketTest"));
+
+ ls1 = ss.accept();
+
+ // Test trivial read and write
+ ls.getOutputStream().write(42);
+
+ assertEquals(42, ls1.getInputStream().read());
+
+ // Test getting credentials
+ Credentials c = ls1.getPeerCredentials();
+
+ MoreAsserts.assertNotEqual(0, c.getPid());
+
+ // Test sending and receiving file descriptors
+ ls.setFileDescriptorsForSend(
+ new FileDescriptor[]{FileDescriptor.in});
+
+ ls.getOutputStream().write(42);
+
+ assertEquals(42, ls1.getInputStream().read());
+
+ FileDescriptor[] out = ls1.getAncillaryFileDescriptors();
+
+ assertEquals(1, out.length);
+
+ // Test multible byte write and available()
+ ls1.getOutputStream().write(new byte[]{0, 1, 2, 3, 4, 5}, 1, 5);
+
+ assertEquals(1, ls.getInputStream().read());
+ assertEquals(4, ls.getInputStream().available());
+
+ byte[] buffer = new byte[16];
+ int countRead;
+
+ countRead = ls.getInputStream().read(buffer, 1, 15);
+
+ assertEquals(4, countRead);
+ assertEquals(2, buffer[1]);
+ assertEquals(3, buffer[2]);
+ assertEquals(4, buffer[3]);
+ assertEquals(5, buffer[4]);
+
+ // Try various array-out-of-bound cases
+ try {
+ ls.getInputStream().read(buffer, 1, 16);
+ fail("expected exception");
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // excpected
+ }
+
+ try {
+ ls.getOutputStream().write(buffer, 1, 16);
+ fail("expected exception");
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // excpected
+ }
+
+ try {
+ ls.getOutputStream().write(buffer, -1, 15);
+ fail("expected exception");
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // excpected
+ }
+
+ try {
+ ls.getOutputStream().write(buffer, 0, -1);
+ fail("expected exception");
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // excpected
+ }
+
+ try {
+ ls.getInputStream().read(buffer, -1, 15);
+ fail("expected exception");
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // excpected
+ }
+
+ try {
+ ls.getInputStream().read(buffer, 0, -1);
+ fail("expected exception");
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // excpected
+ }
+
+ // Try read of length 0
+ ls.getOutputStream().write(42);
+ countRead = ls1.getInputStream().read(buffer, 0, 0);
+ assertEquals(0, countRead);
+ assertEquals(42, ls1.getInputStream().read());
+
+ ss.close();
+
+ ls.close();
+
+ // Try write on closed socket
+
+ try {
+ ls.getOutputStream().write(42);
+ fail("expected exception");
+ } catch (IOException ex) {
+ // Expected
+ }
+
+ // Try read on closed socket
+
+ try {
+ ls.getInputStream().read();
+ fail("expected exception");
+ } catch (IOException ex) {
+ // Expected
+ }
+
+ // Try write on socket whose peer has closed
+
+ try {
+ ls1.getOutputStream().write(42);
+ fail("expected exception");
+ } catch (IOException ex) {
+ // Expected
+ }
+
+ // Try read on socket whose peer has closed
+
+ assertEquals(-1, ls1.getInputStream().read());
+
+ ls1.close();
+ }
+}
diff --git a/core/tests/coretests/src/android/net/SSLTest.java b/core/tests/coretests/src/android/net/SSLTest.java
new file mode 100644
index 0000000..810ed0d
--- /dev/null
+++ b/core/tests/coretests/src/android/net/SSLTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2006 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.net.SSLCertificateSocketFactory;
+import android.test.suitebuilder.annotation.Suppress;
+import junit.framework.TestCase;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+//This test relies on network resources.
+@Suppress
+public class SSLTest extends TestCase {
+ public void testCertificate() throws Exception {
+ // test www.fortify.net/sslcheck.html
+ Socket ssl = SSLCertificateSocketFactory.getDefault().createSocket("www.fortify.net",443);
+ assertNotNull(ssl);
+
+ OutputStream out = ssl.getOutputStream();
+ assertNotNull(out);
+
+ InputStream in = ssl.getInputStream();
+ assertNotNull(in);
+
+ String get = "GET /sslcheck.html HTTP/1.1\r\nHost: 68.178.217.222\r\n\r\n";
+
+ // System.out.println("going for write...");
+ out.write(get.getBytes());
+
+ byte[] b = new byte[1024];
+ // System.out.println("going for read...");
+ int ret = in.read(b);
+
+ // System.out.println(new String(b));
+ }
+}
diff --git a/core/tests/coretests/src/android/net/UriMatcherTest.java b/core/tests/coretests/src/android/net/UriMatcherTest.java
new file mode 100644
index 0000000..2872144
--- /dev/null
+++ b/core/tests/coretests/src/android/net/UriMatcherTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.net;
+
+import android.content.UriMatcher;
+import android.net.Uri;
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+public class UriMatcherTest extends TestCase
+{
+ static final int ROOT = 0;
+ static final int PEOPLE = 1;
+ static final int PEOPLE_ID = 2;
+ static final int PEOPLE_PHONES = 3;
+ static final int PEOPLE_PHONES_ID = 4;
+ static final int PEOPLE_ADDRESSES = 5;
+ static final int PEOPLE_ADDRESSES_ID = 6;
+ static final int PEOPLE_CONTACTMETH = 7;
+ static final int PEOPLE_CONTACTMETH_ID = 8;
+ static final int CALLS = 9;
+ static final int CALLS_ID = 10;
+ static final int CALLERID = 11;
+ static final int CALLERID_TEXT = 12;
+ static final int FILTERRECENT = 13;
+
+ @SmallTest
+ public void testContentUris() {
+ check("content://asdf", UriMatcher.NO_MATCH);
+ check("content://people", PEOPLE);
+ check("content://people/1", PEOPLE_ID);
+ check("content://people/asdf", UriMatcher.NO_MATCH);
+ check("content://people/2/phones", PEOPLE_PHONES);
+ check("content://people/2/phones/3", PEOPLE_PHONES_ID);
+ check("content://people/2/phones/asdf", UriMatcher.NO_MATCH);
+ check("content://people/2/addresses", PEOPLE_ADDRESSES);
+ check("content://people/2/addresses/3", PEOPLE_ADDRESSES_ID);
+ check("content://people/2/addresses/asdf", UriMatcher.NO_MATCH);
+ check("content://people/2/contact-methods", PEOPLE_CONTACTMETH);
+ check("content://people/2/contact-methods/3", PEOPLE_CONTACTMETH_ID);
+ check("content://people/2/contact-methods/asdf", UriMatcher.NO_MATCH);
+ check("content://calls", CALLS);
+ check("content://calls/1", CALLS_ID);
+ check("content://calls/asdf", UriMatcher.NO_MATCH);
+ check("content://caller-id", CALLERID);
+ check("content://caller-id/asdf", CALLERID_TEXT);
+ check("content://caller-id/1", CALLERID_TEXT);
+ check("content://filter-recent", FILTERRECENT);
+ }
+
+ private static final UriMatcher mURLMatcher = new UriMatcher(ROOT);
+
+ static
+ {
+ mURLMatcher.addURI("people", null, PEOPLE);
+ mURLMatcher.addURI("people", "#", PEOPLE_ID);
+ mURLMatcher.addURI("people", "#/phones", PEOPLE_PHONES);
+ mURLMatcher.addURI("people", "#/phones/blah", PEOPLE_PHONES_ID);
+ mURLMatcher.addURI("people", "#/phones/#", PEOPLE_PHONES_ID);
+ mURLMatcher.addURI("people", "#/addresses", PEOPLE_ADDRESSES);
+ mURLMatcher.addURI("people", "#/addresses/#", PEOPLE_ADDRESSES_ID);
+ mURLMatcher.addURI("people", "#/contact-methods", PEOPLE_CONTACTMETH);
+ mURLMatcher.addURI("people", "#/contact-methods/#", PEOPLE_CONTACTMETH_ID);
+ mURLMatcher.addURI("calls", null, CALLS);
+ mURLMatcher.addURI("calls", "#", CALLS_ID);
+ mURLMatcher.addURI("caller-id", null, CALLERID);
+ mURLMatcher.addURI("caller-id", "*", CALLERID_TEXT);
+ mURLMatcher.addURI("filter-recent", null, FILTERRECENT);
+ }
+
+ void check(String uri, int expected)
+ {
+ int result = mURLMatcher.match(Uri.parse(uri));
+ if (result != expected) {
+ String msg = "failed on " + uri;
+ msg += " expected " + expected + " got " + result;
+ throw new RuntimeException(msg);
+ }
+ }
+}
+
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
new file mode 100644
index 0000000..ad71fcb
--- /dev/null
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -0,0 +1,578 @@
+/*
+ * 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.net;
+
+import android.net.Uri;
+import android.content.ContentUris;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.util.Arrays;
+
+public class UriTest extends TestCase {
+
+ @SmallTest
+ public void testToStringWithPathOnly() {
+ Uri.Builder builder = new Uri.Builder();
+
+ // Not a valid path, but this came from a user's test case.
+ builder.path("//foo");
+ Uri uri = builder.build();
+ assertEquals("//foo", uri.toString());
+ }
+
+ @SmallTest
+ public void testParcelling() {
+ parcelAndUnparcel(Uri.parse("foo:bob%20lee"));
+ parcelAndUnparcel(Uri.fromParts("foo", "bob lee", "fragment"));
+ parcelAndUnparcel(new Uri.Builder()
+ .scheme("http")
+ .authority("crazybob.org")
+ .path("/rss/")
+ .encodedQuery("a=b")
+ .fragment("foo")
+ .build());
+ }
+
+ private void parcelAndUnparcel(Uri u) {
+ Parcel p = Parcel.obtain();
+ try {
+ Uri.writeToParcel(p, u);
+ p.setDataPosition(0);
+ assertEquals(u, Uri.CREATOR.createFromParcel(p));
+
+ p.setDataPosition(0);
+ u = u.buildUpon().build();
+ Uri.writeToParcel(p, u);
+ p.setDataPosition(0);
+ assertEquals(u, Uri.CREATOR.createFromParcel(p));
+ }
+ finally {
+ p.recycle();
+ }
+ }
+
+ @SmallTest
+ public void testBuildUponOpaqueStringUri() {
+ Uri u = Uri.parse("bob:lee").buildUpon().scheme("robert").build();
+ assertEquals("robert", u.getScheme());
+ assertEquals("lee", u.getEncodedSchemeSpecificPart());
+ assertEquals("lee", u.getSchemeSpecificPart());
+ assertNull(u.getQuery());
+ assertNull(u.getPath());
+ assertNull(u.getAuthority());
+ assertNull(u.getHost());
+ }
+
+ @SmallTest
+ public void testStringUri() {
+ assertEquals("bob lee",
+ Uri.parse("foo:bob%20lee").getSchemeSpecificPart());
+ assertEquals("bob%20lee",
+ Uri.parse("foo:bob%20lee").getEncodedSchemeSpecificPart());
+ assertEquals("/bob%20lee",
+ Uri.parse("foo:/bob%20lee").getEncodedPath());
+ assertNull(Uri.parse("foo:bob%20lee").getPath());
+ assertEquals("bob%20lee",
+ Uri.parse("foo:?bob%20lee").getEncodedQuery());
+ assertNull(Uri.parse("foo:bar#?bob%20lee").getQuery());
+ assertEquals("bob%20lee",
+ Uri.parse("foo:#bob%20lee").getEncodedFragment());
+ }
+
+ @SmallTest
+ public void testStringUriIsHierarchical() {
+ assertTrue(Uri.parse("bob").isHierarchical());
+ assertFalse(Uri.parse("bob:").isHierarchical());
+ }
+
+ @SmallTest
+ public void testNullUriString() {
+ try {
+ Uri.parse(null);
+ fail();
+ } catch (NullPointerException e) {}
+ }
+
+ @SmallTest
+ public void testNullFile() {
+ try {
+ Uri.fromFile(null);
+ fail();
+ } catch (NullPointerException e) {}
+ }
+
+ @SmallTest
+ public void testCompareTo() {
+ Uri a = Uri.parse("foo:a");
+ Uri b = Uri.parse("foo:b");
+ Uri b2 = Uri.parse("foo:b");
+
+ assertTrue(a.compareTo(b) < 0);
+ assertTrue(b.compareTo(a) > 0);
+ assertEquals(0, b.compareTo(b2));
+ }
+
+ @SmallTest
+ public void testEqualsAndHashCode() {
+
+ Uri a = Uri.parse("http://crazybob.org/test/?foo=bar#tee");
+
+ Uri b = new Uri.Builder()
+ .scheme("http")
+ .authority("crazybob.org")
+ .path("/test/")
+ .encodedQuery("foo=bar")
+ .fragment("tee")
+ .build();
+
+ // Try alternate builder methods.
+ Uri c = new Uri.Builder()
+ .scheme("http")
+ .encodedAuthority("crazybob.org")
+ .encodedPath("/test/")
+ .encodedQuery("foo=bar")
+ .encodedFragment("tee")
+ .build();
+
+ assertFalse(Uri.EMPTY.equals(null));
+
+ assertEquals(a, b);
+ assertEquals(b, c);
+ assertEquals(c, a);
+
+ assertEquals(a.hashCode(), b.hashCode());
+ assertEquals(b.hashCode(), c.hashCode());
+ }
+
+ @SmallTest
+ public void testAuthorityParsing() {
+ Uri uri = Uri.parse("http://localhost:42");
+ assertEquals("localhost", uri.getHost());
+ assertEquals(42, uri.getPort());
+
+ uri = Uri.parse("http://bob@localhost:42");
+ assertEquals("bob", uri.getUserInfo());
+ assertEquals("localhost", uri.getHost());
+ assertEquals(42, uri.getPort());
+
+ uri = Uri.parse("http://bob%20lee@localhost:42");
+ assertEquals("bob lee", uri.getUserInfo());
+ assertEquals("bob%20lee", uri.getEncodedUserInfo());
+
+ uri = Uri.parse("http://bob%40lee%3ajr@local%68ost:4%32");
+ assertEquals("bob@lee:jr", uri.getUserInfo());
+ assertEquals("localhost", uri.getHost());
+ assertEquals(42, uri.getPort());
+
+ uri = Uri.parse("http://localhost");
+ assertEquals("localhost", uri.getHost());
+ assertEquals(-1, uri.getPort());
+ }
+
+ @SmallTest
+ public void testBuildUponOpaqueUri() {
+ Uri a = Uri.fromParts("foo", "bar", "tee");
+ Uri b = a.buildUpon().fragment("new").build();
+ assertEquals("new", b.getFragment());
+ assertEquals("bar", b.getSchemeSpecificPart());
+ assertEquals("foo", b.getScheme());
+ }
+
+ @SmallTest
+ public void testBuildUponEncodedOpaqueUri() {
+ Uri a = new Uri.Builder()
+ .scheme("foo")
+ .encodedOpaquePart("bar")
+ .fragment("tee")
+ .build();
+ Uri b = a.buildUpon().fragment("new").build();
+ assertEquals("new", b.getFragment());
+ assertEquals("bar", b.getSchemeSpecificPart());
+ assertEquals("foo", b.getScheme());
+ }
+
+ @SmallTest
+ public void testPathSegmentDecoding() {
+ Uri uri = Uri.parse("foo://bar/a%20a/b%20b");
+ assertEquals("a a", uri.getPathSegments().get(0));
+ assertEquals("b b", uri.getPathSegments().get(1));
+ }
+
+ @SmallTest
+ public void testSms() {
+ Uri base = Uri.parse("content://sms");
+ Uri appended = base.buildUpon()
+ .appendEncodedPath("conversations/addr=555-1212")
+ .build();
+ assertEquals("content://sms/conversations/addr=555-1212",
+ appended.toString());
+ assertEquals(2, appended.getPathSegments().size());
+ assertEquals("conversations", appended.getPathSegments().get(0));
+ assertEquals("addr=555-1212", appended.getPathSegments().get(1));
+ }
+
+ @SmallTest
+ public void testEncodeWithAllowedChars() {
+ String encoded = Uri.encode("Bob:/", "/");
+ assertEquals(-1, encoded.indexOf(':'));
+ assertTrue(encoded.indexOf('/') > -1);
+ }
+
+ @SmallTest
+ public void testEncodeDecode() {
+ code(null);
+ code("");
+ code("Bob");
+ code(":Bob");
+ code("::Bob");
+ code("Bob::Lee");
+ code("Bob:Lee");
+ code("Bob::");
+ code("Bob:");
+ code("::Bob::");
+ }
+
+ private void code(String s) {
+ assertEquals(s, Uri.decode(Uri.encode(s, null)));
+ }
+
+ @SmallTest
+ public void testFile() {
+ File f = new File("/tmp/bob");
+
+ Uri uri = Uri.fromFile(f);
+
+ assertEquals("file:///tmp/bob", uri.toString());
+ }
+
+ @SmallTest
+ public void testQueryParameters() {
+ Uri uri = Uri.parse("content://user");
+
+ assertEquals(null, uri.getQueryParameter("a"));
+
+ uri = uri.buildUpon().appendQueryParameter("a", "b").build();
+
+ assertEquals("b", uri.getQueryParameter("a"));
+
+ uri = uri.buildUpon().appendQueryParameter("a", "b2").build();
+
+ assertEquals(Arrays.asList("b", "b2"), uri.getQueryParameters("a"));
+
+ uri = uri.buildUpon().appendQueryParameter("c", "d").build();
+
+ assertEquals(Arrays.asList("b", "b2"), uri.getQueryParameters("a"));
+ assertEquals("d", uri.getQueryParameter("c"));
+ }
+
+ @SmallTest
+ public void testSchemeOnly() {
+ Uri uri = Uri.parse("empty:");
+ assertEquals("empty", uri.getScheme());
+ assertTrue(uri.isAbsolute());
+ assertNull(uri.getPath());
+ }
+
+ @SmallTest
+ public void testEmptyPath() {
+ Uri uri = Uri.parse("content://user");
+ assertEquals(0, uri.getPathSegments().size());
+ }
+
+ @SmallTest
+ public void testPathOperations() {
+ Uri uri = Uri.parse("content://user/a/b");
+
+ assertEquals(2, uri.getPathSegments().size());
+ assertEquals("b", uri.getLastPathSegment());
+
+ Uri first = uri;
+ uri = uri.buildUpon().appendPath("c").build();
+
+ assertEquals(3, uri.getPathSegments().size());
+ assertEquals("c", uri.getLastPathSegment());
+ assertEquals("content://user/a/b/c", uri.toString());
+
+ uri = ContentUris.withAppendedId(uri, 100);
+
+ assertEquals(4, uri.getPathSegments().size());
+ assertEquals("100", uri.getLastPathSegment());
+ assertEquals(100, ContentUris.parseId(uri));
+ assertEquals("content://user/a/b/c/100", uri.toString());
+
+ // Make sure the original URI is still intact.
+ assertEquals(2, first.getPathSegments().size());
+ assertEquals("b", first.getLastPathSegment());
+
+ try {
+ first.getPathSegments().get(2);
+ fail();
+ } catch (IndexOutOfBoundsException e) {}
+
+ assertEquals(null, Uri.EMPTY.getLastPathSegment());
+
+ Uri withC = Uri.parse("foo:/a/b/").buildUpon().appendPath("c").build();
+ assertEquals("/a/b/c", withC.getPath());
+ }
+
+ @SmallTest
+ public void testOpaqueUri() {
+ Uri uri = Uri.parse("mailto:nobody");
+ testOpaqueUri(uri);
+
+ uri = uri.buildUpon().build();
+ testOpaqueUri(uri);
+
+ uri = Uri.fromParts("mailto", "nobody", null);
+ testOpaqueUri(uri);
+
+ uri = uri.buildUpon().build();
+ testOpaqueUri(uri);
+
+ uri = new Uri.Builder()
+ .scheme("mailto")
+ .opaquePart("nobody")
+ .build();
+ testOpaqueUri(uri);
+
+ uri = uri.buildUpon().build();
+ testOpaqueUri(uri);
+ }
+
+ private void testOpaqueUri(Uri uri) {
+ assertEquals("mailto", uri.getScheme());
+ assertEquals("nobody", uri.getSchemeSpecificPart());
+ assertEquals("nobody", uri.getEncodedSchemeSpecificPart());
+
+ assertNull(uri.getFragment());
+ assertTrue(uri.isAbsolute());
+ assertTrue(uri.isOpaque());
+ assertFalse(uri.isRelative());
+ assertFalse(uri.isHierarchical());
+
+ assertNull(uri.getAuthority());
+ assertNull(uri.getEncodedAuthority());
+ assertNull(uri.getPath());
+ assertNull(uri.getEncodedPath());
+ assertNull(uri.getUserInfo());
+ assertNull(uri.getEncodedUserInfo());
+ assertNull(uri.getQuery());
+ assertNull(uri.getEncodedQuery());
+ assertNull(uri.getHost());
+ assertEquals(-1, uri.getPort());
+
+ assertTrue(uri.getPathSegments().isEmpty());
+ assertNull(uri.getLastPathSegment());
+
+ assertEquals("mailto:nobody", uri.toString());
+
+ Uri withFragment = uri.buildUpon().fragment("top").build();
+ assertEquals("mailto:nobody#top", withFragment.toString());
+ }
+
+ @SmallTest
+ public void testHierarchicalUris() {
+ testHierarchical("http", "google.com", "/p1/p2", "query", "fragment");
+ testHierarchical("file", null, "/p1/p2", null, null);
+ testHierarchical("content", "contact", "/p1/p2", null, null);
+ testHierarchical("http", "google.com", "/p1/p2", null, "fragment");
+ testHierarchical("http", "google.com", "", null, "fragment");
+ testHierarchical("http", "google.com", "", "query", "fragment");
+ testHierarchical("http", "google.com", "", "query", null);
+ testHierarchical("http", null, "/", "query", null);
+ }
+
+ private static void testHierarchical(String scheme, String authority,
+ String path, String query, String fragment) {
+ StringBuilder sb = new StringBuilder();
+
+ if (authority != null) {
+ sb.append("//").append(authority);
+ }
+ if (path != null) {
+ sb.append(path);
+ }
+ if (query != null) {
+ sb.append('?').append(query);
+ }
+
+ String ssp = sb.toString();
+
+ if (scheme != null) {
+ sb.insert(0, scheme + ":");
+ }
+ if (fragment != null) {
+ sb.append('#').append(fragment);
+ }
+
+ String uriString = sb.toString();
+
+ Uri uri = Uri.parse(uriString);
+
+ // Run these twice to test caching.
+ compareHierarchical(
+ uriString, ssp, uri, scheme, authority, path, query, fragment);
+ compareHierarchical(
+ uriString, ssp, uri, scheme, authority, path, query, fragment);
+
+ // Test rebuilt version.
+ uri = uri.buildUpon().build();
+
+ // Run these twice to test caching.
+ compareHierarchical(
+ uriString, ssp, uri, scheme, authority, path, query, fragment);
+ compareHierarchical(
+ uriString, ssp, uri, scheme, authority, path, query, fragment);
+
+ // The decoded and encoded versions of the inputs are all the same.
+ // We'll test the actual encoding decoding separately.
+
+ // Test building with encoded versions.
+ Uri built = new Uri.Builder()
+ .scheme(scheme)
+ .encodedAuthority(authority)
+ .encodedPath(path)
+ .encodedQuery(query)
+ .encodedFragment(fragment)
+ .build();
+
+ compareHierarchical(
+ uriString, ssp, built, scheme, authority, path, query, fragment);
+ compareHierarchical(
+ uriString, ssp, built, scheme, authority, path, query, fragment);
+
+ // Test building with decoded versions.
+ built = new Uri.Builder()
+ .scheme(scheme)
+ .authority(authority)
+ .path(path)
+ .query(query)
+ .fragment(fragment)
+ .build();
+
+ compareHierarchical(
+ uriString, ssp, built, scheme, authority, path, query, fragment);
+ compareHierarchical(
+ uriString, ssp, built, scheme, authority, path, query, fragment);
+
+ // Rebuild.
+ built = built.buildUpon().build();
+
+ compareHierarchical(
+ uriString, ssp, built, scheme, authority, path, query, fragment);
+ compareHierarchical(
+ uriString, ssp, built, scheme, authority, path, query, fragment);
+ }
+
+ private static void compareHierarchical(String uriString, String ssp,
+ Uri uri,
+ String scheme, String authority, String path, String query,
+ String fragment) {
+ assertEquals(scheme, uri.getScheme());
+ assertEquals(authority, uri.getAuthority());
+ assertEquals(authority, uri.getEncodedAuthority());
+ assertEquals(path, uri.getPath());
+ assertEquals(path, uri.getEncodedPath());
+ assertEquals(query, uri.getQuery());
+ assertEquals(query, uri.getEncodedQuery());
+ assertEquals(fragment, uri.getFragment());
+ assertEquals(fragment, uri.getEncodedFragment());
+ assertEquals(ssp, uri.getSchemeSpecificPart());
+
+ if (scheme != null) {
+ assertTrue(uri.isAbsolute());
+ assertFalse(uri.isRelative());
+ } else {
+ assertFalse(uri.isAbsolute());
+ assertTrue(uri.isRelative());
+ }
+
+ assertFalse(uri.isOpaque());
+ assertTrue(uri.isHierarchical());
+
+ assertEquals(uriString, uri.toString());
+ }
+
+ public void testEmptyToStringNotNull() {
+ assertNotNull(Uri.EMPTY.toString());
+ }
+
+ @SmallTest
+ public void testParcellingWithoutFragment() {
+ parcelAndUnparcel(Uri.parse("foo:bob%20lee"));
+ parcelAndUnparcel(Uri.fromParts("foo", "bob lee", "fragment"));
+ parcelAndUnparcel(new Uri.Builder()
+ .scheme("http")
+ .authority("crazybob.org")
+ .path("/rss/")
+ .encodedQuery("a=b")
+ .build());
+ }
+
+ public void testGetQueryParameter() {
+ String nestedUrl = "http://crazybob.org/?a=1&b=2";
+ Uri uri = Uri.parse("http://test/").buildUpon()
+ .appendQueryParameter("foo", "bar")
+ .appendQueryParameter("nested", nestedUrl).build();
+ assertEquals(nestedUrl, uri.getQueryParameter("nested"));
+ assertEquals(nestedUrl, uri.getQueryParameters("nested").get(0));
+ }
+
+ public void testGetQueryParameterWorkaround() {
+ // This was a workaround for a bug where getQueryParameter called
+ // getQuery() instead of getEncodedQuery().
+ String nestedUrl = "http://crazybob.org/?a=1&b=2";
+ Uri uri = Uri.parse("http://test/").buildUpon()
+ .appendQueryParameter("foo", "bar")
+ .appendQueryParameter("nested", Uri.encode(nestedUrl)).build();
+ assertEquals(nestedUrl, Uri.decode(uri.getQueryParameter("nested")));
+ assertEquals(nestedUrl,
+ Uri.decode(uri.getQueryParameters("nested").get(0)));
+ }
+
+ public void testGetQueryParameterEdgeCases() {
+ Uri uri;
+
+ // key at beginning of URL
+ uri = Uri.parse("http://test/").buildUpon()
+ .appendQueryParameter("key", "a b")
+ .appendQueryParameter("keya", "c d")
+ .appendQueryParameter("bkey", "e f")
+ .build();
+ assertEquals("a b", uri.getQueryParameter("key"));
+
+ // key in middle of URL
+ uri = Uri.parse("http://test/").buildUpon()
+ .appendQueryParameter("akeyb", "a b")
+ .appendQueryParameter("keya", "c d")
+ .appendQueryParameter("key", "e f")
+ .appendQueryParameter("bkey", "g h")
+ .build();
+ assertEquals("e f", uri.getQueryParameter("key"));
+
+ // key at end of URL
+ uri = Uri.parse("http://test/").buildUpon()
+ .appendQueryParameter("akeyb", "a b")
+ .appendQueryParameter("keya", "c d")
+ .appendQueryParameter("key", "y z")
+ .build();
+ assertEquals("y z", uri.getQueryParameter("key"));
+ }
+}
diff --git a/core/tests/coretests/src/android/os/AidlTest.aidl b/core/tests/coretests/src/android/os/AidlTest.aidl
new file mode 100644
index 0000000..6004f4b
--- /dev/null
+++ b/core/tests/coretests/src/android/os/AidlTest.aidl
@@ -0,0 +1,20 @@
+/* //device/apps/AndroidTests/src/com.android.unit_tests/AidlTest.aidl
+**
+** Copyright 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.os;
+
+parcelable AidlTest.TestParcelable;
diff --git a/core/tests/coretests/src/android/os/AidlTest.java b/core/tests/coretests/src/android/os/AidlTest.java
new file mode 100644
index 0000000..bf11d56
--- /dev/null
+++ b/core/tests/coretests/src/android/os/AidlTest.java
@@ -0,0 +1,422 @@
+/*
+ * 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.os;
+
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.test.suitebuilder.annotation.SmallTest;
+import com.google.android.collect.Lists;
+import junit.framework.TestCase;
+
+import java.util.List;
+
+public class AidlTest extends TestCase {
+
+ private IAidlTest mRemote;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ AidlObject mLocal = new AidlObject();
+ mRemote = IAidlTest.Stub.asInterface(mLocal);
+ }
+
+ private static boolean check(TestParcelable p, int n, String s) {
+ return p.mAnInt == n &&
+ ((s == null && p.mAString == null) || s.equals(p.mAString));
+ }
+
+ public static class TestParcelable implements Parcelable {
+ public int mAnInt;
+ public String mAString;
+
+ public TestParcelable() {
+ }
+
+ public TestParcelable(int i, String s) {
+ mAnInt = i;
+ mAString = s;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mAnInt);
+ parcel.writeString(mAString);
+ }
+
+ public void readFromParcel(Parcel parcel) {
+ mAnInt = parcel.readInt();
+ mAString = parcel.readString();
+ }
+
+ public static final Parcelable.Creator<TestParcelable> CREATOR
+ = new Parcelable.Creator<TestParcelable>() {
+ public TestParcelable createFromParcel(Parcel parcel) {
+ return new TestParcelable(parcel.readInt(),
+ parcel.readString());
+ }
+
+ public TestParcelable[] newArray(int size) {
+ return new TestParcelable[size];
+ }
+ };
+
+ public String toString() {
+ return super.toString() + " {" + mAnInt + "/" + mAString + "}";
+ }
+ }
+
+ private static class AidlObject extends IAidlTest.Stub {
+ public IInterface queryLocalInterface(String descriptor) {
+ // overriding this to return null makes asInterface always
+ // generate a proxy
+ return null;
+ }
+
+ public int intMethod(int a) {
+ return a;
+ }
+
+ public TestParcelable parcelableIn(TestParcelable p) {
+ p.mAnInt++;
+ return p;
+ }
+
+ public TestParcelable parcelableOut(TestParcelable p) {
+ p.mAnInt = 44;
+ return p;
+ }
+
+ public TestParcelable parcelableInOut(TestParcelable p) {
+ p.mAnInt++;
+ return p;
+ }
+
+ public TestParcelable listParcelableLonger(List<TestParcelable> list, int index) {
+ list.add(list.get(index));
+ return list.get(index);
+ }
+
+ public int listParcelableShorter(List<TestParcelable> list, int index) {
+ list.remove(index);
+ return list.size();
+ }
+
+ public boolean[] booleanArray(boolean[] a0, boolean[] a1, boolean[] a2) {
+ for (int i = 0; i < a0.length && i < a2.length; i++) {
+ a2[i] = a0[i];
+ }
+ for (int i = 0; i < a0.length && i < a1.length; i++) {
+ a1[i] = a0[i];
+ }
+ return a0;
+ }
+
+ public char[] charArray(char[] a0, char[] a1, char[] a2) {
+ for (int i = 0; i < a0.length && i < a2.length; i++) {
+ a2[i] = a0[i];
+ }
+ for (int i = 0; i < a0.length && i < a1.length; i++) {
+ a1[i] = a0[i];
+ }
+ return a0;
+ }
+
+ public int[] intArray(int[] a0, int[] a1, int[] a2) {
+ for (int i = 0; i < a0.length && i < a2.length; i++) {
+ a2[i] = a0[i];
+ }
+ for (int i = 0; i < a0.length && i < a1.length; i++) {
+ a1[i] = a0[i];
+ }
+ return a0;
+ }
+
+ public long[] longArray(long[] a0, long[] a1, long[] a2) {
+ for (int i = 0; i < a0.length && i < a2.length; i++) {
+ a2[i] = a0[i];
+ }
+ for (int i = 0; i < a0.length && i < a1.length; i++) {
+ a1[i] = a0[i];
+ }
+ return a0;
+ }
+
+ public float[] floatArray(float[] a0, float[] a1, float[] a2) {
+ for (int i = 0; i < a0.length && i < a2.length; i++) {
+ a2[i] = a0[i];
+ }
+ for (int i = 0; i < a0.length && i < a1.length; i++) {
+ a1[i] = a0[i];
+ }
+ return a0;
+ }
+
+ public double[] doubleArray(double[] a0, double[] a1, double[] a2) {
+ for (int i = 0; i < a0.length && i < a2.length; i++) {
+ a2[i] = a0[i];
+ }
+ for (int i = 0; i < a0.length && i < a1.length; i++) {
+ a1[i] = a0[i];
+ }
+ return a0;
+ }
+
+ public String[] stringArray(String[] a0, String[] a1, String[] a2) {
+ for (int i = 0; i < a0.length && i < a2.length; i++) {
+ a2[i] = a0[i];
+ }
+ for (int i = 0; i < a0.length && i < a1.length; i++) {
+ a1[i] = a0[i];
+ }
+ return a0;
+ }
+
+ public TestParcelable[] parcelableArray(TestParcelable[] a0,
+ TestParcelable[] a1, TestParcelable[] a2) {
+ return null;
+ }
+
+ public void voidSecurityException() {
+ throw new SecurityException("gotcha!");
+ }
+
+ public int intSecurityException() {
+ throw new SecurityException("gotcha!");
+ }
+ }
+
+ @SmallTest
+ public void testInt() throws Exception {
+ int result = mRemote.intMethod(42);
+ assertEquals(42, result);
+ }
+
+ @SmallTest
+ public void testParcelableIn() throws Exception {
+ TestParcelable arg = new TestParcelable(43, "hi");
+ TestParcelable result = mRemote.parcelableIn(arg);
+ assertNotSame(arg, result);
+
+ assertEquals(43, arg.mAnInt);
+ assertEquals(44, result.mAnInt);
+ }
+
+ @SmallTest
+ public void testParcelableOut() throws Exception {
+ TestParcelable arg = new TestParcelable(43, "hi");
+ TestParcelable result = mRemote.parcelableOut(arg);
+ assertNotSame(arg, result);
+ assertEquals(44, arg.mAnInt);
+ }
+
+ @SmallTest
+ public void testParcelableInOut() throws Exception {
+ TestParcelable arg = new TestParcelable(43, "hi");
+ TestParcelable result = mRemote.parcelableInOut(arg);
+ assertNotSame(arg, result);
+ assertEquals(44, arg.mAnInt);
+ }
+
+ @SmallTest
+ public void testListParcelableLonger() throws Exception {
+ List<TestParcelable> list = Lists.newArrayList();
+ list.add(new TestParcelable(33, "asdf"));
+ list.add(new TestParcelable(34, "jkl;"));
+
+ TestParcelable result = mRemote.listParcelableLonger(list, 1);
+
+// System.out.println("result=" + result);
+// for (TestParcelable p : list) {
+// System.out.println("longer: " + p);
+// }
+
+ assertEquals("jkl;", result.mAString);
+ assertEquals(34, result.mAnInt);
+
+ assertEquals(3, list.size());
+ assertTrue("out parameter 0: " + list.get(0), check(list.get(0), 33, "asdf"));
+ assertTrue("out parameter 1: " + list.get(1), check(list.get(1), 34, "jkl;"));
+ assertTrue("out parameter 2: " + list.get(2), check(list.get(2), 34, "jkl;"));
+
+ assertNotSame(list.get(1), list.get(2));
+ }
+
+ @SmallTest
+ public void testListParcelableShorter() throws Exception {
+ List<TestParcelable> list = Lists.newArrayList();
+ list.add(new TestParcelable(33, "asdf"));
+ list.add(new TestParcelable(34, "jkl;"));
+ list.add(new TestParcelable(35, "qwerty"));
+
+ int result = mRemote.listParcelableShorter(list, 2);
+
+// System.out.println("result=" + result);
+// for (TestParcelable p : list) {
+// System.out.println("shorter: " + p);
+// }
+
+ assertEquals(2, result);
+ assertEquals(2, list.size());
+ assertTrue("out parameter 0: " + list.get(0), check(list.get(0), 33, "asdf"));
+ assertTrue("out parameter 1: " + list.get(1), check(list.get(1), 34, "jkl;"));
+
+ assertNotSame(list.get(0), list.get(1));
+ }
+
+ @SmallTest
+ public void testArrays() throws Exception {
+ // boolean
+ boolean[] b0 = new boolean[]{true};
+ boolean[] b1 = new boolean[]{false, true};
+ boolean[] b2 = new boolean[]{true, false, true};
+ boolean[] br = mRemote.booleanArray(b0, b1, b2);
+
+ assertEquals(1, br.length);
+ assertTrue(br[0]);
+
+ assertTrue(b1[0]);
+ assertFalse(b1[1]);
+
+ assertTrue(b2[0]);
+ assertFalse(b2[1]);
+ assertTrue(b2[2]);
+
+ // char
+ char[] c0 = new char[]{'a'};
+ char[] c1 = new char[]{'b', 'c'};
+ char[] c2 = new char[]{'d', 'e', 'f'};
+ char[] cr = mRemote.charArray(c0, c1, c2);
+
+ assertEquals(1, cr.length);
+ assertEquals('a', cr[0]);
+
+ assertEquals('a', c1[0]);
+ assertEquals('\0', c1[1]);
+
+ assertEquals('a', c2[0]);
+ assertEquals('e', c2[1]);
+ assertEquals('f', c2[2]);
+
+ // int
+ int[] i0 = new int[]{34};
+ int[] i1 = new int[]{38, 39};
+ int[] i2 = new int[]{42, 43, 44};
+ int[] ir = mRemote.intArray(i0, i1, i2);
+
+ assertEquals(1, ir.length);
+ assertEquals(34, ir[0]);
+
+ assertEquals(34, i1[0]);
+ assertEquals(0, i1[1]);
+
+ assertEquals(34, i2[0]);
+ assertEquals(43, i2[1]);
+ assertEquals(44, i2[2]);
+
+ // long
+ long[] l0 = new long[]{50};
+ long[] l1 = new long[]{51, 52};
+ long[] l2 = new long[]{53, 54, 55};
+ long[] lr = mRemote.longArray(l0, l1, l2);
+
+ assertEquals(1, lr.length);
+ assertEquals(50, lr[0]);
+
+ assertEquals(50, l1[0]);
+ assertEquals(0, l1[1]);
+
+ assertEquals(50, l2[0]);
+ assertEquals(54, l2[1]);
+ assertEquals(55, l2[2]);
+
+ // float
+ float[] f0 = new float[]{90.1f};
+ float[] f1 = new float[]{90.2f, 90.3f};
+ float[] f2 = new float[]{90.4f, 90.5f, 90.6f};
+ float[] fr = mRemote.floatArray(f0, f1, f2);
+
+ assertEquals(1, fr.length);
+ assertEquals(90.1f, fr[0]);
+
+ assertEquals(90.1f, f1[0]);
+ assertEquals(0f, f1[1], 0.0f);
+
+ assertEquals(90.1f, f2[0]);
+ assertEquals(90.5f, f2[1]);
+ assertEquals(90.6f, f2[2]);
+
+ // double
+ double[] d0 = new double[]{100.1};
+ double[] d1 = new double[]{100.2, 100.3};
+ double[] d2 = new double[]{100.4, 100.5, 100.6};
+ double[] dr = mRemote.doubleArray(d0, d1, d2);
+
+ assertEquals(1, dr.length);
+ assertEquals(100.1, dr[0]);
+
+ assertEquals(100.1, d1[0]);
+ assertEquals(0, d1[1], 0.0);
+
+ assertEquals(100.1, d2[0]);
+ assertEquals(100.5, d2[1]);
+ assertEquals(100.6, d2[2]);
+
+ // String
+ String[] s0 = new String[]{"s0[0]"};
+ String[] s1 = new String[]{"s1[0]", "s1[1]"};
+ String[] s2 = new String[]{"s2[0]", "s2[1]", "s2[2]"};
+ String[] sr = mRemote.stringArray(s0, s1, s2);
+
+ assertEquals(1, sr.length);
+ assertEquals("s0[0]", sr[0]);
+
+ assertEquals("s0[0]", s1[0]);
+ assertNull(s1[1]);
+
+ assertEquals("s0[0]", s2[0]);
+ assertEquals("s2[1]", s2[1]);
+ assertEquals("s2[2]", s2[2]);
+ }
+
+ @SmallTest
+ public void testVoidSecurityException() throws Exception {
+ boolean good = false;
+ try {
+ mRemote.voidSecurityException();
+ } catch (SecurityException e) {
+ good = true;
+ }
+ assertEquals(good, true);
+ }
+
+ @SmallTest
+ public void testIntSecurityException() throws Exception {
+ boolean good = false;
+ try {
+ mRemote.intSecurityException();
+ } catch (SecurityException e) {
+ good = true;
+ }
+ assertEquals(good, true);
+ }
+}
+
diff --git a/core/tests/coretests/src/android/os/BroadcasterTest.java b/core/tests/coretests/src/android/os/BroadcasterTest.java
new file mode 100644
index 0000000..551ea8d
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BroadcasterTest.java
@@ -0,0 +1,232 @@
+/*
+ * 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.os;
+
+import android.os.Broadcaster;
+import android.os.Handler;
+import android.os.Message;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+public class BroadcasterTest extends TestCase {
+ private static final int MESSAGE_A = 23234;
+ private static final int MESSAGE_B = 3;
+ private static final int MESSAGE_C = 14;
+ private static final int MESSAGE_D = 95;
+
+ @MediumTest
+ public void test1() throws Exception {
+ /*
+ * One handler requestes one message, with a translation
+ */
+ HandlerTester tester = new HandlerTester() {
+ Handler h;
+
+ public void go() {
+ Broadcaster b = new Broadcaster();
+ h = new H();
+
+ b.request(MESSAGE_A, h, MESSAGE_B);
+
+ Message msg = new Message();
+ msg.what = MESSAGE_A;
+
+ b.broadcast(msg);
+ }
+
+ public void handleMessage(Message msg) {
+ if (msg.what == MESSAGE_B) {
+ success();
+ } else {
+ failure();
+ }
+ }
+ };
+ tester.doTest(1000);
+ }
+
+ private static class Tests2and3 extends HandlerTester {
+ Tests2and3(int n) {
+ N = n;
+ }
+
+ int N;
+ Handler mHandlers[];
+ boolean mSuccess[];
+
+ public void go() {
+ Broadcaster b = new Broadcaster();
+ mHandlers = new Handler[N];
+ mSuccess = new boolean[N];
+ for (int i = 0; i < N; i++) {
+ mHandlers[i] = new H();
+ mSuccess[i] = false;
+ b.request(MESSAGE_A, mHandlers[i], MESSAGE_B + i);
+ }
+
+ Message msg = new Message();
+ msg.what = MESSAGE_A;
+
+ b.broadcast(msg);
+ }
+
+ public void handleMessage(Message msg) {
+ int index = msg.what - MESSAGE_B;
+ if (index < 0 || index >= N) {
+ failure();
+ } else {
+ if (msg.getTarget() == mHandlers[index]) {
+ mSuccess[index] = true;
+ }
+ }
+ boolean winner = true;
+ for (int i = 0; i < N; i++) {
+ if (!mSuccess[i]) {
+ winner = false;
+ }
+ }
+ if (winner) {
+ success();
+ }
+ }
+ }
+
+ @MediumTest
+ public void test2() throws Exception {
+ /*
+ * 2 handlers request the same message, with different translations
+ */
+ HandlerTester tester = new Tests2and3(2);
+ tester.doTest(1000);
+ }
+
+ @MediumTest
+ public void test3() throws Exception {
+ /*
+ * 1000 handlers request the same message, with different translations
+ */
+ HandlerTester tester = new Tests2and3(10);
+ tester.doTest(1000);
+ }
+
+ @MediumTest
+ public void test4() throws Exception {
+ /*
+ * Two handlers request different messages, with translations, sending
+ * only one. The other one should never get sent.
+ */
+ HandlerTester tester = new HandlerTester() {
+ Handler h1;
+ Handler h2;
+
+ public void go() {
+ Broadcaster b = new Broadcaster();
+ h1 = new H();
+ h2 = new H();
+
+ b.request(MESSAGE_A, h1, MESSAGE_C);
+ b.request(MESSAGE_B, h2, MESSAGE_D);
+
+ Message msg = new Message();
+ msg.what = MESSAGE_A;
+
+ b.broadcast(msg);
+ }
+
+ public void handleMessage(Message msg) {
+ if (msg.what == MESSAGE_C && msg.getTarget() == h1) {
+ success();
+ } else {
+ failure();
+ }
+ }
+ };
+ tester.doTest(1000);
+ }
+
+ @MediumTest
+ public void test5() throws Exception {
+ /*
+ * Two handlers request different messages, with translations, sending
+ * only one. The other one should never get sent.
+ */
+ HandlerTester tester = new HandlerTester() {
+ Handler h1;
+ Handler h2;
+
+ public void go() {
+ Broadcaster b = new Broadcaster();
+ h1 = new H();
+ h2 = new H();
+
+ b.request(MESSAGE_A, h1, MESSAGE_C);
+ b.request(MESSAGE_B, h2, MESSAGE_D);
+
+ Message msg = new Message();
+ msg.what = MESSAGE_B;
+
+ b.broadcast(msg);
+ }
+
+ public void handleMessage(Message msg) {
+ if (msg.what == MESSAGE_D && msg.getTarget() == h2) {
+ success();
+ } else {
+ failure();
+ }
+ }
+ };
+ tester.doTest(1000);
+ }
+
+ @MediumTest
+ public void test6() throws Exception {
+ /*
+ * Two handlers request same message. Cancel the request for the
+ * 2nd handler, make sure the first still works.
+ */
+ HandlerTester tester = new HandlerTester() {
+ Handler h1;
+ Handler h2;
+
+ public void go() {
+ Broadcaster b = new Broadcaster();
+ h1 = new H();
+ h2 = new H();
+
+ b.request(MESSAGE_A, h1, MESSAGE_C);
+ b.request(MESSAGE_A, h2, MESSAGE_D);
+ b.cancelRequest(MESSAGE_A, h2, MESSAGE_D);
+
+ Message msg = new Message();
+ msg.what = MESSAGE_A;
+
+ b.broadcast(msg);
+ }
+
+ public void handleMessage(Message msg) {
+ if (msg.what == MESSAGE_C && msg.getTarget() == h1) {
+ success();
+ } else {
+ failure();
+ }
+ }
+ };
+ tester.doTest(1000);
+ }
+}
diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java
new file mode 100644
index 0000000..3758627
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BuildTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.os;
+
+import android.os.Build;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+/**
+ * Provides test cases for android.os.Build and, in turn, many of the
+ * system properties set by the build system.
+ */
+public class BuildTest extends TestCase {
+
+ private static final String TAG = "BuildTest";
+
+ /**
+ * Asserts that a String is non-null and non-empty. If it is not,
+ * an AssertionFailedError is thrown with the given message.
+ */
+ private static void assertNotEmpty(String message, String string) {
+ //Log.i(TAG, "" + message + ": " + string);
+ assertNotNull(message, string);
+ assertFalse(message, string.equals(""));
+ }
+
+ /**
+ * Asserts that a String is non-null and non-empty. If it is not,
+ * an AssertionFailedError is thrown.
+ */
+ private static void assertNotEmpty(String string) {
+ assertNotEmpty(null, string);
+ }
+
+ /**
+ * Asserts that all android.os.Build fields are non-empty and/or in a valid range.
+ */
+ @SmallTest
+ public void testBuildFields() throws Exception {
+ assertNotEmpty("ID", Build.ID);
+ assertNotEmpty("DISPLAY", Build.DISPLAY);
+ assertNotEmpty("PRODUCT", Build.PRODUCT);
+ assertNotEmpty("DEVICE", Build.DEVICE);
+ assertNotEmpty("BOARD", Build.BOARD);
+ assertNotEmpty("BRAND", Build.BRAND);
+ assertNotEmpty("MODEL", Build.MODEL);
+ assertNotEmpty("VERSION.INCREMENTAL", Build.VERSION.INCREMENTAL);
+ assertNotEmpty("VERSION.RELEASE", Build.VERSION.RELEASE);
+ assertNotEmpty("TYPE", Build.TYPE);
+ Assert.assertNotNull("TAGS", Build.TAGS); // TAGS is allowed to be empty.
+ assertNotEmpty("FINGERPRINT", Build.FINGERPRINT);
+ Assert.assertTrue("TIME", Build.TIME > 0);
+ assertNotEmpty("USER", Build.USER);
+ assertNotEmpty("HOST", Build.HOST);
+
+ // TODO: if any of the android.os.Build fields have additional constraints
+ // (e.g., must be a C identifier, must be a valid filename, must not contain any spaces)
+ // add tests for them.
+ }
+}
diff --git a/core/tests/coretests/src/android/os/FileObserverTest.java b/core/tests/coretests/src/android/os/FileObserverTest.java
new file mode 100644
index 0000000..ca4e0d6
--- /dev/null
+++ b/core/tests/coretests/src/android/os/FileObserverTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2006 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.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.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+public class FileObserverTest extends AndroidTestCase {
+ private Observer mObserver;
+ private File mTestFile;
+
+ private static class Observer extends FileObserver {
+ public List<Map> events = Lists.newArrayList();
+ public int totalEvents = 0;
+
+ public Observer(String path) {
+ super(path);
+ }
+
+ public void onEvent(int event, String path) {
+ synchronized (this) {
+ totalEvents++;
+ Map<String, Object> map = Maps.newHashMap();
+
+ map.put("event", event);
+ map.put("path", path);
+
+ events.add(map);
+
+ this.notifyAll();
+ }
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ mTestFile = File.createTempFile(".file_observer_test", ".txt");
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (mTestFile != null && mTestFile.exists()) {
+ mTestFile.delete();
+ }
+ }
+
+ @LargeTest
+ public void testRun() throws Exception {
+ // make file changes and wait for them
+ assertTrue(mTestFile.exists());
+ assertNotNull(mTestFile.getParent());
+
+ mObserver = new Observer(mTestFile.getParent());
+ mObserver.startWatching();
+
+ FileOutputStream out = new FileOutputStream(mTestFile);
+ try {
+ out.write(0x20);
+ waitForEvent(); // open
+ waitForEvent(); // modify
+
+ mTestFile.delete();
+ waitForEvent(); // modify
+ waitForEvent(); // delete
+
+ mObserver.stopWatching();
+
+ // Ensure that we have seen at least 3 events.
+ assertTrue(mObserver.totalEvents > 3);
+ } finally {
+ out.close();
+ }
+ }
+
+ private void waitForEvent() {
+ synchronized (mObserver) {
+ boolean done = false;
+ while (!done) {
+ try {
+ mObserver.wait(2000);
+ done = true;
+ } catch (InterruptedException e) {
+ }
+ }
+
+ Iterator<Map> it = mObserver.events.iterator();
+
+ while (it.hasNext()) {
+ Map map = it.next();
+ Log.i("FileObserverTest", "event: " + getEventString((Integer)map.get("event")) + " path: " + map.get("path"));
+ }
+
+ mObserver.events.clear();
+ }
+ }
+
+ private String getEventString(int event) {
+ switch (event) {
+ case FileObserver.ACCESS:
+ return "ACCESS";
+ case FileObserver.MODIFY:
+ return "MODIFY";
+ case FileObserver.ATTRIB:
+ return "ATTRIB";
+ case FileObserver.CLOSE_WRITE:
+ return "CLOSE_WRITE";
+ case FileObserver.CLOSE_NOWRITE:
+ return "CLOSE_NOWRITE";
+ case FileObserver.OPEN:
+ return "OPEN";
+ case FileObserver.MOVED_FROM:
+ return "MOVED_FROM";
+ case FileObserver.MOVED_TO:
+ return "MOVED_TO";
+ case FileObserver.CREATE:
+ return "CREATE";
+ case FileObserver.DELETE:
+ return "DELETE";
+ case FileObserver.DELETE_SELF:
+ return "DELETE_SELF";
+ case FileObserver.MOVE_SELF:
+ return "MOVE_SELF";
+ default:
+ return "UNKNOWN";
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
new file mode 100644
index 0000000..f12cbe1
--- /dev/null
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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.os;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.os.FileUtils.FileStatus;
+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.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import junit.framework.Assert;
+
+public class FileUtilsTest extends AndroidTestCase {
+ private static final String TEST_DATA =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+ private File mTestFile;
+ private File mCopyFile;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ File testDir = getContext().getDir("testing", Context.MODE_PRIVATE);
+ mTestFile = new File(testDir, "test.file");
+ mCopyFile = new File(testDir, "copy.file");
+ FileWriter writer = new FileWriter(mTestFile);
+ try {
+ writer.write(TEST_DATA, 0, TEST_DATA.length());
+ } finally {
+ writer.close();
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (mTestFile.exists()) mTestFile.delete();
+ if (mCopyFile.exists()) mCopyFile.delete();
+ }
+
+ @LargeTest
+ public void testGetFileStatus() {
+ final byte[] MAGIC = { 0xB, 0xE, 0x0, 0x5 };
+
+ try {
+ // truncate test file and write MAGIC (4 bytes) to it.
+ FileOutputStream os = new FileOutputStream(mTestFile, false);
+ os.write(MAGIC, 0, 4);
+ os.flush();
+ os.close();
+ } catch (FileNotFoundException e) {
+ Assert.fail("File was removed durning test" + e);
+ } catch (IOException e) {
+ Assert.fail("Unexpected IOException: " + e);
+ }
+
+ Assert.assertTrue(mTestFile.exists());
+ Assert.assertTrue(FileUtils.getFileStatus(mTestFile.getPath(), null));
+
+ FileStatus status1 = new FileStatus();
+ FileUtils.getFileStatus(mTestFile.getPath(), status1);
+
+ Assert.assertEquals(4, status1.size);
+
+ // Sleep for at least one second so that the modification time will be different.
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ }
+
+ try {
+ // append so we don't change the creation time.
+ FileOutputStream os = new FileOutputStream(mTestFile, true);
+ os.write(MAGIC, 0, 4);
+ os.flush();
+ os.close();
+ } catch (FileNotFoundException e) {
+ Assert.fail("File was removed durning test" + e);
+ } catch (IOException e) {
+ Assert.fail("Unexpected IOException: " + e);
+ }
+
+ FileStatus status2 = new FileStatus();
+ FileUtils.getFileStatus(mTestFile.getPath(), status2);
+
+ Assert.assertEquals(8, status2.size);
+ Assert.assertTrue(status2.mtime > status1.mtime);
+
+ mTestFile.delete();
+
+ Assert.assertFalse(mTestFile.exists());
+ Assert.assertFalse(FileUtils.getFileStatus(mTestFile.getPath(), null));
+ }
+
+ // TODO: test setPermissions(), getPermissions()
+
+ @MediumTest
+ public void testCopyFile() throws Exception {
+ assertFalse(mCopyFile.exists());
+ FileUtils.copyFile(mTestFile, mCopyFile);
+ assertTrue(mCopyFile.exists());
+ assertEquals(TEST_DATA, FileUtils.readTextFile(mCopyFile, 0, null));
+ }
+
+ @MediumTest
+ public void testCopyToFile() throws Exception {
+ final String s = "Foo Bar";
+ assertFalse(mCopyFile.exists());
+ FileUtils.copyToFile(new ByteArrayInputStream(s.getBytes()), mCopyFile); assertTrue(mCopyFile.exists());
+ assertEquals(s, FileUtils.readTextFile(mCopyFile, 0, null));
+ }
+
+ @MediumTest
+ public void testIsFilenameSafe() throws Exception {
+ assertTrue(FileUtils.isFilenameSafe(new File("foobar")));
+ assertTrue(FileUtils.isFilenameSafe(new File("a_b-c=d.e/0,1+23")));
+ assertFalse(FileUtils.isFilenameSafe(new File("foo*bar")));
+ assertFalse(FileUtils.isFilenameSafe(new File("foo\nbar")));
+ }
+
+ @MediumTest
+ public void testReadTextFile() throws Exception {
+ assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, 0, null));
+
+ assertEquals("ABCDE", FileUtils.readTextFile(mTestFile, 5, null));
+ assertEquals("ABCDE<>", FileUtils.readTextFile(mTestFile, 5, "<>"));
+ assertEquals(TEST_DATA.substring(0, 51) + "<>",
+ FileUtils.readTextFile(mTestFile, 51, "<>"));
+ assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, 52, "<>"));
+ assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, 100, "<>"));
+
+ assertEquals("vwxyz", FileUtils.readTextFile(mTestFile, -5, null));
+ assertEquals("<>vwxyz", FileUtils.readTextFile(mTestFile, -5, "<>"));
+ assertEquals("<>" + TEST_DATA.substring(1, 52),
+ FileUtils.readTextFile(mTestFile, -51, "<>"));
+ assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, -52, "<>"));
+ assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, -100, "<>"));
+ }
+
+ @MediumTest
+ public void testReadTextFileWithZeroLengthFile() throws Exception {
+ new FileOutputStream(mTestFile).close(); // Zero out the file
+ assertEquals("", FileUtils.readTextFile(mTestFile, 0, null));
+ assertEquals("", FileUtils.readTextFile(mTestFile, 1, "<>"));
+ assertEquals("", FileUtils.readTextFile(mTestFile, 10, "<>"));
+ assertEquals("", FileUtils.readTextFile(mTestFile, -1, "<>"));
+ assertEquals("", FileUtils.readTextFile(mTestFile, -10, "<>"));
+ }
+}
diff --git a/core/tests/coretests/src/android/os/HandlerTester.java b/core/tests/coretests/src/android/os/HandlerTester.java
new file mode 100644
index 0000000..a216a0b
--- /dev/null
+++ b/core/tests/coretests/src/android/os/HandlerTester.java
@@ -0,0 +1,89 @@
+/*
+ * 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.os;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+public abstract class HandlerTester extends Thread {
+ public abstract void go();
+ public abstract void handleMessage(Message msg);
+
+ public HandlerTester() {
+ }
+
+ public void doTest(long timeout) {
+ start();
+
+ synchronized (this) {
+ try {
+ wait(timeout);
+ quit();
+ }
+ catch (InterruptedException e) {
+ }
+ }
+
+ if (!mDone) {
+ throw new RuntimeException("test timed out");
+ }
+ if (!mSuccess) {
+ throw new RuntimeException("test failed");
+ }
+ }
+
+ public void success() {
+ mDone = true;
+ mSuccess = true;
+ }
+
+ public void failure() {
+ mDone = true;
+ mSuccess = false;
+ }
+
+ public void run() {
+ Looper.prepare();
+ mLooper = Looper.myLooper();
+ go();
+ Looper.loop();
+ }
+
+ protected class H extends Handler {
+ public void handleMessage(Message msg) {
+ synchronized (HandlerTester.this) {
+ // Call into them with our monitor locked, so they don't have
+ // to deal with other races.
+ HandlerTester.this.handleMessage(msg);
+ if (mDone) {
+ HandlerTester.this.notify();
+ quit();
+ }
+ }
+ }
+ }
+
+ private void quit() {
+ mLooper.quit();
+ }
+
+ private boolean mDone = false;
+ private boolean mSuccess = false;
+ private Looper mLooper;
+}
+
diff --git a/core/tests/coretests/src/android/os/HandlerThreadTest.java b/core/tests/coretests/src/android/os/HandlerThreadTest.java
new file mode 100644
index 0000000..9772aa4
--- /dev/null
+++ b/core/tests/coretests/src/android/os/HandlerThreadTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import junit.framework.TestCase;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.os.Process;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+
+public class HandlerThreadTest extends TestCase {
+ private static final int TEST_WHAT = 1;
+
+ private boolean mGotMessage = false;
+ private int mGotMessageWhat = -1;
+ private volatile boolean mDidSetup = false;
+ private volatile int mLooperTid = -1;
+
+ @MediumTest
+ public void testHandlerThread() throws Exception {
+ HandlerThread th1 = new HandlerThread("HandlerThreadTest") {
+ protected void onLooperPrepared() {
+ synchronized (HandlerThreadTest.this) {
+ mDidSetup = true;
+ mLooperTid = Process.myTid();
+ HandlerThreadTest.this.notify();
+ }
+ }
+ };
+
+ assertFalse(th1.isAlive());
+ assertNull(th1.getLooper());
+
+ th1.start();
+
+ assertTrue(th1.isAlive());
+ assertNotNull(th1.getLooper());
+
+ // The call to getLooper() internally blocks until the looper is
+ // available, but will call onLooperPrepared() after that. So we
+ // need to block here to wait for our onLooperPrepared() to complete
+ // and fill in the values we expect.
+ synchronized (this) {
+ while (!mDidSetup) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ // Make sure that the process was set.
+ assertNotSame(-1, mLooperTid);
+ // Make sure that the onLooperPrepared() was called on a different thread.
+ assertNotSame(Process.myTid(), mLooperTid);
+
+ final Handler h1 = new Handler(th1.getLooper()) {
+ public void handleMessage(Message msg) {
+ assertEquals(TEST_WHAT, msg.what);
+ // Ensure that we are running on the same thread in which the looper was setup on.
+ assertEquals(mLooperTid, Process.myTid());
+
+ mGotMessageWhat = msg.what;
+ mGotMessage = true;
+ synchronized(this) {
+ notifyAll();
+ }
+ }
+ };
+
+ Message msg = h1.obtainMessage(TEST_WHAT);
+
+ synchronized (h1) {
+ // wait until we have the lock before sending the message.
+ h1.sendMessage(msg);
+ try {
+ // wait for the message to be handled
+ h1.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+
+ assertTrue(mGotMessage);
+ assertEquals(TEST_WHAT, mGotMessageWhat);
+ }
+}
diff --git a/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java b/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java
new file mode 100644
index 0000000..6e50c7e
--- /dev/null
+++ b/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java
@@ -0,0 +1,1392 @@
+/**
+ * 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.os;
+
+import junit.framework.TestCase;
+
+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.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;
+
+/**
+ * Test for HierarchicalStateMachine.
+ *
+ * @author wink@google.com (Wink Saville)
+ */
+public class HierarchicalStateMachineTest extends TestCase {
+ private static final int TEST_CMD_1 = 1;
+ private static final int TEST_CMD_2 = 2;
+ private static final int TEST_CMD_3 = 3;
+ private static final int TEST_CMD_4 = 4;
+ private static final int TEST_CMD_5 = 5;
+ private static final int TEST_CMD_6 = 6;
+
+ private static final boolean DBG = true;
+ private static final boolean WAIT_FOR_DEBUGGER = false;
+ private static final String TAG = "HierarchicalStateMachineTest";
+
+ /**
+ * Tests that ProcessedMessage works as a circular buffer.
+ */
+ class StateMachine0 extends HierarchicalStateMachine {
+ StateMachine0(String name) {
+ super(name);
+ mThisSm = this;
+ setDbg(DBG);
+ setProcessedMessagesSize(3);
+
+ // Setup state machine with 1 state
+ addState(mS1);
+
+ // Set the initial state
+ setInitialState(mS1);
+ }
+
+ class S1 extends HierarchicalState {
+ @Override protected boolean processMessage(Message message) {
+ if (message.what == TEST_CMD_6) {
+ transitionToHaltingState();
+ }
+ return true;
+ }
+ }
+
+ @Override
+ protected void halting() {
+ synchronized (mThisSm) {
+ mThisSm.notifyAll();
+ }
+ }
+
+ private StateMachine0 mThisSm;
+ private S1 mS1 = new S1();
+ }
+
+ @SmallTest
+ public void testStateMachine0() throws Exception {
+ if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger();
+
+ StateMachine0 sm0 = new StateMachine0("sm0");
+ sm0.start();
+ if (sm0.isDbg()) Log.d(TAG, "testStateMachine0 E");
+
+ synchronized (sm0) {
+ // Send 6 messages
+ for (int i = 1; i <= 6; i++) {
+ sm0.sendMessage(sm0.obtainMessage(i));
+ }
+
+ try {
+ // wait for the messages to be handled
+ sm0.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "testStateMachine0: exception while waiting " + e.getMessage());
+ }
+ }
+
+ assertTrue(sm0.getProcessedMessagesCount() == 6);
+ assertTrue(sm0.getProcessedMessagesSize() == 3);
+
+ ProcessedMessages.Info pmi;
+ pmi = sm0.getProcessedMessage(0);
+ assertEquals(TEST_CMD_4, pmi.getWhat());
+ assertEquals(sm0.mS1, pmi.getState());
+ assertEquals(sm0.mS1, pmi.getOriginalState());
+
+ pmi = sm0.getProcessedMessage(1);
+ assertEquals(TEST_CMD_5, pmi.getWhat());
+ assertEquals(sm0.mS1, pmi.getState());
+ assertEquals(sm0.mS1, pmi.getOriginalState());
+
+ pmi = sm0.getProcessedMessage(2);
+ assertEquals(TEST_CMD_6, pmi.getWhat());
+ assertEquals(sm0.mS1, pmi.getState());
+ assertEquals(sm0.mS1, pmi.getOriginalState());
+
+ if (sm0.isDbg()) Log.d(TAG, "testStateMachine0 X");
+ }
+
+ /**
+ * This tests enter/exit and transitions to the same state.
+ * The state machine has one state, it receives two messages
+ * in state mS1. With the first message it transitions to
+ * itself which causes it to be exited and reentered.
+ */
+ class StateMachine1 extends HierarchicalStateMachine {
+ StateMachine1(String name) {
+ super(name);
+ mThisSm = this;
+ setDbg(DBG);
+
+ // Setup state machine with 1 state
+ addState(mS1);
+
+ // Set the initial state
+ setInitialState(mS1);
+ if (DBG) Log.d(TAG, "StateMachine1: ctor X");
+ }
+
+ class S1 extends HierarchicalState {
+ @Override protected void enter() {
+ mEnterCount++;
+ }
+
+ @Override protected boolean processMessage(Message message) {
+ if (message.what == TEST_CMD_1) {
+ assertEquals(1, mEnterCount);
+ assertEquals(0, mExitCount);
+ transitionTo(mS1);
+ } else if (message.what == TEST_CMD_2) {
+ assertEquals(2, mEnterCount);
+ assertEquals(1, mExitCount);
+ transitionToHaltingState();
+ }
+ return true;
+ }
+
+ @Override protected void exit() {
+ mExitCount++;
+ }
+ }
+
+ @Override
+ protected void halting() {
+ synchronized (mThisSm) {
+ mThisSm.notifyAll();
+ }
+ }
+
+ private StateMachine1 mThisSm;
+ private S1 mS1 = new S1();
+
+ private int mEnterCount;
+ private int mExitCount;
+ }
+
+ @SmallTest
+ public void testStateMachine1() throws Exception {
+ StateMachine1 sm1 = new StateMachine1("sm1");
+ sm1.start();
+ if (sm1.isDbg()) Log.d(TAG, "testStateMachine1 E");
+
+ synchronized (sm1) {
+ // Send two messages
+ sm1.sendMessage(sm1.obtainMessage(TEST_CMD_1));
+ sm1.sendMessage(sm1.obtainMessage(TEST_CMD_2));
+
+ try {
+ // wait for the messages to be handled
+ sm1.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "testStateMachine1: exception while waiting " + e.getMessage());
+ }
+ }
+
+ assertEquals(2, sm1.mEnterCount);
+ assertEquals(2, sm1.mExitCount);
+
+ assertTrue(sm1.getProcessedMessagesSize() == 2);
+
+ ProcessedMessages.Info pmi;
+ pmi = sm1.getProcessedMessage(0);
+ assertEquals(TEST_CMD_1, pmi.getWhat());
+ assertEquals(sm1.mS1, pmi.getState());
+ assertEquals(sm1.mS1, pmi.getOriginalState());
+
+ pmi = sm1.getProcessedMessage(1);
+ assertEquals(TEST_CMD_2, pmi.getWhat());
+ assertEquals(sm1.mS1, pmi.getState());
+ assertEquals(sm1.mS1, pmi.getOriginalState());
+
+ assertEquals(2, sm1.mEnterCount);
+ assertEquals(2, sm1.mExitCount);
+
+ if (sm1.isDbg()) Log.d(TAG, "testStateMachine1 X");
+ }
+
+ /**
+ * Test deferring messages and states with no parents. The state machine
+ * has two states, it receives two messages in state mS1 deferring them
+ * until what == TEST_CMD_2 and then transitions to state mS2. State
+ * mS2 then receives both of the deferred messages first TEST_CMD_1 and
+ * then TEST_CMD_2.
+ */
+ class StateMachine2 extends HierarchicalStateMachine {
+ StateMachine2(String name) {
+ super(name);
+ mThisSm = this;
+ setDbg(DBG);
+
+ // Setup the hierarchy
+ addState(mS1);
+ addState(mS2);
+
+ // Set the initial state
+ setInitialState(mS1);
+ if (DBG) Log.d(TAG, "StateMachine2: ctor X");
+ }
+
+ class S1 extends HierarchicalState {
+ @Override protected void enter() {
+ mDidEnter = true;
+ }
+
+ @Override protected boolean processMessage(Message message) {
+ deferMessage(message);
+ if (message.what == TEST_CMD_2) {
+ transitionTo(mS2);
+ }
+ return true;
+ }
+
+ @Override protected void exit() {
+ mDidExit = true;
+ }
+ }
+
+ class S2 extends HierarchicalState {
+ @Override protected boolean processMessage(Message message) {
+ if (message.what == TEST_CMD_2) {
+ transitionToHaltingState();
+ }
+ return true;
+ }
+ }
+
+ @Override
+ protected void halting() {
+ synchronized (mThisSm) {
+ mThisSm.notifyAll();
+ }
+ }
+
+ private StateMachine2 mThisSm;
+ private S1 mS1 = new S1();
+ private S2 mS2 = new S2();
+
+ private boolean mDidEnter = false;
+ private boolean mDidExit = false;
+ }
+
+ @SmallTest
+ public void testStateMachine2() throws Exception {
+ StateMachine2 sm2 = new StateMachine2("sm2");
+ sm2.start();
+ if (sm2.isDbg()) Log.d(TAG, "testStateMachine2 E");
+
+ synchronized (sm2) {
+ // Send two messages
+ sm2.sendMessage(sm2.obtainMessage(TEST_CMD_1));
+ sm2.sendMessage(sm2.obtainMessage(TEST_CMD_2));
+
+ try {
+ // wait for the messages to be handled
+ sm2.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "testStateMachine2: exception while waiting " + e.getMessage());
+ }
+ }
+
+ assertTrue(sm2.getProcessedMessagesSize() == 4);
+
+ ProcessedMessages.Info pmi;
+ pmi = sm2.getProcessedMessage(0);
+ assertEquals(TEST_CMD_1, pmi.getWhat());
+ assertEquals(sm2.mS1, pmi.getState());
+
+ pmi = sm2.getProcessedMessage(1);
+ assertEquals(TEST_CMD_2, pmi.getWhat());
+ assertEquals(sm2.mS1, pmi.getState());
+
+ pmi = sm2.getProcessedMessage(2);
+ assertEquals(TEST_CMD_1, pmi.getWhat());
+ assertEquals(sm2.mS2, pmi.getState());
+
+ pmi = sm2.getProcessedMessage(3);
+ assertEquals(TEST_CMD_2, pmi.getWhat());
+ assertEquals(sm2.mS2, pmi.getState());
+
+ assertTrue(sm2.mDidEnter);
+ assertTrue(sm2.mDidExit);
+
+ if (sm2.isDbg()) Log.d(TAG, "testStateMachine2 X");
+ }
+
+ /**
+ * Test that unhandled messages in a child are handled by the parent.
+ * When TEST_CMD_2 is received.
+ */
+ class StateMachine3 extends HierarchicalStateMachine {
+ StateMachine3(String name) {
+ super(name);
+ mThisSm = this;
+ setDbg(DBG);
+
+ // Setup the simplest hierarchy of two states
+ // mParentState and mChildState.
+ // (Use indentation to help visualize hierarchy)
+ addState(mParentState);
+ addState(mChildState, mParentState);
+
+ // Set the initial state will be the child
+ setInitialState(mChildState);
+ if (DBG) Log.d(TAG, "StateMachine3: ctor X");
+ }
+
+ class ParentState extends HierarchicalState {
+ @Override protected boolean processMessage(Message message) {
+ if (message.what == TEST_CMD_2) {
+ transitionToHaltingState();
+ }
+ return true;
+ }
+ }
+
+ class ChildState extends HierarchicalState {
+ @Override protected boolean processMessage(Message message) {
+ return false;
+ }
+ }
+
+ @Override
+ protected void halting() {
+ synchronized (mThisSm) {
+ mThisSm.notifyAll();
+ }
+ }
+
+ private StateMachine3 mThisSm;
+ private ParentState mParentState = new ParentState();
+ private ChildState mChildState = new ChildState();
+ }
+
+ @SmallTest
+ public void testStateMachine3() throws Exception {
+ StateMachine3 sm3 = new StateMachine3("sm3");
+ sm3.start();
+ if (sm3.isDbg()) Log.d(TAG, "testStateMachine3 E");
+
+ synchronized (sm3) {
+ // Send two messages
+ sm3.sendMessage(sm3.obtainMessage(TEST_CMD_1));
+ sm3.sendMessage(sm3.obtainMessage(TEST_CMD_2));
+
+ try {
+ // wait for the messages to be handled
+ sm3.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "testStateMachine3: exception while waiting " + e.getMessage());
+ }
+ }
+
+ assertTrue(sm3.getProcessedMessagesSize() == 2);
+
+ ProcessedMessages.Info pmi;
+ pmi = sm3.getProcessedMessage(0);
+ assertEquals(TEST_CMD_1, pmi.getWhat());
+ assertEquals(sm3.mParentState, pmi.getState());
+ assertEquals(sm3.mChildState, pmi.getOriginalState());
+
+ pmi = sm3.getProcessedMessage(1);
+ assertEquals(TEST_CMD_2, pmi.getWhat());
+ assertEquals(sm3.mParentState, pmi.getState());
+ assertEquals(sm3.mChildState, pmi.getOriginalState());
+
+ if (sm3.isDbg()) Log.d(TAG, "testStateMachine3 X");
+ }
+
+ /**
+ * Test a hierarchy of 3 states a parent and two children
+ * with transition from child 1 to child 2 and child 2
+ * lets the parent handle the messages.
+ */
+ class StateMachine4 extends HierarchicalStateMachine {
+ StateMachine4(String name) {
+ super(name);
+ mThisSm = this;
+ setDbg(DBG);
+
+ // Setup a hierarchy of three states
+ // mParentState, mChildState1 & mChildState2
+ // (Use indentation to help visualize hierarchy)
+ addState(mParentState);
+ addState(mChildState1, mParentState);
+ addState(mChildState2, mParentState);
+
+ // Set the initial state will be child 1
+ setInitialState(mChildState1);
+ if (DBG) Log.d(TAG, "StateMachine4: ctor X");
+ }
+
+ class ParentState extends HierarchicalState {
+ @Override protected boolean processMessage(Message message) {
+ if (message.what == TEST_CMD_2) {
+ transitionToHaltingState();
+ }
+ return true;
+ }
+ }
+
+ class ChildState1 extends HierarchicalState {
+ @Override protected boolean processMessage(Message message) {
+ transitionTo(mChildState2);
+ return true;
+ }
+ }
+
+ class ChildState2 extends HierarchicalState {
+ @Override protected boolean processMessage(Message message) {
+ return false;
+ }
+ }
+
+ @Override
+ protected void halting() {
+ synchronized (mThisSm) {
+ mThisSm.notifyAll();
+ }
+ }
+
+ private StateMachine4 mThisSm;
+ private ParentState mParentState = new ParentState();
+ private ChildState1 mChildState1 = new ChildState1();
+ private ChildState2 mChildState2 = new ChildState2();
+ }
+
+ @SmallTest
+ public void testStateMachine4() throws Exception {
+ StateMachine4 sm4 = new StateMachine4("sm4");
+ sm4.start();
+ if (sm4.isDbg()) Log.d(TAG, "testStateMachine4 E");
+
+ synchronized (sm4) {
+ // Send two messages
+ sm4.sendMessage(sm4.obtainMessage(TEST_CMD_1));
+ sm4.sendMessage(sm4.obtainMessage(TEST_CMD_2));
+
+ try {
+ // wait for the messages to be handled
+ sm4.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "testStateMachine4: exception while waiting " + e.getMessage());
+ }
+ }
+
+
+ assertTrue(sm4.getProcessedMessagesSize() == 2);
+
+ ProcessedMessages.Info pmi;
+ pmi = sm4.getProcessedMessage(0);
+ assertEquals(TEST_CMD_1, pmi.getWhat());
+ assertEquals(sm4.mChildState1, pmi.getState());
+ assertEquals(sm4.mChildState1, pmi.getOriginalState());
+
+ pmi = sm4.getProcessedMessage(1);
+ assertEquals(TEST_CMD_2, pmi.getWhat());
+ assertEquals(sm4.mParentState, pmi.getState());
+ assertEquals(sm4.mChildState2, pmi.getOriginalState());
+
+ if (sm4.isDbg()) Log.d(TAG, "testStateMachine4 X");
+ }
+
+ /**
+ * Test transition from one child to another of a "complex"
+ * hierarchy with two parents and multiple children.
+ */
+ class StateMachine5 extends HierarchicalStateMachine {
+ StateMachine5(String name) {
+ super(name);
+ mThisSm = this;
+ setDbg(DBG);
+
+ // Setup a hierarchy with two parents and some children.
+ // (Use indentation to help visualize hierarchy)
+ addState(mParentState1);
+ addState(mChildState1, mParentState1);
+ addState(mChildState2, mParentState1);
+
+ addState(mParentState2);
+ addState(mChildState3, mParentState2);
+ addState(mChildState4, mParentState2);
+ addState(mChildState5, mChildState4);
+
+ // Set the initial state will be the child
+ setInitialState(mChildState1);
+ if (DBG) Log.d(TAG, "StateMachine5: ctor X");
+ }
+
+ class ParentState1 extends HierarchicalState {
+ @Override protected void enter() {
+ mParentState1EnterCount += 1;
+ }
+ @Override protected boolean processMessage(Message message) {
+ return true;
+ }
+ @Override protected void exit() {
+ mParentState1ExitCount += 1;
+ }
+ }
+
+ class ChildState1 extends HierarchicalState {
+ @Override protected void enter() {
+ mChildState1EnterCount += 1;
+ }
+ @Override protected boolean processMessage(Message message) {
+ assertEquals(1, mParentState1EnterCount);
+ assertEquals(0, mParentState1ExitCount);
+ assertEquals(1, mChildState1EnterCount);
+ assertEquals(0, mChildState1ExitCount);
+ assertEquals(0, mChildState2EnterCount);
+ assertEquals(0, mChildState2ExitCount);
+ assertEquals(0, mParentState2EnterCount);
+ assertEquals(0, mParentState2ExitCount);
+ assertEquals(0, mChildState3EnterCount);
+ assertEquals(0, mChildState3ExitCount);
+ assertEquals(0, mChildState4EnterCount);
+ assertEquals(0, mChildState4ExitCount);
+ assertEquals(0, mChildState5EnterCount);
+ assertEquals(0, mChildState5ExitCount);
+
+ transitionTo(mChildState2);
+ return true;
+ }
+ @Override protected void exit() {
+ mChildState1ExitCount += 1;
+ }
+ }
+
+ class ChildState2 extends HierarchicalState {
+ @Override protected void enter() {
+ mChildState2EnterCount += 1;
+ }
+ @Override protected boolean processMessage(Message message) {
+ assertEquals(1, mParentState1EnterCount);
+ assertEquals(0, mParentState1ExitCount);
+ assertEquals(1, mChildState1EnterCount);
+ assertEquals(1, mChildState1ExitCount);
+ assertEquals(1, mChildState2EnterCount);
+ assertEquals(0, mChildState2ExitCount);
+ assertEquals(0, mParentState2EnterCount);
+ assertEquals(0, mParentState2ExitCount);
+ assertEquals(0, mChildState3EnterCount);
+ assertEquals(0, mChildState3ExitCount);
+ assertEquals(0, mChildState4EnterCount);
+ assertEquals(0, mChildState4ExitCount);
+ assertEquals(0, mChildState5EnterCount);
+ assertEquals(0, mChildState5ExitCount);
+
+ transitionTo(mChildState5);
+ return true;
+ }
+ @Override protected void exit() {
+ mChildState2ExitCount += 1;
+ }
+ }
+
+ class ParentState2 extends HierarchicalState {
+ @Override protected void enter() {
+ mParentState2EnterCount += 1;
+ }
+ @Override protected boolean processMessage(Message message) {
+ assertEquals(1, mParentState1EnterCount);
+ assertEquals(1, mParentState1ExitCount);
+ assertEquals(1, mChildState1EnterCount);
+ assertEquals(1, mChildState1ExitCount);
+ assertEquals(1, mChildState2EnterCount);
+ assertEquals(1, mChildState2ExitCount);
+ assertEquals(2, mParentState2EnterCount);
+ assertEquals(1, mParentState2ExitCount);
+ assertEquals(1, mChildState3EnterCount);
+ assertEquals(1, mChildState3ExitCount);
+ assertEquals(2, mChildState4EnterCount);
+ assertEquals(2, mChildState4ExitCount);
+ assertEquals(1, mChildState5EnterCount);
+ assertEquals(1, mChildState5ExitCount);
+
+ transitionToHaltingState();
+ return true;
+ }
+ @Override protected void exit() {
+ mParentState2ExitCount += 1;
+ }
+ }
+
+ class ChildState3 extends HierarchicalState {
+ @Override protected void enter() {
+ mChildState3EnterCount += 1;
+ }
+ @Override protected boolean processMessage(Message message) {
+ assertEquals(1, mParentState1EnterCount);
+ assertEquals(1, mParentState1ExitCount);
+ assertEquals(1, mChildState1EnterCount);
+ assertEquals(1, mChildState1ExitCount);
+ assertEquals(1, mChildState2EnterCount);
+ assertEquals(1, mChildState2ExitCount);
+ assertEquals(1, mParentState2EnterCount);
+ assertEquals(0, mParentState2ExitCount);
+ assertEquals(1, mChildState3EnterCount);
+ assertEquals(0, mChildState3ExitCount);
+ assertEquals(1, mChildState4EnterCount);
+ assertEquals(1, mChildState4ExitCount);
+ assertEquals(1, mChildState5EnterCount);
+ assertEquals(1, mChildState5ExitCount);
+
+ transitionTo(mChildState4);
+ return true;
+ }
+ @Override protected void exit() {
+ mChildState3ExitCount += 1;
+ }
+ }
+
+ class ChildState4 extends HierarchicalState {
+ @Override protected void enter() {
+ mChildState4EnterCount += 1;
+ }
+ @Override protected boolean processMessage(Message message) {
+ assertEquals(1, mParentState1EnterCount);
+ assertEquals(1, mParentState1ExitCount);
+ assertEquals(1, mChildState1EnterCount);
+ assertEquals(1, mChildState1ExitCount);
+ assertEquals(1, mChildState2EnterCount);
+ assertEquals(1, mChildState2ExitCount);
+ assertEquals(1, mParentState2EnterCount);
+ assertEquals(0, mParentState2ExitCount);
+ assertEquals(1, mChildState3EnterCount);
+ assertEquals(1, mChildState3ExitCount);
+ assertEquals(2, mChildState4EnterCount);
+ assertEquals(1, mChildState4ExitCount);
+ assertEquals(1, mChildState5EnterCount);
+ assertEquals(1, mChildState5ExitCount);
+
+ transitionTo(mParentState2);
+ return true;
+ }
+ @Override protected void exit() {
+ mChildState4ExitCount += 1;
+ }
+ }
+
+ class ChildState5 extends HierarchicalState {
+ @Override protected void enter() {
+ mChildState5EnterCount += 1;
+ }
+ @Override protected boolean processMessage(Message message) {
+ assertEquals(1, mParentState1EnterCount);
+ assertEquals(1, mParentState1ExitCount);
+ assertEquals(1, mChildState1EnterCount);
+ assertEquals(1, mChildState1ExitCount);
+ assertEquals(1, mChildState2EnterCount);
+ assertEquals(1, mChildState2ExitCount);
+ assertEquals(1, mParentState2EnterCount);
+ assertEquals(0, mParentState2ExitCount);
+ assertEquals(0, mChildState3EnterCount);
+ assertEquals(0, mChildState3ExitCount);
+ assertEquals(1, mChildState4EnterCount);
+ assertEquals(0, mChildState4ExitCount);
+ assertEquals(1, mChildState5EnterCount);
+ assertEquals(0, mChildState5ExitCount);
+
+ transitionTo(mChildState3);
+ return true;
+ }
+ @Override protected void exit() {
+ mChildState5ExitCount += 1;
+ }
+ }
+
+ @Override
+ protected void halting() {
+ synchronized (mThisSm) {
+ mThisSm.notifyAll();
+ }
+ }
+
+ private StateMachine5 mThisSm;
+ private ParentState1 mParentState1 = new ParentState1();
+ private ChildState1 mChildState1 = new ChildState1();
+ private ChildState2 mChildState2 = new ChildState2();
+ private ParentState2 mParentState2 = new ParentState2();
+ private ChildState3 mChildState3 = new ChildState3();
+ private ChildState4 mChildState4 = new ChildState4();
+ private ChildState5 mChildState5 = new ChildState5();
+
+ private int mParentState1EnterCount = 0;
+ private int mParentState1ExitCount = 0;
+ private int mChildState1EnterCount = 0;
+ private int mChildState1ExitCount = 0;
+ private int mChildState2EnterCount = 0;
+ private int mChildState2ExitCount = 0;
+ private int mParentState2EnterCount = 0;
+ private int mParentState2ExitCount = 0;
+ private int mChildState3EnterCount = 0;
+ private int mChildState3ExitCount = 0;
+ private int mChildState4EnterCount = 0;
+ private int mChildState4ExitCount = 0;
+ private int mChildState5EnterCount = 0;
+ private int mChildState5ExitCount = 0;
+ }
+
+ @SmallTest
+ public void testStateMachine5() throws Exception {
+ StateMachine5 sm5 = new StateMachine5("sm5");
+ sm5.start();
+ if (sm5.isDbg()) Log.d(TAG, "testStateMachine5 E");
+
+ synchronized (sm5) {
+ // Send 6 messages
+ sm5.sendMessage(sm5.obtainMessage(TEST_CMD_1));
+ sm5.sendMessage(sm5.obtainMessage(TEST_CMD_2));
+ sm5.sendMessage(sm5.obtainMessage(TEST_CMD_3));
+ sm5.sendMessage(sm5.obtainMessage(TEST_CMD_4));
+ sm5.sendMessage(sm5.obtainMessage(TEST_CMD_5));
+ sm5.sendMessage(sm5.obtainMessage(TEST_CMD_6));
+
+ try {
+ // wait for the messages to be handled
+ sm5.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "testStateMachine5: exception while waiting " + e.getMessage());
+ }
+ }
+
+
+ assertTrue(sm5.getProcessedMessagesSize() == 6);
+
+ assertEquals(1, sm5.mParentState1EnterCount);
+ assertEquals(1, sm5.mParentState1ExitCount);
+ assertEquals(1, sm5.mChildState1EnterCount);
+ assertEquals(1, sm5.mChildState1ExitCount);
+ assertEquals(1, sm5.mChildState2EnterCount);
+ assertEquals(1, sm5.mChildState2ExitCount);
+ assertEquals(2, sm5.mParentState2EnterCount);
+ assertEquals(2, sm5.mParentState2ExitCount);
+ assertEquals(1, sm5.mChildState3EnterCount);
+ assertEquals(1, sm5.mChildState3ExitCount);
+ assertEquals(2, sm5.mChildState4EnterCount);
+ assertEquals(2, sm5.mChildState4ExitCount);
+ assertEquals(1, sm5.mChildState5EnterCount);
+ assertEquals(1, sm5.mChildState5ExitCount);
+
+ ProcessedMessages.Info pmi;
+ pmi = sm5.getProcessedMessage(0);
+ assertEquals(TEST_CMD_1, pmi.getWhat());
+ assertEquals(sm5.mChildState1, pmi.getState());
+ assertEquals(sm5.mChildState1, pmi.getOriginalState());
+
+ pmi = sm5.getProcessedMessage(1);
+ assertEquals(TEST_CMD_2, pmi.getWhat());
+ assertEquals(sm5.mChildState2, pmi.getState());
+ assertEquals(sm5.mChildState2, pmi.getOriginalState());
+
+ pmi = sm5.getProcessedMessage(2);
+ assertEquals(TEST_CMD_3, pmi.getWhat());
+ assertEquals(sm5.mChildState5, pmi.getState());
+ assertEquals(sm5.mChildState5, pmi.getOriginalState());
+
+ pmi = sm5.getProcessedMessage(3);
+ assertEquals(TEST_CMD_4, pmi.getWhat());
+ assertEquals(sm5.mChildState3, pmi.getState());
+ assertEquals(sm5.mChildState3, pmi.getOriginalState());
+
+ pmi = sm5.getProcessedMessage(4);
+ assertEquals(TEST_CMD_5, pmi.getWhat());
+ assertEquals(sm5.mChildState4, pmi.getState());
+ assertEquals(sm5.mChildState4, pmi.getOriginalState());
+
+ pmi = sm5.getProcessedMessage(5);
+ assertEquals(TEST_CMD_6, pmi.getWhat());
+ assertEquals(sm5.mParentState2, pmi.getState());
+ assertEquals(sm5.mParentState2, pmi.getOriginalState());
+
+ if (sm5.isDbg()) Log.d(TAG, "testStateMachine5 X");
+ }
+
+ /**
+ * Test that the initial state enter is invoked immediately
+ * after construction and before any other messages arrive and that
+ * sendMessageDelayed works.
+ */
+ class StateMachine6 extends HierarchicalStateMachine {
+ StateMachine6(String name) {
+ super(name);
+ mThisSm = this;
+ setDbg(DBG);
+
+ // Setup state machine with 1 state
+ addState(mS1);
+
+ // Set the initial state
+ setInitialState(mS1);
+ if (DBG) Log.d(TAG, "StateMachine6: ctor X");
+ }
+
+ class S1 extends HierarchicalState {
+
+ @Override protected void enter() {
+ sendMessage(obtainMessage(TEST_CMD_1));
+ }
+
+ @Override protected boolean processMessage(Message message) {
+ if (message.what == TEST_CMD_1) {
+ mArrivalTimeMsg1 = SystemClock.elapsedRealtime();
+ } else if (message.what == TEST_CMD_2) {
+ mArrivalTimeMsg2 = SystemClock.elapsedRealtime();
+ transitionToHaltingState();
+ }
+ return true;
+ }
+
+ @Override protected void exit() {
+ }
+ }
+
+ @Override
+ protected void halting() {
+ synchronized (mThisSm) {
+ mThisSm.notifyAll();
+ }
+ }
+
+ private StateMachine6 mThisSm;
+ private S1 mS1 = new S1();
+
+ private long mArrivalTimeMsg1;
+ private long mArrivalTimeMsg2;
+ }
+
+ @SmallTest
+ public void testStateMachine6() throws Exception {
+ long sentTimeMsg2;
+ final int DELAY_TIME = 250;
+ final int DELAY_FUDGE = 20;
+
+ StateMachine6 sm6 = new StateMachine6("sm6");
+ sm6.start();
+ if (sm6.isDbg()) Log.d(TAG, "testStateMachine6 E");
+
+ synchronized (sm6) {
+ // Send a message
+ sentTimeMsg2 = SystemClock.elapsedRealtime();
+ sm6.sendMessageDelayed(sm6.obtainMessage(TEST_CMD_2), DELAY_TIME);
+
+ try {
+ // wait for the messages to be handled
+ sm6.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "testStateMachine6: exception while waiting " + e.getMessage());
+ }
+ }
+
+ /**
+ * TEST_CMD_1 was sent in enter and must always have been processed
+ * immediately after construction and hence the arrival time difference
+ * should always >= to the DELAY_TIME
+ */
+ long arrivalTimeDiff = sm6.mArrivalTimeMsg2 - sm6.mArrivalTimeMsg1;
+ long expectedDelay = DELAY_TIME - DELAY_FUDGE;
+ if (sm6.isDbg()) Log.d(TAG, "testStateMachine6: expect " + arrivalTimeDiff
+ + " >= " + expectedDelay);
+ assertTrue(arrivalTimeDiff >= expectedDelay);
+
+ if (sm6.isDbg()) Log.d(TAG, "testStateMachine6 X");
+ }
+
+ /**
+ * Test that enter is invoked immediately after exit. This validates
+ * that enter can be used to send a watch dog message for its state.
+ */
+ class StateMachine7 extends HierarchicalStateMachine {
+ private final int SM7_DELAY_TIME = 250;
+
+ StateMachine7(String name) {
+ super(name);
+ mThisSm = this;
+ setDbg(DBG);
+
+ // Setup state machine with 1 state
+ addState(mS1);
+ addState(mS2);
+
+ // Set the initial state
+ setInitialState(mS1);
+ if (DBG) Log.d(TAG, "StateMachine7: ctor X");
+ }
+
+ class S1 extends HierarchicalState {
+ @Override protected boolean processMessage(Message message) {
+ transitionTo(mS2);
+ return true;
+ }
+ @Override protected void exit() {
+ sendMessage(obtainMessage(TEST_CMD_2));
+ }
+ }
+
+ class S2 extends HierarchicalState {
+
+ @Override protected void enter() {
+ // Send a delayed message as a watch dog
+ sendMessageDelayed(obtainMessage(TEST_CMD_3), SM7_DELAY_TIME);
+ }
+
+ @Override protected boolean processMessage(Message message) {
+ if (message.what == TEST_CMD_2) {
+ mMsgCount += 1;
+ mArrivalTimeMsg2 = SystemClock.elapsedRealtime();
+ } else if (message.what == TEST_CMD_3) {
+ mMsgCount += 1;
+ mArrivalTimeMsg3 = SystemClock.elapsedRealtime();
+ }
+
+ if (mMsgCount == 2) {
+ transitionToHaltingState();
+ }
+ return true;
+ }
+
+ @Override protected void exit() {
+ }
+ }
+
+ @Override
+ protected void halting() {
+ synchronized (mThisSm) {
+ mThisSm.notifyAll();
+ }
+ }
+
+ private StateMachine7 mThisSm;
+ private S1 mS1 = new S1();
+ private S2 mS2 = new S2();
+
+ private int mMsgCount = 0;
+ private long mArrivalTimeMsg2;
+ private long mArrivalTimeMsg3;
+ }
+
+ @SmallTest
+ public void testStateMachine7() throws Exception {
+ long sentTimeMsg2;
+ final int SM7_DELAY_FUDGE = 20;
+
+ StateMachine7 sm7 = new StateMachine7("sm7");
+ sm7.start();
+ if (sm7.isDbg()) Log.d(TAG, "testStateMachine7 E");
+
+ synchronized (sm7) {
+ // Send a message
+ sentTimeMsg2 = SystemClock.elapsedRealtime();
+ sm7.sendMessage(sm7.obtainMessage(TEST_CMD_1));
+
+ try {
+ // wait for the messages to be handled
+ sm7.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "testStateMachine7: exception while waiting " + e.getMessage());
+ }
+ }
+
+ /**
+ * TEST_CMD_3 was sent in S2.enter with a delay and must always have been
+ * processed immediately after S1.exit. Since S1.exit sent TEST_CMD_2
+ * without a delay the arrival time difference should always >= to SM7_DELAY_TIME.
+ */
+ long arrivalTimeDiff = sm7.mArrivalTimeMsg3 - sm7.mArrivalTimeMsg2;
+ long expectedDelay = sm7.SM7_DELAY_TIME - SM7_DELAY_FUDGE;
+ if (sm7.isDbg()) Log.d(TAG, "testStateMachine7: expect " + arrivalTimeDiff
+ + " >= " + expectedDelay);
+ assertTrue(arrivalTimeDiff >= expectedDelay);
+
+ if (sm7.isDbg()) Log.d(TAG, "testStateMachine7 X");
+ }
+
+ /**
+ * Test unhandledMessage.
+ */
+ class StateMachineUnhandledMessage extends HierarchicalStateMachine {
+ StateMachineUnhandledMessage(String name) {
+ super(name);
+ mThisSm = this;
+ setDbg(DBG);
+
+ // Setup state machine with 1 state
+ addState(mS1);
+
+ // Set the initial state
+ setInitialState(mS1);
+ }
+
+ @Override protected void unhandledMessage(Message message) {
+ mUnhandledMessageCount += 1;
+ }
+
+ class S1 extends HierarchicalState {
+ @Override protected boolean processMessage(Message message) {
+ if (message.what == TEST_CMD_2) {
+ transitionToHaltingState();
+ }
+ return false;
+ }
+ }
+
+ @Override
+ protected void halting() {
+ synchronized (mThisSm) {
+ mThisSm.notifyAll();
+ }
+ }
+
+ private StateMachineUnhandledMessage mThisSm;
+ private int mUnhandledMessageCount;
+ private S1 mS1 = new S1();
+ }
+
+ @SmallTest
+ public void testStateMachineUnhandledMessage() throws Exception {
+
+ StateMachineUnhandledMessage sm = new StateMachineUnhandledMessage("sm");
+ sm.start();
+ if (sm.isDbg()) Log.d(TAG, "testStateMachineUnhandledMessage E");
+
+ synchronized (sm) {
+ // Send 2 messages
+ for (int i = 1; i <= 2; i++) {
+ sm.sendMessage(sm.obtainMessage(i));
+ }
+
+ try {
+ // wait for the messages to be handled
+ sm.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "testStateMachineUnhandledMessage: exception while waiting "
+ + e.getMessage());
+ }
+ }
+
+ assertTrue(sm.getProcessedMessagesCount() == 2);
+ assertEquals(2, sm.mUnhandledMessageCount);
+
+ if (sm.isDbg()) Log.d(TAG, "testStateMachineUnhandledMessage X");
+ }
+
+ /**
+ * Test state machines sharing the same thread/looper. Multiple instances
+ * of the same state machine will be created. They will all share the
+ * same thread and thus each can update <code>sharedCounter</code> which
+ * will be used to notify testStateMachineSharedThread that the test is
+ * complete.
+ */
+ class StateMachineSharedThread extends HierarchicalStateMachine {
+ StateMachineSharedThread(Looper looper, String name, int maxCount) {
+ super(looper, name);
+ mMaxCount = maxCount;
+ setDbg(DBG);
+
+ // Setup state machine with 1 state
+ addState(mS1);
+
+ // Set the initial state
+ setInitialState(mS1);
+ }
+
+ class S1 extends HierarchicalState {
+ @Override protected boolean processMessage(Message message) {
+ if (message.what == TEST_CMD_4) {
+ transitionToHaltingState();
+ }
+ return true;
+ }
+ }
+
+ @Override
+ protected void halting() {
+ // Update the shared counter, which is OK since all state
+ // machines are using the same thread.
+ sharedCounter += 1;
+ if (sharedCounter == mMaxCount) {
+ synchronized (waitObject) {
+ waitObject.notifyAll();
+ }
+ }
+ }
+
+ private int mMaxCount;
+ private S1 mS1 = new S1();
+ }
+ private static int sharedCounter = 0;
+ private static Object waitObject = new Object();
+
+ @SmallTest
+ public void testStateMachineSharedThread() throws Exception {
+ if (DBG) Log.d(TAG, "testStateMachineSharedThread E");
+
+ // Create and start the handler thread
+ HandlerThread smThread = new HandlerThread("testStateMachineSharedThread");
+ smThread.start();
+
+ // Create the state machines
+ StateMachineSharedThread sms[] = new StateMachineSharedThread[10];
+ for (int i = 0; i < sms.length; i++) {
+ sms[i] = new StateMachineSharedThread(smThread.getLooper(), "sm", sms.length);
+ sms[i].start();
+ }
+
+ synchronized (waitObject) {
+ // Send messages to each of the state machines
+ for (StateMachineSharedThread sm : sms) {
+ for (int i = 1; i <= 4; i++) {
+ sm.sendMessage(sm.obtainMessage(i));
+ }
+ }
+
+ // Wait for the last state machine to notify its done
+ try {
+ waitObject.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "testStateMachineSharedThread: exception while waiting "
+ + e.getMessage());
+ }
+ }
+
+ for (StateMachineSharedThread sm : sms) {
+ assertTrue(sm.getProcessedMessagesCount() == 4);
+ for (int i = 0; i < sm.getProcessedMessagesCount(); i++) {
+ ProcessedMessages.Info pmi = sm.getProcessedMessage(i);
+ assertEquals(i+1, pmi.getWhat());
+ assertEquals(sm.mS1, pmi.getState());
+ assertEquals(sm.mS1, pmi.getOriginalState());
+ }
+ }
+
+ if (DBG) Log.d(TAG, "testStateMachineSharedThread X");
+ }
+
+ @SmallTest
+ public void testHsm1() throws Exception {
+ if (DBG) Log.d(TAG, "testHsm1 E");
+
+ Hsm1 sm = Hsm1.makeHsm1();
+
+ // Send messages
+ sm.sendMessage(sm.obtainMessage(Hsm1.CMD_1));
+ sm.sendMessage(sm.obtainMessage(Hsm1.CMD_2));
+
+ synchronized (sm) {
+ // Wait for the last state machine to notify its done
+ try {
+ sm.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "testHsm1: exception while waiting " + e.getMessage());
+ }
+ }
+
+ assertEquals(7, sm.getProcessedMessagesCount());
+ ProcessedMessages.Info pmi = sm.getProcessedMessage(0);
+ assertEquals(Hsm1.CMD_1, pmi.getWhat());
+ assertEquals(sm.mS1, pmi.getState());
+ assertEquals(sm.mS1, pmi.getOriginalState());
+
+ pmi = sm.getProcessedMessage(1);
+ assertEquals(Hsm1.CMD_2, pmi.getWhat());
+ assertEquals(sm.mP1, pmi.getState());
+ assertEquals(sm.mS1, pmi.getOriginalState());
+
+ pmi = sm.getProcessedMessage(2);
+ assertEquals(Hsm1.CMD_2, pmi.getWhat());
+ assertEquals(sm.mS2, pmi.getState());
+ assertEquals(sm.mS2, pmi.getOriginalState());
+
+ pmi = sm.getProcessedMessage(3);
+ assertEquals(Hsm1.CMD_3, pmi.getWhat());
+ assertEquals(sm.mS2, pmi.getState());
+ assertEquals(sm.mS2, pmi.getOriginalState());
+
+ pmi = sm.getProcessedMessage(4);
+ assertEquals(Hsm1.CMD_3, pmi.getWhat());
+ assertEquals(sm.mP2, pmi.getState());
+ assertEquals(sm.mP2, pmi.getOriginalState());
+
+ pmi = sm.getProcessedMessage(5);
+ assertEquals(Hsm1.CMD_4, pmi.getWhat());
+ assertEquals(sm.mP2, pmi.getState());
+ assertEquals(sm.mP2, pmi.getOriginalState());
+
+ pmi = sm.getProcessedMessage(6);
+ assertEquals(Hsm1.CMD_5, pmi.getWhat());
+ assertEquals(sm.mP2, pmi.getState());
+ assertEquals(sm.mP2, pmi.getOriginalState());
+
+ if (DBG) Log.d(TAG, "testStateMachineSharedThread X");
+ }
+}
+
+class Hsm1 extends HierarchicalStateMachine {
+ private static final String TAG = "hsm1";
+
+ public static final int CMD_1 = 1;
+ public static final int CMD_2 = 2;
+ public static final int CMD_3 = 3;
+ public static final int CMD_4 = 4;
+ public static final int CMD_5 = 5;
+
+ public static Hsm1 makeHsm1() {
+ Log.d(TAG, "makeHsm1 E");
+ Hsm1 sm = new Hsm1("hsm1");
+ sm.start();
+ Log.d(TAG, "makeHsm1 X");
+ return sm;
+ }
+
+ Hsm1(String name) {
+ super(name);
+ Log.d(TAG, "ctor E");
+
+ // Add states, use indentation to show hierarchy
+ addState(mP1);
+ addState(mS1, mP1);
+ addState(mS2, mP1);
+ addState(mP2);
+
+ // Set the initial state
+ setInitialState(mS1);
+ Log.d(TAG, "ctor X");
+ }
+
+ class P1 extends HierarchicalState {
+ @Override protected void enter() {
+ Log.d(TAG, "P1.enter");
+ }
+ @Override protected boolean processMessage(Message message) {
+ boolean retVal;
+ Log.d(TAG, "P1.processMessage what=" + message.what);
+ switch(message.what) {
+ case CMD_2:
+ // CMD_2 will arrive in mS2 before CMD_3
+ sendMessage(obtainMessage(CMD_3));
+ deferMessage(message);
+ transitionTo(mS2);
+ retVal = true;
+ break;
+ default:
+ // Any message we don't understand in this state invokes unhandledMessage
+ retVal = false;
+ break;
+ }
+ return retVal;
+ }
+ @Override protected void exit() {
+ Log.d(TAG, "P1.exit");
+ }
+ }
+
+ class S1 extends HierarchicalState {
+ @Override protected void enter() {
+ Log.d(TAG, "S1.enter");
+ }
+ @Override protected boolean processMessage(Message message) {
+ Log.d(TAG, "S1.processMessage what=" + message.what);
+ if (message.what == CMD_1) {
+ // Transition to ourself to show that enter/exit is called
+ transitionTo(mS1);
+ return true;
+ } else {
+ // Let parent process all other messages
+ return false;
+ }
+ }
+ @Override protected void exit() {
+ Log.d(TAG, "S1.exit");
+ }
+ }
+
+ class S2 extends HierarchicalState {
+ @Override protected void enter() {
+ Log.d(TAG, "S2.enter");
+ }
+ @Override protected boolean processMessage(Message message) {
+ boolean retVal;
+ Log.d(TAG, "S2.processMessage what=" + message.what);
+ switch(message.what) {
+ case(CMD_2):
+ sendMessage(obtainMessage(CMD_4));
+ retVal = true;
+ break;
+ case(CMD_3):
+ deferMessage(message);
+ transitionTo(mP2);
+ retVal = true;
+ break;
+ default:
+ retVal = false;
+ break;
+ }
+ return retVal;
+ }
+ @Override protected void exit() {
+ Log.d(TAG, "S2.exit");
+ }
+ }
+
+ class P2 extends HierarchicalState {
+ @Override protected void enter() {
+ Log.d(TAG, "P2.enter");
+ sendMessage(obtainMessage(CMD_5));
+ }
+ @Override protected boolean processMessage(Message message) {
+ Log.d(TAG, "P2.processMessage what=" + message.what);
+ switch(message.what) {
+ case(CMD_3):
+ break;
+ case(CMD_4):
+ break;
+ case(CMD_5):
+ transitionToHaltingState();
+ break;
+ }
+ return true;
+ }
+ @Override protected void exit() {
+ Log.d(TAG, "P2.exit");
+ }
+ }
+
+ @Override
+ protected void halting() {
+ Log.d(TAG, "halting");
+ synchronized (this) {
+ this.notifyAll();
+ }
+ }
+
+ P1 mP1 = new P1();
+ S1 mS1 = new S1();
+ S2 mS2 = new S2();
+ P2 mP2 = new P2();
+}
diff --git a/core/tests/coretests/src/android/os/IAidlTest.aidl b/core/tests/coretests/src/android/os/IAidlTest.aidl
new file mode 100644
index 0000000..a09022e
--- /dev/null
+++ b/core/tests/coretests/src/android/os/IAidlTest.aidl
@@ -0,0 +1,47 @@
+/* //device/apps/AndroidTests/src/com.android.unit_tests/IAidlTest.aidl
+**
+** Copyright 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.os;
+
+import android.os.AidlTest;
+
+interface IAidlTest {
+ int intMethod(int a);
+
+ AidlTest.TestParcelable parcelableIn(in AidlTest.TestParcelable p);
+ AidlTest.TestParcelable parcelableOut(out AidlTest.TestParcelable p);
+ AidlTest.TestParcelable parcelableInOut(inout AidlTest.TestParcelable p);
+
+ AidlTest.TestParcelable listParcelableLonger(
+ inout List<AidlTest.TestParcelable> list, int index);
+ int listParcelableShorter(
+ inout List<AidlTest.TestParcelable> list, int index);
+
+ boolean[] booleanArray(in boolean[] a0, out boolean[] a1, inout boolean[] a2);
+ char[] charArray(in char[] a0, out char[] a1, inout char[] a2);
+ int[] intArray(in int[] a0, out int[] a1, inout int[] a2);
+ long[] longArray(in long[] a0, out long[] a1, inout long[] a2);
+ float[] floatArray(in float[] a0, out float[] a1, inout float[] a2);
+ double[] doubleArray(in double[] a0, out double[] a1, inout double[] a2);
+ String[] stringArray(in String[] a0, out String[] a1, inout String[] a2);
+ AidlTest.TestParcelable[] parcelableArray(in AidlTest.TestParcelable[] a0,
+ out AidlTest.TestParcelable[] a1,
+ inout AidlTest.TestParcelable[] a2);
+
+ void voidSecurityException();
+ int intSecurityException();
+}
diff --git a/core/tests/coretests/src/android/os/IdleHandlerTest.java b/core/tests/coretests/src/android/os/IdleHandlerTest.java
new file mode 100644
index 0000000..6c0a862
--- /dev/null
+++ b/core/tests/coretests/src/android/os/IdleHandlerTest.java
@@ -0,0 +1,199 @@
+/*
+ * 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.os;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue.IdleHandler;
+import android.test.suitebuilder.annotation.MediumTest;
+import junit.framework.TestCase;
+
+public class IdleHandlerTest extends TestCase {
+
+ private static class BaseTestHandler extends TestHandlerThread {
+ Handler mHandler;
+
+ public BaseTestHandler() {
+ }
+
+ public void go() {
+ mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ BaseTestHandler.this.handleMessage(msg);
+ }
+ };
+ }
+
+ public void addIdleHandler() {
+ Looper.myQueue().addIdleHandler(new IdleHandler() {
+ public boolean queueIdle() {
+ return BaseTestHandler.this.queueIdle();
+ }
+ });
+ }
+
+ public void handleMessage(Message msg) {
+ }
+
+ public boolean queueIdle() {
+ return false;
+ }
+ }
+
+ @MediumTest
+ public void testOneShotFirst() throws Exception {
+ TestHandlerThread tester = new BaseTestHandler() {
+ int mCount;
+
+ public void go() {
+ super.go();
+ mCount = 0;
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(0), 100);
+ addIdleHandler();
+ }
+
+ public void handleMessage(Message msg) {
+ if (msg.what == 0) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(1), 100);
+ } else if (msg.what == 1) {
+ if (mCount == 1) {
+ success();
+ } else {
+ failure(new RuntimeException(
+ "Idle handler called " + mCount + " times"));
+ }
+ }
+ }
+
+ public boolean queueIdle() {
+ mCount++;
+ return false;
+ }
+ };
+
+ tester.doTest(1000);
+ }
+
+ @MediumTest
+ public void testOneShotLater() throws Exception {
+ TestHandlerThread tester = new BaseTestHandler() {
+ int mCount;
+
+ public void go() {
+ super.go();
+ mCount = 0;
+ mHandler.sendMessage(mHandler.obtainMessage(0));
+ }
+
+ public void handleMessage(Message msg) {
+ if (msg.what == 0) {
+ addIdleHandler();
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(1), 100);
+ } else if (msg.what == 1) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(2), 100);
+ } else if (msg.what == 2) {
+ if (mCount == 1) {
+ success();
+ } else {
+ failure(new RuntimeException(
+ "Idle handler called " + mCount + " times"));
+ }
+ }
+ }
+
+ public boolean queueIdle() {
+ mCount++;
+ return false;
+ }
+ };
+
+ tester.doTest(1000);
+ }
+
+
+ @MediumTest
+ public void testRepeatedFirst() throws Exception {
+ TestHandlerThread tester = new BaseTestHandler() {
+ int mCount;
+
+ public void go() {
+ super.go();
+ mCount = 0;
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(0), 100);
+ addIdleHandler();
+ }
+
+ public void handleMessage(Message msg) {
+ if (msg.what == 0) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(1), 100);
+ } else if (msg.what == 1) {
+ if (mCount == 2) {
+ success();
+ } else {
+ failure(new RuntimeException(
+ "Idle handler called " + mCount + " times"));
+ }
+ }
+ }
+
+ public boolean queueIdle() {
+ mCount++;
+ return true;
+ }
+ };
+
+ tester.doTest(1000);
+ }
+
+ @MediumTest
+ public void testRepeatedLater() throws Exception {
+ TestHandlerThread tester = new BaseTestHandler() {
+ int mCount;
+
+ public void go() {
+ super.go();
+ mCount = 0;
+ mHandler.sendMessage(mHandler.obtainMessage(0));
+ }
+
+ public void handleMessage(Message msg) {
+ if (msg.what == 0) {
+ addIdleHandler();
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(1), 100);
+ } else if (msg.what == 1) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(2), 100);
+ } else if (msg.what == 2) {
+ if (mCount == 2) {
+ success();
+ } else {
+ failure(new RuntimeException(
+ "Idle handler called " + mCount + " times"));
+ }
+ }
+ }
+
+ public boolean queueIdle() {
+ mCount++;
+ return true;
+ }
+ };
+
+ tester.doTest(1000);
+ }
+}
+
diff --git a/core/tests/coretests/src/android/os/MemoryFileTest.java b/core/tests/coretests/src/android/os/MemoryFileTest.java
new file mode 100644
index 0000000..411bdaa
--- /dev/null
+++ b/core/tests/coretests/src/android/os/MemoryFileTest.java
@@ -0,0 +1,427 @@
+/*
+ * 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.os;
+
+import android.os.MemoryFile;
+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.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+public class MemoryFileTest extends AndroidTestCase {
+
+ private void compareBuffers(byte[] buffer1, byte[] buffer2, int length) throws Exception {
+ for (int i = 0; i < length; i++) {
+ if (buffer1[i] != buffer2[i]) {
+ throw new Exception("readBytes did not read back what writeBytes wrote");
+ }
+ }
+ }
+
+ /**
+ * Keep allocating new files till the system purges them.
+ */
+ @MediumTest
+ public void testPurge() throws Exception {
+ List<MemoryFile> files = new ArrayList<MemoryFile>();
+ while (true) {
+ MemoryFile newFile = new MemoryFile("MemoryFileTest", 1000000);
+ newFile.allowPurging(true);
+ newFile.writeBytes(testString, 0, 0, testString.length);
+ files.add(newFile);
+ for (MemoryFile file : files) {
+ try {
+ file.readBytes(testString, 0, 0, testString.length);
+ } catch (IOException e) {
+ // Expected
+ for (MemoryFile fileToClose : files) {
+ fileToClose.close();
+ }
+ return;
+ }
+ }
+ }
+ }
+
+ @SmallTest
+ public void testRun() throws Exception {
+ MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
+
+ byte[] buffer = new byte[testString.length];
+
+ // check low level accessors
+ file.writeBytes(testString, 0, 2000, testString.length);
+ file.readBytes(buffer, 2000, 0, testString.length);
+ compareBuffers(testString, buffer, testString.length);
+
+ // check streams
+ buffer = new byte[testString.length];
+
+ OutputStream os = file.getOutputStream();
+ os.write(testString);
+
+ InputStream is = file.getInputStream();
+ is.mark(testString.length);
+ is.read(buffer);
+ compareBuffers(testString, buffer, testString.length);
+
+ // test mark/reset
+ buffer = new byte[testString.length];
+ is.reset();
+ is.read(buffer);
+ compareBuffers(testString, buffer, testString.length);
+
+ file.close();
+ }
+
+ // Tests for the IndexOutOfBoundsException cases in read().
+
+ private void readIndexOutOfBoundsException(int offset, int count, String msg)
+ throws Exception {
+ MemoryFile file = new MemoryFile("MemoryFileTest", testString.length);
+ try {
+ file.writeBytes(testString, 0, 0, testString.length);
+ InputStream is = file.getInputStream();
+ byte[] buffer = new byte[testString.length + 10];
+ try {
+ is.read(buffer, offset, count);
+ fail(msg);
+ } catch (IndexOutOfBoundsException ex) {
+ // this is what should happen
+ } finally {
+ is.close();
+ }
+ } finally {
+ file.close();
+ }
+ }
+
+ @SmallTest
+ public void testReadNegativeOffset() throws Exception {
+ readIndexOutOfBoundsException(-1, 5,
+ "read() with negative offset should throw IndexOutOfBoundsException");
+ }
+
+ @SmallTest
+ public void testReadNegativeCount() throws Exception {
+ readIndexOutOfBoundsException(5, -1,
+ "read() with negative length should throw IndexOutOfBoundsException");
+ }
+
+ @SmallTest
+ public void testReadOffsetOverflow() throws Exception {
+ readIndexOutOfBoundsException(testString.length + 10, 5,
+ "read() with offset outside buffer should throw IndexOutOfBoundsException");
+ }
+
+ @SmallTest
+ public void testReadOffsetCountOverflow() throws Exception {
+ readIndexOutOfBoundsException(testString.length, 11,
+ "read() with offset + count outside buffer should throw IndexOutOfBoundsException");
+ }
+
+ // Test behavior of read() at end of file
+ @SmallTest
+ public void testReadEOF() throws Exception {
+ MemoryFile file = new MemoryFile("MemoryFileTest", testString.length);
+ try {
+ file.writeBytes(testString, 0, 0, testString.length);
+ InputStream is = file.getInputStream();
+ try {
+ byte[] buffer = new byte[testString.length + 10];
+ // read() with count larger than data should succeed, and return # of bytes read
+ assertEquals(testString.length, is.read(buffer));
+ compareBuffers(testString, buffer, testString.length);
+ // Read at EOF should return -1
+ assertEquals(-1, is.read());
+ } finally {
+ is.close();
+ }
+ } finally {
+ file.close();
+ }
+ }
+
+ // Tests that close() is idempotent
+ @SmallTest
+ public void testCloseClose() throws Exception {
+ MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
+ byte[] data = new byte[512];
+ file.writeBytes(data, 0, 0, 128);
+ file.close();
+ file.close();
+ }
+
+ // Tests that we can't read from a closed memory file
+ @SmallTest
+ public void testCloseRead() throws Exception {
+ MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
+ file.close();
+
+ try {
+ byte[] data = new byte[512];
+ assertEquals(128, file.readBytes(data, 0, 0, 128));
+ fail("readBytes() after close() did not throw IOException.");
+ } catch (IOException e) {
+ // this is what should happen
+ }
+ }
+
+ // Tests that we can't write to a closed memory file
+ @SmallTest
+ public void testCloseWrite() throws Exception {
+ MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
+ file.close();
+
+ try {
+ byte[] data = new byte[512];
+ file.writeBytes(data, 0, 0, 128);
+ fail("writeBytes() after close() did not throw IOException.");
+ } catch (IOException e) {
+ // this is what should happen
+ }
+ }
+
+ // Tests that we can't call allowPurging() after close()
+ @SmallTest
+ public void testCloseAllowPurging() throws Exception {
+ MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
+ byte[] data = new byte[512];
+ file.writeBytes(data, 0, 0, 128);
+ file.close();
+
+ try {
+ file.allowPurging(true);
+ fail("allowPurging() after close() did not throw IOException.");
+ } catch (IOException e) {
+ // this is what should happen
+ }
+ }
+
+ // Tests that we don't leak file descriptors or mmap areas
+ @LargeTest
+ public void testCloseLeak() throws Exception {
+ // open enough memory files that we should run out of
+ // file descriptors or address space if we leak either.
+ for (int i = 0; i < 1025; i++) {
+ MemoryFile file = new MemoryFile("MemoryFileTest", 5000000);
+ file.writeBytes(testString, 0, 0, testString.length);
+ file.close();
+ }
+ }
+
+ @SmallTest
+ public void testIsMemoryFile() throws Exception {
+ MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
+ FileDescriptor fd = file.getFileDescriptor();
+ assertNotNull(fd);
+ assertTrue(fd.valid());
+ assertTrue(MemoryFile.isMemoryFile(fd));
+ file.close();
+
+ assertFalse(MemoryFile.isMemoryFile(FileDescriptor.in));
+ assertFalse(MemoryFile.isMemoryFile(FileDescriptor.out));
+ assertFalse(MemoryFile.isMemoryFile(FileDescriptor.err));
+
+ File tempFile = File.createTempFile("MemoryFileTest",".tmp", getContext().getFilesDir());
+ assertNotNull(file);
+ FileOutputStream out = null;
+ try {
+ out = new FileOutputStream(tempFile);
+ FileDescriptor fileFd = out.getFD();
+ assertNotNull(fileFd);
+ assertFalse(MemoryFile.isMemoryFile(fileFd));
+ } finally {
+ if (out != null) {
+ out.close();
+ }
+ tempFile.delete();
+ }
+ }
+
+ @SmallTest
+ public void testFileDescriptor() throws Exception {
+ MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
+ MemoryFile ref = new MemoryFile(file.getFileDescriptor(), file.length(), "r");
+ byte[] buffer;
+
+ // write to original, read from reference
+ file.writeBytes(testString, 0, 2000, testString.length);
+ buffer = new byte[testString.length];
+ ref.readBytes(buffer, 2000, 0, testString.length);
+ compareBuffers(testString, buffer, testString.length);
+
+ file.close();
+ ref.close(); // Doesn't actually do anything, since the file descriptor is not dup(2):ed
+ }
+
+ private static final byte[] testString = new byte[] {
+ 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 2, 3, 8, 4, 6, 2, 6, 4, 3, 3, 8, 3, 2, 7, 9, 5, 0, 2, 8, 8, 4, 1, 9, 7, 1, 6, 9, 3, 9, 9, 3, 7, 5, 1, 0, 5, 8, 2, 0, 9, 7, 4, 9, 4, 4, 5, 9, 2, 3, 0, 7, 8, 1, 6, 4,
+ 0, 6, 2, 8, 6, 2, 0, 8, 9, 9, 8, 6, 2, 8, 0, 3, 4, 8, 2, 5, 3, 4, 2, 1, 1, 7, 0, 6, 7, 9, 8, 2, 1, 4, 8, 0, 8, 6, 5, 1, 3, 2, 8, 2, 3, 0, 6, 6, 4, 7, 0, 9, 3, 8, 4, 4, 6, 0, 9, 5, 5, 0, 5, 8, 2, 2, 3, 1, 7, 2,
+ 5, 3, 5, 9, 4, 0, 8, 1, 2, 8, 4, 8, 1, 1, 1, 7, 4, 5, 0, 2, 8, 4, 1, 0, 2, 7, 0, 1, 9, 3, 8, 5, 2, 1, 1, 0, 5, 5, 5, 9, 6, 4, 4, 6, 2, 2, 9, 4, 8, 9, 5, 4, 9, 3, 0, 3, 8, 1, 9, 6, 4, 4, 2, 8, 8, 1, 0, 9, 7, 5,
+ 6, 6, 5, 9, 3, 3, 4, 4, 6, 1, 2, 8, 4, 7, 5, 6, 4, 8, 2, 3, 3, 7, 8, 6, 7, 8, 3, 1, 6, 5, 2, 7, 1, 2, 0, 1, 9, 0, 9, 1, 4, 5, 6, 4, 8, 5, 6, 6, 9, 2, 3, 4, 6, 0, 3, 4, 8, 6, 1, 0, 4, 5, 4, 3, 2, 6, 6, 4, 8, 2,
+ 1, 3, 3, 9, 3, 6, 0, 7, 2, 6, 0, 2, 4, 9, 1, 4, 1, 2, 7, 3, 7, 2, 4, 5, 8, 7, 0, 0, 6, 6, 0, 6, 3, 1, 5, 5, 8, 8, 1, 7, 4, 8, 8, 1, 5, 2, 0, 9, 2, 0, 9, 6, 2, 8, 2, 9, 2, 5, 4, 0, 9, 1, 7, 1, 5, 3, 6, 4, 3, 6,
+ 7, 8, 9, 2, 5, 9, 0, 3, 6, 0, 0, 1, 1, 3, 3, 0, 5, 3, 0, 5, 4, 8, 8, 2, 0, 4, 6, 6, 5, 2, 1, 3, 8, 4, 1, 4, 6, 9, 5, 1, 9, 4, 1, 5, 1, 1, 6, 0, 9, 4, 3, 3, 0, 5, 7, 2, 7, 0, 3, 6, 5, 7, 5, 9, 5, 9, 1, 9, 5, 3,
+ 0, 9, 2, 1, 8, 6, 1, 1, 7, 3, 8, 1, 9, 3, 2, 6, 1, 1, 7, 9, 3, 1, 0, 5, 1, 1, 8, 5, 4, 8, 0, 7, 4, 4, 6, 2, 3, 7, 9, 9, 6, 2, 7, 4, 9, 5, 6, 7, 3, 5, 1, 8, 8, 5, 7, 5, 2, 7, 2, 4, 8, 9, 1, 2, 2, 7, 9, 3, 8, 1,
+ 8, 3, 0, 1, 1, 9, 4, 9, 1, 2, 9, 8, 3, 3, 6, 7, 3, 3, 6, 2, 4, 4, 0, 6, 5, 6, 6, 4, 3, 0, 8, 6, 0, 2, 1, 3, 9, 4, 9, 4, 6, 3, 9, 5, 2, 2, 4, 7, 3, 7, 1, 9, 0, 7, 0, 2, 1, 7, 9, 8, 6, 0, 9, 4, 3, 7, 0, 2, 7, 7,
+ 0, 5, 3, 9, 2, 1, 7, 1, 7, 6, 2, 9, 3, 1, 7, 6, 7, 5, 2, 3, 8, 4, 6, 7, 4, 8, 1, 8, 4, 6, 7, 6, 6, 9, 4, 0, 5, 1, 3, 2, 0, 0, 0, 5, 6, 8, 1, 2, 7, 1, 4, 5, 2, 6, 3, 5, 6, 0, 8, 2, 7, 7, 8, 5, 7, 7, 1, 3, 4, 2,
+ 7, 5, 7, 7, 8, 9, 6, 0, 9, 1, 7, 3, 6, 3, 7, 1, 7, 8, 7, 2, 1, 4, 6, 8, 4, 4, 0, 9, 0, 1, 2, 2, 4, 9, 5, 3, 4, 3, 0, 1, 4, 6, 5, 4, 9, 5, 8, 5, 3, 7, 1, 0, 5, 0, 7, 9, 2, 2, 7, 9, 6, 8, 9, 2, 5, 8, 9, 2, 3, 5,
+ 4, 2, 0, 1, 9, 9, 5, 6, 1, 1, 2, 1, 2, 9, 0, 2, 1, 9, 6, 0, 8, 6, 4, 0, 3, 4, 4, 1, 8, 1, 5, 9, 8, 1, 3, 6, 2, 9, 7, 7, 4, 7, 7, 1, 3, 0, 9, 9, 6, 0, 5, 1, 8, 7, 0, 7, 2, 1, 1, 3, 4, 9, 9, 9, 9, 9, 9, 8, 3, 7,
+ 2, 9, 7, 8, 0, 4, 9, 9, 5, 1, 0, 5, 9, 7, 3, 1, 7, 3, 2, 8, 1, 6, 0, 9, 6, 3, 1, 8, 5, 9, 5, 0, 2, 4, 4, 5, 9, 4, 5, 5, 3, 4, 6, 9, 0, 8, 3, 0, 2, 6, 4, 2, 5, 2, 2, 3, 0, 8, 2, 5, 3, 3, 4, 4, 6, 8, 5, 0, 3, 5,
+ 2, 6, 1, 9, 3, 1, 1, 8, 8, 1, 7, 1, 0, 1, 0, 0, 0, 3, 1, 3, 7, 8, 3, 8, 7, 5, 2, 8, 8, 6, 5, 8, 7, 5, 3, 3, 2, 0, 8, 3, 8, 1, 4, 2, 0, 6, 1, 7, 1, 7, 7, 6, 6, 9, 1, 4, 7, 3, 0, 3, 5, 9, 8, 2, 5, 3, 4, 9, 0, 4,
+ 2, 8, 7, 5, 5, 4, 6, 8, 7, 3, 1, 1, 5, 9, 5, 6, 2, 8, 6, 3, 8, 8, 2, 3, 5, 3, 7, 8, 7, 5, 9, 3, 7, 5, 1, 9, 5, 7, 7, 8, 1, 8, 5, 7, 7, 8, 0, 5, 3, 2, 1, 7, 1, 2, 2, 6, 8, 0, 6, 6, 1, 3, 0, 0, 1, 9, 2, 7, 8, 7,
+ 6, 6, 1, 1, 1, 9, 5, 9, 0, 9, 2, 1, 6, 4, 2, 0, 1, 9, 8, 9, 3, 8, 0, 9, 5, 2, 5, 7, 2, 0, 1, 0, 6, 5, 4, 8, 5, 8, 6, 3, 2, 7, 8, 8, 6, 5, 9, 3, 6, 1, 5, 3, 3, 8, 1, 8, 2, 7, 9, 6, 8, 2, 3, 0, 3, 0, 1, 9, 5, 2,
+ 0, 3, 5, 3, 0, 1, 8, 5, 2, 9, 6, 8, 9, 9, 5, 7, 7, 3, 6, 2, 2, 5, 9, 9, 4, 1, 3, 8, 9, 1, 2, 4, 9, 7, 2, 1, 7, 7, 5, 2, 8, 3, 4, 7, 9, 1, 3, 1, 5, 1, 5, 5, 7, 4, 8, 5, 7, 2, 4, 2, 4, 5, 4, 1, 5, 0, 6, 9, 5, 9,
+ 5, 0, 8, 2, 9, 5, 3, 3, 1, 1, 6, 8, 6, 1, 7, 2, 7, 8, 5, 5, 8, 8, 9, 0, 7, 5, 0, 9, 8, 3, 8, 1, 7, 5, 4, 6, 3, 7, 4, 6, 4, 9, 3, 9, 3, 1, 9, 2, 5, 5, 0, 6, 0, 4, 0, 0, 9, 2, 7, 7, 0, 1, 6, 7, 1, 1, 3, 9, 0, 0,
+ 9, 8, 4, 8, 8, 2, 4, 0, 1, 2, 8, 5, 8, 3, 6, 1, 6, 0, 3, 5, 6, 3, 7, 0, 7, 6, 6, 0, 1, 0, 4, 7, 1, 0, 1, 8, 1, 9, 4, 2, 9, 5, 5, 5, 9, 6, 1, 9, 8, 9, 4, 6, 7, 6, 7, 8, 3, 7, 4, 4, 9, 4, 4, 8, 2, 5, 5, 3, 7, 9,
+ 7, 7, 4, 7, 2, 6, 8, 4, 7, 1, 0, 4, 0, 4, 7, 5, 3, 4, 6, 4, 6, 2, 0, 8, 0, 4, 6, 6, 8, 4, 2, 5, 9, 0, 6, 9, 4, 9, 1, 2, 9, 3, 3, 1, 3, 6, 7, 7, 0, 2, 8, 9, 8, 9, 1, 5, 2, 1, 0, 4, 7, 5, 2, 1, 6, 2, 0, 5, 6, 9,
+ 6, 6, 0, 2, 4, 0, 5, 8, 0, 3, 8, 1, 5, 0, 1, 9, 3, 5, 1, 1, 2, 5, 3, 3, 8, 2, 4, 3, 0, 0, 3, 5, 5, 8, 7, 6, 4, 0, 2, 4, 7, 4, 9, 6, 4, 7, 3, 2, 6, 3, 9, 1, 4, 1, 9, 9, 2, 7, 2, 6, 0, 4, 2, 6, 9, 9, 2, 2, 7, 9,
+ 6, 7, 8, 2, 3, 5, 4, 7, 8, 1, 6, 3, 6, 0, 0, 9, 3, 4, 1, 7, 2, 1, 6, 4, 1, 2, 1, 9, 9, 2, 4, 5, 8, 6, 3, 1, 5, 0, 3, 0, 2, 8, 6, 1, 8, 2, 9, 7, 4, 5, 5, 5, 7, 0, 6, 7, 4, 9, 8, 3, 8, 5, 0, 5, 4, 9, 4, 5, 8, 8,
+ 5, 8, 6, 9, 2, 6, 9, 9, 5, 6, 9, 0, 9, 2, 7, 2, 1, 0, 7, 9, 7, 5, 0, 9, 3, 0, 2, 9, 5, 5, 3, 2, 1, 1, 6, 5, 3, 4, 4, 9, 8, 7, 2, 0, 2, 7, 5, 5, 9, 6, 0, 2, 3, 6, 4, 8, 0, 6, 6, 5, 4, 9, 9, 1, 1, 9, 8, 8, 1, 8,
+ 3, 4, 7, 9, 7, 7, 5, 3, 5, 6, 6, 3, 6, 9, 8, 0, 7, 4, 2, 6, 5, 4, 2, 5, 2, 7, 8, 6, 2, 5, 5, 1, 8, 1, 8, 4, 1, 7, 5, 7, 4, 6, 7, 2, 8, 9, 0, 9, 7, 7, 7, 7, 2, 7, 9, 3, 8, 0, 0, 0, 8, 1, 6, 4, 7, 0, 6, 0, 0, 1,
+ 6, 1, 4, 5, 2, 4, 9, 1, 9, 2, 1, 7, 3, 2, 1, 7, 2, 1, 4, 7, 7, 2, 3, 5, 0, 1, 4, 1, 4, 4, 1, 9, 7, 3, 5, 6, 8, 5, 4, 8, 1, 6, 1, 3, 6, 1, 1, 5, 7, 3, 5, 2, 5, 5, 2, 1, 3, 3, 4, 7, 5, 7, 4, 1, 8, 4, 9, 4, 6, 8,
+ 4, 3, 8, 5, 2, 3, 3, 2, 3, 9, 0, 7, 3, 9, 4, 1, 4, 3, 3, 3, 4, 5, 4, 7, 7, 6, 2, 4, 1, 6, 8, 6, 2, 5, 1, 8, 9, 8, 3, 5, 6, 9, 4, 8, 5, 5, 6, 2, 0, 9, 9, 2, 1, 9, 2, 2, 2, 1, 8, 4, 2, 7, 2, 5, 5, 0, 2, 5, 4, 2,
+ 5, 6, 8, 8, 7, 6, 7, 1, 7, 9, 0, 4, 9, 4, 6, 0, 1, 6, 5, 3, 4, 6, 6, 8, 0, 4, 9, 8, 8, 6, 2, 7, 2, 3, 2, 7, 9, 1, 7, 8, 6, 0, 8, 5, 7, 8, 4, 3, 8, 3, 8, 2, 7, 9, 6, 7, 9, 7, 6, 6, 8, 1, 4, 5, 4, 1, 0, 0, 9, 5,
+ 3, 8, 8, 3, 7, 8, 6, 3, 6, 0, 9, 5, 0, 6, 8, 0, 0, 6, 4, 2, 2, 5, 1, 2, 5, 2, 0, 5, 1, 1, 7, 3, 9, 2, 9, 8, 4, 8, 9, 6, 0, 8, 4, 1, 2, 8, 4, 8, 8, 6, 2, 6, 9, 4, 5, 6, 0, 4, 2, 4, 1, 9, 6, 5, 2, 8, 5, 0, 2, 2,
+ 2, 1, 0, 6, 6, 1, 1, 8, 6, 3, 0, 6, 7, 4, 4, 2, 7, 8, 6, 2, 2, 0, 3, 9, 1, 9, 4, 9, 4, 5, 0, 4, 7, 1, 2, 3, 7, 1, 3, 7, 8, 6, 9, 6, 0, 9, 5, 6, 3, 6, 4, 3, 7, 1, 9, 1, 7, 2, 8, 7, 4, 6, 7, 7, 6, 4, 6, 5, 7, 5,
+ 7, 3, 9, 6, 2, 4, 1, 3, 8, 9, 0, 8, 6, 5, 8, 3, 2, 6, 4, 5, 9, 9, 5, 8, 1, 3, 3, 9, 0, 4, 7, 8, 0, 2, 7, 5, 9, 0, 0, 9, 9, 4, 6, 5, 7, 6, 4, 0, 7, 8, 9, 5, 1, 2, 6, 9, 4, 6, 8, 3, 9, 8, 3, 5, 2, 5, 9, 5, 7, 0,
+ 9, 8, 2, 5, 8, 2, 2, 6, 2, 0, 5, 2, 2, 4, 8, 9, 4, 0, 7, 7, 2, 6, 7, 1, 9, 4, 7, 8, 2, 6, 8, 4, 8, 2, 6, 0, 1, 4, 7, 6, 9, 9, 0, 9, 0, 2, 6, 4, 0, 1, 3, 6, 3, 9, 4, 4, 3, 7, 4, 5, 5, 3, 0, 5, 0, 6, 8, 2, 0, 3,
+ 4, 9, 6, 2, 5, 2, 4, 5, 1, 7, 4, 9, 3, 9, 9, 6, 5, 1, 4, 3, 1, 4, 2, 9, 8, 0, 9, 1, 9, 0, 6, 5, 9, 2, 5, 0, 9, 3, 7, 2, 2, 1, 6, 9, 6, 4, 6, 1, 5, 1, 5, 7, 0, 9, 8, 5, 8, 3, 8, 7, 4, 1, 0, 5, 9, 7, 8, 8, 5, 9,
+ 5, 9, 7, 7, 2, 9, 7, 5, 4, 9, 8, 9, 3, 0, 1, 6, 1, 7, 5, 3, 9, 2, 8, 4, 6, 8, 1, 3, 8, 2, 6, 8, 6, 8, 3, 8, 6, 8, 9, 4, 2, 7, 7, 4, 1, 5, 5, 9, 9, 1, 8, 5, 5, 9, 2, 5, 2, 4, 5, 9, 5, 3, 9, 5, 9, 4, 3, 1, 0, 4,
+ 9, 9, 7, 2, 5, 2, 4, 6, 8, 0, 8, 4, 5, 9, 8, 7, 2, 7, 3, 6, 4, 4, 6, 9, 5, 8, 4, 8, 6, 5, 3, 8, 3, 6, 7, 3, 6, 2, 2, 2, 6, 2, 6, 0, 9, 9, 1, 2, 4, 6, 0, 8, 0, 5, 1, 2, 4, 3, 8, 8, 4, 3, 9, 0, 4, 5, 1, 2, 4, 4,
+ 1, 3, 6, 5, 4, 9, 7, 6, 2, 7, 8, 0, 7, 9, 7, 7, 1, 5, 6, 9, 1, 4, 3, 5, 9, 9, 7, 7, 0, 0, 1, 2, 9, 6, 1, 6, 0, 8, 9, 4, 4, 1, 6, 9, 4, 8, 6, 8, 5, 5, 5, 8, 4, 8, 4, 0, 6, 3, 5, 3, 4, 2, 2, 0, 7, 2, 2, 2, 5, 8,
+ 2, 8, 4, 8, 8, 6, 4, 8, 1, 5, 8, 4, 5, 6, 0, 2, 8, 5, 0, 6, 0, 1, 6, 8, 4, 2, 7, 3, 9, 4, 5, 2, 2, 6, 7, 4, 6, 7, 6, 7, 8, 8, 9, 5, 2, 5, 2, 1, 3, 8, 5, 2, 2, 5, 4, 9, 9, 5, 4, 6, 6, 6, 7, 2, 7, 8, 2, 3, 9, 8,
+ 6, 4, 5, 6, 5, 9, 6, 1, 1, 6, 3, 5, 4, 8, 8, 6, 2, 3, 0, 5, 7, 7, 4, 5, 6, 4, 9, 8, 0, 3, 5, 5, 9, 3, 6, 3, 4, 5, 6, 8, 1, 7, 4, 3, 2, 4, 1, 1, 2, 5, 1, 5, 0, 7, 6, 0, 6, 9, 4, 7, 9, 4, 5, 1, 0, 9, 6, 5, 9, 6,
+ 0, 9, 4, 0, 2, 5, 2, 2, 8, 8, 7, 9, 7, 1, 0, 8, 9, 3, 1, 4, 5, 6, 6, 9, 1, 3, 6, 8, 6, 7, 2, 2, 8, 7, 4, 8, 9, 4, 0, 5, 6, 0, 1, 0, 1, 5, 0, 3, 3, 0, 8, 6, 1, 7, 9, 2, 8, 6, 8, 0, 9, 2, 0, 8, 7, 4, 7, 6, 0, 9,
+ 1, 7, 8, 2, 4, 9, 3, 8, 5, 8, 9, 0, 0, 9, 7, 1, 4, 9, 0, 9, 6, 7, 5, 9, 8, 5, 2, 6, 1, 3, 6, 5, 5, 4, 9, 7, 8, 1, 8, 9, 3, 1, 2, 9, 7, 8, 4, 8, 2, 1, 6, 8, 2, 9, 9, 8, 9, 4, 8, 7, 2, 2, 6, 5, 8, 8, 0, 4, 8, 5,
+ 7, 5, 6, 4, 0, 1, 4, 2, 7, 0, 4, 7, 7, 5, 5, 5, 1, 3, 2, 3, 7, 9, 6, 4, 1, 4, 5, 1, 5, 2, 3, 7, 4, 6, 2, 3, 4, 3, 6, 4, 5, 4, 2, 8, 5, 8, 4, 4, 4, 7, 9, 5, 2, 6, 5, 8, 6, 7, 8, 2, 1, 0, 5, 1, 1, 4, 1, 3, 5, 4,
+ 7, 3, 5, 7, 3, 9, 5, 2, 3, 1, 1, 3, 4, 2, 7, 1, 6, 6, 1, 0, 2, 1, 3, 5, 9, 6, 9, 5, 3, 6, 2, 3, 1, 4, 4, 2, 9, 5, 2, 4, 8, 4, 9, 3, 7, 1, 8, 7, 1, 1, 0, 1, 4, 5, 7, 6, 5, 4, 0, 3, 5, 9, 0, 2, 7, 9, 9, 3, 4, 4,
+ 0, 3, 7, 4, 2, 0, 0, 7, 3, 1, 0, 5, 7, 8, 5, 3, 9, 0, 6, 2, 1, 9, 8, 3, 8, 7, 4, 4, 7, 8, 0, 8, 4, 7, 8, 4, 8, 9, 6, 8, 3, 3, 2, 1, 4, 4, 5, 7, 1, 3, 8, 6, 8, 7, 5, 1, 9, 4, 3, 5, 0, 6, 4, 3, 0, 2, 1, 8, 4, 5,
+ 3, 1, 9, 1, 0, 4, 8, 4, 8, 1, 0, 0, 5, 3, 7, 0, 6, 1, 4, 6, 8, 0, 6, 7, 4, 9, 1, 9, 2, 7, 8, 1, 9, 1, 1, 9, 7, 9, 3, 9, 9, 5, 2, 0, 6, 1, 4, 1, 9, 6, 6, 3, 4, 2, 8, 7, 5, 4, 4, 4, 0, 6, 4, 3, 7, 4, 5, 1, 2, 3,
+ 7, 1, 8, 1, 9, 2, 1, 7, 9, 9, 9, 8, 3, 9, 1, 0, 1, 5, 9, 1, 9, 5, 6, 1, 8, 1, 4, 6, 7, 5, 1, 4, 2, 6, 9, 1, 2, 3, 9, 7, 4, 8, 9, 4, 0, 9, 0, 7, 1, 8, 6, 4, 9, 4, 2, 3, 1, 9, 6, 1, 5, 6, 7, 9, 4, 5, 2, 0, 8, 0,
+ 9, 5, 1, 4, 6, 5, 5, 0, 2, 2, 5, 2, 3, 1, 6, 0, 3, 8, 8, 1, 9, 3, 0, 1, 4, 2, 0, 9, 3, 7, 6, 2, 1, 3, 7, 8, 5, 5, 9, 5, 6, 6, 3, 8, 9, 3, 7, 7, 8, 7, 0, 8, 3, 0, 3, 9, 0, 6, 9, 7, 9, 2, 0, 7, 7, 3, 4, 6, 7, 2,
+ 2, 1, 8, 2, 5, 6, 2, 5, 9, 9, 6, 6, 1, 5, 0, 1, 4, 2, 1, 5, 0, 3, 0, 6, 8, 0, 3, 8, 4, 4, 7, 7, 3, 4, 5, 4, 9, 2, 0, 2, 6, 0, 5, 4, 1, 4, 6, 6, 5, 9, 2, 5, 2, 0, 1, 4, 9, 7, 4, 4, 2, 8, 5, 0, 7, 3, 2, 5, 1, 8,
+ 6, 6, 6, 0, 0, 2, 1, 3, 2, 4, 3, 4, 0, 8, 8, 1, 9, 0, 7, 1, 0, 4, 8, 6, 3, 3, 1, 7, 3, 4, 6, 4, 9, 6, 5, 1, 4, 5, 3, 9, 0, 5, 7, 9, 6, 2, 6, 8, 5, 6, 1, 0, 0, 5, 5, 0, 8, 1, 0, 6, 6, 5, 8, 7, 9, 6, 9, 9, 8, 1,
+ 6, 3, 5, 7, 4, 7, 3, 6, 3, 8, 4, 0, 5, 2, 5, 7, 1, 4, 5, 9, 1, 0, 2, 8, 9, 7, 0, 6, 4, 1, 4, 0, 1, 1, 0, 9, 7, 1, 2, 0, 6, 2, 8, 0, 4, 3, 9, 0, 3, 9, 7, 5, 9, 5, 1, 5, 6, 7, 7, 1, 5, 7, 7, 0, 0, 4, 2, 0, 3, 3,
+ 7, 8, 6, 9, 9, 3, 6, 0, 0, 7, 2, 3, 0, 5, 5, 8, 7, 6, 3, 1, 7, 6, 3, 5, 9, 4, 2, 1, 8, 7, 3, 1, 2, 5, 1, 4, 7, 1, 2, 0, 5, 3, 2, 9, 2, 8, 1, 9, 1, 8, 2, 6, 1, 8, 6, 1, 2, 5, 8, 6, 7, 3, 2, 1, 5, 7, 9, 1, 9, 8,
+ 4, 1, 4, 8, 4, 8, 8, 2, 9, 1, 6, 4, 4, 7, 0, 6, 0, 9, 5, 7, 5, 2, 7, 0, 6, 9, 5, 7, 2, 2, 0, 9, 1, 7, 5, 6, 7, 1, 1, 6, 7, 2, 2, 9, 1, 0, 9, 8, 1, 6, 9, 0, 9, 1, 5, 2, 8, 0, 1, 7, 3, 5, 0, 6, 7, 1, 2, 7, 4, 8,
+ 5, 8, 3, 2, 2, 2, 8, 7, 1, 8, 3, 5, 2, 0, 9, 3, 5, 3, 9, 6, 5, 7, 2, 5, 1, 2, 1, 0, 8, 3, 5, 7, 9, 1, 5, 1, 3, 6, 9, 8, 8, 2, 0, 9, 1, 4, 4, 4, 2, 1, 0, 0, 6, 7, 5, 1, 0, 3, 3, 4, 6, 7, 1, 1, 0, 3, 1, 4, 1, 2,
+ 6, 7, 1, 1, 1, 3, 6, 9, 9, 0, 8, 6, 5, 8, 5, 1, 6, 3, 9, 8, 3, 1, 5, 0, 1, 9, 7, 0, 1, 6, 5, 1, 5, 1, 1, 6, 8, 5, 1, 7, 1, 4, 3, 7, 6, 5, 7, 6, 1, 8, 3, 5, 1, 5, 5, 6, 5, 0, 8, 8, 4, 9, 0, 9, 9, 8, 9, 8, 5, 9,
+ 9, 8, 2, 3, 8, 7, 3, 4, 5, 5, 2, 8, 3, 3, 1, 6, 3, 5, 5, 0, 7, 6, 4, 7, 9, 1, 8, 5, 3, 5, 8, 9, 3, 2, 2, 6, 1, 8, 5, 4, 8, 9, 6, 3, 2, 1, 3, 2, 9, 3, 3, 0, 8, 9, 8, 5, 7, 0, 6, 4, 2, 0, 4, 6, 7, 5, 2, 5, 9, 0,
+ 7, 0, 9, 1, 5, 4, 8, 1, 4, 1, 6, 5, 4, 9, 8, 5, 9, 4, 6, 1, 6, 3, 7, 1, 8, 0, 2, 7, 0, 9, 8, 1, 9, 9, 4, 3, 0, 9, 9, 2, 4, 4, 8, 8, 9, 5, 7, 5, 7, 1, 2, 8, 2, 8, 9, 0, 5, 9, 2, 3, 2, 3, 3, 2, 6, 0, 9, 7, 2, 9,
+ 9, 7, 1, 2, 0, 8, 4, 4, 3, 3, 5, 7, 3, 2, 6, 5, 4, 8, 9, 3, 8, 2, 3, 9, 1, 1, 9, 3, 2, 5, 9, 7, 4, 6, 3, 6, 6, 7, 3, 0, 5, 8, 3, 6, 0, 4, 1, 4, 2, 8, 1, 3, 8, 8, 3, 0, 3, 2, 0, 3, 8, 2, 4, 9, 0, 3, 7, 5, 8, 9,
+ 8, 5, 2, 4, 3, 7, 4, 4, 1, 7, 0, 2, 9, 1, 3, 2, 7, 6, 5, 6, 1, 8, 0, 9, 3, 7, 7, 3, 4, 4, 4, 0, 3, 0, 7, 0, 7, 4, 6, 9, 2, 1, 1, 2, 0, 1, 9, 1, 3, 0, 2, 0, 3, 3, 0, 3, 8, 0, 1, 9, 7, 6, 2, 1, 1, 0, 1, 1, 0, 0,
+ 4, 4, 9, 2, 9, 3, 2, 1, 5, 1, 6, 0, 8, 4, 2, 4, 4, 4, 8, 5, 9, 6, 3, 7, 6, 6, 9, 8, 3, 8, 9, 5, 2, 2, 8, 6, 8, 4, 7, 8, 3, 1, 2, 3, 5, 5, 2, 6, 5, 8, 2, 1, 3, 1, 4, 4, 9, 5, 7, 6, 8, 5, 7, 2, 6, 2, 4, 3, 3, 4,
+ 4, 1, 8, 9, 3, 0, 3, 9, 6, 8, 6, 4, 2, 6, 2, 4, 3, 4, 1, 0, 7, 7, 3, 2, 2, 6, 9, 7, 8, 0, 2, 8, 0, 7, 3, 1, 8, 9, 1, 5, 4, 4, 1, 1, 0, 1, 0, 4, 4, 6, 8, 2, 3, 2, 5, 2, 7, 1, 6, 2, 0, 1, 0, 5, 2, 6, 5, 2, 2, 7,
+ 2, 1, 1, 1, 6, 6, 0, 3, 9, 6, 6, 6, 5, 5, 7, 3, 0, 9, 2, 5, 4, 7, 1, 1, 0, 5, 5, 7, 8, 5, 3, 7, 6, 3, 4, 6, 6, 8, 2, 0, 6, 5, 3, 1, 0, 9, 8, 9, 6, 5, 2, 6, 9, 1, 8, 6, 2, 0, 5, 6, 4, 7, 6, 9, 3, 1, 2, 5, 7, 0,
+ 5, 8, 6, 3, 5, 6, 6, 2, 0, 1, 8, 5, 5, 8, 1, 0, 0, 7, 2, 9, 3, 6, 0, 6, 5, 9, 8, 7, 6, 4, 8, 6, 1, 1, 7, 9, 1, 0, 4, 5, 3, 3, 4, 8, 8, 5, 0, 3, 4, 6, 1, 1, 3, 6, 5, 7, 6, 8, 6, 7, 5, 3, 2, 4, 9, 4, 4, 1, 6, 6,
+ 8, 0, 3, 9, 6, 2, 6, 5, 7, 9, 7, 8, 7, 7, 1, 8, 5, 5, 6, 0, 8, 4, 5, 5, 2, 9, 6, 5, 4, 1, 2, 6, 6, 5, 4, 0, 8, 5, 3, 0, 6, 1, 4, 3, 4, 4, 4, 3, 1, 8, 5, 8, 6, 7, 6, 9, 7, 5, 1, 4, 5, 6, 6, 1, 4, 0, 6, 8, 0, 0,
+ 7, 0, 0, 2, 3, 7, 8, 7, 7, 6, 5, 9, 1, 3, 4, 4, 0, 1, 7, 1, 2, 7, 4, 9, 4, 7, 0, 4, 2, 0, 5, 6, 2, 2, 3, 0, 5, 3, 8, 9, 9, 4, 5, 6, 1, 3, 1, 4, 0, 7, 1, 1, 2, 7, 0, 0, 0, 4, 0, 7, 8, 5, 4, 7, 3, 3, 2, 6, 9, 9,
+ 3, 9, 0, 8, 1, 4, 5, 4, 6, 6, 4, 6, 4, 5, 8, 8, 0, 7, 9, 7, 2, 7, 0, 8, 2, 6, 6, 8, 3, 0, 6, 3, 4, 3, 2, 8, 5, 8, 7, 8, 5, 6, 9, 8, 3, 0, 5, 2, 3, 5, 8, 0, 8, 9, 3, 3, 0, 6, 5, 7, 5, 7, 4, 0, 6, 7, 9, 5, 4, 5,
+ 7, 1, 6, 3, 7, 7, 5, 2, 5, 4, 2, 0, 2, 1, 1, 4, 9, 5, 5, 7, 6, 1, 5, 8, 1, 4, 0, 0, 2, 5, 0, 1, 2, 6, 2, 2, 8, 5, 9, 4, 1, 3, 0, 2, 1, 6, 4, 7, 1, 5, 5, 0, 9, 7, 9, 2, 5, 9, 2, 3, 0, 9, 9, 0, 7, 9, 6, 5, 4, 7,
+ 3, 7, 6, 1, 2, 5, 5, 1, 7, 6, 5, 6, 7, 5, 1, 3, 5, 7, 5, 1, 7, 8, 2, 9, 6, 6, 6, 4, 5, 4, 7, 7, 9, 1, 7, 4, 5, 0, 1, 1, 2, 9, 9, 6, 1, 4, 8, 9, 0, 3, 0, 4, 6, 3, 9, 9, 4, 7, 1, 3, 2, 9, 6, 2, 1, 0, 7, 3, 4, 0,
+ 4, 3, 7, 5, 1, 8, 9, 5, 7, 3, 5, 9, 6, 1, 4, 5, 8, 9, 0, 1, 9, 3, 8, 9, 7, 1, 3, 1, 1, 1, 7, 9, 0, 4, 2, 9, 7, 8, 2, 8, 5, 6, 4, 7, 5, 0, 3, 2, 0, 3, 1, 9, 8, 6, 9, 1, 5, 1, 4, 0, 2, 8, 7, 0, 8, 0, 8, 5, 9, 9,
+ 0, 4, 8, 0, 1, 0, 9, 4, 1, 2, 1, 4, 7, 2, 2, 1, 3, 1, 7, 9, 4, 7, 6, 4, 7, 7, 7, 2, 6, 2, 2, 4, 1, 4, 2, 5, 4, 8, 5, 4, 5, 4, 0, 3, 3, 2, 1, 5, 7, 1, 8, 5, 3, 0, 6, 1, 4, 2, 2, 8, 8, 1, 3, 7, 5, 8, 5, 0, 4, 3,
+ 0, 6, 3, 3, 2, 1, 7, 5, 1, 8, 2, 9, 7, 9, 8, 6, 6, 2, 2, 3, 7, 1, 7, 2, 1, 5, 9, 1, 6, 0, 7, 7, 1, 6, 6, 9, 2, 5, 4, 7, 4, 8, 7, 3, 8, 9, 8, 6, 6, 5, 4, 9, 4, 9, 4, 5, 0, 1, 1, 4, 6, 5, 4, 0, 6, 2, 8, 4, 3, 3,
+ 6, 6, 3, 9, 3, 7, 9, 0, 0, 3, 9, 7, 6, 9, 2, 6, 5, 6, 7, 2, 1, 4, 6, 3, 8, 5, 3, 0, 6, 7, 3, 6, 0, 9, 6, 5, 7, 1, 2, 0, 9, 1, 8, 0, 7, 6, 3, 8, 3, 2, 7, 1, 6, 6, 4, 1, 6, 2, 7, 4, 8, 8, 8, 8, 0, 0, 7, 8, 6, 9,
+ 2, 5, 6, 0, 2, 9, 0, 2, 2, 8, 4, 7, 2, 1, 0, 4, 0, 3, 1, 7, 2, 1, 1, 8, 6, 0, 8, 2, 0, 4, 1, 9, 0, 0, 0, 4, 2, 2, 9, 6, 6, 1, 7, 1, 1, 9, 6, 3, 7, 7, 9, 2, 1, 3, 3, 7, 5, 7, 5, 1, 1, 4, 9, 5, 9, 5, 0, 1, 5, 6,
+ 6, 0, 4, 9, 6, 3, 1, 8, 6, 2, 9, 4, 7, 2, 6, 5, 4, 7, 3, 6, 4, 2, 5, 2, 3, 0, 8, 1, 7, 7, 0, 3, 6, 7, 5, 1, 5, 9, 0, 6, 7, 3, 5, 0, 2, 3, 5, 0, 7, 2, 8, 3, 5, 4, 0, 5, 6, 7, 0, 4, 0, 3, 8, 6, 7, 4, 3, 5, 1, 3,
+ 6, 2, 2, 2, 2, 4, 7, 7, 1, 5, 8, 9, 1, 5, 0, 4, 9, 5, 3, 0, 9, 8, 4, 4, 4, 8, 9, 3, 3, 3, 0, 9, 6, 3, 4, 0, 8, 7, 8, 0, 7, 6, 9, 3, 2, 5, 9, 9, 3, 9, 7, 8, 0, 5, 4, 1, 9, 3, 4, 1, 4, 4, 7, 3, 7, 7, 4, 4, 1, 8,
+ 4, 2, 6, 3, 1, 2, 9, 8, 6, 0, 8, 0, 9, 9, 8, 8, 8, 6, 8, 7, 4, 1, 3, 2, 6, 0, 4, 7, 2, 1, 5, 6, 9, 5, 1, 6, 2, 3, 9, 6, 5, 8, 6, 4, 5, 7, 3, 0, 2, 1, 6, 3, 1, 5, 9, 8, 1, 9, 3, 1, 9, 5, 1, 6, 7, 3, 5, 3, 8, 1,
+ 2, 9, 7, 4, 1, 6, 7, 7, 2, 9, 4, 7, 8, 6, 7, 2, 4, 2, 2, 9, 2, 4, 6, 5, 4, 3, 6, 6, 8, 0, 0, 9, 8, 0, 6, 7, 6, 9, 2, 8, 2, 3, 8, 2, 8, 0, 6, 8, 9, 9, 6, 4, 0, 0, 4, 8, 2, 4, 3, 5, 4, 0, 3, 7, 0, 1, 4, 1, 6, 3,
+ 1, 4, 9, 6, 5, 8, 9, 7, 9, 4, 0, 9, 2, 4, 3, 2, 3, 7, 8, 9, 6, 9, 0, 7, 0, 6, 9, 7, 7, 9, 4, 2, 2, 3, 6, 2, 5, 0, 8, 2, 2, 1, 6, 8, 8, 9, 5, 7, 3, 8, 3, 7, 9, 8, 6, 2, 3, 0, 0, 1, 5, 9, 3, 7, 7, 6, 4, 7, 1, 6,
+ 5, 1, 2, 2, 8, 9, 3, 5, 7, 8, 6, 0, 1, 5, 8, 8, 1, 6, 1, 7, 5, 5, 7, 8, 2, 9, 7, 3, 5, 2, 3, 3, 4, 4, 6, 0, 4, 2, 8, 1, 5, 1, 2, 6, 2, 7, 2, 0, 3, 7, 3, 4, 3, 1, 4, 6, 5, 3, 1, 9, 7, 7, 7, 7, 4, 1, 6, 0, 3, 1,
+ 9, 9, 0, 6, 6, 5, 5, 4, 1, 8, 7, 6, 3, 9, 7, 9, 2, 9, 3, 3, 4, 4, 1, 9, 5, 2, 1, 5, 4, 1, 3, 4, 1, 8, 9, 9, 4, 8, 5, 4, 4, 4, 7, 3, 4, 5, 6, 7, 3, 8, 3, 1, 6, 2, 4, 9, 9, 3, 4, 1, 9, 1, 3, 1, 8, 1, 4, 8, 0, 9,
+ 2, 7, 7, 7, 7, 1, 0, 3, 8, 6, 3, 8, 7, 7, 3, 4, 3, 1, 7, 7, 2, 0, 7, 5, 4, 5, 6, 5, 4, 5, 3, 2, 2, 0, 7, 7, 7, 0, 9, 2, 1, 2, 0, 1, 9, 0, 5, 1, 6, 6, 0, 9, 6, 2, 8, 0, 4, 9, 0, 9, 2, 6, 3, 6, 0, 1, 9, 7, 5, 9,
+ 8, 8, 2, 8, 1, 6, 1, 3, 3, 2, 3, 1, 6, 6, 6, 3, 6, 5, 2, 8, 6, 1, 9, 3, 2, 6, 6, 8, 6, 3, 3, 6, 0, 6, 2, 7, 3, 5, 6, 7, 6, 3, 0, 3, 5, 4, 4, 7, 7, 6, 2, 8, 0, 3, 5, 0, 4, 5, 0, 7, 7, 7, 2, 3, 5, 5, 4, 7, 1, 0,
+ 5, 8, 5, 9, 5, 4, 8, 7, 0, 2, 7, 9, 0, 8, 1, 4, 3, 5, 6, 2, 4, 0, 1, 4, 5, 1, 7, 1, 8, 0, 6, 2, 4, 6, 4, 3, 6, 2, 6, 7, 9, 4, 5, 6, 1, 2, 7, 5, 3, 1, 8, 1, 3, 4, 0, 7, 8, 3, 3, 0, 3, 3, 6, 2, 5, 4, 2, 3, 2, 7,
+ 8, 3, 9, 4, 4, 9, 7, 5, 3, 8, 2, 4, 3, 7, 2, 0, 5, 8, 3, 5, 3, 1, 1, 4, 7, 7, 1, 1, 9, 9, 2, 6, 0, 6, 3, 8, 1, 3, 3, 4, 6, 7, 7, 6, 8, 7, 9, 6, 9, 5, 9, 7, 0, 3, 0, 9, 8, 3, 3, 9, 1, 3, 0, 7, 7, 1, 0, 9, 8, 7,
+ 0, 4, 0, 8, 5, 9, 1, 3, 3, 7, 4, 6, 4, 1, 4, 4, 2, 8, 2, 2, 7, 7, 2, 6, 3, 4, 6, 5, 9, 4, 7, 0, 4, 7, 4, 5, 8, 7, 8, 4, 7, 7, 8, 7, 2, 0, 1, 9, 2, 7, 7, 1, 5, 2, 8, 0, 7, 3, 1, 7, 6, 7, 9, 0, 7, 7, 0, 7, 1, 5,
+ 7, 2, 1, 3, 4, 4, 4, 7, 3, 0, 6, 0, 5, 7, 0, 0, 7, 3, 3, 4, 9, 2, 4, 3, 6, 9, 3, 1, 1, 3, 8, 3, 5, 0, 4, 9, 3, 1, 6, 3, 1, 2, 8, 4, 0, 4, 2, 5, 1, 2, 1, 9, 2, 5, 6, 5, 1, 7, 9, 8, 0, 6, 9, 4, 1, 1, 3, 5, 2, 8,
+ 0, 1, 3, 1, 4, 7, 0, 1, 3, 0, 4, 7, 8, 1, 6, 4, 3, 7, 8, 8, 5, 1, 8, 5, 2, 9, 0, 9, 2, 8, 5, 4, 5, 2, 0, 1, 1, 6, 5, 8, 3, 9, 3, 4, 1, 9, 6, 5, 6, 2, 1, 3, 4, 9, 1, 4, 3, 4, 1, 5, 9, 5, 6, 2, 5, 8, 6, 5, 8, 6,
+ 5, 5, 7, 0, 5, 5, 2, 6, 9, 0, 4, 9, 6, 5, 2, 0, 9, 8, 5, 8, 0, 3, 3, 8, 5, 0, 7, 2, 2, 4, 2, 6, 4, 8, 2, 9, 3, 9, 7, 2, 8, 5, 8, 4, 7, 8, 3, 1, 6, 3, 0, 5, 7, 7, 7, 7, 5, 6, 0, 6, 8, 8, 8, 7, 6, 4, 4, 6, 2, 4,
+ 8, 2, 4, 6, 8, 5, 7, 9, 2, 6, 0, 3, 9, 5, 3, 5, 2, 7, 7, 3, 4, 8, 0, 3, 0, 4, 8, 0, 2, 9, 0, 0, 5, 8, 7, 6, 0, 7, 5, 8, 2, 5, 1, 0, 4, 7, 4, 7, 0, 9, 1, 6, 4, 3, 9, 6, 1, 3, 6, 2, 6, 7, 6, 0, 4, 4, 9, 2, 5, 6,
+ 2, 7, 4, 2, 0, 4, 2, 0, 8, 3, 2, 0, 8, 5, 6, 6, 1, 1, 9, 0, 6, 2, 5, 4, 5, 4, 3, 3, 7, 2, 1, 3, 1, 5, 3, 5, 9, 5, 8, 4, 5, 0, 6, 8, 7, 7, 2, 4, 6, 0, 2, 9, 0, 1, 6, 1, 8, 7, 6, 6, 7, 9, 5, 2, 4, 0, 6, 1, 6, 3,
+ 4, 2, 5, 2, 2, 5, 7, 7, 1, 9, 5, 4, 2, 9, 1, 6, 2, 9, 9, 1, 9, 3, 0, 6, 4, 5, 5, 3, 7, 7, 9, 9, 1, 4, 0, 3, 7, 3, 4, 0, 4, 3, 2, 8, 7, 5, 2, 6, 2, 8, 8, 8, 9, 6, 3, 9, 9, 5, 8, 7, 9, 4, 7, 5, 7, 2, 9, 1, 7, 4,
+ 6, 4, 2, 6, 3, 5, 7, 4, 5, 5, 2, 5, 4, 0, 7, 9, 0, 9, 1, 4, 5, 1, 3, 5, 7, 1, 1, 1, 3, 6, 9, 4, 1, 0, 9, 1, 1, 9, 3, 9, 3, 2, 5, 1, 9, 1, 0, 7, 6, 0, 2, 0, 8, 2, 5, 2, 0, 2, 6, 1, 8, 7, 9, 8, 5, 3, 1, 8, 8, 7,
+ 7, 0, 5, 8, 4, 2, 9, 7, 2, 5, 9, 1, 6, 7, 7, 8, 1, 3, 1, 4, 9, 6, 9, 9, 0, 0, 9, 0, 1, 9, 2, 1, 1, 6, 9, 7, 1, 7, 3, 7, 2, 7, 8, 4, 7, 6, 8, 4, 7, 2, 6, 8, 6, 0, 8, 4, 9, 0, 0, 3, 3, 7, 7, 0, 2, 4, 2, 4, 2, 9,
+ 1, 6, 5, 1, 3, 0, 0, 5, 0, 0, 5, 1, 6, 8, 3, 2, 3, 3, 6, 4, 3, 5, 0, 3, 8, 9, 5, 1, 7, 0, 2, 9, 8, 9, 3, 9, 2, 2, 3, 3, 4, 5, 1, 7, 2, 2, 0, 1, 3, 8, 1, 2, 8, 0, 6, 9, 6, 5, 0, 1, 1, 7, 8, 4, 4, 0, 8, 7, 4, 5,
+ 1, 9, 6, 0, 1, 2, 1, 2, 2, 8, 5, 9, 9, 3, 7, 1, 6, 2, 3, 1, 3, 0, 1, 7, 1, 1, 4, 4, 4, 8, 4, 6, 4, 0, 9, 0, 3, 8, 9, 0, 6, 4, 4, 9, 5, 4, 4, 4, 0, 0, 6, 1, 9, 8, 6, 9, 0, 7, 5, 4, 8, 5, 1, 6, 0, 2, 6, 3, 2, 7,
+ 5, 0, 5, 2, 9, 8, 3, 4, 9, 1, 8, 7, 4, 0, 7, 8, 6, 6, 8, 0, 8, 8, 1, 8, 3, 3, 8, 5, 1, 0, 2, 2, 8, 3, 3, 4, 5, 0, 8, 5, 0, 4, 8, 6, 0, 8, 2, 5, 0, 3, 9, 3, 0, 2, 1, 3, 3, 2, 1, 9, 7, 1, 5, 5, 1, 8, 4, 3, 0, 6,
+ 3, 5, 4, 5, 5, 0, 0, 7, 6, 6, 8, 2, 8, 2, 9, 4, 9, 3, 0, 4, 1, 3, 7, 7, 6, 5, 5, 2, 7, 9, 3, 9, 7, 5, 1, 7, 5, 4, 6, 1, 3, 9, 5, 3, 9, 8, 4, 6, 8, 3, 3, 9, 3, 6, 3, 8, 3, 0, 4, 7, 4, 6, 1, 1, 9, 9, 6, 6, 5, 3,
+ 8, 5, 8, 1, 5, 3, 8, 4, 2, 0, 5, 6, 8, 5, 3, 3, 8, 6, 2, 1, 8, 6, 7, 2, 5, 2, 3, 3, 4, 0, 2, 8, 3, 0, 8, 7, 1, 1, 2, 3, 2, 8, 2, 7, 8, 9, 2, 1, 2, 5, 0, 7, 7, 1, 2, 6, 2, 9, 4, 6, 3, 2, 2, 9, 5, 6, 3, 9, 8, 9,
+ 8, 9, 8, 9, 3, 5, 8, 2, 1, 1, 6, 7, 4, 5, 6, 2, 7, 0, 1, 0, 2, 1, 8, 3, 5, 6, 4, 6, 2, 2, 0, 1, 3, 4, 9, 6, 7, 1, 5, 1, 8, 8, 1, 9, 0, 9, 7, 3, 0, 3, 8, 1, 1, 9, 8, 0, 0, 4, 9, 7, 3, 4, 0, 7, 2, 3, 9, 6, 1, 0,
+ 3, 6, 8, 5, 4, 0, 6, 6, 4, 3, 1, 9, 3, 9, 5, 0, 9, 7, 9, 0, 1, 9, 0, 6, 9, 9, 6, 3, 9, 5, 5, 2, 4, 5, 3, 0, 0, 5, 4, 5, 0, 5, 8, 0, 6, 8, 5, 5, 0, 1, 9, 5, 6, 7, 3, 0, 2, 2, 9, 2, 1, 9, 1, 3, 9, 3, 3, 9, 1, 8,
+ 5, 6, 8, 0, 3, 4, 4, 9, 0, 3, 9, 8, 2, 0, 5, 9, 5, 5, 1, 0, 0, 2, 2, 6, 3, 5, 3, 5, 3, 6, 1, 9, 2, 0, 4, 1, 9, 9, 4, 7, 4, 5, 5, 3, 8, 5, 9, 3, 8, 1, 0, 2, 3, 4, 3, 9, 5, 5, 4, 4, 9, 5, 9, 7, 7, 8, 3, 7, 7, 9,
+ 0, 2, 3, 7, 4, 2, 1, 6, 1, 7, 2, 7, 1, 1, 1, 7, 2, 3, 6, 4, 3, 4, 3, 5, 4, 3, 9, 4, 7, 8, 2, 2, 1, 8, 1, 8, 5, 2, 8, 6, 2, 4, 0, 8, 5, 1, 4, 0, 0, 6, 6, 6, 0, 4, 4, 3, 3, 2, 5, 8, 8, 8, 5, 6, 9, 8, 6, 7, 0, 5,
+ 4, 3, 1, 5, 4, 7, 0, 6, 9, 6, 5, 7, 4, 7, 4, 5, 8, 5, 5, 0, 3, 3, 2, 3, 2, 3, 3, 4, 2, 1, 0, 7, 3, 0, 1, 5, 4, 5, 9, 4, 0, 5, 1, 6, 5, 5, 3, 7, 9, 0, 6, 8, 6, 6, 2, 7, 3, 3, 3, 7, 9, 9, 5, 8, 5, 1, 1, 5, 6, 2,
+ 5, 7, 8, 4, 3, 2, 2, 9, 8, 8, 2, 7, 3, 7, 2, 3, 1, 9, 8, 9, 8, 7, 5, 7, 1, 4, 1, 5, 9, 5, 7, 8, 1, 1, 1, 9, 6, 3, 5, 8, 3, 3, 0, 0, 5, 9, 4, 0, 8, 7, 3, 0, 6, 8, 1, 2, 1, 6, 0, 2, 8, 7, 6, 4, 9, 6, 2, 8, 6, 7,
+ 4, 4, 6, 0, 4, 7, 7, 4, 6, 4, 9, 1, 5, 9, 9, 5, 0, 5, 4, 9, 7, 3, 7, 4, 2, 5, 6, 2, 6, 9, 0, 1, 0, 4, 9, 0, 3, 7, 7, 8, 1, 9, 8, 6, 8, 3, 5, 9, 3, 8, 1, 4, 6, 5, 7, 4, 1, 2, 6, 8, 0, 4, 9, 2, 5, 6, 4, 8, 7, 9,
+ 8, 5, 5, 6, 1, 4, 5, 3, 7, 2, 3, 4, 7, 8, 6, 7, 3, 3, 0, 3, 9, 0, 4, 6, 8, 8, 3, 8, 3, 4, 3, 6, 3, 4, 6, 5, 5, 3, 7, 9, 4, 9, 8, 6, 4, 1, 9, 2, 7, 0, 5, 6, 3, 8, 7, 2, 9, 3, 1, 7, 4, 8, 7, 2, 3, 3, 2, 0, 8, 3,
+ 7, 6, 0, 1, 1, 2, 3, 0, 2, 9, 9, 1, 1, 3, 6, 7, 9, 3, 8, 6, 2, 7, 0, 8, 9, 4, 3, 8, 7, 9, 9, 3, 6, 2, 0, 1, 6, 2, 9, 5, 1, 5, 4, 1, 3, 3, 7, 1, 4, 2, 4, 8, 9, 2, 8, 3, 0, 7, 2, 2, 0, 1, 2, 6, 9, 0, 1, 4, 7, 5,
+ 4, 6, 6, 8, 4, 7, 6, 5, 3, 5, 7, 6, 1, 6, 4, 7, 7, 3, 7, 9, 4, 6, 7, 5, 2, 0, 0, 4, 9, 0, 7, 5, 7, 1, 5, 5, 5, 2, 7, 8, 1, 9, 6, 5, 3, 6, 2, 1, 3, 2, 3, 9, 2, 6, 4, 0, 6, 1, 6, 0, 1, 3, 6, 3, 5, 8, 1, 5, 5, 9,
+ 0, 7, 4, 2, 2, 0, 2, 0, 2, 0, 3, 1, 8, 7, 2, 7, 7, 6, 0, 5, 2, 7, 7, 2, 1, 9, 0, 0, 5, 5, 6, 1, 4, 8, 4, 2, 5, 5, 5, 1, 8, 7, 9, 2, 5, 3, 0, 3, 4, 3, 5, 1, 3, 9, 8, 4, 4, 2, 5, 3, 2, 2, 3, 4, 1, 5, 7, 6, 2, 3,
+ 3, 6, 1, 0, 6, 4, 2, 5, 0, 6, 3, 9, 0, 4, 9, 7, 5, 0, 0, 8, 6, 5, 6, 2, 7, 1, 0, 9, 5, 3, 5, 9, 1, 9, 4, 6, 5, 8, 9, 7, 5, 1, 4, 1, 3, 1, 0, 3, 4, 8, 2, 2, 7, 6, 9, 3, 0, 6, 2, 4, 7, 4, 3, 5, 3, 6, 3, 2, 5, 6,
+ 9, 1, 6, 0, 7, 8, 1, 5, 4, 7, 8, 1, 8, 1, 1, 5, 2, 8, 4, 3, 6, 6, 7, 9, 5, 7, 0, 6, 1, 1, 0, 8, 6, 1, 5, 3, 3, 1, 5, 0, 4, 4, 5, 2, 1, 2, 7, 4, 7, 3, 9, 2, 4, 5, 4, 4, 9, 4, 5, 4, 2, 3, 6, 8, 2, 8, 8, 6, 0, 6,
+ 1, 3, 4, 0, 8, 4, 1, 4, 8, 6, 3, 7, 7, 6, 7, 0, 0, 9, 6, 1, 2, 0, 7, 1, 5, 1, 2, 4, 9, 1, 4, 0, 4, 3, 0, 2, 7, 2, 5, 3, 8, 6, 0, 7, 6, 4, 8, 2, 3, 6, 3, 4, 1, 4, 3, 3, 4, 6, 2, 3, 5, 1, 8, 9, 7, 5, 7, 6, 6, 4,
+ 5, 2, 1, 6, 4, 1, 3, 7, 6, 7, 9, 6, 9, 0, 3, 1, 4, 9, 5, 0, 1, 9, 1, 0, 8, 5, 7, 5, 9, 8, 4, 4, 2, 3, 9, 1, 9, 8, 6, 2, 9, 1, 6, 4, 2, 1, 9, 3, 9, 9, 4, 9, 0, 7, 2, 3, 6, 2, 3, 4, 6, 4, 6, 8, 4, 4, 1, 1, 7, 3,
+ 9, 4, 0, 3, 2, 6, 5, 9, 1, 8, 4, 0, 4, 4, 3, 7, 8, 0, 5, 1, 3, 3, 3, 8, 9, 4, 5, 2, 5, 7, 4, 2, 3, 9, 9, 5, 0, 8, 2, 9, 6, 5, 9, 1, 2, 2, 8, 5, 0, 8, 5, 5, 5, 8, 2, 1, 5, 7, 2, 5, 0, 3, 1, 0, 7, 1, 2, 5, 7, 0,
+ 1, 2, 6, 6, 8, 3, 0, 2, 4, 0, 2, 9, 2, 9, 5, 2, 5, 2, 2, 0, 1, 1, 8, 7, 2, 6, 7, 6, 7, 5, 6, 2, 2, 0, 4, 1, 5, 4, 2, 0, 5, 1, 6, 1, 8, 4, 1, 6, 3, 4, 8, 4, 7, 5, 6, 5, 1, 6, 9, 9, 9, 8, 1, 1, 6, 1, 4, 1, 0, 1,
+ 0, 0, 2, 9, 9, 6, 0, 7, 8, 3, 8, 6, 9, 0, 9, 2, 9, 1, 6, 0, 3, 0, 2, 8, 8, 4, 0, 0, 2, 6, 9, 1, 0, 4, 1, 4, 0, 7, 9, 2, 8, 8, 6, 2, 1, 5, 0, 7, 8, 4, 2, 4, 5, 1, 6, 7, 0, 9, 0, 8, 7, 0, 0, 0, 6, 9, 9, 2, 8, 2,
+ 1, 2, 0, 6, 6, 0, 4, 1, 8, 3, 7, 1, 8, 0, 6, 5, 3, 5, 5, 6, 7, 2, 5, 2, 5, 3, 2, 5, 6, 7, 5, 3, 2, 8, 6, 1, 2, 9, 1, 0, 4, 2, 4, 8, 7, 7, 6, 1, 8, 2, 5, 8, 2, 9, 7, 6, 5, 1, 5, 7, 9, 5, 9, 8, 4, 7, 0, 3, 5, 6,
+ 2, 2, 2, 6, 2, 9, 3, 4, 8, 6, 0, 0, 3, 4, 1, 5, 8, 7, 2, 2, 9, 8, 0, 5, 3, 4, 9, 8, 9, 6, 5, 0, 2, 2, 6, 2, 9, 1, 7, 4, 8, 7, 8, 8, 2, 0, 2, 7, 3, 4, 2, 0, 9, 2, 2, 2, 2, 4, 5, 3, 3, 9, 8, 5, 6, 2, 6, 4, 7, 6,
+ 6, 9, 1, 4, 9, 0, 5, 5, 6, 2, 8, 4, 2, 5, 0, 3, 9, 1, 2, 7, 5, 7, 7, 1, 0, 2, 8, 4, 0, 2, 7, 9, 9, 8, 0, 6, 6, 3, 6, 5, 8, 2, 5, 4, 8, 8, 9, 2, 6, 4, 8, 8, 0, 2, 5, 4, 5, 6, 6, 1, 0, 1, 7, 2, 9, 6, 7, 0, 2, 6,
+ 6, 4, 0, 7, 6, 5, 5, 9, 0, 4, 2, 9, 0, 9, 9, 4, 5, 6, 8, 1, 5, 0, 6, 5, 2, 6, 5, 3, 0, 5, 3, 7, 1, 8, 2, 9, 4, 1, 2, 7, 0, 3, 3, 6, 9, 3, 1, 3, 7, 8, 5, 1, 7, 8, 6, 0, 9, 0, 4, 0, 7, 0, 8, 6, 6, 7, 1, 1, 4, 9,
+ 6, 5, 5, 8, 3, 4, 3, 4, 3, 4, 7, 6, 9, 3, 3, 8, 5, 7, 8, 1, 7, 1, 1, 3, 8, 6, 4, 5, 5, 8, 7, 3, 6, 7, 8, 1, 2, 3, 0, 1, 4, 5, 8, 7, 6, 8, 7, 1, 2, 6, 6, 0, 3, 4, 8, 9, 1, 3, 9, 0, 9, 5, 6, 2, 0, 0, 9, 9, 3, 9,
+ 3, 6, 1, 0, 3, 1, 0, 2, 9, 1, 6, 1, 6, 1, 5, 2, 8, 8, 1, 3, 8, 4, 3, 7, 9, 0, 9, 9, 0, 4, 2, 3, 1, 7, 4, 7, 3, 3, 6, 3, 9, 4, 8, 0, 4, 5, 7, 5, 9, 3, 1, 4, 9, 3, 1, 4, 0, 5, 2, 9, 7, 6, 3, 4, 7, 5, 7, 4, 8, 1,
+ 1, 9, 3, 5, 6, 7, 0, 9, 1, 1, 0, 1, 3, 7, 7, 5, 1, 7, 2, 1, 0, 0, 8, 0, 3, 1, 5, 5, 9, 0, 2, 4, 8, 5, 3, 0, 9, 0, 6, 6, 9, 2, 0, 3, 7, 6, 7, 1, 9, 2, 2, 0, 3, 3, 2, 2, 9, 0, 9, 4, 3, 3, 4, 6, 7, 6, 8, 5, 1, 4,
+ 2, 2, 1, 4, 4, 7, 7, 3, 7, 9, 3, 9, 3, 7, 5, 1, 7, 0, 3, 4, 4, 3, 6, 6, 1, 9, 9, 1, 0, 4, 0, 3, 3, 7, 5, 1, 1, 1, 7, 3, 5, 4, 7, 1, 9, 1, 8, 5, 5, 0, 4, 6, 4, 4, 9, 0, 2, 6, 3, 6, 5, 5, 1, 2, 8, 1, 6, 2, 2, 8,
+ 8, 2, 4, 4, 6, 2, 5, 7, 5, 9, 1, 6, 3, 3, 3, 0, 3, 9, 1, 0, 7, 2, 2, 5, 3, 8, 3, 7, 4, 2, 1, 8, 2, 1, 4, 0, 8, 8, 3, 5, 0, 8, 6, 5, 7, 3, 9, 1, 7, 7, 1, 5, 0, 9, 6, 8, 2, 8, 8, 7, 4, 7, 8, 2, 6, 5, 6, 9, 9, 5,
+ 9, 9, 5, 7, 4, 4, 9, 0, 6, 6, 1, 7, 5, 8, 3, 4, 4, 1, 3, 7, 5, 2, 2, 3, 9, 7, 0, 9, 6, 8, 3, 4, 0, 8, 0, 0, 5, 3, 5, 5, 9, 8, 4, 9, 1, 7, 5, 4, 1, 7, 3, 8, 1, 8, 8, 3, 9, 9, 9, 4, 4, 6, 9, 7, 4, 8, 6, 7, 6, 2,
+ 6, 5, 5, 1, 6, 5, 8, 2, 7, 6, 5, 8, 4, 8, 3, 5, 8, 8, 4, 5, 3, 1, 4, 2, 7, 7, 5, 6, 8, 7, 9, 0, 0, 2, 9, 0, 9, 5, 1, 7, 0, 2, 8, 3, 5, 2, 9, 7, 1, 6, 3, 4, 4, 5, 6, 2, 1, 2, 9, 6, 4, 0, 4, 3, 5, 2, 3, 1, 1, 7,
+ 6, 0, 0, 6, 6, 5, 1, 0, 1, 2, 4, 1, 2, 0, 0, 6, 5, 9, 7, 5, 5, 8, 5, 1, 2, 7, 6, 1, 7, 8, 5, 8, 3, 8, 2, 9, 2, 0, 4, 1, 9, 7, 4, 8, 4, 4, 2, 3, 6, 0, 8, 0, 0, 7, 1, 9, 3, 0, 4, 5, 7, 6, 1, 8, 9, 3, 2, 3, 4, 9,
+ 2, 2, 9, 2, 7, 9, 6, 5, 0, 1, 9, 8, 7, 5, 1, 8, 7, 2, 1, 2, 7, 2, 6, 7, 5, 0, 7, 9, 8, 1, 2, 5, 5, 4, 7, 0, 9, 5, 8, 9, 0, 4, 5, 5, 6, 3, 5, 7, 9, 2, 1, 2, 2, 1, 0, 3, 3, 3, 4, 6, 6, 9, 7, 4, 9, 9, 2, 3, 5, 6,
+ 3, 0, 2, 5, 4, 9, 4, 7, 8, 0, 2, 4, 9, 0, 1, 1, 4, 1, 9, 5, 2, 1, 2, 3, 8, 2, 8, 1, 5, 3, 0, 9, 1, 1, 4, 0, 7, 9, 0, 7, 3, 8, 6, 0, 2, 5, 1, 5, 2, 2, 7, 4, 2, 9, 9, 5, 8, 1, 8, 0, 7, 2, 4, 7, 1, 6, 2, 5, 9, 1,
+ 6, 6, 8, 5, 4, 5, 1, 3, 3, 3, 1, 2, 3, 9, 4, 8, 0, 4, 9, 4, 7, 0, 7, 9, 1, 1, 9, 1, 5, 3, 2, 6, 7, 3, 4, 3, 0, 2, 8, 2, 4, 4, 1, 8, 6, 0, 4, 1, 4, 2, 6, 3, 6, 3, 9, 5, 4, 8, 0, 0, 0, 4, 4, 8, 0, 0, 2, 6, 7, 0,
+ 4, 9, 6, 2, 4, 8, 2, 0, 1, 7, 9, 2, 8, 9, 6, 4, 7, 6, 6, 9, 7, 5, 8, 3, 1, 8, 3, 2, 7, 1, 3, 1, 4, 2, 5, 1, 7, 0, 2, 9, 6, 9, 2, 3, 4, 8, 8, 9, 6, 2, 7, 6, 6, 8, 4, 4, 0, 3, 2, 3, 2, 6, 0, 9, 2, 7, 5, 2, 4, 9,
+ 6, 0, 3, 5, 7, 9, 9, 6, 4, 6, 9, 2, 5, 6, 5, 0, 4, 9, 3, 6, 8, 1, 8, 3, 6, 0, 9, 0, 0, 3, 2, 3, 8, 0, 9, 2, 9, 3, 4, 5,
+ 9, 5, 8, 8, 9, 7, 0, 6, 9, 5, 3, 6, 5, 3, 4, 9, 4, 0, 6, 0, 3, 4, 0, 2, 1, 6, 6, 5, 4, 4, 3, 7, 5, 5, 8, 9, 0, 0, 4, 5, 6, 3, 2, 8, 8, 2, 2, 5, 0, 5, 4, 5, 2, 5, 5, 6, 4, 0, 5, 6, 4, 4, 8, 2, 4, 6, 5, 1, 5, 1,
+ 8, 7, 5, 4, 7, 1, 1, 9, 6, 2, 1, 8, 4, 4, 3, 9, 6, 5, 8, 2, 5, 3, 3, 7, 5, 4, 3, 8, 8, 5, 6, 9, 0, 9, 4, 1, 1, 3, 0, 3, 1, 5, 0, 9, 5, 2, 6, 1, 7, 9, 3, 7, 8, 0, 0, 2, 9, 7, 4, 1, 2, 0, 7, 6, 6, 5, 1, 4, 7, 9,
+ 3, 9, 4, 2, 5, 9, 0, 2, 9, 8, 9, 6, 9, 5, 9, 4, 6, 9, 9, 5, 5, 6, 5, 7, 6, 1, 2, 1, 8, 6, 5, 6, 1, 9, 6, 7, 3, 3, 7, 8, 6, 2, 3, 6, 2, 5, 6, 1, 2, 5, 2, 1, 6, 3, 2, 0, 8, 6, 2, 8, 6, 9, 2, 2, 2, 1, 0, 3, 2, 7,
+ 4, 8, 8, 9, 2, 1, 8, 6, 5, 4, 3, 6, 4, 8, 0, 2, 2, 9, 6, 7, 8, 0, 7, 0, 5, 7, 6, 5, 6, 1, 5, 1, 4, 4, 6, 3, 2, 0, 4, 6, 9, 2, 7, 9, 0, 6, 8, 2, 1, 2, 0, 7, 3, 8, 8, 3, 7, 7, 8, 1, 4, 2, 3, 3, 5, 6, 2, 8, 2, 3,
+ 6, 0, 8, 9, 6, 3, 2, 0, 8, 0, 6, 8, 2, 2, 2, 4, 6, 8, 0, 1, 2, 2, 4, 8, 2, 6, 1, 1, 7, 7, 1, 8, 5, 8, 9, 6, 3, 8, 1, 4, 0, 9, 1, 8, 3, 9, 0, 3, 6, 7, 3, 6, 7, 2, 2, 2, 0, 8, 8, 8, 3, 2, 1, 5, 1, 3, 7, 5, 5, 6,
+ 0, 0, 3, 7, 2, 7, 9, 8, 3, 9, 4, 0, 0, 4, 1, 5, 2, 9, 7, 0, 0, 2, 8, 7, 8, 3, 0, 7, 6, 6, 7, 0, 9, 4, 4, 4, 7, 4, 5, 6, 0, 1, 3, 4, 5, 5, 6, 4, 1, 7, 2, 5, 4, 3, 7, 0, 9, 0, 6, 9, 7, 9, 3, 9, 6, 1, 2, 2, 5, 7,
+ 1, 4, 2, 9, 8, 9, 4, 6, 7, 1, 5, 4, 3, 5, 7, 8, 4, 6, 8, 7, 8, 8, 6, 1, 4, 4, 4, 5, 8, 1, 2, 3, 1, 4, 5, 9, 3, 5, 7, 1, 9, 8, 4, 9, 2, 2, 5, 2, 8, 4, 7, 1, 6, 0, 5, 0, 4, 9, 2, 2, 1, 2, 4, 2, 4, 7, 0, 1, 4, 1,
+ 2, 1, 4, 7, 8, 0, 5, 7, 3, 4, 5, 5, 1, 0, 5, 0, 0, 8, 0, 1, 9, 0, 8, 6, 9, 9, 6, 0, 3, 3, 0, 2, 7, 6, 3, 4, 7, 8, 7, 0, 8, 1, 0, 8, 1, 7, 5, 4, 5, 0, 1, 1, 9, 3, 0, 7, 1, 4, 1, 2, 2, 3, 3, 9, 0, 8, 6, 6, 3, 9,
+ 3, 8, 3, 3, 9, 5, 2, 9, 4, 2, 5, 7, 8, 6, 9, 0, 5, 0, 7, 6, 4, 3, 1, 0, 0, 6, 3, 8, 3, 5, 1, 9, 8, 3, 4, 3, 8, 9, 3, 4, 1, 5, 9, 6, 1, 3, 1, 8, 5, 4, 3, 4, 7, 5, 4, 6, 4, 9, 5, 5, 6, 9, 7, 8, 1, 0, 3, 8, 2, 9,
+ 3, 0, 9, 7, 1, 6, 4, 6, 5, 1, 4, 3, 8, 4, 0, 7, 0, 0, 7, 0, 7, 3, 6, 0, 4, 1, 1, 2, 3, 7, 3, 5, 9, 9, 8, 4, 3, 4, 5, 2, 2, 5, 1, 6, 1, 0, 5, 0, 7, 0, 2, 7, 0, 5, 6, 2, 3, 5, 2, 6, 6, 0, 1, 2, 7, 6, 4, 8, 4, 8,
+ 3, 0, 8, 4, 0, 7, 6, 1, 1, 8, 3, 0, 1, 3, 0, 5, 2, 7, 9, 3, 2, 0, 5, 4, 2, 7, 4, 6, 2, 8, 6, 5, 4, 0, 3, 6, 0, 3, 6, 7, 4, 5, 3, 2, 8, 6, 5, 1, 0, 5, 7, 0, 6, 5, 8, 7, 4, 8, 8, 2, 2, 5, 6, 9, 8, 1, 5, 7, 9, 3,
+ 6, 7, 8, 9, 7, 6, 6, 9, 7, 4, 2, 2, 0, 5, 7, 5, 0, 5, 9, 6, 8, 3, 4, 4, 0, 8, 6, 9, 7, 3, 5, 0, 2, 0, 1, 4, 1, 0, 2, 0, 6, 7, 2, 3, 5, 8, 5, 0, 2, 0, 0, 7, 2, 4, 5, 2, 2, 5, 6, 3, 2, 6, 5, 1, 3, 4, 1, 0, 5, 5,
+ 9, 2, 4, 0, 1, 9, 0, 2, 7, 4, 2, 1, 6, 2, 4, 8, 4, 3, 9, 1, 4, 0, 3, 5, 9, 9, 8, 9, 5, 3, 5, 3, 9, 4, 5, 9, 0, 9, 4, 4, 0, 7, 0, 4, 6, 9, 1, 2, 0, 9, 1, 4, 0, 9, 3, 8, 7, 0, 0, 1, 2, 6, 4, 5, 6, 0, 0, 1, 6, 2,
+ 3, 7, 4, 2, 8, 8, 0, 2, 1, 0, 9, 2, 7, 6, 4, 5, 7, 9, 3, 1, 0, 6, 5, 7, 9, 2, 2, 9, 5, 5, 2, 4, 9, 8, 8, 7, 2, 7, 5, 8, 4, 6, 1, 0, 1, 2, 6, 4, 8, 3, 6, 9, 9, 9, 8, 9, 2, 2, 5, 6, 9, 5, 9, 6, 8, 8, 1, 5, 9, 2,
+ 0, 5, 6, 0, 0, 1, 0, 1, 6, 5, 5, 2, 5, 6, 3, 7, 5, 6, 7, 8
+ };
+}
diff --git a/core/tests/coretests/src/android/os/MessageQueueTest.java b/core/tests/coretests/src/android/os/MessageQueueTest.java
new file mode 100644
index 0000000..b7c2d1f
--- /dev/null
+++ b/core/tests/coretests/src/android/os/MessageQueueTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.os;
+
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.test.suitebuilder.annotation.MediumTest;
+import junit.framework.TestCase;
+
+public class MessageQueueTest extends TestCase {
+
+ private static class BaseTestHandler extends TestHandlerThread {
+ Handler mHandler;
+ int mLastMessage;
+ int mCount;
+
+ public BaseTestHandler() {
+ }
+
+ public void go() {
+ mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ BaseTestHandler.this.handleMessage(msg);
+ }
+ };
+ }
+
+ public void handleMessage(Message msg) {
+ if (mCount <= mLastMessage) {
+ if (msg.what != mCount) {
+ failure(new RuntimeException(
+ "Expected message #" + mCount
+ + ", received #" + msg.what));
+ } else if (mCount == mLastMessage) {
+ success();
+ }
+ mCount++;
+ } else {
+ failure(new RuntimeException(
+ "Message received after done, #" + msg.what));
+ }
+ }
+ }
+
+ @MediumTest
+ public void testMessageOrder() throws Exception {
+ TestHandlerThread tester = new BaseTestHandler() {
+ public void go() {
+ super.go();
+ long now = SystemClock.uptimeMillis() + 200;
+ mLastMessage = 4;
+ mCount = 0;
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(2), now + 1);
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(3), now + 2);
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(4), now + 2);
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(0), now + 0);
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(1), now + 0);
+ }
+ };
+
+ tester.doTest(1000);
+ }
+
+ @MediumTest
+ public void testAtFrontOfQueue() throws Exception {
+ TestHandlerThread tester = new BaseTestHandler() {
+ public void go() {
+ super.go();
+ long now = SystemClock.uptimeMillis() + 200;
+ mLastMessage = 3;
+ mCount = 0;
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(3), now);
+ mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(2));
+ mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(0));
+ }
+
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+ if (msg.what == 0) {
+ mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(1));
+ }
+ }
+ };
+
+ tester.doTest(1000);
+ }
+}
+
diff --git a/core/tests/coretests/src/android/os/MessengerService.java b/core/tests/coretests/src/android/os/MessengerService.java
new file mode 100644
index 0000000..f15e134
--- /dev/null
+++ b/core/tests/coretests/src/android/os/MessengerService.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+
+public class MessengerService extends Service {
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ Message reply = Message.obtain();
+ reply.copyFrom(msg);
+ try {
+ msg.replyTo.send(reply);
+ } catch (RemoteException e) {
+ }
+ }
+ };
+
+ private final Messenger mMessenger = new Messenger(mHandler);
+
+ public MessengerService() {
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mMessenger.getBinder();
+ }
+}
+
diff --git a/core/tests/coretests/src/android/os/MessengerTest.java b/core/tests/coretests/src/android/os/MessengerTest.java
new file mode 100644
index 0000000..473ffe2
--- /dev/null
+++ b/core/tests/coretests/src/android/os/MessengerTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.os;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.RemoteException;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+public class MessengerTest extends AndroidTestCase {
+ private Messenger mServiceMessenger;
+
+ private ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (MessengerTest.this) {
+ mServiceMessenger = new Messenger(service);
+ MessengerTest.this.notifyAll();
+ }
+ }
+ public void onServiceDisconnected(ComponentName name) {
+ mServiceMessenger = null;
+ }
+ };
+
+ private class TestThread extends TestHandlerThread {
+ private Handler mTestHandler;
+ private Messenger mTestMessenger;
+
+ public void go() {
+ synchronized (MessengerTest.this) {
+ mTestHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ TestThread.this.handleMessage(msg);
+ }
+ };
+ mTestMessenger = new Messenger(mTestHandler);
+ TestThread.this.executeTest();
+ }
+ }
+
+ public void executeTest() {
+ Message msg = Message.obtain();
+ msg.arg1 = 100;
+ msg.arg2 = 1000;
+ msg.replyTo = mTestMessenger;
+ try {
+ mServiceMessenger.send(msg);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void handleMessage(Message msg) {
+ if (msg.arg1 != 100) {
+ failure(new RuntimeException(
+ "Message.arg1 is not 100: " + msg.arg1));
+ return;
+ }
+ if (msg.arg2 != 1000) {
+ failure(new RuntimeException(
+ "Message.arg2 is not 1000: " + msg.arg2));
+ return;
+ }
+ if (!mTestMessenger.equals(msg.replyTo)) {
+ failure(new RuntimeException(
+ "Message.replyTo is not me: " + msg.replyTo));
+ return;
+ }
+ success();
+ }
+ };
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ getContext().bindService(new Intent(mContext, MessengerService.class),
+ mConnection, Context.BIND_AUTO_CREATE);
+ synchronized (this) {
+ while (mServiceMessenger == null) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ getContext().unbindService(mConnection);
+ }
+
+ @MediumTest
+ public void testSend() {
+ (new TestThread()).doTest(1000);
+
+ }
+}
diff --git a/core/tests/coretests/src/android/os/OsTests.java b/core/tests/coretests/src/android/os/OsTests.java
new file mode 100644
index 0000000..582bf1a
--- /dev/null
+++ b/core/tests/coretests/src/android/os/OsTests.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import com.google.android.collect.Lists;
+import junit.framework.TestSuite;
+
+import java.util.Enumeration;
+import java.util.List;
+
+public class OsTests {
+ public static TestSuite suite() {
+ TestSuite suite = new TestSuite(OsTests.class.getName());
+
+ suite.addTestSuite(AidlTest.class);
+ suite.addTestSuite(BroadcasterTest.class);
+ suite.addTestSuite(FileObserverTest.class);
+ suite.addTestSuite(IdleHandlerTest.class);
+ suite.addTestSuite(MessageQueueTest.class);
+ suite.addTestSuite(MessengerTest.class);
+ suite.addTestSuite(SystemPropertiesTest.class);
+
+ return suite;
+ }
+}
diff --git a/core/tests/coretests/src/android/os/PerformanceCollectorTest.java b/core/tests/coretests/src/android/os/PerformanceCollectorTest.java
new file mode 100644
index 0000000..a382239
--- /dev/null
+++ b/core/tests/coretests/src/android/os/PerformanceCollectorTest.java
@@ -0,0 +1,528 @@
+/*
+ * 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.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.SmallTest;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Random;
+
+import junit.framework.TestCase;
+
+public class PerformanceCollectorTest extends TestCase {
+
+ private PerformanceCollector mPerfCollector;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mPerfCollector = new PerformanceCollector();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ mPerfCollector = null;
+ }
+
+ @SmallTest
+ public void testBeginSnapshotNoWriter() throws Exception {
+ mPerfCollector.beginSnapshot("testBeginSnapshotNoWriter");
+
+ assertTrue((Long)readPrivateField("mSnapshotCpuTime", mPerfCollector) > 0);
+ assertTrue((Long)readPrivateField("mSnapshotExecTime", mPerfCollector) > 0);
+ Bundle snapshot = (Bundle)readPrivateField("mPerfSnapshot", mPerfCollector);
+ assertNotNull(snapshot);
+ assertEquals(2, snapshot.size());
+ }
+
+ @SmallTest
+ public void testEndSnapshotNoWriter() throws Exception {
+ mPerfCollector.beginSnapshot("testEndSnapshotNoWriter");
+ workForRandomLongPeriod();
+ Bundle snapshot = mPerfCollector.endSnapshot();
+
+ verifySnapshotBundle(snapshot);
+ }
+
+ @SmallTest
+ public void testStartTimingNoWriter() throws Exception {
+ mPerfCollector.startTiming("testStartTimingNoWriter");
+
+ assertTrue((Long)readPrivateField("mCpuTime", mPerfCollector) > 0);
+ assertTrue((Long)readPrivateField("mExecTime", mPerfCollector) > 0);
+ Bundle measurement = (Bundle)readPrivateField("mPerfMeasurement", mPerfCollector);
+ assertNotNull(measurement);
+ verifyTimingBundle(measurement, new ArrayList<String>());
+ }
+
+ @SmallTest
+ public void testAddIterationNoWriter() throws Exception {
+ mPerfCollector.startTiming("testAddIterationNoWriter");
+ workForRandomTinyPeriod();
+ Bundle iteration = mPerfCollector.addIteration("timing1");
+
+ verifyIterationBundle(iteration, "timing1");
+ }
+
+ @SmallTest
+ public void testStopTimingNoWriter() throws Exception {
+ mPerfCollector.startTiming("testStopTimingNoWriter");
+ workForRandomTinyPeriod();
+ mPerfCollector.addIteration("timing2");
+ workForRandomTinyPeriod();
+ mPerfCollector.addIteration("timing3");
+ workForRandomShortPeriod();
+ Bundle timing = mPerfCollector.stopTiming("timing4");
+
+ ArrayList<String> labels = new ArrayList<String>();
+ labels.add("timing2");
+ labels.add("timing3");
+ labels.add("timing4");
+ verifyTimingBundle(timing, labels);
+ }
+
+ @SmallTest
+ public void testBeginSnapshot() throws Exception {
+ MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
+ mPerfCollector.setPerformanceResultsWriter(writer);
+ mPerfCollector.beginSnapshot("testBeginSnapshot");
+
+ assertEquals("testBeginSnapshot", writer.snapshotLabel);
+ assertTrue((Long)readPrivateField("mSnapshotCpuTime", mPerfCollector) > 0);
+ assertTrue((Long)readPrivateField("mSnapshotExecTime", mPerfCollector) > 0);
+ Bundle snapshot = (Bundle)readPrivateField("mPerfSnapshot", mPerfCollector);
+ assertNotNull(snapshot);
+ assertEquals(2, snapshot.size());
+ }
+
+ @SmallTest
+ public void testEndSnapshot() throws Exception {
+ MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
+ mPerfCollector.setPerformanceResultsWriter(writer);
+ mPerfCollector.beginSnapshot("testEndSnapshot");
+ workForRandomLongPeriod();
+ Bundle snapshot1 = mPerfCollector.endSnapshot();
+ Bundle snapshot2 = writer.snapshotResults;
+
+ assertEqualsBundle(snapshot1, snapshot2);
+ verifySnapshotBundle(snapshot1);
+ }
+
+ @SmallTest
+ public void testStartTiming() throws Exception {
+ MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
+ mPerfCollector.setPerformanceResultsWriter(writer);
+ mPerfCollector.startTiming("testStartTiming");
+
+ assertEquals("testStartTiming", writer.timingLabel);
+ assertTrue((Long)readPrivateField("mCpuTime", mPerfCollector) > 0);
+ assertTrue((Long)readPrivateField("mExecTime", mPerfCollector) > 0);
+ Bundle measurement = (Bundle)readPrivateField("mPerfMeasurement", mPerfCollector);
+ assertNotNull(measurement);
+ verifyTimingBundle(measurement, new ArrayList<String>());
+ }
+
+ @SmallTest
+ public void testAddIteration() throws Exception {
+ mPerfCollector.startTiming("testAddIteration");
+ workForRandomTinyPeriod();
+ Bundle iteration = mPerfCollector.addIteration("timing5");
+
+ verifyIterationBundle(iteration, "timing5");
+ }
+
+ @SmallTest
+ public void testStopTiming() throws Exception {
+ mPerfCollector.startTiming("testStopTiming");
+ workForRandomTinyPeriod();
+ mPerfCollector.addIteration("timing6");
+ workForRandomTinyPeriod();
+ mPerfCollector.addIteration("timing7");
+ workForRandomShortPeriod();
+ Bundle timing = mPerfCollector.stopTiming("timing8");
+
+ ArrayList<String> labels = new ArrayList<String>();
+ labels.add("timing6");
+ labels.add("timing7");
+ labels.add("timing8");
+ verifyTimingBundle(timing, labels);
+ }
+
+ @SmallTest
+ public void testAddMeasurementLong() throws Exception {
+ MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
+ mPerfCollector.setPerformanceResultsWriter(writer);
+ mPerfCollector.startTiming("testAddMeasurementLong");
+ mPerfCollector.addMeasurement("testAddMeasurementLongZero", 0);
+ mPerfCollector.addMeasurement("testAddMeasurementLongPos", 348573);
+ mPerfCollector.addMeasurement("testAddMeasurementLongNeg", -19354);
+ mPerfCollector.stopTiming("");
+
+ assertEquals("testAddMeasurementLong", writer.timingLabel);
+ Bundle results = writer.timingResults;
+ assertEquals(4, results.size());
+ assertTrue(results.containsKey("testAddMeasurementLongZero"));
+ assertEquals(0, results.getLong("testAddMeasurementLongZero"));
+ assertTrue(results.containsKey("testAddMeasurementLongPos"));
+ assertEquals(348573, results.getLong("testAddMeasurementLongPos"));
+ assertTrue(results.containsKey("testAddMeasurementLongNeg"));
+ assertEquals(-19354, results.getLong("testAddMeasurementLongNeg"));
+ }
+
+ @SmallTest
+ public void testAddMeasurementFloat() throws Exception {
+ MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
+ mPerfCollector.setPerformanceResultsWriter(writer);
+ mPerfCollector.startTiming("testAddMeasurementFloat");
+ mPerfCollector.addMeasurement("testAddMeasurementFloatZero", 0.0f);
+ mPerfCollector.addMeasurement("testAddMeasurementFloatPos", 348573.345f);
+ mPerfCollector.addMeasurement("testAddMeasurementFloatNeg", -19354.093f);
+ mPerfCollector.stopTiming("");
+
+ assertEquals("testAddMeasurementFloat", writer.timingLabel);
+ Bundle results = writer.timingResults;
+ assertEquals(4, results.size());
+ assertTrue(results.containsKey("testAddMeasurementFloatZero"));
+ assertEquals(0.0f, results.getFloat("testAddMeasurementFloatZero"));
+ assertTrue(results.containsKey("testAddMeasurementFloatPos"));
+ assertEquals(348573.345f, results.getFloat("testAddMeasurementFloatPos"));
+ assertTrue(results.containsKey("testAddMeasurementFloatNeg"));
+ assertEquals(-19354.093f, results.getFloat("testAddMeasurementFloatNeg"));
+ }
+
+ @SmallTest
+ public void testAddMeasurementString() throws Exception {
+ MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
+ mPerfCollector.setPerformanceResultsWriter(writer);
+ mPerfCollector.startTiming("testAddMeasurementString");
+ mPerfCollector.addMeasurement("testAddMeasurementStringNull", null);
+ mPerfCollector.addMeasurement("testAddMeasurementStringEmpty", "");
+ mPerfCollector.addMeasurement("testAddMeasurementStringNonEmpty", "Hello World");
+ mPerfCollector.stopTiming("");
+
+ assertEquals("testAddMeasurementString", writer.timingLabel);
+ Bundle results = writer.timingResults;
+ assertEquals(4, results.size());
+ assertTrue(results.containsKey("testAddMeasurementStringNull"));
+ assertNull(results.getString("testAddMeasurementStringNull"));
+ assertTrue(results.containsKey("testAddMeasurementStringEmpty"));
+ assertEquals("", results.getString("testAddMeasurementStringEmpty"));
+ assertTrue(results.containsKey("testAddMeasurementStringNonEmpty"));
+ assertEquals("Hello World", results.getString("testAddMeasurementStringNonEmpty"));
+ }
+
+ @SmallTest
+ public void testSimpleSequence() throws Exception {
+ MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
+ mPerfCollector.setPerformanceResultsWriter(writer);
+ mPerfCollector.beginSnapshot("testSimpleSequence");
+ mPerfCollector.startTiming("testSimpleSequenceTiming");
+ workForRandomTinyPeriod();
+ mPerfCollector.addIteration("iteration1");
+ workForRandomTinyPeriod();
+ mPerfCollector.addIteration("iteration2");
+ workForRandomTinyPeriod();
+ mPerfCollector.addIteration("iteration3");
+ workForRandomTinyPeriod();
+ mPerfCollector.addIteration("iteration4");
+ workForRandomShortPeriod();
+ Bundle timing = mPerfCollector.stopTiming("iteration5");
+ workForRandomLongPeriod();
+ Bundle snapshot1 = mPerfCollector.endSnapshot();
+ Bundle snapshot2 = writer.snapshotResults;
+
+ assertEqualsBundle(snapshot1, snapshot2);
+ verifySnapshotBundle(snapshot1);
+
+ ArrayList<String> labels = new ArrayList<String>();
+ labels.add("iteration1");
+ labels.add("iteration2");
+ labels.add("iteration3");
+ labels.add("iteration4");
+ labels.add("iteration5");
+ verifyTimingBundle(timing, labels);
+ }
+
+ @SmallTest
+ public void testLongSequence() throws Exception {
+ MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
+ mPerfCollector.setPerformanceResultsWriter(writer);
+ mPerfCollector.beginSnapshot("testLongSequence");
+ mPerfCollector.startTiming("testLongSequenceTiming1");
+ workForRandomTinyPeriod();
+ mPerfCollector.addIteration("iteration1");
+ workForRandomTinyPeriod();
+ mPerfCollector.addIteration("iteration2");
+ workForRandomShortPeriod();
+ Bundle timing1 = mPerfCollector.stopTiming("iteration3");
+ workForRandomLongPeriod();
+
+ mPerfCollector.startTiming("testLongSequenceTiming2");
+ workForRandomTinyPeriod();
+ mPerfCollector.addIteration("iteration4");
+ workForRandomTinyPeriod();
+ mPerfCollector.addIteration("iteration5");
+ workForRandomShortPeriod();
+ Bundle timing2 = mPerfCollector.stopTiming("iteration6");
+ workForRandomLongPeriod();
+
+ mPerfCollector.startTiming("testLongSequenceTiming3");
+ workForRandomTinyPeriod();
+ mPerfCollector.addIteration("iteration7");
+ workForRandomTinyPeriod();
+ mPerfCollector.addIteration("iteration8");
+ workForRandomShortPeriod();
+ Bundle timing3 = mPerfCollector.stopTiming("iteration9");
+ workForRandomLongPeriod();
+
+ mPerfCollector.startTiming("testLongSequenceTiming4");
+ workForRandomTinyPeriod();
+ mPerfCollector.addIteration("iteration10");
+ workForRandomTinyPeriod();
+ mPerfCollector.addIteration("iteration11");
+ workForRandomShortPeriod();
+ Bundle timing4 = mPerfCollector.stopTiming("iteration12");
+ workForRandomLongPeriod();
+
+ mPerfCollector.startTiming("testLongSequenceTiming5");
+ workForRandomTinyPeriod();
+ mPerfCollector.addIteration("iteration13");
+ workForRandomTinyPeriod();
+ mPerfCollector.addIteration("iteration14");
+ workForRandomShortPeriod();
+ Bundle timing5 = mPerfCollector.stopTiming("iteration15");
+ workForRandomLongPeriod();
+ Bundle snapshot1 = mPerfCollector.endSnapshot();
+ Bundle snapshot2 = writer.snapshotResults;
+
+ assertEqualsBundle(snapshot1, snapshot2);
+ verifySnapshotBundle(snapshot1);
+
+ ArrayList<String> labels1 = new ArrayList<String>();
+ labels1.add("iteration1");
+ labels1.add("iteration2");
+ labels1.add("iteration3");
+ verifyTimingBundle(timing1, labels1);
+ ArrayList<String> labels2 = new ArrayList<String>();
+ labels2.add("iteration4");
+ labels2.add("iteration5");
+ labels2.add("iteration6");
+ verifyTimingBundle(timing2, labels2);
+ ArrayList<String> labels3 = new ArrayList<String>();
+ labels3.add("iteration7");
+ labels3.add("iteration8");
+ labels3.add("iteration9");
+ verifyTimingBundle(timing3, labels3);
+ ArrayList<String> labels4 = new ArrayList<String>();
+ labels4.add("iteration10");
+ labels4.add("iteration11");
+ labels4.add("iteration12");
+ verifyTimingBundle(timing4, labels4);
+ ArrayList<String> labels5 = new ArrayList<String>();
+ labels5.add("iteration13");
+ labels5.add("iteration14");
+ labels5.add("iteration15");
+ verifyTimingBundle(timing5, labels5);
+ }
+
+ /*
+ * Verify that snapshotting and timing do not interfere w/ each other,
+ * by staggering calls to snapshot and timing functions.
+ */
+ @SmallTest
+ public void testOutOfOrderSequence() {
+ MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
+ mPerfCollector.setPerformanceResultsWriter(writer);
+ mPerfCollector.startTiming("testOutOfOrderSequenceTiming");
+ workForRandomShortPeriod();
+ mPerfCollector.beginSnapshot("testOutOfOrderSequenceSnapshot");
+ workForRandomShortPeriod();
+ Bundle timing1 = mPerfCollector.stopTiming("timing1");
+ workForRandomShortPeriod();
+ Bundle snapshot1 = mPerfCollector.endSnapshot();
+
+ Bundle timing2 = writer.timingResults;
+ Bundle snapshot2 = writer.snapshotResults;
+
+ assertEqualsBundle(snapshot1, snapshot2);
+ verifySnapshotBundle(snapshot1);
+
+ assertEqualsBundle(timing1, timing2);
+ ArrayList<String> labels = new ArrayList<String>();
+ labels.add("timing1");
+ verifyTimingBundle(timing1, labels);
+ }
+
+ private void workForRandomPeriod(int minDuration, int maxDuration) {
+ Random random = new Random();
+ int period = minDuration + random.nextInt(maxDuration - minDuration);
+ long start = Process.getElapsedCpuTime();
+ // Generate positive amount of work, so cpu time is measurable in
+ // milliseconds
+ while (Process.getElapsedCpuTime() - start < period) {
+ for (int i = 0, temp = 0; i < 50; i++ ) {
+ temp += i;
+ }
+ }
+ }
+
+ private void workForRandomTinyPeriod() {
+ workForRandomPeriod(2, 5);
+ }
+
+ private void workForRandomShortPeriod() {
+ workForRandomPeriod(10, 25);
+ }
+
+ private void workForRandomLongPeriod() {
+ workForRandomPeriod(50, 100);
+ }
+
+ private void verifySnapshotBundle(Bundle snapshot) {
+ assertTrue("At least 26 metrics collected", 26 <= snapshot.size());
+
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_CPU_TIME));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_CPU_TIME) > 0);
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_EXECUTION_TIME));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_EXECUTION_TIME) > 0);
+
+ assertTrue(snapshot.containsKey(
+ PerformanceCollector.METRIC_KEY_PRE_RECEIVED_TRANSACTIONS));
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_PRE_SENT_TRANSACTIONS));
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_RECEIVED_TRANSACTIONS));
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_SENT_TRANSACTIONS));
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_GC_INVOCATION_COUNT));
+
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_JAVA_ALLOCATED));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_JAVA_ALLOCATED) > 0);
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_JAVA_FREE));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_JAVA_FREE) > 0);
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_JAVA_PRIVATE_DIRTY));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_JAVA_PRIVATE_DIRTY) > 0);
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_JAVA_PSS));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_JAVA_PSS) > 0);
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_JAVA_SHARED_DIRTY));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_JAVA_SHARED_DIRTY) > 0);
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_JAVA_SIZE));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_JAVA_SIZE) > 0);
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_NATIVE_ALLOCATED));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_NATIVE_ALLOCATED) > 0);
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_NATIVE_FREE));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_NATIVE_FREE) > 0);
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_NATIVE_PRIVATE_DIRTY));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_NATIVE_PRIVATE_DIRTY) > 0);
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_NATIVE_PSS));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_NATIVE_PSS) > 0);
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_NATIVE_SHARED_DIRTY));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_NATIVE_SHARED_DIRTY) > 0);
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_NATIVE_SIZE));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_NATIVE_SIZE) > 0);
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_GLOBAL_ALLOC_COUNT));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_GLOBAL_ALLOC_COUNT) > 0);
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_GLOBAL_ALLOC_SIZE));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_GLOBAL_ALLOC_SIZE) > 0);
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_GLOBAL_FREED_COUNT));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_GLOBAL_FREED_COUNT) > 0);
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_GLOBAL_FREED_SIZE));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_GLOBAL_FREED_SIZE) > 0);
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_OTHER_PRIVATE_DIRTY));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_OTHER_PRIVATE_DIRTY) > 0);
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_OTHER_PSS));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_OTHER_PSS) > 0);
+ assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_OTHER_SHARED_DIRTY));
+ assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_OTHER_SHARED_DIRTY) > 0);
+ }
+
+ private void verifyIterationBundle(Bundle iteration, String label) {
+ assertEquals(3, iteration.size());
+ assertTrue(iteration.containsKey(PerformanceCollector.METRIC_KEY_LABEL));
+ assertEquals(label, iteration.getString(PerformanceCollector.METRIC_KEY_LABEL));
+ assertTrue(iteration.containsKey(PerformanceCollector.METRIC_KEY_CPU_TIME));
+ assertTrue(iteration.getLong(PerformanceCollector.METRIC_KEY_CPU_TIME) > 0);
+ assertTrue(iteration.containsKey(PerformanceCollector.METRIC_KEY_EXECUTION_TIME));
+ assertTrue(iteration.getLong(PerformanceCollector.METRIC_KEY_EXECUTION_TIME) > 0);
+ }
+
+ private void verifyTimingBundle(Bundle timing, ArrayList<String> labels) {
+ assertEquals(1, timing.size());
+ assertTrue(timing.containsKey(PerformanceCollector.METRIC_KEY_ITERATIONS));
+ ArrayList<Parcelable> iterations = timing.getParcelableArrayList(
+ PerformanceCollector.METRIC_KEY_ITERATIONS);
+ assertNotNull(iterations);
+ assertEquals(labels.size(), iterations.size());
+ for (int i = 0; i < labels.size(); i ++) {
+ Bundle iteration = (Bundle)iterations.get(i);
+ verifyIterationBundle(iteration, labels.get(i));
+ }
+ }
+
+ private void assertEqualsBundle(Bundle b1, Bundle b2) {
+ assertEquals(b1.keySet(), b2.keySet());
+ for (String key : b1.keySet()) {
+ assertEquals(b1.get(key), b2.get(key));
+ }
+ }
+
+ private Object readPrivateField(String fieldName, Object object) throws Exception {
+ Field f = object.getClass().getDeclaredField(fieldName);
+ f.setAccessible(true);
+ return f.get(object);
+ }
+
+ private class MockPerformanceResultsWriter implements PerformanceResultsWriter {
+
+ public String snapshotLabel;
+ public Bundle snapshotResults = new Bundle();
+ public String timingLabel;
+ public Bundle timingResults = new Bundle();
+
+ public void writeBeginSnapshot(String label) {
+ snapshotLabel = label;
+ }
+
+ public void writeEndSnapshot(Bundle results) {
+ snapshotResults.putAll(results);
+ }
+
+ public void writeStartTiming(String label) {
+ timingLabel = label;
+ }
+
+ public void writeStopTiming(Bundle results) {
+ timingResults.putAll(results);
+ }
+
+ public void writeMeasurement(String label, long value) {
+ timingResults.putLong(label, value);
+ }
+
+ public void writeMeasurement(String label, float value) {
+ timingResults.putFloat(label, value);
+ }
+
+ public void writeMeasurement(String label, String value) {
+ timingResults.putString(label, value);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
new file mode 100644
index 0000000..e089b3e
--- /dev/null
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.os;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+public class PowerManagerTest extends AndroidTestCase {
+
+ private PowerManager mPm;
+
+ /**
+ * Setup any common data for the upcoming tests.
+ */
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mPm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ }
+
+ /**
+ * Confirm that the setup is good.
+ *
+ * @throws Exception
+ */
+ @MediumTest
+ public void testPreconditions() throws Exception {
+ assertNotNull(mPm);
+ }
+
+ /**
+ * Confirm that we can create functional wakelocks.
+ *
+ * @throws Exception
+ */
+ @MediumTest
+ public void testNewWakeLock() throws Exception {
+ PowerManager.WakeLock wl = mPm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "FULL_WAKE_LOCK");
+ doTestWakeLock(wl);
+
+ wl = mPm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "SCREEN_BRIGHT_WAKE_LOCK");
+ doTestWakeLock(wl);
+
+ wl = mPm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "SCREEN_DIM_WAKE_LOCK");
+ doTestWakeLock(wl);
+
+ wl = mPm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "PARTIAL_WAKE_LOCK");
+ doTestWakeLock(wl);
+
+ doTestSetBacklightBrightness();
+
+ // TODO: Some sort of functional test (maybe not in the unit test here?)
+ // that confirms that things are really happening e.g. screen power, keyboard power.
+}
+
+ /**
+ * Confirm that we can't create dysfunctional wakelocks.
+ *
+ * @throws Exception
+ */
+ @MediumTest
+ public void testBadNewWakeLock() throws Exception {
+
+ final int badFlags = PowerManager.SCREEN_BRIGHT_WAKE_LOCK
+ | PowerManager.SCREEN_DIM_WAKE_LOCK;
+ // wrap in try because we want the error here
+ try {
+ PowerManager.WakeLock wl = mPm.newWakeLock(badFlags, "foo");
+ } catch (IllegalArgumentException e) {
+ return;
+ }
+ fail("Bad WakeLock flag was not caught.");
+ }
+
+ /**
+ * Apply a few tests to a wakelock to make sure it's healthy.
+ *
+ * @param wl The wakelock to be tested.
+ */
+ private void doTestWakeLock(PowerManager.WakeLock wl) {
+ // First try simple acquire/release
+ wl.acquire();
+ assertTrue(wl.isHeld());
+ wl.release();
+ assertFalse(wl.isHeld());
+
+ // Try ref-counted acquire/release
+ wl.setReferenceCounted(true);
+ wl.acquire();
+ assertTrue(wl.isHeld());
+ wl.acquire();
+ assertTrue(wl.isHeld());
+ wl.release();
+ assertTrue(wl.isHeld());
+ wl.release();
+ assertFalse(wl.isHeld());
+
+ // Try non-ref-counted
+ wl.setReferenceCounted(false);
+ wl.acquire();
+ assertTrue(wl.isHeld());
+ wl.acquire();
+ assertTrue(wl.isHeld());
+ wl.release();
+ assertFalse(wl.isHeld());
+
+ // TODO: Threaded test (needs handler) to make sure timed wakelocks work too
+ }
+
+
+ /**
+ * Test that calling {@link android.os.IHardwareService#setBacklights(int)} requires
+ * permissions.
+ * <p>Tests permission:
+ * {@link android.Manifest.permission#DEVICE_POWER}
+ */
+ private void doTestSetBacklightBrightness() {
+ try {
+ mPm.setBacklightBrightness(0);
+ fail("setBacklights did not throw SecurityException as expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+}
diff --git a/core/tests/coretests/src/android/os/SystemPropertiesTest.java b/core/tests/coretests/src/android/os/SystemPropertiesTest.java
new file mode 100644
index 0000000..25868ce
--- /dev/null
+++ b/core/tests/coretests/src/android/os/SystemPropertiesTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import static junit.framework.Assert.assertEquals;
+import junit.framework.TestCase;
+
+import android.os.SystemProperties;
+import android.test.suitebuilder.annotation.SmallTest;
+
+public class SystemPropertiesTest extends TestCase {
+ private static final String KEY = "com.android.frameworks.coretests";
+ @SmallTest
+ public void testProperties() throws Exception {
+ if (false) {
+ String value;
+
+ SystemProperties.set(KEY, "");
+ value = SystemProperties.get(KEY, "default");
+ assertEquals("default", value);
+
+ SystemProperties.set(KEY, "AAA");
+ value = SystemProperties.get(KEY, "default");
+ assertEquals("AAA", value);
+
+ value = SystemProperties.get(KEY);
+ assertEquals("AAA", value);
+
+ SystemProperties.set(KEY, "");
+ value = SystemProperties.get(KEY, "default");
+ assertEquals("default", value);
+
+ value = SystemProperties.get(KEY);
+ assertEquals("", value);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/os/TestHandlerThread.java b/core/tests/coretests/src/android/os/TestHandlerThread.java
new file mode 100644
index 0000000..7e84af3
--- /dev/null
+++ b/core/tests/coretests/src/android/os/TestHandlerThread.java
@@ -0,0 +1,104 @@
+/*
+ * 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.os;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue.IdleHandler;
+
+abstract class TestHandlerThread {
+ private boolean mDone = false;
+ private boolean mSuccess = false;
+ private RuntimeException mFailure = null;
+ private Looper mLooper;
+
+ public abstract void go();
+
+ public TestHandlerThread() {
+ }
+
+ public void doTest(long timeout) {
+ (new LooperThread()).start();
+
+ synchronized (this) {
+ long now = System.currentTimeMillis();
+ long endTime = now + timeout;
+ while (!mDone && now < endTime) {
+ try {
+ wait(endTime-now);
+ }
+ catch (InterruptedException e) {
+ }
+ now = System.currentTimeMillis();
+ }
+ }
+
+ mLooper.quit();
+
+ if (!mDone) {
+ throw new RuntimeException("test timed out");
+ }
+ if (!mSuccess) {
+ throw mFailure;
+ }
+ }
+
+ public Looper getLooper() {
+ return mLooper;
+ }
+
+ public void success() {
+ synchronized (this) {
+ mSuccess = true;
+ quit();
+ }
+ }
+
+ public void failure(RuntimeException failure) {
+ synchronized (this) {
+ mSuccess = false;
+ mFailure = failure;
+ quit();
+ }
+ }
+
+ class LooperThread extends Thread {
+ public void run() {
+ Looper.prepare();
+ mLooper = Looper.myLooper();
+ go();
+ Looper.loop();
+
+ synchronized (TestHandlerThread.this) {
+ mDone = true;
+ if (!mSuccess && mFailure == null) {
+ mFailure = new RuntimeException("no failure exception set");
+ }
+ TestHandlerThread.this.notifyAll();
+ }
+ }
+
+ }
+
+ private void quit() {
+ synchronized (this) {
+ mDone = true;
+ notifyAll();
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/os/TraceTest.java b/core/tests/coretests/src/android/os/TraceTest.java
new file mode 100644
index 0000000..7a788ee
--- /dev/null
+++ b/core/tests/coretests/src/android/os/TraceTest.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2006 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.os;
+
+import android.os.Debug;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.util.Log;
+
+/**
+ * This class is used to test the native tracing support. Run this test
+ * while tracing on the emulator and then run traceview to view the trace.
+ */
+public class TraceTest extends AndroidTestCase
+{
+ private static final String TAG = "TraceTest";
+ private int eMethodCalls = 0;
+ private int fMethodCalls = 0;
+ private int gMethodCalls = 0;
+
+ @SmallTest
+ public void testNativeTracingFromJava()
+ {
+ long start = System.currentTimeMillis();
+ Debug.startNativeTracing();
+ //nativeMethod();
+ int count = 0;
+ for (int ii = 0; ii < 20; ii++) {
+ count = eMethod();
+ }
+ Debug.stopNativeTracing();
+ long end = System.currentTimeMillis();
+ long elapsed = end - start;
+ Log.i(TAG, "elapsed millis: " + elapsed);
+ Log.i(TAG, "eMethod calls: " + eMethodCalls
+ + " fMethod calls: " + fMethodCalls
+ + " gMethod calls: " + gMethodCalls);
+ }
+
+ // This should not run in the automated suite.
+ @Suppress
+ public void disableTestNativeTracingFromC()
+ {
+ long start = System.currentTimeMillis();
+ nativeMethodAndStartTracing();
+ long end = System.currentTimeMillis();
+ long elapsed = end - start;
+ Log.i(TAG, "elapsed millis: " + elapsed);
+ }
+
+ native void nativeMethod();
+ native void nativeMethodAndStartTracing();
+
+ @LargeTest
+ public void testMethodTracing()
+ {
+ long start = System.currentTimeMillis();
+ Debug.startMethodTracing("traceTest");
+ topMethod();
+ Debug.stopMethodTracing();
+ long end = System.currentTimeMillis();
+ long elapsed = end - start;
+ Log.i(TAG, "elapsed millis: " + elapsed);
+ }
+
+ private void topMethod() {
+ aMethod();
+ bMethod();
+ cMethod();
+ dMethod(5);
+
+ Thread t1 = new aThread();
+ t1.start();
+ Thread t2 = new aThread();
+ t2.start();
+ Thread t3 = new aThread();
+ t3.start();
+ try {
+ t1.join();
+ t2.join();
+ t3.join();
+ } catch (InterruptedException e) {
+ }
+ }
+
+ private class aThread extends Thread {
+ @Override
+ public void run() {
+ aMethod();
+ bMethod();
+ cMethod();
+ }
+ }
+
+ /** Calls other methods to make some interesting trace data.
+ *
+ * @return a meaningless value
+ */
+ private int aMethod() {
+ int count = 0;
+ for (int ii = 0; ii < 6; ii++) {
+ count += bMethod();
+ }
+ for (int ii = 0; ii < 5; ii++) {
+ count += cMethod();
+ }
+ for (int ii = 0; ii < 4; ii++) {
+ count += dMethod(ii);
+ }
+ return count;
+ }
+
+ /** Calls another method to make some interesting trace data.
+ *
+ * @return a meaningless value
+ */
+ private int bMethod() {
+ int count = 0;
+ for (int ii = 0; ii < 4; ii++) {
+ count += cMethod();
+ }
+ return count;
+ }
+
+ /** Executes a simple loop to make some interesting trace data.
+ *
+ * @return a meaningless value
+ */
+ private int cMethod() {
+ int count = 0;
+ for (int ii = 0; ii < 1000; ii++) {
+ count += ii;
+ }
+ return count;
+ }
+
+ /** Calls itself recursively to make some interesting trace data.
+ *
+ * @return a meaningless value
+ */
+ private int dMethod(int level) {
+ int count = 0;
+ if (level > 0) {
+ count = dMethod(level - 1);
+ }
+ for (int ii = 0; ii < 100; ii++) {
+ count += ii;
+ }
+ if (level == 0) {
+ return count;
+ }
+ return dMethod(level - 1);
+ }
+
+ public int eMethod() {
+ eMethodCalls += 1;
+ int count = fMethod();
+ count += gMethod(3);
+ return count;
+ }
+
+ public int fMethod() {
+ fMethodCalls += 1;
+ int count = 0;
+ for (int ii = 0; ii < 10; ii++) {
+ count += ii;
+ }
+ return count;
+ }
+
+ public int gMethod(int level) {
+ gMethodCalls += 1;
+ int count = level;
+ if (level > 1)
+ count += gMethod(level - 1);
+ return count;
+ }
+
+ /*
+ * This causes the native shared library to be loaded when the
+ * class is first used. The library is only loaded once, even if
+ * multiple classes include this line.
+ *
+ * The library must be in java.library.path, which is derived from
+ * LD_LIBRARY_PATH. The actual library name searched for will be
+ * "libtrace_test.so" under Linux, but may be different on other
+ * platforms.
+ */
+ static {
+ Log.i(TAG, "Loading trace_test native library...");
+ try {
+ System.loadLibrary("trace_test");
+ Log.i(TAG, "Successfully loaded trace_test native library");
+ }
+ catch (UnsatisfiedLinkError ule) {
+ Log.w(TAG, "Could not load trace_test native library");
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/ContentValuesBuilder.java b/core/tests/coretests/src/android/pim/vcard/ContentValuesBuilder.java
new file mode 100644
index 0000000..b3c0773
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/ContentValuesBuilder.java
@@ -0,0 +1,81 @@
+/*
+ * 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.ContentValues;
+
+/**
+ * ContentValues-like class which enables users to chain put() methods and restricts
+ * the other methods.
+ */
+/* package */ class ContentValuesBuilder {
+ private final ContentValues mContentValues;
+
+ public ContentValuesBuilder(final ContentValues contentValues) {
+ mContentValues = contentValues;
+ }
+
+ public ContentValuesBuilder put(String key, String value) {
+ mContentValues.put(key, value);
+ return this;
+ }
+
+ public ContentValuesBuilder put(String key, Byte value) {
+ mContentValues.put(key, value);
+ return this;
+ }
+
+ 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;
+ }
+
+ public ContentValuesBuilder put(String key, Float value) {
+ mContentValues.put(key, value);
+ return this;
+ }
+
+ public ContentValuesBuilder put(String key, Double value) {
+ mContentValues.put(key, value);
+ return this;
+ }
+
+ public ContentValuesBuilder put(String key, Boolean value) {
+ mContentValues.put(key, value);
+ return this;
+ }
+
+ public ContentValuesBuilder put(String key, byte[] value) {
+ mContentValues.put(key, value);
+ return this;
+ }
+
+ public ContentValuesBuilder putNull(String key) {
+ mContentValues.putNull(key);
+ return this;
+ }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/ContentValuesVerifier.java b/core/tests/coretests/src/android/pim/vcard/ContentValuesVerifier.java
new file mode 100644
index 0000000..b9e9875
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/ContentValuesVerifier.java
@@ -0,0 +1,101 @@
+/*
+ * 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/ContentValuesVerifierElem.java b/core/tests/coretests/src/android/pim/vcard/ContentValuesVerifierElem.java
new file mode 100644
index 0000000..2edbb36
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/ContentValuesVerifierElem.java
@@ -0,0 +1,95 @@
+/*
+ * 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.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.exception.VCardException;
+import android.provider.ContactsContract.Data;
+import android.test.AndroidTestCase;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/* package */ class ContentValuesVerifierElem {
+ private final AndroidTestCase mTestCase;
+ private final ImportTestResolver mResolver;
+ private final VCardEntryHandler mHandler;
+
+ public ContentValuesVerifierElem(AndroidTestCase androidTestCase) {
+ mTestCase = androidTestCase;
+ mResolver = new ImportTestResolver(androidTestCase);
+ mHandler = new VCardEntryCommitter(mResolver);
+ }
+
+ public ContentValuesBuilder addExpected(String mimeType) {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(Data.MIMETYPE, mimeType);
+ mResolver.addExpectedContentValues(contentValues);
+ return new ContentValuesBuilder(contentValues);
+ }
+
+ public void verify(int resId, int vCardType)
+ throws IOException, VCardException {
+ 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);
+ builder.addEntryHandler(mHandler);
+ try {
+ vCardParser.parse(is, builder);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ verifyResolver();
+ }
+
+ public void verifyResolver() {
+ mResolver.verify();
+ }
+
+ public void onParsingStart() {
+ mHandler.onStart();
+ }
+
+ public void onEntryCreated(VCardEntry entry) {
+ mHandler.onEntryCreated(entry);
+ }
+
+ public void onParsingEnd() {
+ mHandler.onEnd();
+ }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java b/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java
new file mode 100644
index 0000000..1b3cdcc
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java
@@ -0,0 +1,213 @@
+/*
+ * 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.ContentResolver;
+import android.content.ContentValues;
+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.mock.MockCursor;
+
+import junit.framework.TestCase;
+
+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();
+ }
+}
+
+/* package */ 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);
+ }
+ mEntityList.add(entity);
+ mIterator = mEntityList.iterator();
+ }
+
+ public boolean hasNext() {
+ return mIterator.hasNext();
+ }
+
+ public Entity next() {
+ return mIterator.next();
+ }
+
+ public void reset() {
+ mIterator = mEntityList.iterator();
+ }
+
+ public void close() {
+ }
+}
+
+/**
+ * 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 List<ContentValues> getList() {
+ return mContentValuesList;
+ }
+}
+
+/* package */ class ExportTestProvider extends MockContentProvider {
+ final private TestCase mTestCase;
+ final private ArrayList<ContactEntry> mContactEntryList = new ArrayList<ContactEntry>();
+
+ public ExportTestProvider(TestCase testCase) {
+ mTestCase = testCase;
+ }
+
+ public ContactEntry buildInputEntry() {
+ ContactEntry contactEntry = new ContactEntry();
+ mContactEntryList.add(contactEntry);
+ return contactEntry;
+ }
+
+ /**
+ * <p>
+ * An old method which had existed but was removed from ContentResolver.
+ * </p>
+ * <p>
+ * 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()));
+ 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);
+ final int id = Integer.parseInt(selectionArgs[0]);
+ mTestCase.assertTrue(id >= 0 && id < mContactEntryList.size());
+
+ return new MockEntityIterator(mContactEntryList.get(id).getList());
+ }
+
+ @Override
+ public Cursor query(Uri uri,String[] projection,
+ String selection, String[] selectionArgs, String sortOrder) {
+ mTestCase.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);
+
+ return new MockCursor() {
+ int mCurrentPosition = -1;
+
+ @Override
+ public int getCount() {
+ return mContactEntryList.size();
+ }
+
+ @Override
+ public boolean moveToFirst() {
+ mCurrentPosition = 0;
+ return true;
+ }
+
+ @Override
+ public boolean moveToNext() {
+ if (mCurrentPosition < mContactEntryList.size()) {
+ mCurrentPosition++;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isBeforeFirst() {
+ return mCurrentPosition < 0;
+ }
+
+ @Override
+ public boolean isAfterLast() {
+ return mCurrentPosition >= mContactEntryList.size();
+ }
+
+ @Override
+ public int getColumnIndex(String columnName) {
+ mTestCase.assertEquals(Contacts._ID, columnName);
+ return 0;
+ }
+
+ @Override
+ public int getInt(int columnIndex) {
+ mTestCase.assertEquals(0, columnIndex);
+ mTestCase.assertTrue(mCurrentPosition >= 0
+ && mCurrentPosition < mContactEntryList.size());
+ return mCurrentPosition;
+ }
+
+ @Override
+ public String getString(int columnIndex) {
+ return String.valueOf(getInt(columnIndex));
+ }
+
+ @Override
+ public void close() {
+ }
+ };
+ }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java b/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java
new file mode 100644
index 0000000..019b9e3
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java
@@ -0,0 +1,299 @@
+/*
+ * 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.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;
+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;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+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.text.TextUtils;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+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 {
+ private static final Set<String> sKnownMimeTypeSet =
+ new HashSet<String>(Arrays.asList(StructuredName.CONTENT_ITEM_TYPE,
+ Nickname.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE,
+ Email.CONTENT_ITEM_TYPE, StructuredPostal.CONTENT_ITEM_TYPE,
+ Im.CONTENT_ITEM_TYPE, Organization.CONTENT_ITEM_TYPE,
+ Event.CONTENT_ITEM_TYPE, Photo.CONTENT_ITEM_TYPE,
+ Note.CONTENT_ITEM_TYPE, Website.CONTENT_ITEM_TYPE,
+ Relation.CONTENT_ITEM_TYPE, Event.CONTENT_ITEM_TYPE,
+ GroupMembership.CONTENT_ITEM_TYPE));
+
+ final Map<String, Collection<ContentValues>> mMimeTypeToExpectedContentValues;
+
+ private final TestCase mTestCase;
+
+ public ImportTestProvider(TestCase testCase) {
+ mTestCase = testCase;
+ mMimeTypeToExpectedContentValues =
+ new HashMap<String, Collection<ContentValues>>();
+ for (String acceptanbleMimeType : sKnownMimeTypeSet) {
+ // Do not use HashSet since the current implementation changes the content of
+ // ContentValues after the insertion, which make the result of hashCode()
+ // changes...
+ mMimeTypeToExpectedContentValues.put(
+ acceptanbleMimeType, new ArrayList<ContentValues>());
+ }
+ }
+
+ public void addExpectedContentValues(ContentValues expectedContentValues) {
+ final String mimeType = expectedContentValues.getAsString(Data.MIMETYPE);
+ if (!sKnownMimeTypeSet.contains(mimeType)) {
+ mTestCase.fail(String.format(
+ "Unknow MimeType %s in the test code. Test code should be broken.",
+ mimeType));
+ }
+
+ final Collection<ContentValues> contentValuesCollection =
+ mMimeTypeToExpectedContentValues.get(mimeType);
+ contentValuesCollection.add(expectedContentValues);
+ }
+
+ @Override
+ public ContentProviderResult[] applyBatch(
+ ArrayList<ContentProviderOperation> operations) {
+ if (operations == null) {
+ mTestCase.fail("There is no operation.");
+ }
+
+ final int size = operations.size();
+ ContentProviderResult[] fakeResultArray = new ContentProviderResult[size];
+ for (int i = 0; i < size; i++) {
+ Uri uri = Uri.withAppendedPath(RawContacts.CONTENT_URI, String.valueOf(i));
+ fakeResultArray[i] = new ContentProviderResult(uri);
+ }
+
+ for (int i = 0; i < size; i++) {
+ ContentProviderOperation operation = operations.get(i);
+ ContentValues contentValues = operation.resolveValueBackReferences(
+ fakeResultArray, i);
+ }
+ for (int i = 0; i < size; i++) {
+ ContentProviderOperation operation = operations.get(i);
+ ContentValues actualContentValues = operation.resolveValueBackReferences(
+ 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));
+ } else if (uri.equals(Data.CONTENT_URI)) {
+ final String mimeType = actualContentValues.getAsString(Data.MIMETYPE);
+ if (!sKnownMimeTypeSet.contains(mimeType)) {
+ mTestCase.fail(String.format(
+ "Unknown MimeType %s. Probably added after developing this test",
+ mimeType));
+ }
+ // Remove data meaningless in this unit tests.
+ // Specifically, Data.DATA1 - DATA7 are set to null or empty String
+ // regardless of the input, but it may change depending on how
+ // resolver-related code handles it.
+ // Here, we ignore these implementation-dependent specs and
+ // just check whether vCard importer correctly inserts rellevent data.
+ Set<String> keyToBeRemoved = new HashSet<String>();
+ for (Entry<String, Object> entry : actualContentValues.valueSet()) {
+ Object value = entry.getValue();
+ if (value == null || TextUtils.isEmpty(value.toString())) {
+ keyToBeRemoved.add(entry.getKey());
+ }
+ }
+ for (String key: keyToBeRemoved) {
+ actualContentValues.remove(key);
+ }
+ /* for testing
+ Log.d("@@@",
+ String.format("MimeType: %s, data: %s",
+ mimeType, actualContentValues.toString())); */
+ // Remove RAW_CONTACT_ID entry just for safety, since we do not care
+ // how resolver-related code handles the entry in this unit test,
+ if (actualContentValues.containsKey(Data.RAW_CONTACT_ID)) {
+ actualContentValues.remove(Data.RAW_CONTACT_ID);
+ }
+ final Collection<ContentValues> contentValuesCollection =
+ mMimeTypeToExpectedContentValues.get(mimeType);
+ if (contentValuesCollection.isEmpty()) {
+ mTestCase.fail("ContentValues for MimeType " + mimeType
+ + " is not expected at all (" + actualContentValues + ")");
+ }
+ boolean checked = false;
+ for (ContentValues expectedContentValues : contentValuesCollection) {
+ /*for testing
+ Log.d("@@@", "expected: "
+ + convertToEasilyReadableString(expectedContentValues));
+ Log.d("@@@", "actual : "
+ + convertToEasilyReadableString(actualContentValues));*/
+ if (equalsForContentValues(expectedContentValues,
+ actualContentValues)) {
+ mTestCase.assertTrue(contentValuesCollection.remove(expectedContentValues));
+ checked = true;
+ break;
+ }
+ }
+ if (!checked) {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("Unexpected: ");
+ builder.append(convertToEasilyReadableString(actualContentValues));
+ builder.append("\nExpected: ");
+ for (ContentValues expectedContentValues : contentValuesCollection) {
+ builder.append(convertToEasilyReadableString(expectedContentValues));
+ }
+ mTestCase.fail(builder.toString());
+ }
+ } else {
+ mTestCase.fail("Unexpected Uri has come: " + uri);
+ }
+ } // for (int i = 0; i < size; i++) {
+ return null;
+ }
+
+ public void verify() {
+ StringBuilder builder = new StringBuilder();
+ for (Collection<ContentValues> contentValuesCollection :
+ mMimeTypeToExpectedContentValues.values()) {
+ for (ContentValues expectedContentValues: contentValuesCollection) {
+ builder.append(convertToEasilyReadableString(expectedContentValues));
+ builder.append("\n");
+ }
+ }
+ if (builder.length() > 0) {
+ final String failMsg =
+ "There is(are) remaining expected ContentValues instance(s): \n"
+ + builder.toString();
+ mTestCase.fail(failMsg);
+ }
+ }
+
+ /**
+ * Utility method to print ContentValues whose content is printed with sorted keys.
+ */
+ private String convertToEasilyReadableString(ContentValues contentValues) {
+ if (contentValues == null) {
+ return "null";
+ }
+ String mimeTypeValue = "";
+ SortedMap<String, String> sortedMap = new TreeMap<String, String>();
+ for (Entry<String, Object> entry : contentValues.valueSet()) {
+ final String key = entry.getKey();
+ final Object value = entry.getValue();
+ final String valueString = (value != null ? value.toString() : null);
+ if (Data.MIMETYPE.equals(key)) {
+ mimeTypeValue = valueString;
+ } else {
+ mTestCase.assertNotNull(key);
+ sortedMap.put(key, valueString);
+ }
+ }
+ StringBuilder builder = new StringBuilder();
+ builder.append(Data.MIMETYPE);
+ builder.append('=');
+ builder.append(mimeTypeValue);
+ for (Entry<String, String> entry : sortedMap.entrySet()) {
+ final String key = entry.getKey();
+ final String value = entry.getValue();
+ builder.append(' ');
+ builder.append(key);
+ builder.append("=\"");
+ builder.append(value);
+ builder.append('"');
+ }
+ return builder.toString();
+ }
+
+ private static boolean equalsForContentValues(
+ ContentValues expected, ContentValues actual) {
+ if (expected == actual) {
+ return true;
+ } else if (expected == null || actual == null || expected.size() != actual.size()) {
+ return false;
+ }
+
+ for (Entry<String, Object> entry : expected.valueSet()) {
+ final String key = entry.getKey();
+ final Object value = entry.getValue();
+ if (!actual.containsKey(key)) {
+ return false;
+ }
+ if (value instanceof byte[]) {
+ Object actualValue = actual.get(key);
+ if (!Arrays.equals((byte[])value, (byte[])actualValue)) {
+ return false;
+ }
+ } else if (!value.equals(actual.get(key))) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/LineVerifier.java b/core/tests/coretests/src/android/pim/vcard/LineVerifier.java
new file mode 100644
index 0000000..cef15fd
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/LineVerifier.java
@@ -0,0 +1,65 @@
+/*
+ * 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.Context;
+import android.pim.vcard.VCardComposer;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+
+class LineVerifier implements VCardComposer.OneEntryHandler {
+ private final TestCase mTestCase;
+ private final ArrayList<LineVerifierElem> mLineVerifierElemList;
+ private int mVCardType;
+ private int index;
+
+ public LineVerifier(TestCase testCase, int vcardType) {
+ mTestCase = testCase;
+ mLineVerifierElemList = new ArrayList<LineVerifierElem>();
+ mVCardType = vcardType;
+ }
+
+ public LineVerifierElem addLineVerifierElem() {
+ LineVerifierElem lineVerifier = new LineVerifierElem(mTestCase, mVCardType);
+ mLineVerifierElemList.add(lineVerifier);
+ return lineVerifier;
+ }
+
+ public void verify(String vcard) {
+ if (index >= mLineVerifierElemList.size()) {
+ mTestCase.fail("Insufficient number of LineVerifier (" + index + ")");
+ }
+
+ LineVerifierElem lineVerifier = mLineVerifierElemList.get(index);
+ lineVerifier.verify(vcard);
+
+ index++;
+ }
+
+ public boolean onEntryCreated(String vcard) {
+ verify(vcard);
+ return true;
+ }
+
+ public boolean onInit(Context context) {
+ return true;
+ }
+
+ public void onTerminate() {
+ }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/LineVerifierElem.java b/core/tests/coretests/src/android/pim/vcard/LineVerifierElem.java
new file mode 100644
index 0000000..b23b29b
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/LineVerifierElem.java
@@ -0,0 +1,105 @@
+/*
+ * 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.text.TextUtils;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class LineVerifierElem {
+ private final TestCase mTestCase;
+ private final List<String> mExpectedLineList = new ArrayList<String>();
+ private final boolean mIsV30;
+
+ public LineVerifierElem(TestCase testCase, int vcardType) {
+ mTestCase = testCase;
+ mIsV30 = VCardConfig.isV30(vcardType);
+ }
+
+ public LineVerifierElem addExpected(final String line) {
+ if (!TextUtils.isEmpty(line)) {
+ mExpectedLineList.add(line);
+ }
+ return this;
+ }
+
+ public void verify(final String vcard) {
+ final String[] lineArray = vcard.split("\\r?\\n");
+ final int length = lineArray.length;
+ boolean beginExists = false;
+ boolean endExists = false;
+ boolean versionExists = false;
+
+ for (int i = 0; i < length; i++) {
+ final String line = lineArray[i];
+ if (TextUtils.isEmpty(line)) {
+ continue;
+ }
+
+ if ("BEGIN:VCARD".equalsIgnoreCase(line)) {
+ if (beginExists) {
+ mTestCase.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");
+ } else {
+ endExists = true;
+ continue;
+ }
+ } else if ((mIsV30 ? "VERSION:3.0" : "VERSION:2.1").equalsIgnoreCase(line)) {
+ if (versionExists) {
+ mTestCase.fail("Multiple VERSION line + found");
+ } else {
+ versionExists = true;
+ continue;
+ }
+ }
+
+ if (!beginExists) {
+ mTestCase.fail("Property other than BEGIN came before BEGIN property: "
+ + line);
+ } else if (endExists) {
+ mTestCase.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);
+ }
+ }
+
+ if (!mExpectedLineList.isEmpty()) {
+ StringBuffer buffer = new StringBuffer();
+ for (String expectedLine : mExpectedLineList) {
+ buffer.append(expectedLine);
+ buffer.append("\n");
+ }
+
+ mTestCase.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/PropertyNode.java
new file mode 100644
index 0000000..2c1f6d2
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/PropertyNode.java
@@ -0,0 +1,198 @@
+/*
+ * 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.ContentValues;
+import android.pim.vcard.VCardEntry;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Previously used in main vCard handling code but now exists only for testing.
+ *
+ * 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.
+ */
+public class PropertyNode {
+ public String propName;
+ public String propValue;
+ public List<String> propValue_vector;
+
+ /** Store value as byte[],after decode.
+ * Used when propValue is encoded by something like BASE64, QUOTED-PRINTABLE, etc.
+ */
+ public byte[] propValue_bytes;
+
+ /** 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"]...
+ * TODO: fix this.
+ */
+ public ContentValues paramMap;
+
+ /** Only for TYPE=??? param store. */
+ public Set<String> paramMap_TYPE;
+
+ /** Store group values. Used only in VCard. */
+ public Set<String> propGroupSet;
+
+ public PropertyNode() {
+ propName = "";
+ propValue = "";
+ propValue_vector = new ArrayList<String>();
+ paramMap = new ContentValues();
+ paramMap_TYPE = new HashSet<String>();
+ propGroupSet = new HashSet<String>();
+ }
+
+ public PropertyNode(
+ String propName, String propValue, List<String> propValue_vector,
+ byte[] propValue_bytes, ContentValues paramMap, Set<String> paramMap_TYPE,
+ Set<String> propGroupSet) {
+ if (propName != null) {
+ this.propName = propName;
+ } else {
+ this.propName = "";
+ }
+ if (propValue != null) {
+ this.propValue = propValue;
+ } else {
+ this.propValue = "";
+ }
+ if (propValue_vector != null) {
+ this.propValue_vector = propValue_vector;
+ } else {
+ this.propValue_vector = new ArrayList<String>();
+ }
+ this.propValue_bytes = propValue_bytes;
+ if (paramMap != null) {
+ this.paramMap = paramMap;
+ } else {
+ this.paramMap = new ContentValues();
+ }
+ if (paramMap_TYPE != null) {
+ this.paramMap_TYPE = paramMap_TYPE;
+ } else {
+ this.paramMap_TYPE = new HashSet<String>();
+ }
+ if (propGroupSet != null) {
+ this.propGroupSet = propGroupSet;
+ } else {
+ this.propGroupSet = new HashSet<String>();
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ // vCard may contain more than one same line in one entry, while HashSet or any other
+ // library which utilize hashCode() does not honor that, so intentionally throw an
+ // Exception.
+ throw new UnsupportedOperationException(
+ "PropertyNode does not provide hashCode() implementation intentionally.");
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof PropertyNode)) {
+ return false;
+ }
+
+ PropertyNode node = (PropertyNode)obj;
+
+ if (propName == null || !propName.equals(node.propName)) {
+ return false;
+ } else if (!paramMap_TYPE.equals(node.paramMap_TYPE)) {
+ return false;
+ } else if (!paramMap_TYPE.equals(node.paramMap_TYPE)) {
+ return false;
+ } else if (!propGroupSet.equals(node.propGroupSet)) {
+ return false;
+ }
+
+ if (propValue_bytes != null && Arrays.equals(propValue_bytes, node.propValue_bytes)) {
+ return true;
+ } else {
+ if (!propValue.equals(node.propValue)) {
+ return false;
+ }
+
+ // The value in propValue_vector is not decoded even if it should be
+ // decoded by BASE64 or QUOTED-PRINTABLE. When the size of propValue_vector
+ // is 1, the encoded value is stored in propValue, so we do not have to
+ // check it.
+ return (propValue_vector.equals(node.propValue_vector) ||
+ propValue_vector.size() == 1 ||
+ node.propValue_vector.size() == 1);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("propName: ");
+ builder.append(propName);
+ builder.append(", paramMap: ");
+ builder.append(paramMap.toString());
+ builder.append(", paramMap_TYPE: [");
+ boolean first = true;
+ for (String elem : paramMap_TYPE) {
+ if (first) {
+ first = false;
+ } else {
+ builder.append(", ");
+ }
+ builder.append('"');
+ builder.append(elem);
+ builder.append('"');
+ }
+ builder.append("]");
+ if (!propGroupSet.isEmpty()) {
+ builder.append(", propGroupSet: [");
+ first = true;
+ for (String elem : propGroupSet) {
+ if (first) {
+ first = false;
+ } else {
+ builder.append(", ");
+ }
+ builder.append('"');
+ builder.append(elem);
+ builder.append('"');
+ }
+ builder.append("]");
+ }
+ if (propValue_vector != null && propValue_vector.size() > 1) {
+ builder.append(", propValue_vector size: ");
+ builder.append(propValue_vector.size());
+ }
+ if (propValue_bytes != null) {
+ builder.append(", propValue_bytes size: ");
+ builder.append(propValue_bytes.length);
+ }
+ builder.append(", propValue: \"");
+ builder.append(propValue);
+ builder.append("\"");
+ return builder.toString();
+ }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/PropertyNodesVerifier.java b/core/tests/coretests/src/android/pim/vcard/PropertyNodesVerifier.java
new file mode 100644
index 0000000..cfdd074
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/PropertyNodesVerifier.java
@@ -0,0 +1,386 @@
+/*
+ * 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.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.
+ *
+ * This class first checks whether each propertyNode in the VNode is in the
+ * "ordered expected property 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 static class TypeSet extends HashSet<String> {
+ public TypeSet(String ... array) {
+ super(Arrays.asList(array));
+ }
+ }
+
+ public static class GroupSet extends HashSet<String> {
+ public GroupSet(String ... array) {
+ super(Arrays.asList(array));
+ }
+ }
+
+ private final HashMap<String, List<PropertyNode>> mOrderedNodeMap;
+ // 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) {
+ mOrderedNodeMap = new HashMap<String, List<PropertyNode>>();
+ mUnorderedNodeList = new ArrayList<PropertyNode>();
+ mTestCase = testCase;
+ }
+
+ // WithOrder
+
+ public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue) {
+ return addExpectedNodeWithOrder(propName, propValue, null, null, null, null, null);
+ }
+
+ public PropertyNodesVerifierElem addExpectedNodeWithOrder(
+ String propName, String propValue, ContentValues contentValues) {
+ return addExpectedNodeWithOrder(propName, propValue, null,
+ null, contentValues, null, null);
+ }
+
+ public PropertyNodesVerifierElem addExpectedNodeWithOrder(
+ String propName, List<String> propValueList, ContentValues contentValues) {
+ return addExpectedNodeWithOrder(propName, null, propValueList,
+ null, contentValues, null, null);
+ }
+
+ public PropertyNodesVerifierElem addExpectedNodeWithOrder(
+ String propName, String propValue, List<String> propValueList) {
+ return addExpectedNodeWithOrder(propName, propValue, propValueList, null,
+ null, null, null);
+ }
+
+ public PropertyNodesVerifierElem addExpectedNodeWithOrder(
+ String propName, List<String> propValueList) {
+ final String propValue = concatinateListWithSemiColon(propValueList);
+ return addExpectedNodeWithOrder(propName, propValue.toString(), propValueList,
+ null, null, null, null);
+ }
+
+ public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue,
+ TypeSet paramMap_TYPE) {
+ return addExpectedNodeWithOrder(propName, propValue, null,
+ null, null, paramMap_TYPE, null);
+ }
+
+ public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName,
+ List<String> propValueList, TypeSet paramMap_TYPE) {
+ return addExpectedNodeWithOrder(propName, null, propValueList, null, null,
+ paramMap_TYPE, null);
+ }
+
+ public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue,
+ ContentValues paramMap, TypeSet paramMap_TYPE) {
+ return addExpectedNodeWithOrder(propName, propValue, null, null,
+ paramMap, paramMap_TYPE, null);
+ }
+
+ public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue,
+ List<String> propValueList, TypeSet paramMap_TYPE) {
+ return addExpectedNodeWithOrder(propName, propValue, propValueList, null, null,
+ paramMap_TYPE, 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,
+ propValue, propValueList, propValue_bytes,
+ paramMap, paramMap_TYPE, propGroupSet);
+ List<PropertyNode> expectedNodeList = mOrderedNodeMap.get(propName);
+ if (expectedNodeList == null) {
+ expectedNodeList = new ArrayList<PropertyNode>();
+ mOrderedNodeMap.put(propName, expectedNodeList);
+ }
+ expectedNodeList.add(propertyNode);
+ return this;
+ }
+
+ // WithoutOrder
+
+ public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue) {
+ return addExpectedNode(propName, propValue, null, null, null, null, null);
+ }
+
+ public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue,
+ ContentValues contentValues) {
+ return addExpectedNode(propName, propValue, null, null, contentValues, null, null);
+ }
+
+ public PropertyNodesVerifierElem addExpectedNode(String propName,
+ List<String> propValueList, ContentValues contentValues) {
+ return addExpectedNode(propName, null,
+ propValueList, null, contentValues, null, null);
+ }
+
+ public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue,
+ List<String> propValueList) {
+ return addExpectedNode(propName, propValue, propValueList, null, null, null, null);
+ }
+
+ public PropertyNodesVerifierElem addExpectedNode(String propName,
+ List<String> propValueList) {
+ return addExpectedNode(propName, null, propValueList,
+ null, null, null, null);
+ }
+
+ public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue,
+ TypeSet paramMap_TYPE) {
+ return addExpectedNode(propName, propValue, null, null, null, paramMap_TYPE, null);
+ }
+
+ public PropertyNodesVerifierElem addExpectedNode(String propName,
+ List<String> propValueList, TypeSet paramMap_TYPE) {
+ final String propValue = concatinateListWithSemiColon(propValueList);
+ return addExpectedNode(propName, propValue, propValueList, null, null,
+ paramMap_TYPE, null);
+ }
+
+ public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue,
+ List<String> propValueList, TypeSet paramMap_TYPE) {
+ return addExpectedNode(propName, propValue, propValueList, null, null,
+ paramMap_TYPE, null);
+ }
+
+ public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue,
+ ContentValues paramMap, TypeSet paramMap_TYPE) {
+ return addExpectedNode(propName, propValue, null, null,
+ paramMap, paramMap_TYPE, null);
+ }
+
+ public PropertyNodesVerifierElem addExpectedNode(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);
+ }
+ mUnorderedNodeList.add(new PropertyNode(propName, propValue,
+ propValueList, propValue_bytes, paramMap, paramMap_TYPE, propGroupSet));
+ return this;
+ }
+
+ public void verify(VNode vnode) {
+ for (PropertyNode actualNode : vnode.propList) {
+ verifyNode(actualNode.propName, actualNode);
+ }
+ if (!mOrderedNodeMap.isEmpty() || !mUnorderedNodeList.isEmpty()) {
+ List<String> expectedProps = new ArrayList<String>();
+ for (List<PropertyNode> nodes : mOrderedNodeMap.values()) {
+ for (PropertyNode node : nodes) {
+ if (!expectedProps.contains(node.propName)) {
+ expectedProps.add(node.propName);
+ }
+ }
+ }
+ for (PropertyNode node : mUnorderedNodeList) {
+ if (!expectedProps.contains(node.propName)) {
+ expectedProps.add(node.propName);
+ }
+ }
+ mTestCase.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 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>();
+ if (expectedNode.propName.equals(propName)) {
+ if (expectedNode.equals(actualNode)) {
+ expectedNodeList.remove(i);
+ if (expectedNodeList.size() == 0) {
+ mOrderedNodeMap.remove(propName);
+ }
+ return;
+ } else {
+ expectedButDifferentValueList.add(expectedNode);
+ }
+ }
+
+ // "actualNode" is not in ordered expected list.
+ // Try looking over unordered expected list.
+ if (tryFoundExpectedNodeFromUnorderedList(actualNode,
+ expectedButDifferentValueList)) {
+ return;
+ }
+
+ if (!expectedButDifferentValueList.isEmpty()) {
+ // Same propName exists but with different value(s).
+ failWithExpectedNodeList(propName, actualNode,
+ expectedButDifferentValueList);
+ } else {
+ // There's no expected node with same propName.
+ mTestCase.fail("Unexpected property \"" + propName + "\" exists.");
+ }
+ }
+ } else {
+ List<PropertyNode> expectedButDifferentValueList =
+ new ArrayList<PropertyNode>();
+ if (tryFoundExpectedNodeFromUnorderedList(actualNode, expectedButDifferentValueList)) {
+ return;
+ } else {
+ if (!expectedButDifferentValueList.isEmpty()) {
+ // Same propName exists but with different value(s).
+ failWithExpectedNodeList(propName, actualNode,
+ expectedButDifferentValueList);
+ } else {
+ // There's no expected node with same propName.
+ mTestCase.fail("Unexpected property \"" + propName + "\" exists.");
+ }
+ }
+ }
+ }
+
+ private String concatinateListWithSemiColon(List<String> array) {
+ StringBuffer buffer = new StringBuffer();
+ boolean first = true;
+ for (String propValueElem : array) {
+ if (first) {
+ first = false;
+ } else {
+ buffer.append(';');
+ }
+ buffer.append(propValueElem);
+ }
+
+ return buffer.toString();
+ }
+
+ private boolean tryFoundExpectedNodeFromUnorderedList(PropertyNode actualNode,
+ List<PropertyNode> expectedButDifferentValueList) {
+ final String propName = actualNode.propName;
+ int unorderedListSize = mUnorderedNodeList.size();
+ for (int i = 0; i < unorderedListSize; i++) {
+ PropertyNode unorderedExpectedNode = mUnorderedNodeList.get(i);
+ if (unorderedExpectedNode.propName.equals(propName)) {
+ if (unorderedExpectedNode.equals(actualNode)) {
+ mUnorderedNodeList.remove(i);
+ return true;
+ }
+ expectedButDifferentValueList.add(unorderedExpectedNode);
+ }
+ }
+ return false;
+ }
+
+ private void failWithExpectedNodeList(String propName, PropertyNode actualNode,
+ List<PropertyNode> expectedNodeList) {
+ StringBuilder builder = new StringBuilder();
+ for (PropertyNode expectedNode : expectedNodeList) {
+ builder.append("expected: ");
+ builder.append(expectedNode.toString());
+ builder.append("\n");
+ }
+ mTestCase.fail("Property \"" + propName + "\" has wrong value.\n"
+ + builder.toString()
+ + " actual: " + actualNode.toString());
+ }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java b/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java
new file mode 100644
index 0000000..004a197
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java
@@ -0,0 +1,959 @@
+/*
+ * 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.ContentValues;
+import android.pim.vcard.VCardConfig;
+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;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+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.pim.vcard.PropertyNodesVerifierElem.TypeSet;
+
+import java.util.Arrays;
+
+/**
+ * Tests for the code related to vCard exporter, inculding vCard composer.
+ * This test class depends on vCard importer code, so if tests for vCard importer fail,
+ * the result of this class will not be reliable.
+ */
+public class VCardExporterTests extends VCardTestsBase {
+ private static final byte[] sPhotoByteArray =
+ VCardImporterTests.sPhotoByteArrayForComplicatedCase;
+
+ public void testSimpleV21() {
+ mVerifier.initForExportTest(V21);
+ mVerifier.addInputEntry().addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "Ando")
+ .put(StructuredName.GIVEN_NAME, "Roid");
+ mVerifier.addPropertyNodesVerifierElem()
+ .addExpectedNode("FN", "Roid Ando")
+ .addExpectedNode("N", "Ando;Roid;;;",
+ Arrays.asList("Ando", "Roid", "", "", ""));
+ }
+
+ 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")
+ .put(StructuredName.GIVEN_NAME, "AppropriateGivenName")
+ .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");
+
+ 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");
+ }
+ }
+
+ public void testStructuredNameBasicV21() {
+ testStructuredNameBasic(V21);
+ }
+
+ public void testStructuredNameBasicV30() {
+ testStructuredNameBasic(V30);
+ }
+
+ /**
+ * Test that only "primary" StructuredName is emitted, so that our vCard file
+ * will not confuse the external importer, assuming there may be some importer
+ * which presume that there's only one property toward each of "N", "FN", etc.
+ * 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();
+ 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");
+
+ // With "IS_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.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily")
+ .put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven")
+ .put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle")
+ .put(StructuredName.IS_PRIMARY, 1);
+
+ // With "IS_PRIMARY=1", but we should ignore this time, since this is second, not first.
+ 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.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");
+ }
+ }
+
+ public void testStructuredNameUsePrimaryV21() {
+ testStructuredNameUsePrimaryCommon(V21);
+ }
+
+ public void testStructuredNameUsePrimaryV30() {
+ testStructuredNameUsePrimaryCommon(V30);
+ }
+
+ /**
+ * 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();
+ 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");
+
+ // 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.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily2")
+ .put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven2")
+ .put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle2")
+ .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.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily")
+ .put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven")
+ .put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle")
+ .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.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily3")
+ .put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven3")
+ .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");
+ }
+ }
+
+ public void testStructuredNameUseSuperPrimaryV21() {
+ testStructuredNameUseSuperPrimaryCommon(V21);
+ }
+
+ public void testStructuredNameUseSuperPrimaryV30() {
+ testStructuredNameUseSuperPrimaryCommon(V30);
+ }
+
+ public void testNickNameV30() {
+ mVerifier.initForExportTest(V30);
+ 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)
+ .put(Phone.NUMBER, "1")
+ .put(Phone.TYPE, Phone.TYPE_HOME);
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("TEL", "1", new TypeSet("HOME"));
+ }
+
+ public void testPhoneBasicV21() {
+ testPhoneBasicCommon(V21);
+ }
+
+ public void testPhoneBasicV30() {
+ testPhoneBasicCommon(V30);
+ }
+
+ /**
+ * Tests that vCard composer emits corresponding type param which we expect.
+ */
+ private void testPhoneVariousTypeSupport(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "10")
+ .put(Phone.TYPE, Phone.TYPE_HOME);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "20")
+ .put(Phone.TYPE, Phone.TYPE_WORK);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "30")
+ .put(Phone.TYPE, Phone.TYPE_FAX_HOME);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "40")
+ .put(Phone.TYPE, Phone.TYPE_FAX_WORK);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "50")
+ .put(Phone.TYPE, Phone.TYPE_MOBILE);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "60")
+ .put(Phone.TYPE, Phone.TYPE_PAGER);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "70")
+ .put(Phone.TYPE, Phone.TYPE_OTHER);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "80")
+ .put(Phone.TYPE, Phone.TYPE_CAR);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "90")
+ .put(Phone.TYPE, Phone.TYPE_COMPANY_MAIN);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "100")
+ .put(Phone.TYPE, Phone.TYPE_ISDN);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "110")
+ .put(Phone.TYPE, Phone.TYPE_MAIN);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "120")
+ .put(Phone.TYPE, Phone.TYPE_OTHER_FAX);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "130")
+ .put(Phone.TYPE, Phone.TYPE_TELEX);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "140")
+ .put(Phone.TYPE, Phone.TYPE_WORK_MOBILE);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "150")
+ .put(Phone.TYPE, Phone.TYPE_WORK_PAGER);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "160")
+ .put(Phone.TYPE, Phone.TYPE_MMS);
+
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("TEL", "10", new TypeSet("HOME"))
+ .addExpectedNode("TEL", "20", new TypeSet("WORK"))
+ .addExpectedNode("TEL", "30", new TypeSet("HOME", "FAX"))
+ .addExpectedNode("TEL", "40", new TypeSet("WORK", "FAX"))
+ .addExpectedNode("TEL", "50", new TypeSet("CELL"))
+ .addExpectedNode("TEL", "60", new TypeSet("PAGER"))
+ .addExpectedNode("TEL", "70", new TypeSet("VOICE"))
+ .addExpectedNode("TEL", "80", new TypeSet("CAR"))
+ .addExpectedNode("TEL", "90", new TypeSet("WORK", "PREF"))
+ .addExpectedNode("TEL", "100", new TypeSet("ISDN"))
+ .addExpectedNode("TEL", "110", new TypeSet("PREF"))
+ .addExpectedNode("TEL", "120", new TypeSet("FAX"))
+ .addExpectedNode("TEL", "130", new TypeSet("TLX"))
+ .addExpectedNode("TEL", "140", new TypeSet("WORK", "CELL"))
+ .addExpectedNode("TEL", "150", new TypeSet("WORK", "PAGER"))
+ .addExpectedNode("TEL", "160", new TypeSet("MSG"));
+ }
+
+ public void testPhoneVariousTypeSupportV21() {
+ testPhoneVariousTypeSupport(V21);
+ }
+
+ public void testPhoneVariousTypeSupportV30() {
+ testPhoneVariousTypeSupport(V30);
+ }
+
+ /**
+ * Tests that "PREF"s are emitted appropriately.
+ */
+ private void testPhonePrefHandlingCommon(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "1")
+ .put(Phone.TYPE, Phone.TYPE_HOME);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "2")
+ .put(Phone.TYPE, Phone.TYPE_WORK)
+ .put(Phone.IS_PRIMARY, 1);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "3")
+ .put(Phone.TYPE, Phone.TYPE_FAX_HOME)
+ .put(Phone.IS_PRIMARY, 1);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "4")
+ .put(Phone.TYPE, Phone.TYPE_FAX_WORK);
+
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("TEL", "4", new TypeSet("WORK", "FAX"))
+ .addExpectedNode("TEL", "3", new TypeSet("HOME", "FAX", "PREF"))
+ .addExpectedNode("TEL", "2", new TypeSet("WORK", "PREF"))
+ .addExpectedNode("TEL", "1", new TypeSet("HOME"));
+ }
+
+ public void testPhonePrefHandlingV21() {
+ testPhonePrefHandlingCommon(V21);
+ }
+
+ public void testPhonePrefHandlingV30() {
+ testPhonePrefHandlingCommon(V30);
+ }
+
+ private void testMiscPhoneTypeHandling(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "1")
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "Modem");
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "2")
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "MSG");
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "3")
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "BBS");
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "4")
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "VIDEO");
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "5")
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "6")
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "_AUTO_CELL"); // The old indicator for the type mobile.
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "7")
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "\u643A\u5E2F"); // Mobile phone in Japanese Kanji
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "8")
+ .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"));
+ }
+
+ public void testPhoneTypeHandlingV21() {
+ testMiscPhoneTypeHandling(V21);
+ }
+
+ public void testPhoneTypeHandlingV30() {
+ testMiscPhoneTypeHandling(V30);
+ }
+
+ private void testEmailBasicCommon(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ mVerifier.addInputEntry().addContentValues(Email.CONTENT_ITEM_TYPE)
+ .put(Email.DATA, "sample@example.com");
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("EMAIL", "sample@example.com");
+ }
+
+ public void testEmailBasicV21() {
+ testEmailBasicCommon(V21);
+ }
+
+ public void testEmailBasicV30() {
+ testEmailBasicCommon(V30);
+ }
+
+ private void testEmailVariousTypeSupportCommon(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(Email.CONTENT_ITEM_TYPE)
+ .put(Email.DATA, "type_home@example.com")
+ .put(Email.TYPE, Email.TYPE_HOME);
+ entry.addContentValues(Email.CONTENT_ITEM_TYPE)
+ .put(Email.DATA, "type_work@example.com")
+ .put(Email.TYPE, Email.TYPE_WORK);
+ entry.addContentValues(Email.CONTENT_ITEM_TYPE)
+ .put(Email.DATA, "type_mobile@example.com")
+ .put(Email.TYPE, Email.TYPE_MOBILE);
+ entry.addContentValues(Email.CONTENT_ITEM_TYPE)
+ .put(Email.DATA, "type_other@example.com")
+ .put(Email.TYPE, Email.TYPE_OTHER);
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("EMAIL", "type_home@example.com", new TypeSet("HOME"))
+ .addExpectedNode("EMAIL", "type_work@example.com", new TypeSet("WORK"))
+ .addExpectedNode("EMAIL", "type_mobile@example.com", new TypeSet("CELL"))
+ .addExpectedNode("EMAIL", "type_other@example.com");
+ }
+
+ public void testEmailVariousTypeSupportV21() {
+ testEmailVariousTypeSupportCommon(V21);
+ }
+
+ public void testEmailVariousTypeSupportV30() {
+ testEmailVariousTypeSupportCommon(V30);
+ }
+
+ private void testEmailPrefHandlingCommon(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(Email.CONTENT_ITEM_TYPE)
+ .put(Email.DATA, "type_home@example.com")
+ .put(Email.TYPE, Email.TYPE_HOME)
+ .put(Email.IS_PRIMARY, 1);
+ entry.addContentValues(Email.CONTENT_ITEM_TYPE)
+ .put(Email.DATA, "type_notype@example.com")
+ .put(Email.IS_PRIMARY, 1);
+
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("EMAIL", "type_notype@example.com", new TypeSet("PREF"))
+ .addExpectedNode("EMAIL", "type_home@example.com", new TypeSet("HOME", "PREF"));
+ }
+
+ public void testEmailPrefHandlingV21() {
+ testEmailPrefHandlingCommon(V21);
+ }
+
+ public void testEmailPrefHandlingV30() {
+ testEmailPrefHandlingCommon(V30);
+ }
+
+ private void testPostalAddressCommon(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.POBOX, "Pobox")
+ .put(StructuredPostal.NEIGHBORHOOD, "Neighborhood")
+ .put(StructuredPostal.STREET, "Street")
+ .put(StructuredPostal.CITY, "City")
+ .put(StructuredPostal.REGION, "Region")
+ .put(StructuredPostal.POSTCODE, "100")
+ .put(StructuredPostal.COUNTRY, "Country")
+ .put(StructuredPostal.FORMATTED_ADDRESS, "Formatted Address")
+ .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK);
+ // adr-value = 0*6(text-value ";") text-value
+ // ; PO Box, Extended Address, Street, Locality, Region, Postal Code,
+ // ; Country Name
+ //
+ // The NEIGHBORHOOD field is appended after the CITY field.
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("ADR",
+ Arrays.asList("Pobox", "", "Street", "City Neighborhood",
+ "Region", "100", "Country"), new TypeSet("WORK"));
+ }
+
+ public void testPostalAddressV21() {
+ testPostalAddressCommon(V21);
+ }
+
+ public void testPostalAddressV30() {
+ testPostalAddressCommon(V30);
+ }
+
+ private void testPostalAddressNonNeighborhood(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.CITY, "City");
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("ADR",
+ Arrays.asList("", "", "", "City", "", "", ""), new TypeSet("HOME"));
+ }
+
+ public void testPostalAddressNonNeighborhoodV21() {
+ testPostalAddressNonNeighborhood(V21);
+ }
+
+ public void testPostalAddressNonNeighborhoodV30() {
+ testPostalAddressNonNeighborhood(V30);
+ }
+
+ private void testPostalAddressNonCity(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.NEIGHBORHOOD, "Neighborhood");
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("ADR",
+ Arrays.asList("", "", "", "Neighborhood", "", "", ""), new TypeSet("HOME"));
+ }
+
+ public void testPostalAddressNonCityV21() {
+ testPostalAddressNonCity(V21);
+ }
+
+ public void testPostalAddressNonCityV30() {
+ testPostalAddressNonCity(V30);
+ }
+
+ private void testPostalOnlyWithFormattedAddressCommon(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.REGION, "") // Must be ignored.
+ .put(StructuredPostal.FORMATTED_ADDRESS,
+ "Formatted address CA 123-334 United Statue");
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNodeWithOrder("ADR", ";Formatted address CA 123-334 United Statue;;;;;",
+ Arrays.asList("", "Formatted address CA 123-334 United Statue",
+ "", "", "", "", ""), new TypeSet("HOME"));
+ }
+
+ public void testPostalOnlyWithFormattedAddressV21() {
+ testPostalOnlyWithFormattedAddressCommon(V21);
+ }
+
+ public void testPostalOnlyWithFormattedAddressV30() {
+ testPostalOnlyWithFormattedAddressCommon(V30);
+ }
+
+ /**
+ * Tests that the vCard composer honors formatted data when it is available
+ * even when it is partial.
+ */
+ private void testPostalWithBothStructuredAndFormattedCommon(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.POBOX, "Pobox")
+ .put(StructuredPostal.COUNTRY, "Country")
+ .put(StructuredPostal.FORMATTED_ADDRESS,
+ "Formatted address CA 123-334 United Statue"); // Should be ignored
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("ADR", "Pobox;;;;;;Country",
+ Arrays.asList("Pobox", "", "", "", "", "", "Country"),
+ new TypeSet("HOME"));
+ }
+
+ public void testPostalWithBothStructuredAndFormattedV21() {
+ testPostalWithBothStructuredAndFormattedCommon(V21);
+ }
+
+ public void testPostalWithBothStructuredAndFormattedV30() {
+ testPostalWithBothStructuredAndFormattedCommon(V30);
+ }
+
+ private void testOrganizationCommon(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(Organization.CONTENT_ITEM_TYPE)
+ .put(Organization.COMPANY, "CompanyX")
+ .put(Organization.DEPARTMENT, "DepartmentY")
+ .put(Organization.TITLE, "TitleZ")
+ .put(Organization.JOB_DESCRIPTION, "Description Rambda") // Ignored.
+ .put(Organization.OFFICE_LOCATION, "Mountain View") // Ignored.
+ .put(Organization.PHONETIC_NAME, "PhoneticName!") // Ignored
+ .put(Organization.SYMBOL, "(^o^)/~~"); // Ignore him (her).
+ entry.addContentValues(Organization.CONTENT_ITEM_TYPE)
+ .putNull(Organization.COMPANY)
+ .put(Organization.DEPARTMENT, "DepartmentXX")
+ .putNull(Organization.TITLE);
+ entry.addContentValues(Organization.CONTENT_ITEM_TYPE)
+ .put(Organization.COMPANY, "CompanyXYZ")
+ .putNull(Organization.DEPARTMENT)
+ .put(Organization.TITLE, "TitleXYZYX");
+ // Currently we do not use group but depend on the order.
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNodeWithOrder("ORG", "CompanyX;DepartmentY",
+ Arrays.asList("CompanyX", "DepartmentY"))
+ .addExpectedNodeWithOrder("TITLE", "TitleZ")
+ .addExpectedNodeWithOrder("ORG", "DepartmentXX")
+ .addExpectedNodeWithOrder("ORG", "CompanyXYZ")
+ .addExpectedNodeWithOrder("TITLE", "TitleXYZYX");
+ }
+
+ public void testOrganizationV21() {
+ testOrganizationCommon(V21);
+ }
+
+ public void testOrganizationV30() {
+ testOrganizationCommon(V30);
+ }
+
+ private void testImVariousTypeSupportCommon(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+ .put(Im.PROTOCOL, Im.PROTOCOL_AIM)
+ .put(Im.DATA, "aim");
+ entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+ .put(Im.PROTOCOL, Im.PROTOCOL_MSN)
+ .put(Im.DATA, "msn");
+ entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+ .put(Im.PROTOCOL, Im.PROTOCOL_YAHOO)
+ .put(Im.DATA, "yahoo");
+ entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+ .put(Im.PROTOCOL, Im.PROTOCOL_SKYPE)
+ .put(Im.DATA, "skype");
+ entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+ .put(Im.PROTOCOL, Im.PROTOCOL_QQ)
+ .put(Im.DATA, "qq");
+ entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+ .put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK)
+ .put(Im.DATA, "google talk");
+ entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+ .put(Im.PROTOCOL, Im.PROTOCOL_ICQ)
+ .put(Im.DATA, "icq");
+ entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+ .put(Im.PROTOCOL, Im.PROTOCOL_JABBER)
+ .put(Im.DATA, "jabber");
+ entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+ .put(Im.PROTOCOL, Im.PROTOCOL_NETMEETING)
+ .put(Im.DATA, "netmeeting");
+
+ // No determined way to express unknown type...
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("X-JABBER", "jabber")
+ .addExpectedNode("X-ICQ", "icq")
+ .addExpectedNode("X-GOOGLE-TALK", "google talk")
+ .addExpectedNode("X-QQ", "qq")
+ .addExpectedNode("X-SKYPE-USERNAME", "skype")
+ .addExpectedNode("X-YAHOO", "yahoo")
+ .addExpectedNode("X-MSN", "msn")
+ .addExpectedNode("X-NETMEETING", "netmeeting")
+ .addExpectedNode("X-AIM", "aim");
+ }
+
+ public void testImBasiV21() {
+ testImVariousTypeSupportCommon(V21);
+ }
+
+ public void testImBasicV30() {
+ testImVariousTypeSupportCommon(V30);
+ }
+
+ private void testImPrefHandlingCommon(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+ .put(Im.PROTOCOL, Im.PROTOCOL_AIM)
+ .put(Im.DATA, "aim1");
+ entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+ .put(Im.PROTOCOL, Im.PROTOCOL_AIM)
+ .put(Im.DATA, "aim2")
+ .put(Im.TYPE, Im.TYPE_HOME)
+ .put(Im.IS_PRIMARY, 1);
+
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("X-AIM", "aim1")
+ .addExpectedNode("X-AIM", "aim2", new TypeSet("HOME", "PREF"));
+ }
+
+ public void testImPrefHandlingV21() {
+ testImPrefHandlingCommon(V21);
+ }
+
+ public void testImPrefHandlingV30() {
+ testImPrefHandlingCommon(V30);
+ }
+
+ private void testWebsiteCommon(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(Website.CONTENT_ITEM_TYPE)
+ .put(Website.URL, "http://website.example.android.com/index.html")
+ .put(Website.TYPE, Website.TYPE_BLOG);
+ entry.addContentValues(Website.CONTENT_ITEM_TYPE)
+ .put(Website.URL, "ftp://ftp.example.android.com/index.html")
+ .put(Website.TYPE, Website.TYPE_FTP);
+
+ // We drop TYPE information since vCard (especially 3.0) does not allow us to emit it.
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("URL", "ftp://ftp.example.android.com/index.html")
+ .addExpectedNode("URL", "http://website.example.android.com/index.html");
+ }
+
+ public void testWebsiteV21() {
+ testWebsiteCommon(V21);
+ }
+
+ public void testWebsiteV30() {
+ testWebsiteCommon(V30);
+ }
+
+ private String getAndroidPropValue(final String mimeType, String value, Integer type) {
+ return getAndroidPropValue(mimeType, value, type, null);
+ }
+
+ private String getAndroidPropValue(final String mimeType, String value,
+ Integer type, String label) {
+ return (mimeType + ";" + value + ";"
+ + (type != null ? type : "") + ";"
+ + (label != null ? label : "") + ";;;;;;;;;;;;");
+ }
+
+ private void testEventCommon(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(Event.CONTENT_ITEM_TYPE)
+ .put(Event.TYPE, Event.TYPE_ANNIVERSARY)
+ .put(Event.START_DATE, "1982-06-16");
+ entry.addContentValues(Event.CONTENT_ITEM_TYPE)
+ .put(Event.TYPE, Event.TYPE_BIRTHDAY)
+ .put(Event.START_DATE, "2008-10-22");
+ entry.addContentValues(Event.CONTENT_ITEM_TYPE)
+ .put(Event.TYPE, Event.TYPE_OTHER)
+ .put(Event.START_DATE, "2018-03-12");
+ entry.addContentValues(Event.CONTENT_ITEM_TYPE)
+ .put(Event.TYPE, Event.TYPE_CUSTOM)
+ .put(Event.LABEL, "The last day")
+ .put(Event.START_DATE, "When the Tower of Hanoi with 64 rings is completed.");
+ entry.addContentValues(Event.CONTENT_ITEM_TYPE)
+ .put(Event.TYPE, Event.TYPE_BIRTHDAY)
+ .put(Event.START_DATE, "2009-05-19"); // Should be ignored.
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("BDAY", "2008-10-22")
+ .addExpectedNode("X-ANDROID-CUSTOM",
+ getAndroidPropValue(
+ Event.CONTENT_ITEM_TYPE, "1982-06-16", Event.TYPE_ANNIVERSARY))
+ .addExpectedNode("X-ANDROID-CUSTOM",
+ getAndroidPropValue(
+ Event.CONTENT_ITEM_TYPE, "2018-03-12", Event.TYPE_OTHER))
+ .addExpectedNode("X-ANDROID-CUSTOM",
+ getAndroidPropValue(
+ Event.CONTENT_ITEM_TYPE,
+ "When the Tower of Hanoi with 64 rings is completed.",
+ Event.TYPE_CUSTOM, "The last day"));
+ }
+
+ public void testEventV21() {
+ testEventCommon(V21);
+ }
+
+ public void testEventV30() {
+ testEventCommon(V30);
+ }
+
+ private void testNoteCommon(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(Note.CONTENT_ITEM_TYPE)
+ .put(Note.NOTE, "note1");
+ entry.addContentValues(Note.CONTENT_ITEM_TYPE)
+ .put(Note.NOTE, "note2")
+ .put(Note.IS_PRIMARY, 1); // Just ignored.
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNodeWithOrder("NOTE", "note1")
+ .addExpectedNodeWithOrder("NOTE", "note2");
+ }
+
+ public void testNoteV21() {
+ testNoteCommon(V21);
+ }
+
+ public void testNoteV30() {
+ testNoteCommon(V30);
+ }
+
+ private void testPhotoCommon(int vcardType) {
+ final boolean isV30 = vcardType == V30;
+ mVerifier.initForExportTest(vcardType);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "PhotoTest");
+ entry.addContentValues(Photo.CONTENT_ITEM_TYPE)
+ .put(Photo.PHOTO, sPhotoByteArray);
+
+ ContentValues contentValuesForPhoto = new ContentValues();
+ contentValuesForPhoto.put("ENCODING", (isV30 ? "b" : "BASE64"));
+ mVerifier.addPropertyNodesVerifierElem()
+ .addExpectedNode("FN", "PhotoTest")
+ .addExpectedNode("N", "PhotoTest;;;;",
+ Arrays.asList("PhotoTest", "", "", "", ""))
+ .addExpectedNodeWithOrder("PHOTO", null, null, sPhotoByteArray,
+ contentValuesForPhoto, new TypeSet("JPEG"), null);
+ }
+
+ public void testPhotoV21() {
+ testPhotoCommon(V21);
+ }
+
+ public void testPhotoV30() {
+ testPhotoCommon(V30);
+ }
+
+ private void testRelationCommon(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ mVerifier.addInputEntry().addContentValues(Relation.CONTENT_ITEM_TYPE)
+ .put(Relation.TYPE, Relation.TYPE_MOTHER)
+ .put(Relation.NAME, "Ms. Mother");
+ mVerifier.addContentValuesVerifierElem().addExpected(Relation.CONTENT_ITEM_TYPE)
+ .put(Relation.TYPE, Relation.TYPE_MOTHER)
+ .put(Relation.NAME, "Ms. Mother");
+ }
+
+ public void testRelationV21() {
+ testRelationCommon(V21);
+ }
+
+ public void testRelationV30() {
+ testRelationCommon(V30);
+ }
+
+ public void testV30HandleEscape() {
+ mVerifier.initForExportTest(V30);
+ mVerifier.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}>]");
+ // Verifies the vCard String correctly escapes each character which must be escaped.
+ mVerifier.addLineVerifierElem()
+ .addExpected("N:\\\\;\\;;\\,;\\n;")
+ .addExpected("FN:[<{Unescaped:Asciis}>]");
+ mVerifier.addPropertyNodesVerifierElem()
+ .addExpectedNode("FN", "[<{Unescaped:Asciis}>]")
+ .addExpectedNode("N", Arrays.asList("\\", ";", ",", "\n", ""));
+ }
+
+ /**
+ * There's no "NICKNAME" property in vCard 2.1, while there is in vCard 3.0.
+ * We use Android-specific "X-ANDROID-CUSTOM" property.
+ * This test verifies the functionality.
+ */
+ public void testNickNameV21() {
+ mVerifier.initForExportTest(V21);
+ mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE)
+ .put(Nickname.NAME, "Nicky");
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("X-ANDROID-CUSTOM",
+ Nickname.CONTENT_ITEM_TYPE + ";Nicky;;;;;;;;;;;;;;");
+ mVerifier.addContentValuesVerifierElem().addExpected(Nickname.CONTENT_ITEM_TYPE)
+ .put(Nickname.NAME, "Nicky");
+ }
+
+ public void testTolerateBrokenPhoneNumberEntryV21() {
+ mVerifier.initForExportTest(V21);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_HOME)
+ .put(Phone.NUMBER, "111-222-3333 (Miami)\n444-5555-666 (Tokyo);"
+ + "777-888-9999 (Chicago);111-222-3333 (Miami)");
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("TEL", "111-222-3333", new TypeSet("HOME"))
+ .addExpectedNode("TEL", "444-555-5666", new TypeSet("HOME"))
+ .addExpectedNode("TEL", "777-888-9999", new TypeSet("HOME"));
+ }
+
+ private void testPickUpNonEmptyContentValuesCommon(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ ContactEntry entry = mVerifier.addInputEntry();
+ 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.
+ entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.IS_PRIMARY, 1)
+ .put(StructuredName.FAMILY_NAME, "family2"); // This entry is what we want.
+ entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.IS_PRIMARY, 1)
+ .put(StructuredName.FAMILY_NAME, "family3");
+ entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "family4");
+ mVerifier.addPropertyNodesVerifierElem()
+ .addExpectedNode("N", Arrays.asList("family2", "", "", "", ""))
+ .addExpectedNode("FN", "family2");
+ }
+
+ public void testPickUpNonEmptyContentValuesV21() {
+ testPickUpNonEmptyContentValuesCommon(V21);
+ }
+
+ public void testPickUpNonEmptyContentValuesV30() {
+ testPickUpNonEmptyContentValuesCommon(V30);
+ }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java b/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java
new file mode 100644
index 0000000..21f2254
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java
@@ -0,0 +1,1011 @@
+/*
+ * 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.ContentValues;
+import android.pim.vcard.VCardConfig;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+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 java.util.Arrays;
+
+public class VCardImporterTests extends VCardTestsBase {
+ // Push data into int array at first since values like 0x80 are
+ // interpreted as int by the compiler and casting all of them is
+ // cumbersome...
+ private static final int[] sPhotoIntArrayForComplicatedCase = {
+ 0xff, 0xd8, 0xff, 0xe1, 0x0a, 0x0f, 0x45, 0x78, 0x69, 0x66, 0x00,
+ 0x00, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d,
+ 0x01, 0x0e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0xaa, 0x01, 0x0f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+ 0x00, 0xba, 0x01, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00,
+ 0x00, 0x00, 0xc2, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0xd0, 0x01, 0x28, 0x00, 0x03, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x31, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xd8, 0x01, 0x32, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xe6, 0x02, 0x13,
+ 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x82,
+ 0x98, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xfa,
+ 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
+ 0x84, 0xc4, 0xa5, 0x00, 0x07, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00,
+ 0x01, 0x08, 0x00, 0x00, 0x04, 0x1e, 0x32, 0x30, 0x30, 0x38, 0x31,
+ 0x30, 0x32, 0x39, 0x31, 0x33, 0x35, 0x35, 0x33, 0x31, 0x00, 0x00,
+ 0x44, 0x6f, 0x43, 0x6f, 0x4d, 0x6f, 0x00, 0x00, 0x44, 0x39, 0x30,
+ 0x35, 0x69, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x44, 0x39, 0x30,
+ 0x35, 0x69, 0x20, 0x56, 0x65, 0x72, 0x31, 0x2e, 0x30, 0x30, 0x00,
+ 0x32, 0x30, 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20,
+ 0x31, 0x33, 0x3a, 0x35, 0x35, 0x3a, 0x34, 0x37, 0x00, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x00, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x49, 0x4d, 0x00, 0x30, 0x33,
+ 0x30, 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x14, 0x00,
+ 0x14, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+ 0x00, 0x34, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x01, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x09, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x0f, 0x0b, 0x00,
+ 0x00, 0x27, 0x10, 0x00, 0x00, 0x05, 0x97, 0x00, 0x00, 0x27, 0x10,
+ 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x1c,
+ 0x01, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x02, 0x5e, 0x00, 0x00,
+ 0x27, 0x10, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x27, 0x10, 0x00,
+ 0x00, 0x03, 0xcb, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x1b, 0xe5,
+ 0x00, 0x00, 0x27, 0x10, 0x00, 0x28, 0x82, 0x9a, 0x00, 0x05, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x6a, 0x82, 0x9d, 0x00, 0x05,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x72, 0x88, 0x22, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x90, 0x00,
+ 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30, 0x32, 0x32, 0x30, 0x90,
+ 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x03, 0x7a,
+ 0x90, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x03,
+ 0x8e, 0x91, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x01, 0x02,
+ 0x03, 0x00, 0x91, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x03, 0xa2, 0x92, 0x01, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x03, 0xaa, 0x92, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x03, 0xb2, 0x92, 0x04, 0x00, 0x0a, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x03, 0xba, 0x92, 0x05, 0x00, 0x05, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xc2, 0x92, 0x07, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x92, 0x08, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x92, 0x09,
+ 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x92,
+ 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xca,
+ 0x92, 0x7c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x92, 0x86, 0x00, 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00,
+ 0x03, 0xd2, 0xa0, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30,
+ 0x31, 0x30, 0x30, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x60, 0x00, 0x00, 0xa0, 0x03, 0x00, 0x03, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x48, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0xa2, 0x0e, 0x00, 0x05,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xe8, 0xa2, 0x0f, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xf0, 0xa2, 0x10,
+ 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0xa2,
+ 0x17, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00,
+ 0xa3, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00,
+ 0x00, 0xa3, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0xa4, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0xa4, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0xa4, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x04, 0x00, 0x05, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x03, 0xf8, 0xa4, 0x05, 0x00, 0x03, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x00, 0xa4, 0x06, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x07, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x08,
+ 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4,
+ 0x09, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0xa4, 0x0a, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0xa4, 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00,
+ 0x00, 0x27, 0x10, 0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x00, 0x64,
+ 0x32, 0x30, 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20,
+ 0x31, 0x33, 0x3a, 0x35, 0x35, 0x3a, 0x33, 0x31, 0x00, 0x32, 0x30,
+ 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20, 0x31, 0x33,
+ 0x3a, 0x35, 0x35, 0x3a, 0x34, 0x37, 0x00, 0x00, 0x00, 0x29, 0x88,
+ 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x02, 0xb2, 0x00, 0x00, 0x00,
+ 0x64, 0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x25, 0x00,
+ 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0e, 0x92, 0x00, 0x00, 0x03, 0xe8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x30, 0x30,
+ 0x38, 0x31, 0x30, 0x32, 0x39, 0x31, 0x33, 0x35, 0x35, 0x33, 0x31,
+ 0x00, 0x00, 0x20, 0x2a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x2a,
+ 0xe2, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x04, 0x52, 0x39, 0x38, 0x00, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00,
+ 0x00, 0x04, 0x30, 0x31, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06,
+ 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x04, 0x6c, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x04, 0x74, 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x02, 0x00, 0x00, 0x02, 0x01, 0x00, 0x04, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x04, 0x7c, 0x02, 0x02, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x8b, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x01, 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84,
+ 0x00, 0x20, 0x16, 0x18, 0x1c, 0x18, 0x14, 0x20, 0x1c, 0x1a, 0x1c,
+ 0x24, 0x22, 0x20, 0x26, 0x30, 0x50, 0x34, 0x30, 0x2c, 0x2c, 0x30,
+ 0x62, 0x46, 0x4a, 0x3a, 0x50, 0x74, 0x66, 0x7a, 0x78, 0x72, 0x66,
+ 0x70, 0x6e, 0x80, 0x90, 0xb8, 0x9c, 0x80, 0x88, 0xae, 0x8a, 0x6e,
+ 0x70, 0xa0, 0xda, 0xa2, 0xae, 0xbe, 0xc4, 0xce, 0xd0, 0xce, 0x7c,
+ 0x9a, 0xe2, 0xf2, 0xe0, 0xc8, 0xf0, 0xb8, 0xca, 0xce, 0xc6, 0x01,
+ 0x22, 0x24, 0x24, 0x30, 0x2a, 0x30, 0x5e, 0x34, 0x34, 0x5e, 0xc6,
+ 0x84, 0x70, 0x84, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xff, 0xc0,
+ 0x00, 0x11, 0x08, 0x00, 0x78, 0x00, 0xa0, 0x03, 0x01, 0x21, 0x00,
+ 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x01, 0xa2, 0x00,
+ 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03,
+ 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01,
+ 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31,
+ 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81,
+ 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
+ 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19,
+ 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
+ 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92,
+ 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4,
+ 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+ 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
+ 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
+ 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1,
+ 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01, 0x00,
+ 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04,
+ 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77,
+ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12,
+ 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14,
+ 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15,
+ 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17,
+ 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
+ 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
+ 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3,
+ 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5,
+ 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
+ 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2,
+ 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00,
+ 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00,
+ 0x14, 0x54, 0xaa, 0x2a, 0x46, 0x48, 0xa2, 0xa4, 0x55, 0xa6, 0x04,
+ 0x8a, 0x29, 0xe0, 0x53, 0x10, 0xe0, 0x29, 0xc0, 0x50, 0x03, 0xb1,
+ 0x46, 0x29, 0x80, 0x84, 0x52, 0x11, 0x40, 0x0d, 0x22, 0x9a, 0x45,
+ 0x20, 0x23, 0x61, 0x51, 0x30, 0xa0, 0x08, 0xc8, 0xa8, 0xd8, 0x52,
+ 0x02, 0x26, 0x15, 0x0b, 0x0a, 0x00, 0xb4, 0xa2, 0xa5, 0x5a, 0x00,
+ 0x91, 0x45, 0x4a, 0xa2, 0x81, 0x92, 0x01, 0x4e, 0x02, 0x98, 0x87,
+ 0x0a, 0x70, 0xa0, 0x07, 0x62, 0x8c, 0x50, 0x21, 0x0d, 0x25, 0x00,
+ 0x34, 0x8a, 0x61, 0x14, 0x0c, 0x63, 0x0a, 0x89, 0x85, 0x00, 0x46,
+ 0xd5, 0x1b, 0x52, 0x02, 0x16, 0xa8, 0x98, 0x50, 0x05, 0x94, 0xa9,
+ 0x16, 0x80, 0x25, 0x5a, 0x95, 0x68, 0x18, 0xf1, 0x4f, 0x14, 0xc4,
+ 0x3b, 0xb5, 0x22, 0xb6, 0x38, 0x34, 0x00, 0xe3, 0x22, 0x8e, 0xf4,
+ 0x79, 0x8a, 0x7b, 0xd1, 0x71, 0x03, 0x30, 0xc7, 0x14, 0x83, 0xa5,
+ 0x00, 0x06, 0x98, 0x68, 0x01, 0x8d, 0x51, 0x35, 0x03, 0x22, 0x6a,
+ 0x8d, 0xa9, 0x01, 0x13, 0x54, 0x4d, 0x40, 0x13, 0xa5, 0x4a, 0x28,
+ 0x02, 0x45, 0x35, 0x2a, 0x9a, 0x00, 0x78, 0x34, 0xf0, 0x69, 0x80,
+ 0x34, 0x81, 0x45, 0x40, 0xce, 0x58, 0xe6, 0xa2, 0x4c, 0x06, 0xe4,
+ 0xfa, 0xd1, 0x93, 0x50, 0x21, 0xca, 0xe4, 0x55, 0x84, 0x90, 0x30,
+ 0xab, 0x8b, 0x18, 0xa6, 0x9a, 0x6a, 0xc4, 0x31, 0xaa, 0x26, 0xa0,
+ 0x64, 0x4d, 0x51, 0xb5, 0x20, 0x23, 0x6a, 0x89, 0xa8, 0x02, 0x44,
+ 0x35, 0x2a, 0x9a, 0x00, 0x95, 0x4d, 0x48, 0xa6, 0x80, 0x24, 0x53,
+ 0x4e, 0xce, 0x05, 0x30, 0x2b, 0x3b, 0xee, 0x6a, 0x91, 0x5d, 0x76,
+ 0x63, 0xbd, 0x65, 0x7d, 0x40, 0x66, 0x68, 0xa9, 0x02, 0x45, 0x2b,
+ 0xb3, 0x9e, 0xb4, 0xc5, 0x6d, 0xad, 0x9a, 0xa0, 0x2c, 0x06, 0xc8,
+ 0xcd, 0x04, 0xd6, 0xa2, 0x23, 0x63, 0x51, 0xb1, 0xa0, 0x64, 0x4d,
+ 0x51, 0x93, 0x48, 0x08, 0xda, 0xa2, 0x6a, 0x00, 0x72, 0x1a, 0x99,
+ 0x4d, 0x00, 0x48, 0xa6, 0xa4, 0x53, 0x4c, 0x07, 0x86, 0x03, 0xbd,
+ 0x2b, 0x9c, 0xa7, 0x14, 0x98, 0x10, 0x85, 0x34, 0xe0, 0xa6, 0xb3,
+ 0xb0, 0x0b, 0xb5, 0xa8, 0x0a, 0xd4, 0x58, 0x42, 0xed, 0x3e, 0x94,
+ 0xd2, 0xa6, 0x8b, 0x01, 0x34, 0x44, 0xed, 0xe6, 0x9c, 0x4d, 0x6a,
+ 0x80, 0x8d, 0x8d, 0x46, 0xc6, 0x80, 0x23, 0x63, 0x51, 0x9a, 0x06,
+ 0x46, 0xd5, 0x13, 0x52, 0x01, 0x54, 0xd4, 0xaa, 0x68, 0x02, 0x40,
+ 0x6a, 0x40, 0x78, 0xa0, 0x08, 0x59, 0xce, 0xee, 0xb5, 0x2a, 0x39,
+ 0xd9, 0x59, 0xa7, 0xa8, 0x00, 0x73, 0xeb, 0x4e, 0x0e, 0x7d, 0x69,
+ 0x5c, 0x05, 0xf3, 0x0f, 0xad, 0x1e, 0x61, 0xf5, 0xa7, 0x71, 0x0b,
+ 0xe6, 0x35, 0x21, 0x90, 0xd3, 0xb8, 0x0e, 0x32, 0x10, 0x95, 0x10,
+ 0x91, 0xb3, 0xd6, 0x9b, 0x60, 0x4b, 0x9c, 0x8a, 0x63, 0x1a, 0xb0,
+ 0x18, 0x4d, 0x46, 0xc6, 0x80, 0x22, 0x6a, 0x61, 0xa4, 0x31, 0xaa,
+ 0x6a, 0x55, 0x34, 0x01, 0x2a, 0x9a, 0x7e, 0x78, 0xa0, 0x08, 0x09,
+ 0xf9, 0xaa, 0x58, 0xcf, 0xca, 0x6b, 0x3e, 0xa0, 0x00, 0xd3, 0x81,
+ 0xa9, 0x01, 0x73, 0x46, 0x69, 0x80, 0xb9, 0xa4, 0xcd, 0x00, 0x2b,
+ 0x1f, 0x92, 0xa3, 0x07, 0x9a, 0x6f, 0x70, 0x26, 0xcf, 0x14, 0xd2,
+ 0x6b, 0x51, 0x0c, 0x63, 0x51, 0xb1, 0xa0, 0x08, 0xda, 0x98, 0x69,
+ 0x0c, 0x8d, 0x4d, 0x4a, 0xa6, 0x80, 0x24, 0x53, 0x52, 0x03, 0xc5,
+ 0x02, 0x21, 0x27, 0xe6, 0xa9, 0x23, 0x3f, 0x29, 0xac, 0xfa, 0x8c,
+ 0x01, 0xe6, 0x9c, 0x0d, 0x48, 0x0a, 0x0d, 0x2e, 0x68, 0x01, 0x73,
+ 0x49, 0x9a, 0x60, 0x2b, 0x1f, 0x92, 0x98, 0x3a, 0xd3, 0x7b, 0x81,
+ 0x36, 0x78, 0xa6, 0x93, 0x5a, 0x88, 0x8c, 0x9a, 0x63, 0x1a, 0x00,
+ 0x8c, 0xd3, 0x0d, 0x21, 0x91, 0x29, 0xa9, 0x14, 0xd0, 0x04, 0x8a,
+ 0x69, 0xe0, 0xd3, 0x11, 0x1b, 0x1e, 0x6a, 0x48, 0xcf, 0xca, 0x6b,
+ 0x3e, 0xa3, 0x10, 0x1a, 0x70, 0x35, 0x20, 0x38, 0x1a, 0x5c, 0xd2,
+ 0x01, 0x73, 0x49, 0x9a, 0x60, 0x39, 0x8f, 0xca, 0x29, 0x8b, 0xf7,
+ 0xaa, 0xba, 0x88, 0x96, 0x9a, 0x6b, 0x40, 0x18, 0xc6, 0xa3, 0x26,
+ 0x80, 0x18, 0x69, 0xa6, 0x90, 0xc8, 0x14, 0xd4, 0x8a, 0x69, 0x80,
+ 0xf0, 0x6a, 0x40, 0x68, 0x10, 0xbb, 0x41, 0xa7, 0xe3, 0x0b, 0xc5,
+ 0x2b, 0x01, 0x10, 0xa7, 0x03, 0x59, 0x0c, 0x76, 0x69, 0x73, 0x40,
+ 0x0b, 0x9a, 0x28, 0x11, 0x28, 0x19, 0x5e, 0x69, 0x02, 0x81, 0x5a,
+ 0xd8, 0x00, 0xd3, 0x4d, 0x50, 0x0c, 0x6a, 0x8c, 0xd2, 0x01, 0xa6,
+ 0x98, 0x69, 0x0c, 0xae, 0xa6, 0xa4, 0x06, 0x80, 0x1e, 0xa6, 0x9e,
+ 0x0d, 0x31, 0x12, 0x03, 0x4f, 0x06, 0x80, 0x13, 0x60, 0x34, 0xd3,
+ 0xc1, 0xa8, 0x92, 0x01, 0xf1, 0x8d, 0xdd, 0x69, 0xcc, 0xa1, 0x69,
+ 0x5b, 0x4b, 0x80, 0x83, 0x93, 0x52, 0x04, 0x14, 0xe2, 0xae, 0x03,
+ 0xa9, 0x0d, 0x68, 0x03, 0x4d, 0x34, 0xd0, 0x03, 0x0d, 0x30, 0xd2,
+ 0x01, 0x86, 0x9a, 0x68, 0x19, 0x58, 0x1a, 0x78, 0xa4, 0x04, 0x8a,
+ 0x69, 0xe0, 0xd3, 0x10, 0xe0, 0x69, 0xe0, 0xd0, 0x03, 0xc1, 0xa8,
+ 0xdb, 0xad, 0x4c, 0x81, 0x12, 0x45, 0xd6, 0x9d, 0x25, 0x1d, 0x00,
+ 0x6a, 0xf5, 0xa9, 0xe8, 0x80, 0x31, 0x29, 0x0d, 0x58, 0x08, 0x69,
+ 0x86, 0x80, 0x1a, 0x69, 0x86, 0x90, 0x0c, 0x34, 0xd3, 0x48, 0x65,
+ 0x51, 0x4f, 0x06, 0x98, 0x0f, 0x14, 0xf0, 0x68, 0x10, 0xf0, 0x69,
+ 0xe0, 0xd0, 0x03, 0x81, 0xa5, 0x2b, 0x9a, 0x1a, 0xb8, 0x87, 0xa8,
+ 0xdb, 0x4a, 0x46, 0x68, 0xb6, 0x80, 0x2a, 0xa8, 0x14, 0xea, 0x12,
+ 0xb0, 0x05, 0x21, 0xa6, 0x02, 0x1a, 0x61, 0xa0, 0x06, 0x9a, 0x61,
+ 0xa4, 0x31, 0x86, 0x9a, 0x69, 0x0c, 0xa8, 0x0d, 0x3c, 0x53, 0x01,
+ 0xe2, 0x9e, 0x28, 0x10, 0xf1, 0x4e, 0x06, 0x98, 0x0f, 0x06, 0x9e,
+ 0x0d, 0x02, 0x1c, 0x29, 0xc2, 0x80, 0x16, 0x96, 0x80, 0x0a, 0x4a,
+ 0x00, 0x43, 0x4d, 0x34, 0x0c, 0x61, 0xa6, 0x1a, 0x40, 0x34, 0xd3,
+ 0x4d, 0x21, 0x80, 0xff, 0xd9, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0a,
+ 0x07, 0x07, 0x08, 0x07, 0x06, 0x0a, 0x08, 0x08, 0x08, 0x0b, 0x0a,
+ 0x0a, 0x0b, 0x0e, 0x18, 0x10, 0x0e, 0x0d, 0x0d, 0x0e, 0x1d, 0x15,
+ 0x16, 0x11, 0x18, 0x23, 0x1f, 0x25, 0x24, 0x22, 0x1f, 0x22, 0x21,
+ 0x26, 0x2b, 0x37, 0x2f, 0x26, 0x29, 0x34, 0x29, 0x21, 0x22, 0x30,
+ 0x41, 0x31, 0x34, 0x39, 0x3b, 0x3e, 0x3e, 0x3e, 0x25, 0x2e, 0x44,
+ 0x49, 0x43, 0x3c, 0x48, 0x37, 0x3d, 0x3e, 0x3b, 0x01, 0x0a, 0x0b,
+ 0x0b, 0x0e, 0x0d, 0x0e, 0x1c, 0x10, 0x10, 0x1c, 0x3b, 0x28, 0x22,
+ 0x28, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xc0, 0x00, 0x11,
+ 0x08, 0x00, 0x48, 0x00, 0x60, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11,
+ 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01,
+ 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02,
+ 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01,
+ 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
+ 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1,
+ 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33,
+ 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54,
+ 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
+ 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94,
+ 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,
+ 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8,
+ 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
+ 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
+ 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3,
+ 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01, 0x00, 0x03, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03,
+ 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01,
+ 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51,
+ 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+ 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72,
+ 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19,
+ 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54,
+ 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
+ 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93,
+ 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
+ 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
+ 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2,
+ 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4,
+ 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03,
+ 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0x9e, 0xd2,
+ 0x2e, 0x07, 0x15, 0xaf, 0x6d, 0x08, 0xe2, 0xb3, 0x45, 0x1a, 0xf6,
+ 0xd0, 0x00, 0x01, 0xc5, 0x68, 0x45, 0x17, 0x4a, 0xb4, 0x22, 0xe4,
+ 0x70, 0x8c, 0x74, 0xa9, 0x3c, 0xa1, 0x8e, 0x95, 0x48, 0x96, 0x31,
+ 0xe2, 0x18, 0xe9, 0x55, 0xa5, 0x8c, 0x7a, 0x50, 0x05, 0x0b, 0x88,
+ 0x86, 0x0f, 0x15, 0x8f, 0x75, 0x1f, 0x26, 0x93, 0x19, 0x91, 0x77,
+ 0x18, 0xc1, 0xac, 0x4b, 0xc8, 0xfa, 0xd6, 0x63, 0x37, 0x6d, 0x31,
+ 0xb4, 0x73, 0x5b, 0x36, 0xa0, 0x1c, 0x50, 0x80, 0xd7, 0x83, 0xa0,
+ 0xab, 0xd1, 0x62, 0xad, 0x09, 0x8f, 0x17, 0x29, 0x03, 0xb2, 0xcc,
+ 0xe0, 0x77, 0x14, 0xa3, 0x56, 0xb3, 0x27, 0x1e, 0x67, 0xe9, 0x52,
+ 0xea, 0xc6, 0x3a, 0x36, 0x48, 0xef, 0x3d, 0x27, 0x70, 0x22, 0x60,
+ 0x47, 0x52, 0x69, 0xb2, 0xe2, 0xad, 0x3b, 0xea, 0x80, 0xa3, 0x38,
+ 0xe0, 0xd6, 0x3d, 0xd8, 0x1c, 0xd0, 0xca, 0x46, 0x3d, 0xd0, 0x18,
+ 0x35, 0x89, 0x78, 0xa3, 0x9a, 0xcd, 0x8c, 0xd2, 0xb3, 0x93, 0x2a,
+ 0x2b, 0x66, 0xd5, 0xf1, 0x8a, 0x10, 0x1a, 0xd6, 0xf2, 0x03, 0x8a,
+ 0x9e, 0xe6, 0xf4, 0x5a, 0xdb, 0xef, 0xfe, 0x23, 0xc0, 0xa7, 0x27,
+ 0xcb, 0x16, 0xc4, 0xcc, 0xdd, 0xe2, 0x78, 0x9a, 0x69, 0x66, 0xcc,
+ 0x99, 0xe1, 0x4d, 0x47, 0xba, 0xbc, 0xd9, 0x6a, 0xee, 0x26, 0x59,
+ 0x59, 0x4d, 0xac, 0x69, 0x34, 0x52, 0xe5, 0x8f, 0x55, 0xad, 0x58,
+ 0xae, 0x85, 0xc4, 0x22, 0x41, 0xdf, 0xad, 0x76, 0x61, 0xe5, 0x6f,
+ 0x74, 0x45, 0x69, 0xdc, 0x00, 0x79, 0xac, 0x8b, 0xa6, 0xc9, 0x35,
+ 0xd4, 0x34, 0x64, 0xdc, 0x37, 0x06, 0xb1, 0xae, 0x88, 0xc1, 0xac,
+ 0xd8, 0xc9, 0x2c, 0xa6, 0xe0, 0x73, 0x5b, 0x36, 0xf3, 0x74, 0xe6,
+ 0x84, 0x05, 0xe3, 0xa9, 0x47, 0x6a, 0x14, 0xb6, 0x49, 0x3d, 0x85,
+ 0x3a, 0xee, 0xee, 0x2b, 0xa8, 0xe2, 0x6f, 0x30, 0x81, 0xe9, 0x8a,
+ 0xca, 0xa4, 0xe2, 0xd3, 0x8b, 0x01, 0xb1, 0xf9, 0x04, 0x7f, 0xaf,
+ 0x23, 0xf0, 0xa9, 0x54, 0x41, 0x9c, 0xfd, 0xa3, 0xf4, 0xae, 0x65,
+ 0x18, 0xf7, 0x25, 0x8a, 0xe2, 0x02, 0x38, 0xb8, 0xfd, 0x2a, 0x7b,
+ 0x5b, 0xa8, 0x6d, 0x6d, 0x5d, 0x9a, 0x5d, 0xcb, 0xbb, 0xd2, 0xb6,
+ 0xa6, 0xa3, 0x19, 0x5e, 0xe2, 0x03, 0x7b, 0x1d, 0xc2, 0x17, 0x8d,
+ 0xb8, 0xac, 0xfb, 0x89, 0x39, 0x35, 0xd6, 0x9a, 0x6a, 0xe8, 0x66,
+ 0x55, 0xcb, 0xf5, 0xac, 0x7b, 0x96, 0xeb, 0x50, 0xc6, 0x88, 0x6d,
+ 0x66, 0xe9, 0xcd, 0x6c, 0xdb, 0x4f, 0xd3, 0x9a, 0x00, 0x2f, 0xe6,
+ 0xf9, 0xa3, 0xe7, 0xb5, 0x4a, 0x93, 0x7f, 0xa2, 0xc6, 0x73, 0xdc,
+ 0xd7, 0x15, 0x55, 0xef, 0x48, 0x7d, 0x09, 0x52, 0x6e, 0x3a, 0xd4,
+ 0xab, 0x2f, 0xbd, 0x61, 0x16, 0x0c, 0x73, 0x49, 0xc5, 0x24, 0x92,
+ 0x7f, 0xa2, 0x63, 0xfd, 0xaa, 0xd6, 0x2f, 0x71, 0x0e, 0xb1, 0x93,
+ 0xf7, 0x2d, 0xf5, 0xa4, 0x9e, 0x4e, 0xb5, 0xdd, 0x4b, 0xf8, 0x68,
+ 0x4c, 0xcb, 0xb9, 0x93, 0xad, 0x65, 0xce, 0xd9, 0x26, 0xa9, 0x8d,
+ 0x19, 0xf6, 0xf2, 0xf4, 0xe6, 0xb5, 0xad, 0xe7, 0xc6, 0x39, 0xa0,
+ 0x18, 0xeb, 0xc9, 0x77, 0x6c, 0x35, 0x2a, 0x4b, 0xfe, 0x8a, 0x9c,
+ 0xff, 0x00, 0x11, 0xae, 0x3a, 0x8b, 0xde, 0x61, 0xd0, 0x9e, 0x39,
+ 0xb8, 0xeb, 0x53, 0xac, 0xb9, 0xae, 0x5b, 0x00, 0xf3, 0x27, 0x14,
+ 0x92, 0xc9, 0xfe, 0x8a, 0x3f, 0xde, 0x35, 0xac, 0x3a, 0x88, 0x92,
+ 0xcd, 0xb1, 0x6e, 0x7d, 0xcd, 0x32, 0x67, 0xeb, 0xcd, 0x7a, 0x14,
+ 0xfe, 0x04, 0x26, 0x66, 0xce, 0xf9, 0x26, 0xb3, 0xe6, 0x6e, 0xb4,
+ 0xd9, 0x48, 0xc8, 0x82, 0x4e, 0x07, 0x35, 0xa7, 0x6f, 0x2f, 0x02,
+ 0x9a, 0x06, 0x5f, 0x8c, 0xa4, 0x83, 0x0e, 0x32, 0x2a, 0x69, 0xe3,
+ 0xdd, 0x12, 0x08, 0x97, 0x85, 0xec, 0x2a, 0x2a, 0x42, 0xf1, 0x76,
+ 0x26, 0xe4, 0x6a, 0x59, 0x0e, 0x18, 0x10, 0x6a, 0xd2, 0x89, 0x02,
+ 0x6e, 0x2a, 0x71, 0xeb, 0x5c, 0x1c, 0x8c, 0xa6, 0x48, 0xbb, 0xdc,
+ 0x61, 0x41, 0x35, 0x72, 0x28, 0x87, 0xd9, 0xf6, 0x4a, 0xb9, 0xe7,
+ 0x38, 0xae, 0x8c, 0x3d, 0x36, 0xdd, 0xde, 0xc4, 0xb0, 0x21, 0x51,
+ 0x76, 0xa8, 0xc0, 0xaa, 0x93, 0x31, 0xe6, 0xbb, 0x2d, 0x65, 0x61,
+ 0x19, 0xd3, 0x1e, 0xb5, 0x46, 0x5a, 0x96, 0x5a, 0x30, 0xa0, 0x7e,
+ 0x05, 0x69, 0x5b, 0xc9, 0xc6, 0x28, 0x40, 0xcd, 0x08, 0x64, 0x3c,
+ 0x73, 0x57, 0xe1, 0x94, 0xf1, 0xcd, 0x5a, 0x21, 0x8c, 0xb9, 0x63,
+ 0xe7, 0x67, 0x1d, 0xab, 0x40, 0xb1, 0xfb, 0x00, 0x1d, 0xf0, 0x2b,
+ 0x99, 0x2d, 0x66, 0x3e, 0x88, 0x75, 0x81, 0x3f, 0x31, 0xf6, 0xab,
+ 0x64, 0xd6, 0xb4, 0x17, 0xee, 0xd0, 0x9e, 0xe4, 0x32, 0x1a, 0xa7,
+ 0x31, 0xad, 0x18, 0x14, 0x26, 0xef, 0x54, 0xa5, 0xa8, 0x65, 0xa3,
+ 0x9c, 0x81, 0xfa, 0x56, 0x8c, 0x2d, 0xce, 0x68, 0x40, 0xcb, 0xf1,
+ 0x37, 0xbd, 0x5e, 0x85, 0xea, 0xd1, 0x0c, 0xbb, 0x19, 0x56, 0x23,
+ 0x20, 0x1f, 0xad, 0x5c, 0x42, 0x08, 0x03, 0xb5, 0x55, 0x91, 0x04,
+ 0xc9, 0x80, 0x38, 0x00, 0x0a, 0x71, 0x34, 0x6c, 0x32, 0x27, 0xe9,
+ 0x55, 0x25, 0x15, 0x2c, 0x68, 0xa3, 0x30, 0xeb, 0x54, 0xa5, 0x15,
+ 0x0c, 0xd1, 0x00, 0xff, 0xd9};
+
+ /* package */ static final byte[] sPhotoByteArrayForComplicatedCase;
+
+ static {
+ final int length = sPhotoIntArrayForComplicatedCase.length;
+ sPhotoByteArrayForComplicatedCase = new byte[length];
+ for (int i = 0; i < length; i++) {
+ sPhotoByteArrayForComplicatedCase[i] = (byte)sPhotoIntArrayForComplicatedCase[i];
+ }
+ }
+
+ public void testV21SimpleCase1_Parsing() {
+ mVerifier.initForImportTest(V21, R.raw.v21_simple_1);
+ mVerifier.addPropertyNodesVerifierElem()
+ .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.addContentValuesVerifierElem()
+ .addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "Ando")
+ .put(StructuredName.GIVEN_NAME, "Roid")
+ .put(StructuredName.DISPLAY_NAME, "Roid Ando");
+ }
+
+ public void testV21SimpleCase1_Type_Japanese() {
+ mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS, R.raw.v21_simple_1);
+ mVerifier.addContentValuesVerifierElem()
+ .addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "Ando")
+ .put(StructuredName.GIVEN_NAME, "Roid")
+ // If name-related strings only contains printable Ascii,
+ // the order is remained to be US's:
+ // "Prefix Given Middle Family Suffix"
+ .put(StructuredName.DISPLAY_NAME, "Roid Ando");
+ }
+
+ public void testV21SimpleCase2() {
+ mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS, R.raw.v21_simple_2);
+ mVerifier.addContentValuesVerifierElem()
+ .addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.DISPLAY_NAME, "Ando Roid");
+ }
+
+ public void testV21SimpleCase3() {
+ mVerifier.initForImportTest(V21, R.raw.v21_simple_3);
+ mVerifier.addContentValuesVerifierElem()
+ .addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "Ando")
+ .put(StructuredName.GIVEN_NAME, "Roid")
+ // "FN" field should be prefered since it should contain the original
+ // order intended by the author of the file.
+ .put(StructuredName.DISPLAY_NAME, "Ando Roid");
+ }
+
+ /**
+ * Tests ';' is properly handled by VCardParser implementation.
+ */
+ 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\\\\");
+
+ }
+
+ /**
+ * Tests ContactStruct correctly ignores redundant fields in "N" property values and
+ * inserts name related data.
+ */
+ public void testV21BackslashCase() {
+ mVerifier.initForImportTest(V21, R.raw.v21_backslash);
+ mVerifier.addContentValuesVerifierElem()
+ .addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ // FAMILY_NAME is empty and removed in this test...
+ .put(StructuredName.GIVEN_NAME, "A;B\\")
+ .put(StructuredName.MIDDLE_NAME, "C\\;")
+ .put(StructuredName.PREFIX, "D")
+ .put(StructuredName.SUFFIX, ":E")
+ .put(StructuredName.DISPLAY_NAME, "A;B\\C\\;D:E\\\\");
+ }
+
+ public void testOrgBeforTitle() {
+ mVerifier.initForImportTest(V21, R.raw.v21_org_before_title);
+ ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+ elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.DISPLAY_NAME, "Normal Guy");
+ elem.addExpected(Organization.CONTENT_ITEM_TYPE)
+ .put(Organization.COMPANY, "Company")
+ .put(Organization.DEPARTMENT, "Organization Devision Room Sheet No.")
+ .put(Organization.TITLE, "Excellent Janitor")
+ .put(Organization.TYPE, Organization.TYPE_WORK);
+ }
+
+ public void testTitleBeforOrg() {
+ mVerifier.initForImportTest(V21, R.raw.v21_title_before_org);
+ ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+ elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.DISPLAY_NAME, "Nice Guy");
+ elem.addExpected(Organization.CONTENT_ITEM_TYPE)
+ .put(Organization.COMPANY, "Marverous")
+ .put(Organization.DEPARTMENT, "Perfect Great Good Bad Poor")
+ .put(Organization.TITLE, "Cool Title")
+ .put(Organization.TYPE, Organization.TYPE_WORK);
+ }
+
+ /**
+ * Verifies that vCard importer correctly interpret "PREF" attribute to IS_PRIMARY.
+ * The data contain three cases: one "PREF", no "PREF" and multiple "PREF", in each type.
+ */
+ public void testV21PrefToIsPrimary() {
+ mVerifier.initForImportTest(V21, R.raw.v21_pref_handling);
+ ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+ elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ .put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.DISPLAY_NAME, "Smith");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "1")
+ .put(Phone.TYPE, Phone.TYPE_HOME);
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "2")
+ .put(Phone.TYPE, Phone.TYPE_WORK)
+ .put(Phone.IS_PRIMARY, 1);
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "3")
+ .put(Phone.TYPE, Phone.TYPE_ISDN);
+ elem.addExpected(Email.CONTENT_ITEM_TYPE)
+ .put(Email.DATA, "test@example.com")
+ .put(Email.TYPE, Email.TYPE_HOME)
+ .put(Email.IS_PRIMARY, 1);
+ elem.addExpected(Email.CONTENT_ITEM_TYPE)
+ .put(Email.DATA, "test2@examination.com")
+ .put(Email.TYPE, Email.TYPE_MOBILE)
+ .put(Email.IS_PRIMARY, 1);
+ elem.addExpected(Organization.CONTENT_ITEM_TYPE)
+ .put(Organization.COMPANY, "Company")
+ .put(Organization.TITLE, "Engineer")
+ .put(Organization.TYPE, Organization.TYPE_WORK);
+ elem.addExpected(Organization.CONTENT_ITEM_TYPE)
+ .put(Organization.COMPANY, "Mystery")
+ .put(Organization.TITLE, "Blogger")
+ .put(Organization.TYPE, Organization.TYPE_WORK);
+ elem.addExpected(Organization.CONTENT_ITEM_TYPE)
+ .put(Organization.COMPANY, "Poetry")
+ .put(Organization.TITLE, "Poet")
+ .put(Organization.TYPE, Organization.TYPE_WORK);
+ }
+
+ /**
+ * Tests all the properties in a complicated vCard are correctly parsed by the VCardParser.
+ */
+ 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",
+ Arrays.asList("Gump Shrimp Co.", "Sales Dept.;Manager", "Fish keeper"))
+ .addExpectedNodeWithOrder("ROLE", "Fish Cake Keeper!")
+ .addExpectedNodeWithOrder("TITLE", "Shrimp Man")
+ .addExpectedNodeWithOrder("X-CLASS", "PUBLIC")
+ .addExpectedNodeWithOrder("TEL", "(111) 555-1212", new TypeSet("WORK", "VOICE"))
+ .addExpectedNodeWithOrder("TEL", "(404) 555-1212", new TypeSet("HOME", "VOICE"))
+ .addExpectedNodeWithOrder("TEL", "0311111111", new TypeSet("CELL"))
+ .addExpectedNodeWithOrder("TEL", "0322222222", new TypeSet("VIDEO"))
+ .addExpectedNodeWithOrder("TEL", "0333333333", new TypeSet("VOICE"))
+ .addExpectedNodeWithOrder("ADR",
+ ";;100 Waters Edge;Baytown;LA;30314;United States of America",
+ Arrays.asList("", "", "100 Waters Edge", "Baytown",
+ "LA", "30314", "United States of America"),
+ null, null, new TypeSet("WORK"), null)
+ .addExpectedNodeWithOrder("LABEL",
+ "100 Waters Edge\r\nBaytown, LA 30314\r\nUnited States of America",
+ null, null, mContentValuesForQP, new TypeSet("WORK"), null)
+ .addExpectedNodeWithOrder("ADR",
+ ";;42 Plantation St.;Baytown;LA;30314;United States of America",
+ Arrays.asList("", "", "42 Plantation St.", "Baytown",
+ "LA", "30314", "United States of America"), null, null,
+ new TypeSet("HOME"), null)
+ .addExpectedNodeWithOrder("LABEL",
+ "42 Plantation St.\r\nBaytown, LA 30314\r\nUnited States of America",
+ null, null, mContentValuesForQP,
+ new TypeSet("HOME"), null)
+ .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",
+ "Now's the time for all folk to come to the aid of their country.",
+ null, null, mContentValuesForQP, null, null)
+ .addExpectedNodeWithOrder("PHOTO", null,
+ null, sPhotoByteArrayForComplicatedCase, mContentValuesForBase64V21,
+ new TypeSet("JPEG"), null)
+ .addExpectedNodeWithOrder("X-ATTRIBUTE", "Some String")
+ .addExpectedNodeWithOrder("BDAY", "19800101")
+ .addExpectedNodeWithOrder("GEO", "35.6563854,139.6994233")
+ .addExpectedNodeWithOrder("URL", "http://www.example.com/")
+ .addExpectedNodeWithOrder("REV", "20080424T195243Z");
+ }
+
+ /**
+ * Checks ContactStruct correctly inserts values in a complicated vCard
+ * into ContentResolver.
+ */
+ public void testV21ComplicatedCase() {
+ mVerifier.initForImportTest(V21, R.raw.v21_complicated);
+ ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+ elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "Gump")
+ .put(StructuredName.GIVEN_NAME, "Forrest")
+ .put(StructuredName.MIDDLE_NAME, "Hoge")
+ .put(StructuredName.PREFIX, "Pos")
+ .put(StructuredName.SUFFIX, "Tao")
+ .put(StructuredName.DISPLAY_NAME, "Joe Due");
+ elem.addExpected(Organization.CONTENT_ITEM_TYPE)
+ .put(Organization.TYPE, Organization.TYPE_WORK)
+ .put(Organization.COMPANY, "Gump Shrimp Co.")
+ .put(Organization.DEPARTMENT, "Sales Dept.;Manager Fish keeper")
+ .put(Organization.TITLE, "Shrimp Man");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_WORK)
+ // Phone number is expected to be formated with NAMP format in default.
+ .put(Phone.NUMBER, "111-555-1212");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_HOME)
+ .put(Phone.NUMBER, "404-555-1212");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_MOBILE)
+ .put(Phone.NUMBER, "031-111-1111");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "VIDEO")
+ .put(Phone.NUMBER, "032-222-2222");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "VOICE")
+ .put(Phone.NUMBER, "033-333-3333");
+ elem.addExpected(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK)
+ .put(StructuredPostal.COUNTRY, "United States of America")
+ .put(StructuredPostal.POSTCODE, "30314")
+ .put(StructuredPostal.REGION, "LA")
+ .put(StructuredPostal.CITY, "Baytown")
+ .put(StructuredPostal.STREET, "100 Waters Edge")
+ .put(StructuredPostal.FORMATTED_ADDRESS,
+ "100 Waters Edge Baytown LA 30314 United States of America");
+ elem.addExpected(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME)
+ .put(StructuredPostal.COUNTRY, "United States of America")
+ .put(StructuredPostal.POSTCODE, "30314")
+ .put(StructuredPostal.REGION, "LA")
+ .put(StructuredPostal.CITY, "Baytown")
+ .put(StructuredPostal.STREET, "42 Plantation St.")
+ .put(StructuredPostal.FORMATTED_ADDRESS,
+ "42 Plantation St. Baytown LA 30314 United States of America");
+ elem.addExpected(Email.CONTENT_ITEM_TYPE)
+ // "TYPE=INTERNET" -> TYPE_CUSTOM + the label "INTERNET"
+ .put(Email.TYPE, Email.TYPE_CUSTOM)
+ .put(Email.LABEL, "INTERNET")
+ .put(Email.DATA, "forrestgump@walladalla.com")
+ .put(Email.IS_PRIMARY, 1);
+ elem.addExpected(Email.CONTENT_ITEM_TYPE)
+ .put(Email.TYPE, Email.TYPE_MOBILE)
+ .put(Email.DATA, "cell@example.com");
+ elem.addExpected(Note.CONTENT_ITEM_TYPE)
+ .put(Note.NOTE, "The following note is the example from RFC 2045.");
+ elem.addExpected(Note.CONTENT_ITEM_TYPE)
+ .put(Note.NOTE,
+ "Now's the time for all folk to come to the aid of their country.");
+ elem.addExpected(Photo.CONTENT_ITEM_TYPE)
+ // No information about its image format can be inserted.
+ .put(Photo.PHOTO, sPhotoByteArrayForComplicatedCase);
+ elem.addExpected(Event.CONTENT_ITEM_TYPE)
+ .put(Event.START_DATE, "19800101")
+ .put(Event.TYPE, Event.TYPE_BIRTHDAY);
+ elem.addExpected(Website.CONTENT_ITEM_TYPE)
+ .put(Website.URL, "http://www.example.com/")
+ .put(Website.TYPE, Website.TYPE_HOMEPAGE);
+ }
+
+ 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("ORG", "Open;Handset; Alliance",
+ Arrays.asList("Open", "Handset", " Alliance"))
+ .addExpectedNodeWithOrder("SORT-STRING", "android")
+ .addExpectedNodeWithOrder("TEL", "0300000000", new TypeSet("PREF", "VOICE"))
+ .addExpectedNodeWithOrder("CLASS", "PUBLIC")
+ .addExpectedNodeWithOrder("X-GNO", "0")
+ .addExpectedNodeWithOrder("X-GN", "group0")
+ .addExpectedNodeWithOrder("X-REDUCTION", "0")
+ .addExpectedNodeWithOrder("REV", "20081031T065854Z");
+ }
+
+ public void testV30Simple() {
+ mVerifier.initForImportTest(V30, R.raw.v30_simple);
+ ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+ elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "And")
+ .put(StructuredName.GIVEN_NAME, "Roid")
+ .put(StructuredName.DISPLAY_NAME, "And Roid")
+ .put(StructuredName.PHONETIC_GIVEN_NAME, "android");
+ elem.addExpected(Organization.CONTENT_ITEM_TYPE)
+ .put(Organization.COMPANY, "Open")
+ .put(Organization.DEPARTMENT, "Handset Alliance")
+ .put(Organization.TYPE, Organization.TYPE_WORK);
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "VOICE")
+ .put(Phone.NUMBER, "030-000-0000")
+ .put(Phone.IS_PRIMARY, 1);
+ }
+
+ public void testV21Japanese1_Parsing() {
+ // 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.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)
+ .addExpectedNodeWithOrder("SOUND",
+ "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E;;;;",
+ null, null, mContentValuesForSJis,
+ new TypeSet("X-IRMC-N"), null)
+ .addExpectedNodeWithOrder("TEL", "0300000000", null, null, null,
+ new TypeSet("VOICE", "PREF"), null);
+ }
+
+ private void testV21Japanese1Common(int resId, int vcardType, boolean japanese) {
+ mVerifier.initForImportTest(vcardType, resId);
+ ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+ elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9")
+ .put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9")
+ // While vCard parser does not split "SOUND" property values,
+ // ContactStruct care it.
+ .put(StructuredName.PHONETIC_GIVEN_NAME,
+ "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ // Phone number formatting is different.
+ .put(Phone.NUMBER, (japanese ? "03-0000-0000" : "030-000-0000"))
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "VOICE")
+ .put(Phone.IS_PRIMARY, 1);
+ }
+
+ /**
+ * Verifies vCard with Japanese can be parsed correctly with
+ * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_GENERIC_UTF8}.
+ */
+ public void testV21Japanese1_Type_Generic_Utf8() {
+ testV21Japanese1Common(
+ R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8, false);
+ }
+
+ /**
+ * Verifies vCard with Japanese can be parsed correctly with
+ * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE_SJIS}.
+ */
+ public void testV21Japanese1_Type_Japanese_Sjis() {
+ testV21Japanese1Common(
+ R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS, true);
+ }
+
+ /**
+ * Verifies vCard with Japanese can be parsed correctly with
+ * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE_UTF8}.
+ * 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);
+ }
+
+ public void testV21Japanese2_Parsing() {
+ mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS,
+ 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",
+ "", "", ""),
+ null, mContentValuesForSJis, null, null)
+ .addExpectedNodeWithOrder("FN", "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9\u0020\u0031",
+ null, null, mContentValuesForSJis, null, null)
+ .addExpectedNodeWithOrder("SOUND",
+ "\uFF71\uFF9D\uFF84\uFF9E\uFF73;\uFF9B\uFF72\uFF84\uFF9E\u0031;;;",
+ null, null, mContentValuesForSJis,
+ new TypeSet("X-IRMC-N"), null)
+ .addExpectedNodeWithOrder("ADR",
+ ";\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" +
+ "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" +
+ "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC\u0036" +
+ "\u968E;;;;150-8512;",
+ Arrays.asList("",
+ "\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" +
+ "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" +
+ "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC" +
+ "\u0036\u968E", "", "", "", "150-8512", ""),
+ null, mContentValuesForQPAndSJis, new TypeSet("HOME"), null)
+ .addExpectedNodeWithOrder("NOTE", "\u30E1\u30E2", null, null,
+ mContentValuesForQPAndSJis, null, null);
+ }
+
+ public void testV21Japanese2_Type_Generic_Utf8() {
+ mVerifier.initForImportTest(V21, R.raw.v21_japanese_2);
+ ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+ elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4")
+ .put(StructuredName.GIVEN_NAME, "\u30ED\u30A4\u30C9\u0031")
+ .put(StructuredName.DISPLAY_NAME,
+ "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9\u0020\u0031")
+ // ContactStruct should correctly split "SOUND" property into several elements,
+ // even though VCardParser side does not care it.
+ .put(StructuredName.PHONETIC_FAMILY_NAME, "\uFF71\uFF9D\uFF84\uFF9E\uFF73")
+ .put(StructuredName.PHONETIC_GIVEN_NAME, "\uFF9B\uFF72\uFF84\uFF9E\u0031");
+ elem.addExpected(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.POSTCODE, "150-8512")
+ .put(StructuredPostal.STREET,
+ "\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" +
+ "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" +
+ "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC" +
+ "\u0036\u968E")
+ .put(StructuredPostal.FORMATTED_ADDRESS,
+ "\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" +
+ "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" +
+ "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC" +
+ "\u0036\u968E 150-8512")
+ .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME);
+ elem.addExpected(Note.CONTENT_ITEM_TYPE)
+ .put(Note.NOTE, "\u30E1\u30E2");
+ }
+
+ public void testV21MultipleEntryCase_Parse() {
+ mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS,
+ 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)
+ .addExpectedNodeWithOrder("SOUND",
+ "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0033;;;;",
+ null, null, mContentValuesForSJis,
+ new TypeSet("X-IRMC-N"), null)
+ .addExpectedNodeWithOrder("TEL", "9", new TypeSet("X-NEC-SECRET"))
+ .addExpectedNodeWithOrder("TEL", "10", new TypeSet("X-NEC-HOTEL"))
+ .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)
+ .addExpectedNodeWithOrder("SOUND",
+ "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0034;;;;",
+ null, null, mContentValuesForSJis,
+ new TypeSet("X-IRMC-N"), null)
+ .addExpectedNodeWithOrder("TEL", "13", new TypeSet("MODEM"))
+ .addExpectedNodeWithOrder("TEL", "14", new TypeSet("PAGER"))
+ .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)
+ .addExpectedNodeWithOrder("SOUND",
+ "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0035;;;;",
+ null, null, mContentValuesForSJis,
+ new TypeSet("X-IRMC-N"), null)
+ .addExpectedNodeWithOrder("TEL", "17", new TypeSet("X-NEC-BOY"))
+ .addExpectedNodeWithOrder("TEL", "18", new TypeSet("X-NEC-FRIEND"))
+ .addExpectedNodeWithOrder("TEL", "19", new TypeSet("X-NEC-PHS"))
+ .addExpectedNodeWithOrder("TEL", "20", new TypeSet("X-NEC-RESTAURANT"));
+ }
+
+ public void testV21MultipleEntryCase() {
+ mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS,
+ 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")
+ .put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033")
+ .put(StructuredName.PHONETIC_GIVEN_NAME,
+ "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0033");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "NEC-SECRET")
+ .put(Phone.NUMBER, "9");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "NEC-HOTEL")
+ .put(Phone.NUMBER, "10");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "NEC-SCHOOL")
+ .put(Phone.NUMBER, "11");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_FAX_HOME)
+ .put(Phone.NUMBER, "12");
+
+ elem = mVerifier.addContentValuesVerifierElem();
+ elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0034")
+ .put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0034")
+ .put(StructuredName.PHONETIC_GIVEN_NAME,
+ "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0034");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "MODEM")
+ .put(Phone.NUMBER, "13");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_PAGER)
+ .put(Phone.NUMBER, "14");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "NEC-FAMILY")
+ .put(Phone.NUMBER, "15");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "NEC-GIRL")
+ .put(Phone.NUMBER, "16");
+
+ elem = mVerifier.addContentValuesVerifierElem();
+ elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0035")
+ .put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0035")
+ .put(StructuredName.PHONETIC_GIVEN_NAME,
+ "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0035");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "NEC-BOY")
+ .put(Phone.NUMBER, "17");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "NEC-FRIEND")
+ .put(Phone.NUMBER, "18");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "NEC-PHS")
+ .put(Phone.NUMBER, "19");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "NEC-RESTAURANT")
+ .put(Phone.NUMBER, "20");
+ }
+
+ public void testIgnoreAgentV21_Parse() {
+ mVerifier.initForImportTest(V21, R.raw.v21_winmo_65);
+ 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)
+ .addExpectedNodeWithOrder("AGENT", "")
+ .addExpectedNodeWithOrder("X-CLASS", "PUBLIC")
+ .addExpectedNodeWithOrder("X-REDUCTION", "")
+ .addExpectedNodeWithOrder("X-NO", "");
+ }
+
+ public void testIgnoreAgentV21() {
+ mVerifier.initForImportTest(V21, R.raw.v21_winmo_65);
+ ContentValuesVerifier verifier = new ContentValuesVerifier();
+ ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+ elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "Example")
+ .put(StructuredName.DISPLAY_NAME, "Example");
+ }
+
+ public void testTolerateInvalidCommentLikeLineV21() {
+ mVerifier.initForImportTest(V21, R.raw.v21_invalid_comment_line);
+ ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+ elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.GIVEN_NAME, "Conference Call")
+ .put(StructuredName.DISPLAY_NAME, "Conference Call");
+ elem.addExpected(Note.CONTENT_ITEM_TYPE)
+ .put(Note.NOTE, "This is an (sharp ->#<- sharp) example. "
+ + "This message must NOT be ignored.");
+ }
+
+ public void testPagerV30_Parse() {
+ mVerifier.initForImportTest(V30, R.raw.v30_comma_separated);
+ 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);
+ 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_PAGER)
+ .put(Phone.NUMBER, "6101231234@pagersample.com");
+ }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardJapanizationTests.java b/core/tests/coretests/src/android/pim/vcard/VCardJapanizationTests.java
new file mode 100644
index 0000000..5b60342
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/VCardJapanizationTests.java
@@ -0,0 +1,434 @@
+/*
+ * 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.ContentValues;
+import android.pim.vcard.VCardConfig;
+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 {
+ private void testNameUtf8Common(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069")
+ .put(StructuredName.GIVEN_NAME, "\u3091\u308A\u304B")
+ .put(StructuredName.MIDDLE_NAME, "B")
+ .put(StructuredName.PREFIX, "Dr.")
+ .put(StructuredName.SUFFIX, "Ph.D");
+ ContentValues contentValues =
+ (VCardConfig.isV30(vcardType) ? null : mContentValuesForQPAndUtf8);
+ mVerifier.addPropertyNodesVerifierElem()
+ .addExpectedNode("FN", "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D",
+ contentValues)
+ .addExpectedNode("N", "\u3075\u308B\u3069;\u3091\u308A\u304B;B;Dr.;Ph.D",
+ Arrays.asList(
+ "\u3075\u308B\u3069", "\u3091\u308A\u304B", "B", "Dr.", "Ph.D"),
+ null, contentValues, null, null);
+ }
+
+ public void testNameUtf8V21() {
+ testNameUtf8Common(VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8);
+ }
+
+ public void testNameUtf8V30() {
+ testNameUtf8Common(VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8);
+ }
+
+ public void testNameShiftJis() {
+ mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V30_JAPANESE_SJIS);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069")
+ .put(StructuredName.GIVEN_NAME, "\u3091\u308A\u304B")
+ .put(StructuredName.MIDDLE_NAME, "B")
+ .put(StructuredName.PREFIX, "Dr.")
+ .put(StructuredName.SUFFIX, "Ph.D");
+
+ mVerifier.addPropertyNodesVerifierElem()
+ .addExpectedNode("FN", "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D",
+ mContentValuesForSJis)
+ .addExpectedNode("N", "\u3075\u308B\u3069;\u3091\u308A\u304B;B;Dr.;Ph.D",
+ Arrays.asList(
+ "\u3075\u308B\u3069", "\u3091\u308A\u304B", "B", "Dr.", "Ph.D"),
+ null, mContentValuesForSJis, null, null);
+ }
+
+ /**
+ * DoCoMo phones require all name elements should be in "family name" field.
+ */
+ public void testNameDoCoMo() {
+ mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069")
+ .put(StructuredName.GIVEN_NAME, "\u3091\u308A\u304B")
+ .put(StructuredName.MIDDLE_NAME, "B")
+ .put(StructuredName.PREFIX, "Dr.")
+ .put(StructuredName.SUFFIX, "Ph.D");
+
+ final String fullName = "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D";
+ mVerifier.addPropertyNodesVerifierElem()
+ .addExpectedNode("N", fullName + ";;;;",
+ Arrays.asList(fullName, "", "", "", ""),
+ null, mContentValuesForSJis, null, null)
+ .addExpectedNode("FN", fullName, mContentValuesForSJis)
+ .addExpectedNode("SOUND", ";;;;", new TypeSet("X-IRMC-N"))
+ .addExpectedNode("TEL", "", new TypeSet("HOME"))
+ .addExpectedNode("EMAIL", "", new TypeSet("HOME"))
+ .addExpectedNode("ADR", "", new TypeSet("HOME"))
+ .addExpectedNode("X-CLASS", "PUBLIC")
+ .addExpectedNode("X-REDUCTION", "")
+ .addExpectedNode("X-NO", "")
+ .addExpectedNode("X-DCM-HMN-MODE", "");
+ }
+
+ private void testPhoneticNameCommon(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060")
+ .put(StructuredName.PHONETIC_MIDDLE_NAME, "\u30DF\u30C9\u30EB\u30CD\u30FC\u30E0")
+ .put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046");
+
+ final ContentValues contentValues =
+ (VCardConfig.usesShiftJis(vcardType) ?
+ (VCardConfig.isV30(vcardType) ? mContentValuesForSJis :
+ mContentValuesForQPAndSJis) :
+ (VCardConfig.isV30(vcardType) ? null : mContentValuesForQPAndUtf8));
+ PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName();
+ elem.addExpectedNode("X-PHONETIC-LAST-NAME", "\u3084\u307E\u3060",
+ contentValues)
+ .addExpectedNode("X-PHONETIC-MIDDLE-NAME",
+ "\u30DF\u30C9\u30EB\u30CD\u30FC\u30E0",
+ contentValues)
+ .addExpectedNode("X-PHONETIC-FIRST-NAME", "\u305F\u308D\u3046",
+ contentValues);
+ if (VCardConfig.isV30(vcardType)) {
+ elem.addExpectedNode("SORT-STRING",
+ "\u3084\u307E\u3060 \u30DF\u30C9\u30EB\u30CD\u30FC\u30E0 \u305F\u308D\u3046",
+ contentValues);
+ }
+ ContentValuesBuilder builder = mVerifier.addContentValuesVerifierElem()
+ .addExpected(StructuredName.CONTENT_ITEM_TYPE);
+ builder.put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060")
+ .put(StructuredName.PHONETIC_MIDDLE_NAME, "\u30DF\u30C9\u30EB\u30CD\u30FC\u30E0")
+ .put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046")
+ .put(StructuredName.DISPLAY_NAME,
+ "\u3084\u307E\u3060 \u30DF\u30C9\u30EB\u30CD\u30FC\u30E0 " +
+ "\u305F\u308D\u3046");
+ }
+
+ public void testPhoneticNameForJapaneseV21Utf8() {
+ testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8);
+ }
+
+ public void testPhoneticNameForJapaneseV21Sjis() {
+ testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS);
+ }
+
+ public void testPhoneticNameForJapaneseV30Utf8() {
+ testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8);
+ }
+
+ public void testPhoneticNameForJapaneseV30SJis() {
+ testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE_SJIS);
+ }
+
+ public void testPhoneticNameForMobileV21_1() {
+ mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060")
+ .put(StructuredName.PHONETIC_MIDDLE_NAME, "\u30DF\u30C9\u30EB\u30CD\u30FC\u30E0")
+ .put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046");
+
+ mVerifier.addPropertyNodesVerifierElem()
+ .addExpectedNode("SOUND",
+ "\uFF94\uFF8F\uFF80\uFF9E \uFF90\uFF84\uFF9E\uFF99\uFF88\uFF70\uFF91 " +
+ "\uFF80\uFF9B\uFF73;;;;",
+ mContentValuesForSJis, new TypeSet("X-IRMC-N"));
+ ContentValuesBuilder builder = mVerifier.addContentValuesVerifierElem()
+ .addExpected(StructuredName.CONTENT_ITEM_TYPE);
+ builder.put(StructuredName.PHONETIC_FAMILY_NAME, "\uFF94\uFF8F\uFF80\uFF9E")
+ .put(StructuredName.PHONETIC_MIDDLE_NAME,
+ "\uFF90\uFF84\uFF9E\uFF99\uFF88\uFF70\uFF91")
+ .put(StructuredName.PHONETIC_GIVEN_NAME, "\uFF80\uFF9B\uFF73")
+ .put(StructuredName.DISPLAY_NAME,
+ "\uFF94\uFF8F\uFF80\uFF9E \uFF90\uFF84\uFF9E\uFF99\uFF88\uFF70\uFF91 " +
+ "\uFF80\uFF9B\uFF73");
+ }
+
+ public void testPhoneticNameForMobileV21_2() {
+ mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060")
+ .put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046");
+
+ mVerifier.addPropertyNodesVerifierElem()
+ .addExpectedNode("SOUND", "\uFF94\uFF8F\uFF80\uFF9E \uFF80\uFF9B\uFF73;;;;",
+ mContentValuesForSJis, new TypeSet("X-IRMC-N"));
+ ContentValuesBuilder builder = mVerifier.addContentValuesVerifierElem()
+ .addExpected(StructuredName.CONTENT_ITEM_TYPE);
+ builder.put(StructuredName.PHONETIC_FAMILY_NAME, "\uFF94\uFF8F\uFF80\uFF9E")
+ .put(StructuredName.PHONETIC_GIVEN_NAME, "\uFF80\uFF9B\uFF73")
+ .put(StructuredName.DISPLAY_NAME, "\uFF94\uFF8F\uFF80\uFF9E \uFF80\uFF9B\uFF73");
+ }
+
+ private void testPostalAddressWithJapaneseCommon(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.POBOX, "\u79C1\u66F8\u7BB107")
+ .put(StructuredPostal.STREET, "\u96DB\u898B\u6CA2\u6751")
+ .put(StructuredPostal.CITY, "\u9E7F\u9AA8\u5E02")
+ .put(StructuredPostal.REGION, "\u00D7\u00D7\u770C")
+ .put(StructuredPostal.POSTCODE, "494-1313")
+ .put(StructuredPostal.COUNTRY, "\u65E5\u672C")
+ .put(StructuredPostal.FORMATTED_ADDRESS,
+ "\u3053\u3093\u306A\u3068\u3053\u308D\u3092\u898B"
+ + "\u308B\u306A\u3093\u3066\u6687\u4EBA\u3067\u3059\u304B\uFF1F")
+ .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));
+
+ PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName();
+ // LABEL must be ignored in vCard 2.1. As for vCard 3.0, the current behavior is
+ // same as that in vCard 3.0, which can be changed in the future.
+ elem.addExpectedNode("ADR", Arrays.asList("\u79C1\u66F8\u7BB107",
+ "", "\u96DB\u898B\u6CA2\u6751", "\u9E7F\u9AA8\u5E02", "\u00D7\u00D7\u770C",
+ "494-1313", "\u65E5\u672C"),
+ contentValues);
+ mVerifier.addContentValuesVerifierElem().addExpected(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.POBOX, "\u79C1\u66F8\u7BB107")
+ .put(StructuredPostal.STREET, "\u96DB\u898B\u6CA2\u6751")
+ .put(StructuredPostal.CITY, "\u9E7F\u9AA8\u5E02")
+ .put(StructuredPostal.REGION, "\u00D7\u00D7\u770C")
+ .put(StructuredPostal.POSTCODE, "494-1313")
+ .put(StructuredPostal.COUNTRY, "\u65E5\u672C")
+ .put(StructuredPostal.FORMATTED_ADDRESS,
+ "\u65E5\u672C 494-1313 \u00D7\u00D7\u770C \u9E7F\u9AA8\u5E02 " +
+ "\u96DB\u898B\u6CA2\u6751 " + "\u79C1\u66F8\u7BB107")
+ .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME);
+ }
+ public void testPostalAddresswithJapaneseV21() {
+ testPostalAddressWithJapaneseCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS);
+ }
+
+ /**
+ * Verifies that only one address field is emitted toward DoCoMo phones.
+ * Prefered type must (should?) be: HOME > WORK > OTHER > CUSTOM
+ */
+ public void testPostalAdrressForDoCoMo_1() {
+ mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK)
+ .put(StructuredPostal.POBOX, "1");
+ entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER)
+ .put(StructuredPostal.POBOX, "2");
+ entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME)
+ .put(StructuredPostal.POBOX, "3");
+ entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
+ .put(StructuredPostal.LABEL, "custom")
+ .put(StructuredPostal.POBOX, "4");
+
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("TEL", "", new TypeSet("HOME"))
+ .addExpectedNode("EMAIL", "", new TypeSet("HOME"))
+ .addExpectedNode("X-CLASS", "PUBLIC")
+ .addExpectedNode("X-REDUCTION", "")
+ .addExpectedNode("X-NO", "")
+ .addExpectedNode("X-DCM-HMN-MODE", "")
+ .addExpectedNode("ADR",
+ Arrays.asList("3", "", "", "", "", "", ""), new TypeSet("HOME"));
+ }
+
+ public void testPostalAdrressForDoCoMo_2() {
+ mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER)
+ .put(StructuredPostal.POBOX, "1");
+ entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK)
+ .put(StructuredPostal.POBOX, "2");
+ entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
+ .put(StructuredPostal.LABEL, "custom")
+ .put(StructuredPostal.POBOX, "3");
+
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("TEL", "", new TypeSet("HOME"))
+ .addExpectedNode("EMAIL", "", new TypeSet("HOME"))
+ .addExpectedNode("X-CLASS", "PUBLIC")
+ .addExpectedNode("X-REDUCTION", "")
+ .addExpectedNode("X-NO", "")
+ .addExpectedNode("X-DCM-HMN-MODE", "")
+ .addExpectedNode("ADR",
+ Arrays.asList("2", "", "", "", "", "", ""), new TypeSet("WORK"));
+ }
+
+ public void testPostalAdrressForDoCoMo_3() {
+ mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
+ .put(StructuredPostal.LABEL, "custom1")
+ .put(StructuredPostal.POBOX, "1");
+ entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER)
+ .put(StructuredPostal.POBOX, "2");
+ entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
+ .put(StructuredPostal.LABEL, "custom2")
+ .put(StructuredPostal.POBOX, "3");
+
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("TEL", "", new TypeSet("HOME"))
+ .addExpectedNode("EMAIL", "", new TypeSet("HOME"))
+ .addExpectedNode("X-CLASS", "PUBLIC")
+ .addExpectedNode("X-REDUCTION", "")
+ .addExpectedNode("X-NO", "")
+ .addExpectedNode("X-DCM-HMN-MODE", "")
+ .addExpectedNode("ADR", Arrays.asList("2", "", "", "", "", "", ""));
+ }
+
+ /**
+ * Verifies the vCard exporter tolerates null TYPE.
+ */
+ public void testPostalAdrressForDoCoMo_4() {
+ mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.POBOX, "1");
+ entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER)
+ .put(StructuredPostal.POBOX, "2");
+ entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME)
+ .put(StructuredPostal.POBOX, "3");
+ entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK)
+ .put(StructuredPostal.POBOX, "4");
+ entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+ .put(StructuredPostal.POBOX, "5");
+
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("TEL", "", new TypeSet("HOME"))
+ .addExpectedNode("EMAIL", "", new TypeSet("HOME"))
+ .addExpectedNode("X-CLASS", "PUBLIC")
+ .addExpectedNode("X-REDUCTION", "")
+ .addExpectedNode("X-NO", "")
+ .addExpectedNode("X-DCM-HMN-MODE", "")
+ .addExpectedNode("ADR",
+ Arrays.asList("3", "", "", "", "", "", ""), new TypeSet("HOME"));
+ }
+
+ private void testJapanesePhoneNumberCommon(int vcardType) {
+ mVerifier.initForExportTest(vcardType);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "0312341234")
+ .put(Phone.TYPE, Phone.TYPE_HOME);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "09012341234")
+ .put(Phone.TYPE, Phone.TYPE_MOBILE);
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("TEL", "03-1234-1234", new TypeSet("HOME"))
+ .addExpectedNode("TEL", "090-1234-1234", new TypeSet("CELL"));
+ }
+
+ public void testJapanesePhoneNumberV21_1() {
+ testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8);
+ }
+
+ public void testJapanesePhoneNumberV30() {
+ testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8);
+ }
+
+ public void testJapanesePhoneNumberDoCoMo() {
+ mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "0312341234")
+ .put(Phone.TYPE, Phone.TYPE_HOME);
+ entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.NUMBER, "09012341234")
+ .put(Phone.TYPE, Phone.TYPE_MOBILE);
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("EMAIL", "", new TypeSet("HOME"))
+ .addExpectedNode("X-CLASS", "PUBLIC")
+ .addExpectedNode("X-REDUCTION", "")
+ .addExpectedNode("X-NO", "")
+ .addExpectedNode("X-DCM-HMN-MODE", "")
+ .addExpectedNode("ADR", "", new TypeSet("HOME"))
+ .addExpectedNode("TEL", "03-1234-1234", new TypeSet("HOME"))
+ .addExpectedNode("TEL", "090-1234-1234", new TypeSet("CELL"));
+ }
+
+ public void testNoteDoCoMo() {
+ mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+ ContactEntry entry = mVerifier.addInputEntry();
+ entry.addContentValues(Note.CONTENT_ITEM_TYPE)
+ .put(Note.NOTE, "note1");
+ entry.addContentValues(Note.CONTENT_ITEM_TYPE)
+ .put(Note.NOTE, "note2");
+ entry.addContentValues(Note.CONTENT_ITEM_TYPE)
+ .put(Note.NOTE, "note3");
+
+ // More than one note fields must be aggregated into one note.
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("TEL", "", new TypeSet("HOME"))
+ .addExpectedNode("EMAIL", "", new TypeSet("HOME"))
+ .addExpectedNode("X-CLASS", "PUBLIC")
+ .addExpectedNode("X-REDUCTION", "")
+ .addExpectedNode("X-NO", "")
+ .addExpectedNode("X-DCM-HMN-MODE", "")
+ .addExpectedNode("ADR", "", new TypeSet("HOME"))
+ .addExpectedNode("NOTE", "note1\nnote2\nnote3", mContentValuesForQP);
+ }
+
+ public void testAndroidCustomV21() {
+ mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
+ mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE)
+ .put(Nickname.NAME, "\u304D\u3083\u30FC\u30A8\u30C3\u30C1\u30FC");
+ mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+ .addExpectedNode("X-ANDROID-CUSTOM",
+ Arrays.asList(Nickname.CONTENT_ITEM_TYPE,
+ "\u304D\u3083\u30FC\u30A8\u30C3\u30C1\u30FC",
+ "", "", "", "", "", "", "", "", "", "", "", "", "", ""),
+ mContentValuesForQPAndUtf8);
+ }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java b/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java
new file mode 100644
index 0000000..0857e0c
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java
@@ -0,0 +1,169 @@
+/*
+ * 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
new file mode 100644
index 0000000..59299f9
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java
@@ -0,0 +1,85 @@
+/*
+ * 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.VCardUtils;
+
+import junit.framework.TestCase;
+
+import java.util.List;
+
+public class VCardUtilsTests extends TestCase {
+ public void testContainsOnlyPrintableAscii() {
+ assertTrue(VCardUtils.containsOnlyPrintableAscii((String)null));
+ assertTrue(VCardUtils.containsOnlyPrintableAscii((String[])null));
+ assertTrue(VCardUtils.containsOnlyPrintableAscii((List<String>)null));
+ assertTrue(VCardUtils.containsOnlyPrintableAscii(""));
+ assertTrue(VCardUtils.containsOnlyPrintableAscii("abcdefghijklmnopqrstuvwxyz"));
+ assertTrue(VCardUtils.containsOnlyPrintableAscii("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0x20; i < 0x7F; i++) {
+ builder.append((char)i);
+ }
+ assertTrue(VCardUtils.containsOnlyPrintableAscii(builder.toString()));
+ assertTrue(VCardUtils.containsOnlyPrintableAscii("\r\n"));
+ assertFalse(VCardUtils.containsOnlyPrintableAscii("\u0019"));
+ assertFalse(VCardUtils.containsOnlyPrintableAscii("\u007F"));
+ }
+
+ public void testContainsOnlyNonCrLfPrintableAscii() {
+ assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii((String)null));
+ assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii((String[])null));
+ assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii((List<String>)null));
+ assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii(""));
+ assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("abcdefghijklmnopqrstuvwxyz"));
+ assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0x20; i < 0x7F; i++) {
+ builder.append((char)i);
+ }
+ assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii(builder.toString()));
+ assertFalse(VCardUtils.containsOnlyNonCrLfPrintableAscii("\u0019"));
+ assertFalse(VCardUtils.containsOnlyNonCrLfPrintableAscii("\u007F"));
+ assertFalse(VCardUtils.containsOnlyNonCrLfPrintableAscii("\r"));
+ assertFalse(VCardUtils.containsOnlyNonCrLfPrintableAscii("\n"));
+ }
+
+ public void testContainsOnlyAlphaDigitHyphen() {
+ assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen((String)null));
+ assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen((String[])null));
+ assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen((List<String>)null));
+ assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen(""));
+ assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("abcdefghijklmnopqrstuvwxyz"));
+ assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+ assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("0123456789-"));
+ for (int i = 0; i < 0x30; i++) {
+ if (i == 0x2D) { // -
+ continue;
+ }
+ assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i)));
+ }
+ for (int i = 0x3A; i < 0x41; i++) {
+ assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i)));
+ }
+ for (int i = 0x5B; i < 0x61; i++) {
+ assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i)));
+ }
+ for (int i = 0x7B; i < 0x100; i++) {
+ assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i)));
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardVerifier.java b/core/tests/coretests/src/android/pim/vcard/VCardVerifier.java
new file mode 100644
index 0000000..bfc3158
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/VCardVerifier.java
@@ -0,0 +1,306 @@
+/*
+ * 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/VNode.java b/core/tests/coretests/src/android/pim/vcard/VNode.java
new file mode 100644
index 0000000..79f10dc
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/VNode.java
@@ -0,0 +1,30 @@
+/*
+ * 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 java.util.ArrayList;
+
+/**
+ * Previously used in main vCard handling code but now exists only for testing.
+ */
+public class VNode {
+ public String VName;
+
+ public ArrayList<PropertyNode> propList = new ArrayList<PropertyNode>();
+
+ /** 0:parse over. 1:parsing. */
+ public int parseStatus = 1;
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/VNodeBuilder.java b/core/tests/coretests/src/android/pim/vcard/VNodeBuilder.java
new file mode 100644
index 0000000..0e6c325
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/VNodeBuilder.java
@@ -0,0 +1,314 @@
+/*
+ * 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.ContentValues;
+import android.pim.vcard.VCardInterpreter;
+import android.pim.vcard.VCardConfig;
+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;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Store the parse result to custom datastruct: VNode, 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.
+ */
+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.
+ */
+ private String mTargetCharset;
+
+ private boolean mStrictLineBreakParsing;
+
+ public VNodeBuilder() {
+ this(VCardConfig.DEFAULT_CHARSET, TARGET_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;
+ }
+ if (targetCharset != null) {
+ mTargetCharset = targetCharset;
+ } else {
+ mTargetCharset = TARGET_CHARSET;
+ }
+ mStrictLineBreakParsing = strictLineBreakParsing;
+ }
+
+ public void start() {
+ }
+
+ public void end() {
+ }
+
+ // Note: I guess that this code assumes the Record may nest like this:
+ // START:VPOS
+ // ...
+ // START:VPOS2
+ // ...
+ // END:VPOS2
+ // ...
+ // END:VPOS
+ //
+ // However the following code has a bug.
+ // When error occurs after calling startRecord(), the entry which is probably
+ // the cause of the error remains to be in vNodeList, while endRecord() is not called.
+ //
+ // I leave this code as is since I'm not familiar with vcalendar specification.
+ // But I believe we should refactor this code in the future.
+ // Until this, the last entry has to be removed when some error occurs.
+ public void startEntry() {
+ VNode vnode = new VNode();
+ vnode.parseStatus = 1;
+ vnode.VName = "VCARD";
+ // I feel this should be done in endRecord(), but it cannot be done because of
+ // the reason above.
+ vNodeList.add(vnode);
+ mNodeListPos = vNodeList.size() - 1;
+ mCurrentVNode = vNodeList.get(mNodeListPos);
+ }
+
+ public void endEntry() {
+ VNode endNode = vNodeList.get(mNodeListPos);
+ endNode.parseStatus = 0;
+ while(mNodeListPos > 0){
+ mNodeListPos--;
+ if((vNodeList.get(mNodeListPos)).parseStatus == 1)
+ break;
+ }
+ mCurrentVNode = vNodeList.get(mNodeListPos);
+ }
+
+ public void startProperty() {
+ mCurrentPropNode = new PropertyNode();
+ }
+
+ public void endProperty() {
+ mCurrentVNode.propList.add(mCurrentPropNode);
+ }
+
+ public void propertyName(String name) {
+ mCurrentPropNode.propName = name;
+ }
+
+ // Used only in VCard.
+ public void propertyGroup(String group) {
+ mCurrentPropNode.propGroupSet.add(group);
+ }
+
+ public void propertyParamType(String type) {
+ mCurrentParamType = type;
+ }
+
+ public void propertyParamValue(String value) {
+ if (mCurrentParamType == null ||
+ mCurrentParamType.equalsIgnoreCase("TYPE")) {
+ mCurrentPropNode.paramMap_TYPE.add(value);
+ } else {
+ mCurrentPropNode.paramMap.put(mCurrentParamType, value);
+ }
+
+ mCurrentParamType = null;
+ }
+
+ private String encodeString(String originalString, String targetCharset) {
+ if (mSourceCharset.equalsIgnoreCase(targetCharset)) {
+ return originalString;
+ }
+ Charset charset = Charset.forName(mSourceCharset);
+ ByteBuffer byteBuffer = charset.encode(originalString);
+ // byteBuffer.array() "may" return byte array which is larger than
+ // byteBuffer.remaining(). Here, we keep on the safe side.
+ byte[] bytes = new byte[byteBuffer.remaining()];
+ byteBuffer.get(bytes);
+ try {
+ return new String(bytes, targetCharset);
+ } catch (UnsupportedEncodingException e) {
+ Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
+ return null;
+ }
+ }
+
+ private String handleOneValue(String value, String targetCharset, String encoding) {
+ if (encoding != null) {
+ 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());
+ 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);
+ }
+ }
+ // Unknown encoding. Fall back to default.
+ }
+ return encodeString(value, targetCharset);
+ }
+
+ public void propertyValues(List<String> values) {
+ if (values == null || values.size() == 0) {
+ mCurrentPropNode.propValue_bytes = null;
+ mCurrentPropNode.propValue_vector.clear();
+ mCurrentPropNode.propValue_vector.add("");
+ mCurrentPropNode.propValue = "";
+ return;
+ }
+
+ ContentValues paramMap = mCurrentPropNode.paramMap;
+
+ String targetCharset = CharsetUtils.nameForDefaultVendor(paramMap.getAsString("CHARSET"));
+ String encoding = paramMap.getAsString("ENCODING");
+
+ if (targetCharset == null || targetCharset.length() == 0) {
+ targetCharset = mTargetCharset;
+ }
+
+ for (String value : values) {
+ mCurrentPropNode.propValue_vector.add(
+ handleOneValue(value, targetCharset, encoding));
+ }
+
+ mCurrentPropNode.propValue = listToString(mCurrentPropNode.propValue_vector);
+ }
+
+ private String listToString(List<String> list){
+ int size = list.size();
+ if (size > 1) {
+ StringBuilder typeListB = new StringBuilder();
+ for (String type : list) {
+ typeListB.append(type).append(";");
+ }
+ int len = typeListB.length();
+ if (len > 0 && typeListB.charAt(len - 1) == ';') {
+ return typeListB.substring(0, len - 1);
+ }
+ return typeListB.toString();
+ } else if (size == 1) {
+ return list.get(0);
+ } else {
+ return "";
+ }
+ }
+
+ public String getResult(){
+ return null;
+ }
+}
diff --git a/core/tests/coretests/src/android/provider/SettingsProviderTest.java b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
new file mode 100644
index 0000000..f82d79a
--- /dev/null
+++ b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.provider;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.Settings;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+/** Unit test for SettingsProvider. */
+public class SettingsProviderTest extends AndroidTestCase {
+ @MediumTest
+ public void testNameValueCache() {
+ ContentResolver r = getContext().getContentResolver();
+ Settings.Secure.putString(r, "test_service", "Value");
+ assertEquals("Value", Settings.Secure.getString(r, "test_service"));
+
+ // Make sure the value can be overwritten.
+ Settings.Secure.putString(r, "test_service", "New");
+ assertEquals("New", Settings.Secure.getString(r, "test_service"));
+
+ // Also that delete works.
+ assertEquals(1, r.delete(Settings.Secure.getUriFor("test_service"), null, null));
+ assertEquals(null, Settings.Secure.getString(r, "test_service"));
+
+ // Try all the same things in the System table
+ Settings.System.putString(r, "test_setting", "Value");
+ assertEquals("Value", Settings.System.getString(r, "test_setting"));
+
+ Settings.System.putString(r, "test_setting", "New");
+ assertEquals("New", Settings.System.getString(r, "test_setting"));
+
+ assertEquals(1, r.delete(Settings.System.getUriFor("test_setting"), null, null));
+ assertEquals(null, Settings.System.getString(r, "test_setting"));
+ }
+
+ @MediumTest
+ public void testRowNameContentUri() {
+ ContentResolver r = getContext().getContentResolver();
+
+ assertEquals("content://settings/system/test_setting",
+ Settings.System.getUriFor("test_setting").toString());
+ assertEquals("content://settings/gservices/test_service",
+ Settings.Secure.getUriFor("test_service").toString());
+
+ // These tables use the row name (not ID) as their content URI.
+ Uri tables[] = { Settings.System.CONTENT_URI, Settings.Secure.CONTENT_URI };
+ for (Uri table : tables) {
+ ContentValues v = new ContentValues();
+ v.put(Settings.System.NAME, "test_key");
+ v.put(Settings.System.VALUE, "Test");
+ Uri uri = r.insert(table, v);
+ assertEquals(table.toString() + "/test_key", uri.toString());
+
+ // Query with a specific URI and no WHERE clause succeeds.
+ Cursor c = r.query(uri, null, null, null, null);
+ try {
+ assertTrue(c.moveToNext());
+ assertEquals("test_key", c.getString(c.getColumnIndex(Settings.System.NAME)));
+ assertEquals("Test", c.getString(c.getColumnIndex(Settings.System.VALUE)));
+ assertFalse(c.moveToNext());
+ } finally {
+ c.close();
+ }
+
+ // Query with a specific URI and a WHERE clause fails.
+ try {
+ r.query(uri, null, "1", null, null);
+ fail("UnsupportedOperationException expected");
+ } catch (UnsupportedOperationException e) {
+ if (!e.toString().contains("WHERE clause")) throw e;
+ }
+
+ // Query with a tablewide URI and a WHERE clause succeeds.
+ c = r.query(table, null, "name='test_key'", null, null);
+ try {
+ assertTrue(c.moveToNext());
+ assertEquals("test_key", c.getString(c.getColumnIndex(Settings.System.NAME)));
+ assertEquals("Test", c.getString(c.getColumnIndex(Settings.System.VALUE)));
+ assertFalse(c.moveToNext());
+ } finally {
+ c.close();
+ }
+
+ v = new ContentValues();
+ v.put(Settings.System.VALUE, "Toast");
+ assertEquals(1, r.update(uri, v, null, null));
+
+ c = r.query(uri, null, null, null, null);
+ try {
+ assertTrue(c.moveToNext());
+ assertEquals("test_key", c.getString(c.getColumnIndex(Settings.System.NAME)));
+ assertEquals("Toast", c.getString(c.getColumnIndex(Settings.System.VALUE)));
+ assertFalse(c.moveToNext());
+ } finally {
+ c.close();
+ }
+
+ assertEquals(1, r.delete(uri, null, null));
+ }
+
+ assertEquals(null, Settings.System.getString(r, "test_key"));
+ assertEquals(null, Settings.Secure.getString(r, "test_key"));
+ }
+
+ @MediumTest
+ public void testRowNumberContentUri() {
+ ContentResolver r = getContext().getContentResolver();
+
+ // The bookmarks table (and everything else) uses standard row number content URIs.
+ Uri uri = Settings.Bookmarks.add(r, new Intent("TEST"),
+ "Test Title", "Test Folder", '*', 123);
+
+ assertTrue(ContentUris.parseId(uri) > 0);
+
+ assertEquals("TEST", Settings.Bookmarks.getIntentForShortcut(r, '*').getAction());
+
+ ContentValues v = new ContentValues();
+ v.put(Settings.Bookmarks.INTENT, "#Intent;action=TOAST;end");
+ assertEquals(1, r.update(uri, v, null, null));
+
+ assertEquals("TOAST", Settings.Bookmarks.getIntentForShortcut(r, '*').getAction());
+
+ assertEquals(1, r.delete(uri, null, null));
+
+ assertEquals(null, Settings.Bookmarks.getIntentForShortcut(r, '*'));
+ }
+}
diff --git a/core/tests/coretests/src/android/provider/SmsProviderTest.java b/core/tests/coretests/src/android/provider/SmsProviderTest.java
new file mode 100644
index 0000000..c8ed728
--- /dev/null
+++ b/core/tests/coretests/src/android/provider/SmsProviderTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2006 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.provider;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.Telephony.Sms;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import java.util.GregorianCalendar;
+
+public class SmsProviderTest extends AndroidTestCase {
+
+ @LargeTest
+ public void testProvider() throws Exception {
+ // This test does the following
+ // 1. Insert 10 messages from the same number at different times.
+ //
+ // . Delete the messages and make sure that they were deleted.
+
+ long now = System.currentTimeMillis();
+
+ Uri[] urls = new Uri[10];
+ String[] dates = new String[]{
+ Long.toString(new GregorianCalendar(1970, 1, 1, 0, 0, 0).getTimeInMillis()),
+ Long.toString(new GregorianCalendar(1971, 2, 13, 16, 35, 3).getTimeInMillis()),
+ Long.toString(new GregorianCalendar(1978, 10, 22, 0, 1, 0).getTimeInMillis()),
+ Long.toString(new GregorianCalendar(1980, 1, 11, 10, 22, 30).getTimeInMillis()),
+ Long.toString(now - (5 * 24 * 60 * 60 * 1000)),
+ Long.toString(now - (2 * 24 * 60 * 60 * 1000)),
+ Long.toString(now - (5 * 60 * 60 * 1000)),
+ Long.toString(now - (30 * 60 * 1000)),
+ Long.toString(now - (5 * 60 * 1000)),
+ Long.toString(now)
+ };
+
+ ContentValues map = new ContentValues();
+ map.put("address", "+15045551337");
+ map.put("read", 0);
+
+ ContentResolver contentResolver = mContext.getContentResolver();
+
+ for (int i = 0; i < urls.length; i++) {
+ map.put("body", "Test " + i + " !");
+ map.put("date", dates[i]);
+ urls[i] = contentResolver.insert(Sms.Inbox.CONTENT_URI, map);
+ assertNotNull(urls[i]);
+ }
+
+ Cursor c = contentResolver.query(Sms.Inbox.CONTENT_URI, null, null, null, "date");
+
+ //DatabaseUtils.dumpCursor(c);
+
+ for (Uri url : urls) {
+ int count = contentResolver.delete(url, null, null);
+ assertEquals(1, count);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/text/SpannedTest.java b/core/tests/coretests/src/android/text/SpannedTest.java
new file mode 100644
index 0000000..1c22cf9
--- /dev/null
+++ b/core/tests/coretests/src/android/text/SpannedTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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.text;
+
+import android.graphics.Typeface;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.*;
+import android.text.style.*;
+import android.util.Log;
+
+import junit.framework.TestCase;
+
+/**
+ * SpannedTest tests some features of Spanned
+ */
+public class SpannedTest extends TestCase {
+ private int mExpect;
+
+ @SmallTest
+ public void testSpannableString() throws Exception {
+ checkPriority(new SpannableString("the quick brown fox"));
+ }
+
+ @SmallTest
+ public void testSpannableStringBuilder() throws Exception {
+ checkPriority2(new SpannableStringBuilder("the quick brown fox"));
+ }
+
+ @SmallTest
+ public void testAppend() throws Exception {
+ Object o = new Object();
+ SpannableString ss = new SpannableString("Test");
+ ss.setSpan(o, 0, ss.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ SpannableStringBuilder ssb = new SpannableStringBuilder();
+ ssb.append(ss);
+ assertEquals(0, ssb.getSpanStart(o));
+ assertEquals(4, ssb.getSpanEnd(o));
+ assertEquals(1, ssb.getSpans(0, 4, Object.class).length);
+
+ ssb.insert(0, ss);
+ assertEquals(4, ssb.getSpanStart(o));
+ assertEquals(8, ssb.getSpanEnd(o));
+ assertEquals(0, ssb.getSpans(0, 4, Object.class).length);
+ assertEquals(1, ssb.getSpans(4, 8, Object.class).length);
+ }
+
+ @SmallTest
+ public void testWrapParcel() {
+ SpannableString s = new SpannableString("Hello there world");
+ CharacterStyle mark = new StyleSpan(Typeface.BOLD);
+ s.setSpan(mark, 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ s.setSpan(CharacterStyle.wrap(mark), 3, 7,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ s.setSpan(new TextAppearanceSpan("mono", 0, -1, null, null), 7, 8,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ s.setSpan(CharacterStyle.wrap(new TypefaceSpan("mono")), 8, 9,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ Parcel p = Parcel.obtain();
+ TextUtils.writeToParcel(s, p, 0);
+ p.setDataPosition(0);
+
+ Spanned s2 = (Spanned) TextUtils.CHAR_SEQUENCE_CREATOR.
+ createFromParcel(p);
+ StyleSpan[] style;
+
+ style = s2.getSpans(1, 2, StyleSpan.class);
+ assertEquals(1, style.length);
+ assertEquals(1, s2.getSpanStart(style[0]));
+ assertEquals(2, s2.getSpanEnd(style[0]));
+
+ style = s2.getSpans(3, 7, StyleSpan.class);
+ assertEquals(1, style.length);
+ assertEquals(3, s2.getSpanStart(style[0]));
+ assertEquals(7, s2.getSpanEnd(style[0]));
+
+ TextAppearanceSpan[] appearance = s2.getSpans(7, 8,
+ TextAppearanceSpan.class);
+ assertEquals(1, appearance.length);
+ assertEquals(7, s2.getSpanStart(appearance[0]));
+ assertEquals(8, s2.getSpanEnd(appearance[0]));
+
+ TypefaceSpan[] tf = s2.getSpans(8, 9, TypefaceSpan.class);
+ assertEquals(1, tf.length);
+ assertEquals(8, s2.getSpanStart(tf[0]));
+ assertEquals(9, s2.getSpanEnd(tf[0]));
+ }
+
+ private void checkPriority(Spannable s) {
+ s.setSpan(new Object(), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE |
+ (5 << Spannable.SPAN_PRIORITY_SHIFT));
+ s.setSpan(new Object(), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE |
+ (10 << Spannable.SPAN_PRIORITY_SHIFT));
+ s.setSpan(new Object(), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE |
+ (0 << Spannable.SPAN_PRIORITY_SHIFT));
+ s.setSpan(new Object(), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE |
+ (15 << Spannable.SPAN_PRIORITY_SHIFT));
+ s.setSpan(new Object(), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE |
+ (3 << Spannable.SPAN_PRIORITY_SHIFT));
+ s.setSpan(new Object(), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE |
+ (6 << Spannable.SPAN_PRIORITY_SHIFT));
+ s.setSpan(new Object(), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE |
+ (0 << Spannable.SPAN_PRIORITY_SHIFT));
+
+ Object[] spans = s.getSpans(0, s.length(), Object.class);
+
+ for (int i = 0; i < spans.length - 1; i++) {
+ assertEquals((s.getSpanFlags(spans[i]) & Spanned.SPAN_PRIORITY) >=
+ (s.getSpanFlags(spans[i + 1]) & Spanned.SPAN_PRIORITY),
+ true);
+ }
+
+ mExpect = 0;
+
+ s.setSpan(new Watcher(2), 0, s.length(),
+ Spannable.SPAN_INCLUSIVE_INCLUSIVE |
+ (2 << Spannable.SPAN_PRIORITY_SHIFT));
+ s.setSpan(new Watcher(4), 0, s.length(),
+ Spannable.SPAN_INCLUSIVE_INCLUSIVE |
+ (4 << Spannable.SPAN_PRIORITY_SHIFT));
+ s.setSpan(new Watcher(1), 0, s.length(),
+ Spannable.SPAN_INCLUSIVE_INCLUSIVE |
+ (1 << Spannable.SPAN_PRIORITY_SHIFT));
+ s.setSpan(new Watcher(3), 0, s.length(),
+ Spannable.SPAN_INCLUSIVE_INCLUSIVE |
+ (3 << Spannable.SPAN_PRIORITY_SHIFT));
+
+ mExpect = 4;
+ s.setSpan(new Object(), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ assertEquals(mExpect, 0);
+ }
+
+ private void checkPriority2(SpannableStringBuilder ssb) {
+ checkPriority(ssb);
+
+ mExpect = 4;
+ ssb.insert(3, "something");
+ assertEquals(mExpect, 0);
+ }
+
+ private class Watcher implements SpanWatcher, TextWatcher {
+ private int mSequence;
+
+ public Watcher(int sequence) {
+ mSequence = sequence;
+ }
+
+ public void onSpanChanged(Spannable b, Object o, int s, int e,
+ int st, int en) { }
+ public void onSpanRemoved(Spannable b, Object o, int s, int e) { }
+
+ public void onSpanAdded(Spannable b, Object o, int s, int e) {
+ if (mExpect != 0) {
+ assertEquals(mSequence, mExpect);
+ mExpect = mSequence - 1;
+ }
+ }
+
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) { }
+ public void onTextChanged(CharSequence s, int start, int before,
+ int count) {
+ if (mExpect != 0) {
+ assertEquals(mSequence, mExpect);
+ mExpect = mSequence - 1;
+ }
+ }
+
+ public void afterTextChanged(Editable s) { }
+ }
+}
diff --git a/core/tests/coretests/src/android/text/TextLayoutTest.java b/core/tests/coretests/src/android/text/TextLayoutTest.java
new file mode 100644
index 0000000..6cf3000
--- /dev/null
+++ b/core/tests/coretests/src/android/text/TextLayoutTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2006 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 android.test.suitebuilder.annotation.SmallTest;
+import android.text.DynamicLayout;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import junit.framework.TestCase;
+
+
+public class TextLayoutTest extends TestCase {
+
+ protected String mString;
+ protected TextPaint mPaint;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ mString = "The quick brown fox";
+ mPaint = new TextPaint();
+ }
+
+ @SmallTest
+ public void testStaticLayout() throws Exception {
+ Layout l = new StaticLayout(mString, mPaint, 200,
+ Layout.Alignment.ALIGN_NORMAL, 1, 0,
+ true);
+ }
+
+ @SmallTest
+ public void testDynamicLayoutTest() throws Exception {
+ Layout l = new DynamicLayout(mString, mPaint, 200,
+ Layout.Alignment.ALIGN_NORMAL, 1, 0,
+ true);
+ }
+}
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
new file mode 100644
index 0000000..5b427be
--- /dev/null
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -0,0 +1,359 @@
+/*
+ * 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.text;
+
+import android.graphics.Paint;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.SpannedString;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.text.style.StyleSpan;
+import android.test.MoreAsserts;
+
+import com.android.common.Rfc822Validator;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
+import junit.framework.TestCase;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * TextUtilsTest tests {@link TextUtils}.
+ */
+public class TextUtilsTest extends TestCase {
+
+ @SmallTest
+ public void testBasic() throws Exception {
+ assertEquals("", TextUtils.concat());
+ assertEquals("foo", TextUtils.concat("foo"));
+ assertEquals("foobar", TextUtils.concat("foo", "bar"));
+ assertEquals("foobarbaz", TextUtils.concat("foo", "bar", "baz"));
+
+ SpannableString foo = new SpannableString("foo");
+ foo.setSpan("foo", 1, 2, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
+
+ SpannableString bar = new SpannableString("bar");
+ bar.setSpan("bar", 1, 2, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
+
+ SpannableString baz = new SpannableString("baz");
+ baz.setSpan("baz", 1, 2, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
+
+ assertEquals("foo", TextUtils.concat(foo).toString());
+ assertEquals("foobar", TextUtils.concat(foo, bar).toString());
+ assertEquals("foobarbaz", TextUtils.concat(foo, bar, baz).toString());
+
+ assertEquals(1, ((Spanned) TextUtils.concat(foo)).getSpanStart("foo"));
+
+ assertEquals(1, ((Spanned) TextUtils.concat(foo, bar)).getSpanStart("foo"));
+ assertEquals(4, ((Spanned) TextUtils.concat(foo, bar)).getSpanStart("bar"));
+
+ assertEquals(1, ((Spanned) TextUtils.concat(foo, bar, baz)).getSpanStart("foo"));
+ assertEquals(4, ((Spanned) TextUtils.concat(foo, bar, baz)).getSpanStart("bar"));
+ assertEquals(7, ((Spanned) TextUtils.concat(foo, bar, baz)).getSpanStart("baz"));
+
+ assertTrue(TextUtils.concat("foo", "bar") instanceof String);
+ assertTrue(TextUtils.concat(foo, bar) instanceof SpannedString);
+ }
+
+ @SmallTest
+ public void testTemplateString() throws Exception {
+ CharSequence result;
+
+ result = TextUtils.expandTemplate("This is a ^1 of the ^2 broadcast ^3.",
+ "test", "emergency", "system");
+ assertEquals("This is a test of the emergency broadcast system.",
+ result.toString());
+
+ result = TextUtils.expandTemplate("^^^1^^^2^3^a^1^^b^^^c",
+ "one", "two", "three");
+ assertEquals("^one^twothree^aone^b^^c",
+ result.toString());
+
+ result = TextUtils.expandTemplate("^");
+ assertEquals("^", result.toString());
+
+ result = TextUtils.expandTemplate("^^");
+ assertEquals("^", result.toString());
+
+ result = TextUtils.expandTemplate("^^^");
+ assertEquals("^^", result.toString());
+
+ result = TextUtils.expandTemplate("shorter ^1 values ^2.", "a", "");
+ assertEquals("shorter a values .", result.toString());
+
+ try {
+ TextUtils.expandTemplate("Only ^1 value given, but ^2 used.", "foo");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ try {
+ TextUtils.expandTemplate("^1 value given, and ^0 used.", "foo");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ result = TextUtils.expandTemplate("^1 value given, and ^9 used.",
+ "one", "two", "three", "four", "five",
+ "six", "seven", "eight", "nine");
+ assertEquals("one value given, and nine used.", result.toString());
+
+ try {
+ TextUtils.expandTemplate("^1 value given, and ^10 used.",
+ "one", "two", "three", "four", "five",
+ "six", "seven", "eight", "nine", "ten");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ // putting carets in the values: expansion is not recursive.
+
+ result = TextUtils.expandTemplate("^2", "foo", "^^");
+ assertEquals("^^", result.toString());
+
+ result = TextUtils.expandTemplate("^^2", "foo", "1");
+ assertEquals("^2", result.toString());
+
+ result = TextUtils.expandTemplate("^1", "value with ^2 in it", "foo");
+ assertEquals("value with ^2 in it", result.toString());
+ }
+
+ /** Fail unless text+spans contains a span 'spanName' with the given start and end. */
+ private void checkContains(Spanned text, String[] spans, String spanName,
+ int start, int end) throws Exception {
+ for (String i: spans) {
+ if (i.equals(spanName)) {
+ assertEquals(start, text.getSpanStart(i));
+ assertEquals(end, text.getSpanEnd(i));
+ return;
+ }
+ }
+ fail();
+ }
+
+ @SmallTest
+ public void testTemplateSpan() throws Exception {
+ SpannableString template;
+ Spanned result;
+ String[] spans;
+
+ // ordinary replacement
+
+ template = new SpannableString("a^1b");
+ template.setSpan("before", 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ template.setSpan("during", 1, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ template.setSpan("after", 3, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ template.setSpan("during+after", 1, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ result = (Spanned) TextUtils.expandTemplate(template, "foo");
+ assertEquals(5, result.length());
+ spans = result.getSpans(0, result.length(), String.class);
+
+ // value is one character longer, so span endpoints should change.
+ assertEquals(4, spans.length);
+ checkContains(result, spans, "before", 0, 1);
+ checkContains(result, spans, "during", 1, 4);
+ checkContains(result, spans, "after", 4, 5);
+ checkContains(result, spans, "during+after", 1, 5);
+
+
+ // replacement with empty string
+
+ result = (Spanned) TextUtils.expandTemplate(template, "");
+ assertEquals(2, result.length());
+ spans = result.getSpans(0, result.length(), String.class);
+
+ // the "during" span should disappear.
+ assertEquals(3, spans.length);
+ checkContains(result, spans, "before", 0, 1);
+ checkContains(result, spans, "after", 1, 2);
+ checkContains(result, spans, "during+after", 1, 2);
+ }
+
+ @SmallTest
+ public void testStringSplitterSimple() {
+ stringSplitterTestHelper("a,b,cde", new String[] {"a", "b", "cde"});
+ }
+
+ @SmallTest
+ public void testStringSplitterEmpty() {
+ stringSplitterTestHelper("", new String[] {});
+ }
+
+ @SmallTest
+ public void testStringSplitterWithLeadingEmptyString() {
+ stringSplitterTestHelper(",a,b,cde", new String[] {"", "a", "b", "cde"});
+ }
+
+ @SmallTest
+ public void testStringSplitterWithInternalEmptyString() {
+ stringSplitterTestHelper("a,b,,cde", new String[] {"a", "b", "", "cde"});
+ }
+
+ @SmallTest
+ public void testStringSplitterWithTrailingEmptyString() {
+ // A single trailing emtpy string should be ignored.
+ stringSplitterTestHelper("a,b,cde,", new String[] {"a", "b", "cde"});
+ }
+
+ private void stringSplitterTestHelper(String string, String[] expectedStrings) {
+ TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
+ splitter.setString(string);
+ List<String> strings = Lists.newArrayList();
+ for (String s : splitter) {
+ strings.add(s);
+ }
+ MoreAsserts.assertEquals(expectedStrings, strings.toArray(new String[]{}));
+ }
+
+ @SmallTest
+ public void testTrim() {
+ String[] strings = { "abc", " abc", " abc", "abc ", "abc ",
+ " abc ", " abc ", "\nabc\n", "\nabc", "abc\n" };
+
+ for (String s : strings) {
+ assertEquals(s.trim().length(), TextUtils.getTrimmedLength(s));
+ }
+ }
+
+ //==============================================================================================
+ // Email validator
+ //==============================================================================================
+
+ @SmallTest
+ public void testEmailValidator() {
+ Rfc822Validator validator = new Rfc822Validator("gmail.com");
+ String[] validEmails = new String[] {
+ "a@b.com", "a@b.fr", "a+b@c.com", "a@b.info",
+ };
+
+ for (String email : validEmails) {
+ assertTrue(email + " should be a valid email address", validator.isValid(email));
+ }
+
+ String[] invalidEmails = new String[] {
+ "a", "a@b", "a b", "a@b.12"
+ };
+
+ for (String email : invalidEmails) {
+ assertFalse(email + " should not be a valid email address", validator.isValid(email));
+ }
+
+ Map<String, String> fixes = Maps.newHashMap();
+ fixes.put("a", "<a@gmail.com>");
+ fixes.put("a b", "<ab@gmail.com>");
+ fixes.put("a@b", "<a@b>");
+
+ for (Map.Entry<String, String> e : fixes.entrySet()) {
+ assertEquals(e.getValue(), validator.fixText(e.getKey()).toString());
+ }
+ }
+
+ @LargeTest
+ public void testEllipsize() {
+ CharSequence s1 = "The quick brown fox jumps over \u00FEhe lazy dog.";
+ CharSequence s2 = new Wrapper(s1);
+ Spannable s3 = new SpannableString(s1);
+ s3.setSpan(new StyleSpan(0), 5, 10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ TextPaint p = new TextPaint();
+ p.setFlags(p.getFlags() & ~p.DEV_KERN_TEXT_FLAG);
+
+ for (int i = 0; i < 100; i++) {
+ for (int j = 0; j < 3; j++) {
+ TextUtils.TruncateAt kind = null;
+
+ switch (j) {
+ case 0:
+ kind = TextUtils.TruncateAt.START;
+ break;
+
+ case 1:
+ kind = TextUtils.TruncateAt.END;
+ break;
+
+ case 2:
+ kind = TextUtils.TruncateAt.MIDDLE;
+ break;
+ }
+
+ String out1 = TextUtils.ellipsize(s1, p, i, kind).toString();
+ String out2 = TextUtils.ellipsize(s2, p, i, kind).toString();
+ String out3 = TextUtils.ellipsize(s3, p, i, kind).toString();
+
+ String keep1 = TextUtils.ellipsize(s1, p, i, kind, true, null).toString();
+ String keep2 = TextUtils.ellipsize(s2, p, i, kind, true, null).toString();
+ String keep3 = TextUtils.ellipsize(s3, p, i, kind, true, null).toString();
+
+ String trim1 = keep1.replace("\uFEFF", "");
+
+ // Are all normal output strings identical?
+ assertEquals("wid " + i + " pass " + j, out1, out2);
+ assertEquals("wid " + i + " pass " + j, out2, out3);
+
+ // Are preserved output strings identical?
+ assertEquals("wid " + i + " pass " + j, keep1, keep2);
+ assertEquals("wid " + i + " pass " + j, keep2, keep3);
+
+ // Does trimming padding from preserved yield normal?
+ assertEquals("wid " + i + " pass " + j, out1, trim1);
+
+ // Did preserved output strings preserve length?
+ assertEquals("wid " + i + " pass " + j, keep1.length(), s1.length());
+
+ // Does the output string actually fit in the space?
+ assertTrue("wid " + i + " pass " + j, p.measureText(out1) <= i);
+
+ // Is the padded output the same width as trimmed output?
+ assertTrue("wid " + i + " pass " + j, p.measureText(keep1) == p.measureText(out1));
+ }
+ }
+ }
+
+ /**
+ * CharSequence wrapper for testing the cases where text is copied into
+ * a char array instead of working from a String or a Spanned.
+ */
+ private static class Wrapper implements CharSequence {
+ private CharSequence mString;
+
+ public Wrapper(CharSequence s) {
+ mString = s;
+ }
+
+ public int length() {
+ return mString.length();
+ }
+
+ public char charAt(int off) {
+ return mString.charAt(off);
+ }
+
+ public String toString() {
+ return mString.toString();
+ }
+
+ public CharSequence subSequence(int start, int end) {
+ return new Wrapper(mString.subSequence(start, end));
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/text/format/TimeTest.java b/core/tests/coretests/src/android/text/format/TimeTest.java
new file mode 100644
index 0000000..489f58b
--- /dev/null
+++ b/core/tests/coretests/src/android/text/format/TimeTest.java
@@ -0,0 +1,608 @@
+/*
+ * Copyright (C) 2006 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.format;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.text.format.Time;
+import android.util.Log;
+import android.util.TimeFormatException;
+
+import junit.framework.TestCase;
+
+public class TimeTest extends TestCase {
+
+ @SmallTest
+ public void testNormalize0() throws Exception {
+ Time t = new Time(Time.TIMEZONE_UTC);
+ t.parse("20060432T010203");
+ t.normalize(false /* use isDst */);
+// System.out.println("got: " + t.year + '-'
+// + t.month + '-' + t.monthDay
+// + ' ' + t.hour + ':' + t.minute
+// + ':' + t.second
+// + "( " + t.isDst + ',' + t.gmtoff
+// + ',' + t.weekDay
+// + ',' + t.yearDay + ')');
+ }
+
+ private static class DateTest {
+ public int year1;
+ public int month1;
+ public int day1;
+ public int hour1;
+ public int minute1;
+ public int dst1;
+
+ public int offset;
+
+ public int year2;
+ public int month2;
+ public int day2;
+ public int hour2;
+ public int minute2;
+ public int dst2;
+
+ public DateTest(int year1, int month1, int day1, int hour1, int minute1, int dst1,
+ int offset, int year2, int month2, int day2, int hour2, int minute2,
+ int dst2) {
+ this.year1 = year1;
+ this.month1 = month1;
+ this.day1 = day1;
+ this.hour1 = hour1;
+ this.minute1 = minute1;
+ this.dst1 = dst1;
+ this.offset = offset;
+ this.year2 = year2;
+ this.month2 = month2;
+ this.day2 = day2;
+ this.hour2 = hour2;
+ this.minute2 = minute2;
+ this.dst2 = dst2;
+ }
+
+ public DateTest(int year1, int month1, int day1, int hour1, int minute1,
+ int offset, int year2, int month2, int day2, int hour2, int minute2) {
+ this.year1 = year1;
+ this.month1 = month1;
+ this.day1 = day1;
+ this.hour1 = hour1;
+ this.minute1 = minute1;
+ this.dst1 = -1;
+ this.offset = offset;
+ this.year2 = year2;
+ this.month2 = month2;
+ this.day2 = day2;
+ this.hour2 = hour2;
+ this.minute2 = minute2;
+ this.dst2 = -1;
+ }
+ }
+
+ // These tests assume that DST changes on Nov 4, 2007 at 2am (to 1am).
+
+ // The "offset" field in "dayTests" represents days.
+ // Use normalize(true) with these tests to change the date by 1 day.
+ private DateTest[] dayTests = {
+ // The month numbers are 0-relative, so Jan=0, Feb=1,...Dec=11
+
+ // Nov 4, 12am + 0 day = Nov 4, 12am
+ // Nov 5, 12am + 0 day = Nov 5, 12am
+ new DateTest(2007, 10, 4, 0, 0, 0, 2007, 10, 4, 0, 0),
+ new DateTest(2007, 10, 5, 0, 0, 0, 2007, 10, 5, 0, 0),
+
+ // Nov 3, 12am + 1 day = Nov 4, 12am
+ // Nov 4, 12am + 1 day = Nov 5, 12am
+ // Nov 5, 12am + 1 day = Nov 6, 12am
+ new DateTest(2007, 10, 3, 0, 0, 1, 2007, 10, 4, 0, 0),
+ new DateTest(2007, 10, 4, 0, 0, 1, 2007, 10, 5, 0, 0),
+ new DateTest(2007, 10, 5, 0, 0, 1, 2007, 10, 6, 0, 0),
+
+ // Nov 3, 1am + 1 day = Nov 4, 1am
+ // Nov 4, 1am + 1 day = Nov 5, 1am
+ // Nov 5, 1am + 1 day = Nov 6, 1am
+ new DateTest(2007, 10, 3, 1, 0, 1, 2007, 10, 4, 1, 0),
+ new DateTest(2007, 10, 4, 1, 0, 1, 2007, 10, 5, 1, 0),
+ new DateTest(2007, 10, 5, 1, 0, 1, 2007, 10, 6, 1, 0),
+
+ // Nov 3, 2am + 1 day = Nov 4, 2am
+ // Nov 4, 2am + 1 day = Nov 5, 2am
+ // Nov 5, 2am + 1 day = Nov 6, 2am
+ new DateTest(2007, 10, 3, 2, 0, 1, 2007, 10, 4, 2, 0),
+ new DateTest(2007, 10, 4, 2, 0, 1, 2007, 10, 5, 2, 0),
+ new DateTest(2007, 10, 5, 2, 0, 1, 2007, 10, 6, 2, 0),
+ };
+
+ // The "offset" field in "minuteTests" represents minutes.
+ // Use normalize(false) with these tests.
+ private DateTest[] minuteTests = {
+ // The month numbers are 0-relative, so Jan=0, Feb=1,...Dec=11
+
+ // Nov 4, 12am + 0 minutes = Nov 4, 12am
+ // Nov 5, 12am + 0 minutes = Nov 5, 12am
+ new DateTest(2007, 10, 4, 0, 0, 0, 2007, 10, 4, 0, 0),
+ new DateTest(2007, 10, 5, 0, 0, 0, 2007, 10, 5, 0, 0),
+
+ // Nov 3, 12am + 60 minutes = Nov 3, 1am
+ // Nov 4, 12am + 60 minutes = Nov 4, 1am
+ // Nov 5, 12am + 60 minutes = Nov 5, 1am
+ new DateTest(2007, 10, 3, 0, 0, 60, 2007, 10, 3, 1, 0),
+ new DateTest(2007, 10, 4, 0, 0, 60, 2007, 10, 4, 1, 0),
+ new DateTest(2007, 10, 5, 0, 0, 60, 2007, 10, 5, 1, 0),
+
+ // Nov 3, 1am + 60 minutes = Nov 3, 2am
+ // Nov 4, 1am (PDT) + 30 minutes = Nov 4, 1:30am (PDT)
+ // Nov 4, 1am (PDT) + 60 minutes = Nov 4, 1am (PST)
+ new DateTest(2007, 10, 3, 1, 0, 60, 2007, 10, 3, 2, 0),
+ new DateTest(2007, 10, 4, 1, 0, 1, 30, 2007, 10, 4, 1, 30, 1),
+ new DateTest(2007, 10, 4, 1, 0, 1, 60, 2007, 10, 4, 1, 0, 0),
+
+ // Nov 4, 1:30am (PDT) + 15 minutes = Nov 4, 1:45am (PDT)
+ // Nov 4, 1:30am (PDT) + 30 minutes = Nov 4, 1:00am (PST)
+ // Nov 4, 1:30am (PDT) + 60 minutes = Nov 4, 1:30am (PST)
+ new DateTest(2007, 10, 4, 1, 30, 1, 15, 2007, 10, 4, 1, 45, 1),
+ new DateTest(2007, 10, 4, 1, 30, 1, 30, 2007, 10, 4, 1, 0, 0),
+ new DateTest(2007, 10, 4, 1, 30, 1, 60, 2007, 10, 4, 1, 30, 0),
+
+ // Nov 4, 1:30am (PST) + 15 minutes = Nov 4, 1:45am (PST)
+ // Nov 4, 1:30am (PST) + 30 minutes = Nov 4, 2:00am (PST)
+ // Nov 5, 1am + 60 minutes = Nov 5, 2am
+ new DateTest(2007, 10, 4, 1, 30, 0, 15, 2007, 10, 4, 1, 45, 0),
+ new DateTest(2007, 10, 4, 1, 30, 0, 30, 2007, 10, 4, 2, 0, 0),
+ new DateTest(2007, 10, 5, 1, 0, 60, 2007, 10, 5, 2, 0),
+
+ // Nov 3, 2am + 60 minutes = Nov 3, 3am
+ // Nov 4, 2am + 30 minutes = Nov 4, 2:30am
+ // Nov 4, 2am + 60 minutes = Nov 4, 3am
+ // Nov 5, 2am + 60 minutes = Nov 5, 3am
+ new DateTest(2007, 10, 3, 2, 0, 60, 2007, 10, 3, 3, 0),
+ new DateTest(2007, 10, 4, 2, 0, 30, 2007, 10, 4, 2, 30),
+ new DateTest(2007, 10, 4, 2, 0, 60, 2007, 10, 4, 3, 0),
+ new DateTest(2007, 10, 5, 2, 0, 60, 2007, 10, 5, 3, 0),
+ };
+
+ @SmallTest
+ public void testNormalize1() throws Exception {
+ Time local = new Time("America/Los_Angeles");
+
+ int len = dayTests.length;
+ for (int index = 0; index < len; index++) {
+ DateTest test = dayTests[index];
+ local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1);
+ // call normalize() to make sure that isDst is set
+ local.normalize(false /* use isDst */);
+ local.monthDay += test.offset;
+ local.normalize(true /* ignore isDst */);
+ if (local.year != test.year2 || local.month != test.month2
+ || local.monthDay != test.day2 || local.hour != test.hour2
+ || local.minute != test.minute2) {
+ String expectedTime = String.format("%d-%02d-%02d %02d:%02d",
+ test.year2, test.month2, test.day2, test.hour2, test.minute2);
+ String actualTime = String.format("%d-%02d-%02d %02d:%02d",
+ local.year, local.month, local.monthDay, local.hour, local.minute);
+ throw new RuntimeException(
+ "day test index " + index + ", normalize(): expected local " + expectedTime
+ + " got: " + actualTime);
+ }
+
+ local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1);
+ // call normalize() to make sure that isDst is set
+ local.normalize(false /* use isDst */);
+ local.monthDay += test.offset;
+ long millis = local.toMillis(true /* ignore isDst */);
+ local.set(millis);
+ if (local.year != test.year2 || local.month != test.month2
+ || local.monthDay != test.day2 || local.hour != test.hour2
+ || local.minute != test.minute2) {
+ String expectedTime = String.format("%d-%02d-%02d %02d:%02d",
+ test.year2, test.month2, test.day2, test.hour2, test.minute2);
+ String actualTime = String.format("%d-%02d-%02d %02d:%02d",
+ local.year, local.month, local.monthDay, local.hour, local.minute);
+ throw new RuntimeException(
+ "day test index " + index + ", toMillis(): expected local " + expectedTime
+ + " got: " + actualTime);
+ }
+ }
+
+ len = minuteTests.length;
+ for (int index = 0; index < len; index++) {
+ DateTest test = minuteTests[index];
+ local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1);
+ local.isDst = test.dst1;
+ // call normalize() to make sure that isDst is set
+ local.normalize(false /* use isDst */);
+ if (test.dst2 == -1) test.dst2 = local.isDst;
+ local.minute += test.offset;
+ local.normalize(false /* use isDst */);
+ if (local.year != test.year2 || local.month != test.month2
+ || local.monthDay != test.day2 || local.hour != test.hour2
+ || local.minute != test.minute2 || local.isDst != test.dst2) {
+ String expectedTime = String.format("%d-%02d-%02d %02d:%02d isDst: %d",
+ test.year2, test.month2, test.day2, test.hour2, test.minute2,
+ test.dst2);
+ String actualTime = String.format("%d-%02d-%02d %02d:%02d isDst: %d",
+ local.year, local.month, local.monthDay, local.hour, local.minute,
+ local.isDst);
+ throw new RuntimeException(
+ "minute test index " + index + ", normalize(): expected local " + expectedTime
+ + " got: " + actualTime);
+ }
+
+ local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1);
+ local.isDst = test.dst1;
+ // call normalize() to make sure that isDst is set
+ local.normalize(false /* use isDst */);
+ if (test.dst2 == -1) test.dst2 = local.isDst;
+ local.minute += test.offset;
+ long millis = local.toMillis(false /* use isDst */);
+ local.set(millis);
+ if (local.year != test.year2 || local.month != test.month2
+ || local.monthDay != test.day2 || local.hour != test.hour2
+ || local.minute != test.minute2 || local.isDst != test.dst2) {
+ String expectedTime = String.format("%d-%02d-%02d %02d:%02d isDst: %d",
+ test.year2, test.month2, test.day2, test.hour2, test.minute2,
+ test.dst2);
+ String actualTime = String.format("%d-%02d-%02d %02d:%02d isDst: %d",
+ local.year, local.month, local.monthDay, local.hour, local.minute,
+ local.isDst);
+ throw new RuntimeException(
+ "minute test index " + index + ", toMillis(): expected local " + expectedTime
+ + " got: " + actualTime);
+ }
+ }
+ }
+
+ @SmallTest
+ public void testSwitchTimezone0() throws Exception {
+ Time t = new Time(Time.TIMEZONE_UTC);
+ t.parse("20061005T120000");
+ t.switchTimezone("America/Los_Angeles");
+ // System.out.println("got: " + t);
+ }
+
+ @SmallTest
+ public void testCtor0() throws Exception {
+ Time t = new Time(Time.TIMEZONE_UTC);
+ assertEquals(Time.TIMEZONE_UTC, t.timezone);
+ }
+
+ @SmallTest
+ public void testGetActualMaximum0() throws Exception {
+ Time t = new Time(Time.TIMEZONE_UTC);
+ int r = t.getActualMaximum(Time.SECOND);
+ // System.out.println("r=" + r);
+ }
+
+ @SmallTest
+ public void testClear0() throws Exception {
+ Time t = new Time(Time.TIMEZONE_UTC);
+ t.clear(Time.TIMEZONE_UTC);
+ }
+
+ @SmallTest
+ public void testCompare0() throws Exception {
+ Time a = new Time(Time.TIMEZONE_UTC);
+ Time b = new Time("America/Los_Angeles");
+ int r = Time.compare(a, b);
+ // System.out.println("r=" + r);
+ }
+
+ @SmallTest
+ public void testFormat0() throws Exception {
+ Time t = new Time(Time.TIMEZONE_UTC);
+ String r = t.format("%Y%m%dT%H%M%S");
+ // System.out.println("r='" + r + "'");
+ }
+
+ @SmallTest
+ public void testToString0() throws Exception {
+ Time t = new Time(Time.TIMEZONE_UTC);
+ String r = t.toString();
+ // System.out.println("r='" + r + "'");
+ }
+
+ @SmallTest
+ public void testGetCurrentTimezone0() throws Exception {
+ String r = Time.getCurrentTimezone();
+ // System.out.println("r='" + r + "'");
+ }
+
+ @SmallTest
+ public void testSetToNow0() throws Exception {
+ Time t = new Time(Time.TIMEZONE_UTC);
+ t.setToNow();
+ // System.out.println("t=" + t);
+ }
+
+ @SmallTest
+ public void testMillis0() throws Exception {
+ Time t = new Time(Time.TIMEZONE_UTC);
+ t.set(0, 0, 0, 1, 1, 2006);
+ long r = t.toMillis(true /* ignore isDst */);
+ // System.out.println("r=" + r);
+ t.set(1, 0, 0, 1, 1, 2006);
+ r = t.toMillis(true /* ignore isDst */);
+ // System.out.println("r=" + r);
+ }
+
+ @SmallTest
+ public void testMillis1() throws Exception {
+ Time t = new Time(Time.TIMEZONE_UTC);
+ t.set(1, 0, 0, 1, 0, 1970);
+ long r = t.toMillis(true /* ignore isDst */);
+ // System.out.println("r=" + r);
+ }
+
+ @SmallTest
+ public void testParse0() throws Exception {
+ Time t = new Time(Time.TIMEZONE_UTC);
+ t.parse("12345678T901234");
+ // System.out.println("t=" + t);
+ }
+
+ @SmallTest
+ public void testParse33390() throws Exception {
+ Time t = new Time(Time.TIMEZONE_UTC);
+
+ t.parse3339("1980-05-23");
+ if (!t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23) {
+ fail("Did not parse all-day date correctly");
+ }
+
+ t.parse3339("1980-05-23T09:50:50");
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
+ t.hour != 9 || t.minute != 50 || t.second != 50 ||
+ t.gmtoff != 0) {
+ fail("Did not parse timezone-offset-less date correctly");
+ }
+
+ t.parse3339("1980-05-23T09:50:50Z");
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
+ t.hour != 9 || t.minute != 50 || t.second != 50 ||
+ t.gmtoff != 0) {
+ fail("Did not parse UTC date correctly");
+ }
+
+ t.parse3339("1980-05-23T09:50:50.0Z");
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
+ t.hour != 9 || t.minute != 50 || t.second != 50 ||
+ t.gmtoff != 0) {
+ fail("Did not parse UTC date correctly");
+ }
+
+ t.parse3339("1980-05-23T09:50:50.12Z");
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
+ t.hour != 9 || t.minute != 50 || t.second != 50 ||
+ t.gmtoff != 0) {
+ fail("Did not parse UTC date correctly");
+ }
+
+ t.parse3339("1980-05-23T09:50:50.123Z");
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
+ t.hour != 9 || t.minute != 50 || t.second != 50 ||
+ t.gmtoff != 0) {
+ fail("Did not parse UTC date correctly");
+ }
+
+ // The time should be normalized to UTC
+ t.parse3339("1980-05-23T09:50:50-01:05");
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
+ t.hour != 10 || t.minute != 55 || t.second != 50 ||
+ t.gmtoff != 0) {
+ fail("Did not parse timezone-offset date correctly");
+ }
+
+ // The time should be normalized to UTC
+ t.parse3339("1980-05-23T09:50:50.123-01:05");
+ if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
+ t.hour != 10 || t.minute != 55 || t.second != 50 ||
+ t.gmtoff != 0) {
+ fail("Did not parse timezone-offset date correctly");
+ }
+
+ try {
+ t.parse3339("1980");
+ fail("Did not throw error on truncated input length");
+ } catch (TimeFormatException e) {
+ // Successful
+ }
+
+ try {
+ t.parse3339("1980-05-23T09:50:50.123+");
+ fail("Did not throw error on truncated timezone offset");
+ } catch (TimeFormatException e1) {
+ // Successful
+ }
+
+ try {
+ t.parse3339("1980-05-23T09:50:50.123+05:0");
+ fail("Did not throw error on truncated timezone offset");
+ } catch (TimeFormatException e1) {
+ // Successful
+ }
+ }
+
+ @SmallTest
+ public void testSet0() throws Exception {
+ Time t = new Time(Time.TIMEZONE_UTC);
+ t.set(1000L);
+ // System.out.println("t.year=" + t.year);
+ // System.out.println("t=" + t);
+ t.set(2000L);
+ // System.out.println("t=" + t);
+ t.set(1000L * 60);
+ // System.out.println("t=" + t);
+ t.set((1000L * 60 * 60 * 24) + 1000L);
+ // System.out.println("t=" + t);
+ }
+
+ @SmallTest
+ public void testSet1() throws Exception {
+ Time t = new Time(Time.TIMEZONE_UTC);
+ t.set(1, 2, 3, 4, 5, 6);
+ // System.out.println("t=" + t);
+ }
+
+ // Timezones that cover the world. Some GMT offsets occur more than
+ // once in case some cities decide to change their GMT offset.
+ private static final String[] mTimeZones = {
+ "Pacific/Kiritimati",
+ "Pacific/Enderbury",
+ "Pacific/Fiji",
+ "Antarctica/South_Pole",
+ "Pacific/Norfolk",
+ "Pacific/Ponape",
+ "Asia/Magadan",
+ "Australia/Lord_Howe",
+ "Australia/Sydney",
+ "Australia/Adelaide",
+ "Asia/Tokyo",
+ "Asia/Seoul",
+ "Asia/Taipei",
+ "Asia/Singapore",
+ "Asia/Hong_Kong",
+ "Asia/Saigon",
+ "Asia/Bangkok",
+ "Indian/Cocos",
+ "Asia/Rangoon",
+ "Asia/Omsk",
+ "Antarctica/Mawson",
+ "Asia/Colombo",
+ "Asia/Calcutta",
+ "Asia/Oral",
+ "Asia/Kabul",
+ "Asia/Dubai",
+ "Asia/Tehran",
+ "Europe/Moscow",
+ "Asia/Baghdad",
+ "Africa/Mogadishu",
+ "Europe/Athens",
+ "Africa/Cairo",
+ "Europe/Rome",
+ "Europe/Berlin",
+ "Europe/Amsterdam",
+ "Africa/Tunis",
+ "Europe/London",
+ "Europe/Dublin",
+ "Atlantic/St_Helena",
+ "Africa/Monrovia",
+ "Africa/Accra",
+ "Atlantic/Azores",
+ "Atlantic/South_Georgia",
+ "America/Noronha",
+ "America/Sao_Paulo",
+ "America/Cayenne",
+ "America/St_Johns",
+ "America/Puerto_Rico",
+ "America/Aruba",
+ "America/New_York",
+ "America/Chicago",
+ "America/Denver",
+ "America/Los_Angeles",
+ "America/Anchorage",
+ "Pacific/Marquesas",
+ "America/Adak",
+ "Pacific/Honolulu",
+ "Pacific/Midway",
+ };
+
+ @Suppress
+ public void disableTestGetJulianDay() throws Exception {
+ Time time = new Time();
+
+ // For each day of the year, and for each timezone, get the Julian
+ // day for 12am and then check that if we change the time we get the
+ // same Julian day.
+ for (int monthDay = 1; monthDay <= 366; monthDay++) {
+ for (int zoneIndex = 0; zoneIndex < mTimeZones.length; zoneIndex++) {
+ // We leave the "month" as zero because we are changing the
+ // "monthDay" from 1 to 366. The call to normalize() will
+ // then change the "month" (but we don't really care).
+ time.set(0, 0, 0, monthDay, 0, 2008);
+ time.timezone = mTimeZones[zoneIndex];
+ long millis = time.normalize(true);
+ if (zoneIndex == 0) {
+ Log.i("TimeTest", time.format("%B %d, %Y"));
+ }
+
+ // This is the Julian day for 12am for this day of the year
+ int julianDay = Time.getJulianDay(millis, time.gmtoff);
+
+ // Change the time during the day and check that we get the same
+ // Julian day.
+ for (int hour = 0; hour < 24; hour++) {
+ for (int minute = 0; minute < 60; minute += 15) {
+ time.set(0, minute, hour, monthDay, 0, 2008);
+ millis = time.normalize(true);
+ int day = Time.getJulianDay(millis, time.gmtoff);
+ if (day != julianDay) {
+ Log.e("TimeTest", "Julian day: " + day + " at time "
+ + time.hour + ":" + time.minute
+ + " != today's Julian day: " + julianDay
+ + " timezone: " + time.timezone);
+ }
+ assertEquals(day, julianDay);
+ }
+ }
+ }
+ }
+ }
+
+ @Suppress
+ public void disableTestSetJulianDay() throws Exception {
+ Time time = new Time();
+
+ // For each day of the year in 2008, and for each timezone,
+ // test that we can set the Julian day correctly.
+ for (int monthDay = 1; monthDay <= 366; monthDay++) {
+ for (int zoneIndex = 0; zoneIndex < mTimeZones.length; zoneIndex++) {
+ // We leave the "month" as zero because we are changing the
+ // "monthDay" from 1 to 366. The call to normalize() will
+ // then change the "month" (but we don't really care).
+ time.set(0, 0, 0, monthDay, 0, 2008);
+ time.timezone = mTimeZones[zoneIndex];
+ long millis = time.normalize(true);
+ if (zoneIndex == 0) {
+ Log.i("TimeTest", time.format("%B %d, %Y"));
+ }
+ int julianDay = Time.getJulianDay(millis, time.gmtoff);
+
+ time.setJulianDay(julianDay);
+
+ // Some places change daylight saving time at 12am and so there
+ // is no 12am on some days in some timezones. In those cases,
+ // the time is set to 1am.
+ // Examples: Africa/Cairo on April 25, 2008
+ // America/Sao_Paulo on October 12, 2008
+ // Atlantic/Azores on March 30, 2008
+ assertTrue(time.hour == 0 || time.hour == 1);
+ assertEquals(0, time.minute);
+ assertEquals(0, time.second);
+
+ millis = time.toMillis(false);
+ int day = Time.getJulianDay(millis, time.gmtoff);
+ if (day != julianDay) {
+ Log.i("TimeTest", "Error: gmtoff " + (time.gmtoff / 3600.0)
+ + " day " + julianDay
+ + " millis " + millis
+ + " " + time.format("%B %d, %Y") + " " + time.timezone);
+ }
+ assertEquals(day, julianDay);
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/util/LogTest.java b/core/tests/coretests/src/android/util/LogTest.java
new file mode 100644
index 0000000..41947d7
--- /dev/null
+++ b/core/tests/coretests/src/android/util/LogTest.java
@@ -0,0 +1,152 @@
+package android.util;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import android.os.SystemProperties;
+import android.test.PerformanceTestCase;
+import android.test.suitebuilder.annotation.Suppress;
+import android.util.Log;
+
+//This is an empty TestCase.
+@Suppress
+public class LogTest extends TestCase {
+ private static final String PROPERTY_TAG = "log.tag.LogTest";
+ private static final String LOG_TAG = "LogTest";
+
+
+ // TODO: remove this test once we uncomment out the following test.
+ public void testLogTestDummy() {
+ return;
+ }
+
+
+ /* TODO: This test is commented out because we will not be able to set properities. Fix the test.
+ public void testIsLoggable() {
+ // First clear any SystemProperty setting for our test key.
+ SystemProperties.set(PROPERTY_TAG, null);
+
+ String value = SystemProperties.get(PROPERTY_TAG);
+ Assert.assertTrue(value == null || value.length() == 0);
+
+ // Check to make sure that all levels expect for INFO, WARN, ERROR, and ASSERT are loggable.
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.VERBOSE));
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.DEBUG));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.INFO));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.WARN));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ERROR));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ASSERT));
+
+ // Set the log level to be VERBOSE for this tag.
+ SystemProperties.set(PROPERTY_TAG, "VERBOSE");
+
+ // Test to make sure all log levels >= VERBOSE are loggable.
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.VERBOSE));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.DEBUG));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.INFO));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.WARN));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ERROR));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ASSERT));
+
+ // Set the log level to be DEBUG for this tag.
+ SystemProperties.set(PROPERTY_TAG, "DEBUG");
+
+ // Test to make sure all log levels >= DEBUG are loggable.
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.VERBOSE));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.DEBUG));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.INFO));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.WARN));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ERROR));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ASSERT));
+
+ // Set the log level to be INFO for this tag.
+ SystemProperties.set(PROPERTY_TAG, "INFO");
+
+ // Test to make sure all log levels >= INFO are loggable.
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.VERBOSE));
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.DEBUG));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.INFO));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.WARN));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ERROR));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ASSERT));
+
+ // Set the log level to be WARN for this tag.
+ SystemProperties.set(PROPERTY_TAG, "WARN");
+
+ // Test to make sure all log levels >= WARN are loggable.
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.VERBOSE));
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.DEBUG));
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.INFO));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.WARN));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ERROR));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ASSERT));
+
+ // Set the log level to be ERROR for this tag.
+ SystemProperties.set(PROPERTY_TAG, "ERROR");
+
+ // Test to make sure all log levels >= ERROR are loggable.
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.VERBOSE));
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.DEBUG));
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.INFO));
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.WARN));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ERROR));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ASSERT));
+
+ // Set the log level to be ASSERT for this tag.
+ SystemProperties.set(PROPERTY_TAG, "ASSERT");
+
+ // Test to make sure all log levels >= ASSERT are loggable.
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.VERBOSE));
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.DEBUG));
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.INFO));
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.WARN));
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.ERROR));
+ Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ASSERT));
+
+ // Set the log level to be SUPPRESS for this tag.
+ SystemProperties.set(PROPERTY_TAG, "SUPPRESS");
+
+ // Test to make sure all log levels >= ASSERT are loggable.
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.VERBOSE));
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.DEBUG));
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.INFO));
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.WARN));
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.ERROR));
+ Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.ASSERT));
+ }
+ */
+
+ public static class PerformanceTest extends TestCase implements PerformanceTestCase {
+ private static final int ITERATIONS = 1000;
+
+ @Override
+ public void setUp() {
+ SystemProperties.set(LOG_TAG, "VERBOSE");
+ }
+
+ public boolean isPerformanceOnly() {
+ return true;
+ }
+
+ public int startPerformance(PerformanceTestCase.Intermediates intermediates) {
+ intermediates.setInternalIterations(ITERATIONS * 10);
+ return 0;
+ }
+
+ public void testIsLoggable() {
+ boolean canLog = false;
+ for (int i = ITERATIONS - 1; i >= 0; i--) {
+ canLog = Log.isLoggable(LOG_TAG, Log.VERBOSE);
+ canLog = Log.isLoggable(LOG_TAG, Log.VERBOSE);
+ canLog = Log.isLoggable(LOG_TAG, Log.VERBOSE);
+ canLog = Log.isLoggable(LOG_TAG, Log.VERBOSE);
+ canLog = Log.isLoggable(LOG_TAG, Log.VERBOSE);
+ canLog = Log.isLoggable(LOG_TAG, Log.VERBOSE);
+ canLog = Log.isLoggable(LOG_TAG, Log.VERBOSE);
+ canLog = Log.isLoggable(LOG_TAG, Log.VERBOSE);
+ canLog = Log.isLoggable(LOG_TAG, Log.VERBOSE);
+ canLog = Log.isLoggable(LOG_TAG, Log.VERBOSE);
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/util/TimeUtilsTest.java b/core/tests/coretests/src/android/util/TimeUtilsTest.java
new file mode 100644
index 0000000..65a6078
--- /dev/null
+++ b/core/tests/coretests/src/android/util/TimeUtilsTest.java
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import junit.framework.TestCase;
+
+import android.util.TimeUtils;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+
+/**
+ * TimeUtilsTest tests the time zone guesser.
+ */
+public class TimeUtilsTest extends TestCase {
+ public void testMainstream() throws Exception {
+ String[] mainstream = new String[] {
+ "America/New_York", // Eastern
+ "America/Chicago", // Central
+ "America/Denver", // Mountain
+ "America/Los_Angeles", // Pacific
+ "America/Anchorage", // Alaska
+ "Pacific/Honolulu", // Hawaii, no DST
+ };
+
+ for (String name : mainstream) {
+ TimeZone tz = TimeZone.getTimeZone(name);
+ Calendar c = Calendar.getInstance(tz);
+ TimeZone guess;
+
+ c.set(2008, Calendar.OCTOBER, 20, 12, 00, 00);
+ guess = guess(c, "us");
+ assertEquals(name, guess.getID());
+
+ c.set(2009, Calendar.JANUARY, 20, 12, 00, 00);
+ guess = guess(c, "us");
+ assertEquals(name, guess.getID());
+ }
+ }
+
+ public void testWeird() throws Exception {
+ String[] weird = new String[] {
+ "America/Phoenix", // Mountain, no DST
+ "America/Adak", // Same as Hawaii, but with DST
+ };
+
+ for (String name : weird) {
+ TimeZone tz = TimeZone.getTimeZone(name);
+ Calendar c = Calendar.getInstance(tz);
+ TimeZone guess;
+
+ c.set(2008, Calendar.OCTOBER, 20, 12, 00, 00);
+ guess = guess(c, "us");
+ assertEquals(name, guess.getID());
+ }
+ }
+
+ public void testOld() throws Exception {
+ String[] old = new String[] {
+ "America/Indiana/Indianapolis", // Eastern, formerly no DST
+ };
+
+ for (String name : old) {
+ TimeZone tz = TimeZone.getTimeZone(name);
+ Calendar c = Calendar.getInstance(tz);
+ TimeZone guess;
+
+ c.set(2005, Calendar.OCTOBER, 20, 12, 00, 00);
+ guess = guess(c, "us");
+ assertEquals(name, guess.getID());
+ }
+ }
+
+ public void testWorld() throws Exception {
+ String[] world = new String[] {
+ "ad", "Europe/Andorra",
+ "ae", "Asia/Dubai",
+ "af", "Asia/Kabul",
+ "ag", "America/Antigua",
+ "ai", "America/Anguilla",
+ "al", "Europe/Tirane",
+ "am", "Asia/Yerevan",
+ "an", "America/Curacao",
+ "ao", "Africa/Luanda",
+ "aq", "Antarctica/McMurdo",
+ "aq", "Antarctica/DumontDUrville",
+ "aq", "Antarctica/Casey",
+ "aq", "Antarctica/Davis",
+ "aq", "Antarctica/Mawson",
+ "aq", "Antarctica/Syowa",
+ "aq", "Antarctica/Rothera",
+ "aq", "Antarctica/Palmer",
+ "ar", "America/Argentina/Buenos_Aires",
+ "as", "Pacific/Pago_Pago",
+ "at", "Europe/Vienna",
+ "au", "Australia/Sydney",
+ "au", "Australia/Adelaide",
+ "au", "Australia/Perth",
+ "au", "Australia/Eucla",
+ "aw", "America/Aruba",
+ "ax", "Europe/Mariehamn",
+ "az", "Asia/Baku",
+ "ba", "Europe/Sarajevo",
+ "bb", "America/Barbados",
+ "bd", "Asia/Dhaka",
+ "be", "Europe/Brussels",
+ "bf", "Africa/Ouagadougou",
+ "bg", "Europe/Sofia",
+ "bh", "Asia/Bahrain",
+ "bi", "Africa/Bujumbura",
+ "bj", "Africa/Porto-Novo",
+ "bm", "Atlantic/Bermuda",
+ "bn", "Asia/Brunei",
+ "bo", "America/La_Paz",
+ "br", "America/Noronha",
+ "br", "America/Sao_Paulo",
+ "br", "America/Manaus",
+ "bs", "America/Nassau",
+ "bt", "Asia/Thimphu",
+ "bw", "Africa/Gaborone",
+ "by", "Europe/Minsk",
+ "bz", "America/Belize",
+ "ca", "America/St_Johns",
+ "ca", "America/Halifax",
+ "ca", "America/Toronto",
+ "ca", "America/Winnipeg",
+ "ca", "America/Edmonton",
+ "ca", "America/Vancouver",
+ "cc", "Indian/Cocos",
+ "cd", "Africa/Lubumbashi",
+ "cd", "Africa/Kinshasa",
+ "cf", "Africa/Bangui",
+ "cg", "Africa/Brazzaville",
+ "ch", "Europe/Zurich",
+ "ci", "Africa/Abidjan",
+ "ck", "Pacific/Rarotonga",
+ "cl", "America/Santiago",
+ "cl", "Pacific/Easter",
+ "cm", "Africa/Douala",
+ "cn", "Asia/Shanghai",
+ "co", "America/Bogota",
+ "cr", "America/Costa_Rica",
+ "cu", "America/Havana",
+ "cv", "Atlantic/Cape_Verde",
+ "cx", "Indian/Christmas",
+ "cy", "Asia/Nicosia",
+ "cz", "Europe/Prague",
+ "de", "Europe/Berlin",
+ "dj", "Africa/Djibouti",
+ "dk", "Europe/Copenhagen",
+ "dm", "America/Dominica",
+ "do", "America/Santo_Domingo",
+ "dz", "Africa/Algiers",
+ "ec", "America/Guayaquil",
+ "ec", "Pacific/Galapagos",
+ "ee", "Europe/Tallinn",
+ "eg", "Africa/Cairo",
+ "eh", "Africa/El_Aaiun",
+ "er", "Africa/Asmara",
+ "es", "Europe/Madrid",
+ "es", "Atlantic/Canary",
+ "et", "Africa/Addis_Ababa",
+ "fi", "Europe/Helsinki",
+ "fj", "Pacific/Fiji",
+ "fk", "Atlantic/Stanley",
+ "fm", "Pacific/Ponape",
+ "fm", "Pacific/Truk",
+ "fo", "Atlantic/Faroe",
+ "fr", "Europe/Paris",
+ "ga", "Africa/Libreville",
+ "gb", "Europe/London",
+ "gd", "America/Grenada",
+ "ge", "Asia/Tbilisi",
+ "gf", "America/Cayenne",
+ "gg", "Europe/Guernsey",
+ "gh", "Africa/Accra",
+ "gi", "Europe/Gibraltar",
+ "gl", "America/Danmarkshavn",
+ "gl", "America/Scoresbysund",
+ "gl", "America/Godthab",
+ "gl", "America/Thule",
+ "gm", "Africa/Banjul",
+ "gn", "Africa/Conakry",
+ "gp", "America/Guadeloupe",
+ "gq", "Africa/Malabo",
+ "gr", "Europe/Athens",
+ "gs", "Atlantic/South_Georgia",
+ "gt", "America/Guatemala",
+ "gu", "Pacific/Guam",
+ "gw", "Africa/Bissau",
+ "gy", "America/Guyana",
+ "hk", "Asia/Hong_Kong",
+ "hn", "America/Tegucigalpa",
+ "hr", "Europe/Zagreb",
+ "ht", "America/Port-au-Prince",
+ "hu", "Europe/Budapest",
+ "id", "Asia/Jayapura",
+ "id", "Asia/Makassar",
+ "id", "Asia/Jakarta",
+ "ie", "Europe/Dublin",
+ "il", "Asia/Jerusalem",
+ "im", "Europe/Isle_of_Man",
+ "in", "Asia/Calcutta",
+ "io", "Indian/Chagos",
+ "iq", "Asia/Baghdad",
+ "ir", "Asia/Tehran",
+ "is", "Atlantic/Reykjavik",
+ "it", "Europe/Rome",
+ "je", "Europe/Jersey",
+ "jm", "America/Jamaica",
+ "jo", "Asia/Amman",
+ "jp", "Asia/Tokyo",
+ "ke", "Africa/Nairobi",
+ "kg", "Asia/Bishkek",
+ "kh", "Asia/Phnom_Penh",
+ "ki", "Pacific/Kiritimati",
+ "ki", "Pacific/Enderbury",
+ "ki", "Pacific/Tarawa",
+ "km", "Indian/Comoro",
+ "kn", "America/St_Kitts",
+ "kp", "Asia/Pyongyang",
+ "kr", "Asia/Seoul",
+ "kw", "Asia/Kuwait",
+ "ky", "America/Cayman",
+ "kz", "Asia/Almaty",
+ "kz", "Asia/Aqtau",
+ "la", "Asia/Vientiane",
+ "lb", "Asia/Beirut",
+ "lc", "America/St_Lucia",
+ "li", "Europe/Vaduz",
+ "lk", "Asia/Colombo",
+ "lr", "Africa/Monrovia",
+ "ls", "Africa/Maseru",
+ "lt", "Europe/Vilnius",
+ "lu", "Europe/Luxembourg",
+ "lv", "Europe/Riga",
+ "ly", "Africa/Tripoli",
+ "ma", "Africa/Casablanca",
+ "mc", "Europe/Monaco",
+ "md", "Europe/Chisinau",
+ "me", "Europe/Podgorica",
+ "mg", "Indian/Antananarivo",
+ "mh", "Pacific/Majuro",
+ "mk", "Europe/Skopje",
+ "ml", "Africa/Bamako",
+ "mm", "Asia/Rangoon",
+ "mn", "Asia/Choibalsan",
+ "mn", "Asia/Hovd",
+ "mo", "Asia/Macau",
+ "mp", "Pacific/Saipan",
+ "mq", "America/Martinique",
+ "mr", "Africa/Nouakchott",
+ "ms", "America/Montserrat",
+ "mt", "Europe/Malta",
+ "mu", "Indian/Mauritius",
+ "mv", "Indian/Maldives",
+ "mw", "Africa/Blantyre",
+ "mx", "America/Mexico_City",
+ "mx", "America/Chihuahua",
+ "mx", "America/Tijuana",
+ "my", "Asia/Kuala_Lumpur",
+ "mz", "Africa/Maputo",
+ "na", "Africa/Windhoek",
+ "nc", "Pacific/Noumea",
+ "ne", "Africa/Niamey",
+ "nf", "Pacific/Norfolk",
+ "ng", "Africa/Lagos",
+ "ni", "America/Managua",
+ "nl", "Europe/Amsterdam",
+ "no", "Europe/Oslo",
+ "np", "Asia/Katmandu",
+ "nr", "Pacific/Nauru",
+ "nu", "Pacific/Niue",
+ "nz", "Pacific/Auckland",
+ "nz", "Pacific/Chatham",
+ "om", "Asia/Muscat",
+ "pa", "America/Panama",
+ "pe", "America/Lima",
+ "pf", "Pacific/Gambier",
+ "pf", "Pacific/Marquesas",
+ "pf", "Pacific/Tahiti",
+ "pg", "Pacific/Port_Moresby",
+ "ph", "Asia/Manila",
+ "pk", "Asia/Karachi",
+ "pl", "Europe/Warsaw",
+ "pm", "America/Miquelon",
+ "pn", "Pacific/Pitcairn",
+ "pr", "America/Puerto_Rico",
+ "ps", "Asia/Gaza",
+ "pt", "Europe/Lisbon",
+ "pt", "Atlantic/Azores",
+ "pw", "Pacific/Palau",
+ "py", "America/Asuncion",
+ "qa", "Asia/Qatar",
+ "re", "Indian/Reunion",
+ "ro", "Europe/Bucharest",
+ "rs", "Europe/Belgrade",
+ "ru", "Asia/Kamchatka",
+ "ru", "Asia/Magadan",
+ "ru", "Asia/Vladivostok",
+ "ru", "Asia/Yakutsk",
+ "ru", "Asia/Irkutsk",
+ "ru", "Asia/Krasnoyarsk",
+ "ru", "Asia/Novosibirsk",
+ "ru", "Asia/Yekaterinburg",
+ "ru", "Europe/Samara",
+ "ru", "Europe/Moscow",
+ "ru", "Europe/Kaliningrad",
+ "rw", "Africa/Kigali",
+ "sa", "Asia/Riyadh",
+ "sb", "Pacific/Guadalcanal",
+ "sc", "Indian/Mahe",
+ "sd", "Africa/Khartoum",
+ "se", "Europe/Stockholm",
+ "sg", "Asia/Singapore",
+ "sh", "Atlantic/St_Helena",
+ "si", "Europe/Ljubljana",
+ "sj", "Arctic/Longyearbyen",
+ "sk", "Europe/Bratislava",
+ "sl", "Africa/Freetown",
+ "sm", "Europe/San_Marino",
+ "sn", "Africa/Dakar",
+ "so", "Africa/Mogadishu",
+ "sr", "America/Paramaribo",
+ "st", "Africa/Sao_Tome",
+ "sv", "America/El_Salvador",
+ "sy", "Asia/Damascus",
+ "sz", "Africa/Mbabane",
+ "tc", "America/Grand_Turk",
+ "td", "Africa/Ndjamena",
+ "tf", "Indian/Kerguelen",
+ "tg", "Africa/Lome",
+ "th", "Asia/Bangkok",
+ "tj", "Asia/Dushanbe",
+ "tk", "Pacific/Fakaofo",
+ "tl", "Asia/Dili",
+ "tm", "Asia/Ashgabat",
+ "tn", "Africa/Tunis",
+ "to", "Pacific/Tongatapu",
+ "tr", "Europe/Istanbul",
+ "tt", "America/Port_of_Spain",
+ "tv", "Pacific/Funafuti",
+ "tw", "Asia/Taipei",
+ "tz", "Africa/Dar_es_Salaam",
+ "ua", "Europe/Kiev",
+ "ug", "Africa/Kampala",
+ "um", "Pacific/Wake",
+ "um", "Pacific/Johnston",
+ "um", "Pacific/Midway",
+ "us", "America/New_York",
+ "us", "America/Chicago",
+ "us", "America/Denver",
+ "us", "America/Los_Angeles",
+ "us", "America/Anchorage",
+ "us", "Pacific/Honolulu",
+ "uy", "America/Montevideo",
+ "uz", "Asia/Tashkent",
+ "va", "Europe/Vatican",
+ "vc", "America/St_Vincent",
+ "ve", "America/Caracas",
+ "vg", "America/Tortola",
+ "vi", "America/St_Thomas",
+ "vn", "Asia/Saigon",
+ "vu", "Pacific/Efate",
+ "wf", "Pacific/Wallis",
+ "ws", "Pacific/Apia",
+ "ye", "Asia/Aden",
+ "yt", "Indian/Mayotte",
+ "za", "Africa/Johannesburg",
+ "zm", "Africa/Lusaka",
+ "zw", "Africa/Harare",
+ };
+
+ for (int i = 0; i < world.length; i += 2) {
+ String country = world[i];
+ String name = world[i + 1];
+
+ TimeZone tz = TimeZone.getTimeZone(name);
+ Calendar c = Calendar.getInstance(tz);
+ TimeZone guess;
+
+ c.set(2009, Calendar.JULY, 20, 12, 00, 00);
+ guess = guess(c, country);
+ assertEquals(name, guess.getID());
+
+ c.set(2009, Calendar.JANUARY, 20, 12, 00, 00);
+ guess = guess(c, country);
+ assertEquals(name, guess.getID());
+ }
+ }
+
+ public void testWorldWeird() throws Exception {
+ String[] world = new String[] {
+ // Distinguisable from Sydney only when DST not in effect
+ "au", "Australia/Lord_Howe",
+ };
+
+ for (int i = 0; i < world.length; i += 2) {
+ String country = world[i];
+ String name = world[i + 1];
+
+ TimeZone tz = TimeZone.getTimeZone(name);
+ Calendar c = Calendar.getInstance(tz);
+ TimeZone guess;
+
+ c.set(2009, Calendar.JULY, 20, 12, 00, 00);
+ guess = guess(c, country);
+ assertEquals(name, guess.getID());
+ }
+ }
+
+ private static TimeZone guess(Calendar c, String country) {
+ return TimeUtils.getTimeZone(c.get(c.ZONE_OFFSET) + c.get(c.DST_OFFSET),
+ c.get(c.DST_OFFSET) != 0,
+ c.getTimeInMillis(),
+ country);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/CreateViewTest.java b/core/tests/coretests/src/android/view/CreateViewTest.java
new file mode 100644
index 0000000..16656f6
--- /dev/null
+++ b/core/tests/coretests/src/android/view/CreateViewTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2006 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.view;
+
+import android.content.Context;
+import android.test.AndroidTestCase;
+import android.test.PerformanceTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.View;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class CreateViewTest extends AndroidTestCase implements PerformanceTestCase {
+
+ public boolean isPerformanceOnly() {
+ return false;
+ }
+
+ public int startPerformance(PerformanceTestCase.Intermediates intermediates) {
+ return 0;
+ }
+
+ @SmallTest
+ public void testLayout1() throws Exception {
+ new CreateViewTest.ViewOne(mContext);
+ }
+
+ @SmallTest
+ public void testLayout2() throws Exception {
+ LinearLayout vert = new LinearLayout(mContext);
+ vert.addView(new CreateViewTest.ViewOne(mContext),
+ new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT, 0));
+ }
+
+ @SmallTest
+ public void testLayout3() throws Exception {
+ LinearLayout vert = new LinearLayout(mContext);
+
+ ViewOne one = new ViewOne(mContext);
+ vert.addView(one, new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT, 0));
+
+ ViewOne two = new ViewOne(mContext);
+ vert.addView(two, new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT, 0));
+
+ ViewOne three = new ViewOne(mContext);
+ vert.addView(three, new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT, 0));
+
+ ViewOne four = new ViewOne(mContext);
+ vert.addView(four, new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT, 0));
+
+ ViewOne five = new ViewOne(mContext);
+ vert.addView(five, new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT, 0));
+
+ ViewOne six = new ViewOne(mContext);
+ vert.addView(six, new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT, 0));
+ }
+
+ @SmallTest
+ public void testLayout4() throws Exception {
+ TextView text = new TextView(mContext);
+ text.setText("S");
+ }
+
+ @SmallTest
+ public void testLayout5() throws Exception {
+ TextView text = new TextView(mContext);
+ text.setText("S");
+
+ LinearLayout vert = new LinearLayout(mContext);
+ vert.addView(text, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT, 0));
+ }
+
+ @SmallTest
+ public void testLayout6() throws Exception {
+ LinearLayout vert = new LinearLayout(mContext);
+
+ TextView one = new TextView(mContext);
+ one.setText("S");
+ vert.addView(one, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT, 0));
+
+ TextView two = new TextView(mContext);
+ two.setText("M");
+ vert.addView(two, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT, 0));
+
+ TextView three = new TextView(mContext);
+ three.setText("T");
+ vert.addView(three, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT, 0));
+
+ TextView four = new TextView(mContext);
+ four.setText("W");
+ vert.addView(four, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT, 0));
+
+ TextView five = new TextView(mContext);
+ five.setText("H");
+ vert.addView(five, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT, 0));
+
+ TextView six = new TextView(mContext);
+ six.setText("F");
+ vert.addView(six, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT, 0));
+ }
+
+ public static class ViewOne extends View {
+ public ViewOne(Context context) {
+ super(context);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/view/InflateTest.java b/core/tests/coretests/src/android/view/InflateTest.java
new file mode 100644
index 0000000..cb4f8e2
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InflateTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2006 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.view;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.test.AndroidTestCase;
+import android.test.PerformanceTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import com.android.frameworks.coretests.R;
+
+import java.util.Map;
+
+public class InflateTest extends AndroidTestCase implements PerformanceTestCase {
+ private LayoutInflater mInflater;
+ private Resources mResources;
+ private View mView;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mInflater = LayoutInflater.from(mContext);
+ mResources = mContext.getResources();
+
+ // to try to make things consistent, before doing timing
+ // do an initial instantiation of the layout and then clear
+ // out the layout cache.
+// mInflater.inflate(mResId, null, null);
+// mResources.flushLayoutCache();
+ }
+
+ public int startPerformance(PerformanceTestCase.Intermediates intermediates) {
+ return 0;
+ }
+
+ public boolean isPerformanceOnly() {
+ return false;
+ }
+
+ public void inflateTest(int resourceId) {
+ mView = mInflater.inflate(resourceId, null);
+ mResources.flushLayoutCache();
+ }
+
+ public void inflateCachedTest(int resourceId) {
+ // Make sure this layout is in the cache.
+ mInflater.inflate(resourceId, null);
+
+ mInflater.inflate(resourceId, null);
+ }
+
+ @SmallTest
+ public void testLayout1() throws Exception {
+ inflateTest(R.layout.layout_one);
+ }
+
+ @SmallTest
+ public void testLayout2() throws Exception {
+ inflateTest(R.layout.layout_two);
+ }
+
+ @SmallTest
+ public void testLayout3() throws Exception {
+ inflateTest(R.layout.layout_three);
+ }
+
+ @SmallTest
+ public void testLayout4() throws Exception {
+ inflateTest(R.layout.layout_four);
+ }
+
+ @SmallTest
+ public void testLayout5() throws Exception {
+ inflateTest(R.layout.layout_five);
+ }
+
+ @SmallTest
+ public void testLayout6() throws Exception {
+ inflateTest(R.layout.layout_six);
+ }
+
+ @SmallTest
+ public void testCachedLayout1() throws Exception {
+ inflateCachedTest(R.layout.layout_one);
+ }
+
+ @SmallTest
+ public void testCachedLayout2() throws Exception {
+ inflateCachedTest(R.layout.layout_two);
+ }
+
+ @SmallTest
+ public void testCachedLayout3() throws Exception {
+ inflateCachedTest(R.layout.layout_three);
+ }
+
+ @SmallTest
+ public void testCachedLayout4() throws Exception {
+ inflateCachedTest(R.layout.layout_four);
+ }
+
+ @SmallTest
+ public void testCachedLayout5() throws Exception {
+ inflateCachedTest(R.layout.layout_five);
+ }
+
+ @SmallTest
+ public void testCachedLayout6() throws Exception {
+ inflateCachedTest(R.layout.layout_six);
+ }
+
+// public void testLayoutTag() throws Exception {
+// public void setUp
+// (Context
+// context){
+// setUp(context, R.layout.layout_tag);
+// }
+// public void run
+// ()
+// {
+// super.run();
+// if (!"MyTag".equals(mView.getTag())) {
+// throw new RuntimeException("Incorrect tag: " + mView.getTag());
+// }
+// }
+// }
+
+ public static class ViewOne extends View {
+ public ViewOne(Context context) {
+ super(context);
+ }
+
+ public ViewOne(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/view/MenuTest.java b/core/tests/coretests/src/android/view/MenuTest.java
new file mode 100644
index 0000000..e8a8438
--- /dev/null
+++ b/core/tests/coretests/src/android/view/MenuTest.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2006 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.view;
+
+import com.android.internal.view.menu.MenuBuilder;
+
+import junit.framework.Assert;
+
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.SubMenu;
+
+import com.android.frameworks.coretests.R;
+
+public class MenuTest extends AndroidTestCase {
+
+ private MenuBuilder mMenu;
+
+ public void setUp() throws Exception {
+ super.setUp();
+ mMenu = new MenuBuilder(super.getContext());
+ }
+
+ @SmallTest
+ public void testItemId() {
+ final int id = 512;
+ final MenuItem item = mMenu.add(0, id, 0, "test");
+
+ Assert.assertEquals(id, item.getItemId());
+ Assert.assertEquals(item, mMenu.findItem(id));
+ Assert.assertEquals(0, mMenu.findItemIndex(id));
+ }
+
+ @SmallTest
+ public void testGroupId() {
+ final int groupId = 541;
+ final int item1Index = 1;
+ final int item2Index = 3;
+
+ mMenu.add(0, 0, item1Index - 1, "ignore");
+ final MenuItem item = mMenu.add(groupId, 0, item1Index, "test");
+ mMenu.add(0, 0, item2Index - 1, "ignore");
+ final MenuItem item2 = mMenu.add(groupId, 0, item2Index, "test2");
+
+ Assert.assertEquals(groupId, item.getGroupId());
+ Assert.assertEquals(groupId, item2.getGroupId());
+ Assert.assertEquals(item1Index, mMenu.findGroupIndex(groupId));
+ Assert.assertEquals(item2Index, mMenu.findGroupIndex(groupId, item1Index + 1));
+ }
+
+ @SmallTest
+ public void testGroup() {
+ // This test does the following
+ // 1. Create a grouped item in the menu
+ // 2. Check that findGroupIndex() finds the grouped item.
+ // 3. Check that findGroupIndex() doesn't find a non-existent group.
+
+ final int GROUP_ONE = Menu.FIRST;
+ final int GROUP_TWO = Menu.FIRST + 1;
+
+ mMenu.add(GROUP_ONE, 0, 0, "Menu text");
+ Assert.assertEquals(mMenu.findGroupIndex(GROUP_ONE), 0);
+ Assert.assertEquals(mMenu.findGroupIndex(GROUP_TWO), -1);
+ //TODO: expand this test case to do multiple groups,
+ //adding and removing, hiding and showing, etc.
+ }
+
+ @SmallTest
+ public void testIsShortcutWithAlpha() throws Exception {
+ mMenu.setQwertyMode(true);
+ mMenu.add(0, 0, 0, "test").setShortcut('2', 'a');
+ Assert.assertTrue(mMenu.isShortcutKey(KeyEvent.KEYCODE_A,
+ makeKeyEvent(KeyEvent.KEYCODE_A, 0)));
+ Assert.assertFalse(mMenu.isShortcutKey(KeyEvent.KEYCODE_B,
+ makeKeyEvent(KeyEvent.KEYCODE_B, 0)));
+ }
+
+ @SmallTest
+ public void testIsShortcutWithNumeric() throws Exception {
+ mMenu.setQwertyMode(false);
+ mMenu.add(0, 0, 0, "test").setShortcut('2', 'a');
+ Assert.assertTrue(mMenu.isShortcutKey(KeyEvent.KEYCODE_2,
+ makeKeyEvent(KeyEvent.KEYCODE_2, 0)));
+ Assert.assertFalse(mMenu.isShortcutKey(KeyEvent.KEYCODE_A,
+ makeKeyEvent(KeyEvent.KEYCODE_A, 0)));
+ }
+
+ @SmallTest
+ public void testIsShortcutWithAlt() throws Exception {
+ mMenu.setQwertyMode(true);
+ mMenu.add(0, 0, 0, "test").setShortcut('2', 'a');
+ Assert.assertTrue(mMenu.isShortcutKey(KeyEvent.KEYCODE_A,
+ makeKeyEvent(KeyEvent.KEYCODE_A,
+ KeyEvent.META_ALT_ON)));
+ Assert.assertFalse(mMenu.isShortcutKey(KeyEvent.KEYCODE_A,
+ makeKeyEvent(KeyEvent.KEYCODE_A,
+ KeyEvent.META_SYM_ON)));
+ }
+
+ @SmallTest
+ public void testIsNotShortcutWithShift() throws Exception {
+ mMenu.setQwertyMode(true);
+ mMenu.add(0, 0, 0, "test").setShortcut('2', 'a');
+ Assert.assertFalse(mMenu.isShortcutKey(KeyEvent.KEYCODE_A,
+ makeKeyEvent(KeyEvent.KEYCODE_A,
+ KeyEvent.META_SHIFT_ON)));
+ }
+
+ @SmallTest
+ public void testIsNotShortcutWithSym() throws Exception {
+ mMenu.setQwertyMode(true);
+ mMenu.add(0, 0, 0, "test").setShortcut('2', 'a');
+ Assert.assertFalse(mMenu.isShortcutKey(KeyEvent.KEYCODE_A,
+ makeKeyEvent(KeyEvent.KEYCODE_A,
+ KeyEvent.META_SYM_ON)));
+ }
+
+ @SmallTest
+ public void testIsShortcutWithUpperCaseAlpha() throws Exception {
+ mMenu.setQwertyMode(true);
+ mMenu.add(0, 0, 0, "test").setShortcut('2', 'A');
+ Assert.assertTrue(mMenu.isShortcutKey(KeyEvent.KEYCODE_A,
+ makeKeyEvent(KeyEvent.KEYCODE_A, 0)));
+ }
+
+ @SmallTest
+ public void testIsShortcutWithBackspace() throws Exception {
+ mMenu.setQwertyMode(true);
+ mMenu.add(0, 0, 0, "test").setShortcut('2', '\b');
+ Assert.assertTrue(mMenu.isShortcutKey(KeyEvent.KEYCODE_DEL,
+ makeKeyEvent(KeyEvent.KEYCODE_DEL, 0)));
+ }
+
+ @SmallTest
+ public void testIsShortcutWithNewline() throws Exception {
+ mMenu.setQwertyMode(true);
+ mMenu.add(0, 0, 0, "test").setShortcut('2', '\n');
+ Assert.assertTrue(mMenu.isShortcutKey(KeyEvent.KEYCODE_ENTER,
+ makeKeyEvent(KeyEvent.KEYCODE_ENTER, 0)));
+ }
+
+ @SmallTest
+ public void testOrder() {
+ final String a = "a", b = "b", c = "c";
+ final int firstOrder = 7, midOrder = 8, lastOrder = 9;
+
+ mMenu.add(0, 0, lastOrder, c);
+ mMenu.add(0, 0, firstOrder, a);
+ mMenu.add(0, 0, midOrder, b);
+
+ Assert.assertEquals(firstOrder, mMenu.getItem(0).getOrder());
+ Assert.assertEquals(a, mMenu.getItem(0).getTitle());
+ Assert.assertEquals(midOrder, mMenu.getItem(1).getOrder());
+ Assert.assertEquals(b, mMenu.getItem(1).getTitle());
+ Assert.assertEquals(lastOrder, mMenu.getItem(2).getOrder());
+ Assert.assertEquals(c, mMenu.getItem(2).getTitle());
+ }
+
+ @SmallTest
+ public void testTitle() {
+ final String title = "test";
+ final MenuItem stringItem = mMenu.add(title);
+ final MenuItem resItem = mMenu.add(R.string.menu_test);
+
+ Assert.assertEquals(title, stringItem.getTitle());
+ Assert.assertEquals(getContext().getResources().getString(R.string.menu_test), resItem
+ .getTitle());
+ }
+
+ @SmallTest
+ public void testCheckable() {
+ final int groupId = 1;
+ final MenuItem item1 = mMenu.add(groupId, 1, 0, "item1");
+ final MenuItem item2 = mMenu.add(groupId, 2, 0, "item2");
+
+ // Set to exclusive
+ mMenu.setGroupCheckable(groupId, true, true);
+ Assert.assertTrue("Item was not set to checkable", item1.isCheckable());
+ item1.setChecked(true);
+ Assert.assertTrue("Item did not get checked", item1.isChecked());
+ Assert.assertFalse("Item was not unchecked due to exclusive checkable", item2.isChecked());
+ mMenu.findItem(2).setChecked(true);
+ Assert.assertTrue("Item did not get checked", item2.isChecked());
+ Assert.assertFalse("Item was not unchecked due to exclusive checkable", item1.isChecked());
+
+ // Multiple non-exlusive checkable items
+ mMenu.setGroupCheckable(groupId, true, false);
+ Assert.assertTrue("Item was not set to checkable", item1.isCheckable());
+ item1.setChecked(false);
+ Assert.assertFalse("Item did not get unchecked", item1.isChecked());
+ item1.setChecked(true);
+ Assert.assertTrue("Item did not get checked", item1.isChecked());
+ mMenu.findItem(2).setChecked(true);
+ Assert.assertTrue("Item did not get checked", item2.isChecked());
+ Assert.assertTrue("Item was unchecked when it shouldnt have been", item1.isChecked());
+ }
+
+ @SmallTest
+ public void testVisibility() {
+ final MenuItem item1 = mMenu.add(0, 1, 0, "item1");
+ final MenuItem item2 = mMenu.add(0, 2, 0, "item2");
+
+ // Should start as visible
+ Assert.assertTrue("Item did not start as visible", item1.isVisible());
+ Assert.assertTrue("Item did not start as visible", item2.isVisible());
+
+ // Hide
+ item1.setVisible(false);
+ Assert.assertFalse("Item did not become invisible", item1.isVisible());
+ mMenu.findItem(2).setVisible(false);
+ Assert.assertFalse("Item did not become invisible", item2.isVisible());
+ }
+
+ @SmallTest
+ public void testSubMenu() {
+ final SubMenu subMenu = mMenu.addSubMenu(0, 0, 0, "submenu");
+ final MenuItem subMenuItem = subMenu.getItem();
+ final MenuItem item1 = subMenu.add(0, 1, 0, "item1");
+ final MenuItem item2 = subMenu.add(0, 2, 0, "item2");
+
+ // findItem should recurse into submenus
+ Assert.assertEquals(item1, mMenu.findItem(1));
+ Assert.assertEquals(item2, mMenu.findItem(2));
+ }
+
+ @SmallTest
+ public void testRemove() {
+ final int groupId = 1;
+ final MenuItem item1 = mMenu.add(groupId, 1, 0, "item1");
+ final MenuItem item2 = mMenu.add(groupId, 2, 0, "item2");
+ final MenuItem item3 = mMenu.add(groupId, 3, 0, "item3");
+ final MenuItem item4 = mMenu.add(groupId, 4, 0, "item4");
+ final MenuItem item5 = mMenu.add(groupId, 5, 0, "item5");
+ final MenuItem item6 = mMenu.add(0, 6, 0, "item6");
+
+ Assert.assertEquals(item1, mMenu.findItem(1));
+ mMenu.removeItemAt(0);
+ Assert.assertNull(mMenu.findItem(1));
+
+ Assert.assertEquals(item2, mMenu.findItem(2));
+ mMenu.removeItem(2);
+ Assert.assertNull(mMenu.findItem(2));
+
+ Assert.assertEquals(item3, mMenu.findItem(3));
+ Assert.assertEquals(item4, mMenu.findItem(4));
+ Assert.assertEquals(item5, mMenu.findItem(5));
+ mMenu.removeGroup(groupId);
+ Assert.assertNull(mMenu.findItem(3));
+ Assert.assertNull(mMenu.findItem(4));
+ Assert.assertNull(mMenu.findItem(5));
+
+ Assert.assertEquals(item6, mMenu.findItem(6));
+ mMenu.clear();
+ Assert.assertNull(mMenu.findItem(6));
+ }
+
+ private KeyEvent makeKeyEvent(int keyCode, int metaState) {
+ return new KeyEvent(0L, 0L, KeyEvent.ACTION_DOWN, keyCode, 0, metaState);
+ }
+}
diff --git a/core/tests/coretests/src/android/webkit/WebkitTest.java b/core/tests/coretests/src/android/webkit/WebkitTest.java
new file mode 100644
index 0000000..17b4088
--- /dev/null
+++ b/core/tests/coretests/src/android/webkit/WebkitTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2006 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.webkit;
+
+import android.test.AndroidTestCase;
+import android.text.format.DateFormat;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
+import android.webkit.DateSorter;
+
+import java.util.Calendar;
+import java.util.Date;
+
+public class WebkitTest extends AndroidTestCase {
+
+ private static final String LOGTAG = WebkitTest.class.getName();
+
+ @MediumTest
+ public void testDateSorter() throws Exception {
+ /**
+ * Note: check the logging output manually to test
+ * nothing automated yet, besides object creation
+ */
+ DateSorter dateSorter = new DateSorter(mContext);
+ Date date = new Date();
+
+ for (int i = 0; i < DateSorter.DAY_COUNT; i++) {
+ Log.i(LOGTAG, "Boundary " + i + " " + dateSorter.getBoundary(i));
+ Log.i(LOGTAG, "Label " + i + " " + dateSorter.getLabel(i));
+ }
+
+ Calendar c = Calendar.getInstance();
+ long time = c.getTimeInMillis();
+ int index;
+ Log.i(LOGTAG, "now: " + dateSorter.getIndex(time));
+ for (int i = 0; i < 20; i++) {
+ time -= 8 * 60 * 60 * 1000; // 8 hours
+ date.setTime(time);
+ c.setTime(date);
+ index = dateSorter.getIndex(time);
+ Log.i(LOGTAG, "time: " + DateFormat.format("yyyy/MM/dd kk:mm:ss", c).toString() +
+ " " + index + " " + dateSorter.getLabel(index));
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/LabelView.java b/core/tests/coretests/src/android/widget/LabelView.java
new file mode 100644
index 0000000..4661c01
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/LabelView.java
@@ -0,0 +1,191 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.view.View;
+
+/**
+ * Example of how to write a custom subclass of View. LabelView
+ * is used to draw simple text views. Note that it does not handle
+ * styled text or right-to-left writing systems.
+ *
+ */
+public class LabelView extends View {
+ /**
+ * Constructor. This version is only needed if you will be instantiating
+ * the object manually (not from a layout XML file).
+ * @param context the application environment
+ */
+ public LabelView(Context context) {
+ super(context);
+ initLabelView();
+ }
+
+ /**
+ * Construct object, initializing with any attributes we understand from a
+ * layout file. These attributes are defined in
+ * SDK/assets/res/any/classes.xml.
+ *
+ * @see android.view.View#View(android.content.Context, android.util.AttributeSet)
+ public LabelView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initLabelView();
+
+ Resources.StyledAttributes a = context.obtainStyledAttributes(attrs,
+ R.styleable.LabelView);
+
+ CharSequence s = a.getString(R.styleable.LabelView_text);
+ if (s != null) {
+ setText(s.toString());
+ }
+
+ ColorStateList textColor = a.getColorList(R.styleable.
+ LabelView_textColor);
+ if (textColor != null) {
+ setTextColor(textColor.getDefaultColor(0));
+ }
+
+ int textSize = a.getInt(R.styleable.LabelView_textSize, 0);
+ if (textSize > 0) {
+ setTextSize(textSize);
+ }
+
+ a.recycle();
+ }
+
+ */
+ private void initLabelView() {
+ mTextPaint = new Paint();
+ mTextPaint.setAntiAlias(true);
+ mTextPaint.setTextSize(16);
+ mTextPaint.setColor(0xFF000000);
+
+ mPaddingLeft = 3;
+ mPaddingTop = 3;
+ mPaddingRight = 3;
+ mPaddingBottom = 3;
+ }
+
+ /**
+ * Sets the text to display in this label
+ * @param text The text to display. This will be drawn as one line.
+ */
+ public void setText(String text) {
+ mText = text;
+ requestLayout();
+ invalidate();
+ }
+
+ /**
+ * Sets the text size for this label
+ * @param size Font size
+ */
+ public void setTextSize(int size) {
+ mTextPaint.setTextSize(size);
+ requestLayout();
+ invalidate();
+ }
+
+ /**
+ * Sets the text color for this label
+ * @param color ARGB value for the text
+ */
+ public void setTextColor(int color) {
+ mTextPaint.setColor(color);
+ invalidate();
+ }
+
+
+ /**
+ * @see android.view.View#measure(int, int)
+ */
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ setMeasuredDimension(measureWidth(widthMeasureSpec),
+ measureHeight(heightMeasureSpec));
+ }
+
+ /**
+ * Determines the width of this view
+ * @param measureSpec A measureSpec packed into an int
+ * @return The width of the view, honoring constraints from measureSpec
+ */
+ private int measureWidth(int measureSpec) {
+ int result;
+ int specMode = MeasureSpec.getMode(measureSpec);
+ int specSize = MeasureSpec.getSize(measureSpec);
+
+ if (specMode == MeasureSpec.EXACTLY) {
+ // We were told how big to be
+ result = specSize;
+ } else {
+ // Measure the text
+ result = (int) mTextPaint.measureText(mText) + mPaddingLeft
+ + mPaddingRight;
+ if (specMode == MeasureSpec.AT_MOST) {
+ // Respect AT_MOST value if that was what is called for by measureSpec
+ result = Math.min(result, specSize);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Determines the height of this view
+ * @param measureSpec A measureSpec packed into an int
+ * @return The height of the view, honoring constraints from measureSpec
+ */
+ private int measureHeight(int measureSpec) {
+ int result;
+ int specMode = MeasureSpec.getMode(measureSpec);
+ int specSize = MeasureSpec.getSize(measureSpec);
+
+ mAscent = (int) mTextPaint.ascent();
+ if (specMode == MeasureSpec.EXACTLY) {
+ // We were told how big to be
+ result = specSize;
+ } else {
+ // Measure the text (beware: ascent is a negative number)
+ result = (int) (-mAscent + mTextPaint.descent()) + mPaddingTop
+ + mPaddingBottom;
+ if (specMode == MeasureSpec.AT_MOST) {
+ // Respect AT_MOST value if that was what is called for by measureSpec
+ result = Math.min(result, specSize);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Render the text
+ *
+ * @see android.view.View#onDraw(android.graphics.Canvas)
+ */
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawText(mText, mPaddingLeft, mPaddingTop - mAscent, mTextPaint);
+ }
+
+ private Paint mTextPaint;
+ private String mText;
+ private int mAscent;
+}
diff --git a/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java b/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java
new file mode 100644
index 0000000..c25df7c
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2006 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.widget;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.SpannedString;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class TextViewPerformanceTest extends AndroidTestCase {
+
+ private String mString = "The quick brown fox";
+ private Canvas mCanvas;
+ private PerformanceTextView mTextView;
+ private Paint mPaint;
+ private PerformanceLabelView mLabelView;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ Bitmap mBitmap = Bitmap.createBitmap(320, 240, Bitmap.Config.RGB_565);
+ mCanvas = new Canvas(mBitmap);
+
+ ViewGroup.LayoutParams p = new ViewGroup.LayoutParams(320, 240);
+
+ mLabelView = new PerformanceLabelView(mContext);
+ mLabelView.setText(mString);
+ mLabelView.measure(View.MeasureSpec.AT_MOST | 320, View.MeasureSpec.AT_MOST | 240);
+ mLabelView.mySetFrame(320, 240);
+ mLabelView.setLayoutParams(p);
+ mLabelView.myDraw(mCanvas);
+
+ mPaint = new Paint();
+ mCanvas.save();
+ mTextView = new PerformanceTextView(mContext);
+ mTextView.setLayoutParams(p);
+ mTextView.setText(mString);
+ mTextView.mySetFrame(320, 240);
+ mTextView.measure(View.MeasureSpec.AT_MOST | 320, View.MeasureSpec.AT_MOST | 240);
+ }
+
+ @MediumTest
+ public void testDrawTextViewLine() throws Exception {
+ mTextView.myDraw(mCanvas);
+ mTextView.myDraw(mCanvas);
+ mTextView.myDraw(mCanvas);
+ mTextView.myDraw(mCanvas);
+ mTextView.myDraw(mCanvas);
+ mTextView.myDraw(mCanvas);
+ mTextView.myDraw(mCanvas);
+ mTextView.myDraw(mCanvas);
+ mTextView.myDraw(mCanvas);
+ mTextView.myDraw(mCanvas);
+ }
+
+ @SmallTest
+ public void testSpan() throws Exception {
+ CharSequence charSeq = new SpannedString(mString);
+ mTextView.setText(charSeq);
+
+ mTextView.myDraw(mCanvas);
+ mTextView.myDraw(mCanvas);
+ mTextView.myDraw(mCanvas);
+ mTextView.myDraw(mCanvas);
+ mTextView.myDraw(mCanvas);
+ mTextView.myDraw(mCanvas);
+ mTextView.myDraw(mCanvas);
+ mTextView.myDraw(mCanvas);
+ mTextView.myDraw(mCanvas);
+ mTextView.myDraw(mCanvas);
+ }
+
+ @SmallTest
+ public void testCanvasDrawText() throws Exception {
+ mCanvas.drawText(mString, 30, 30, mPaint);
+ }
+
+ @SmallTest
+ public void testLabelViewDraw() throws Exception {
+ mLabelView.myDraw(mCanvas);
+ }
+
+ private class PerformanceTextView extends TextView {
+ public PerformanceTextView(Context context) {
+ super(context);
+ }
+
+ final void myDraw(Canvas c) {
+ super.onDraw(c);
+ }
+
+ final void mySetFrame(int w, int h) {
+ super.setFrame(0, 0, w, h);
+ }
+ }
+
+ private class PerformanceLabelView extends LabelView {
+ public PerformanceLabelView(Context context) {
+ super(context);
+ }
+
+ final void myDraw(Canvas c) {
+ super.onDraw(c);
+ }
+
+ final void mySetFrame(int w, int h) {
+ super.setFrame(0, 0, w, h);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
new file mode 100644
index 0000000..d8d145c
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.widget;
+
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.GetChars;
+import android.widget.TextView;
+
+/**
+ * TextViewTest tests {@link TextView}.
+ */
+public class TextViewTest extends AndroidTestCase {
+
+ @SmallTest
+ public void testArray() throws Exception {
+ TextView tv = new TextView(mContext);
+
+ char[] c = new char[] { 'H', 'e', 'l', 'l', 'o', ' ',
+ 'W', 'o', 'r', 'l', 'd', '!' };
+
+ tv.setText(c, 1, 4);
+ CharSequence oldText = tv.getText();
+
+ tv.setText(c, 4, 5);
+ CharSequence newText = tv.getText();
+
+ assertTrue(newText == oldText);
+
+ assertEquals(5, newText.length());
+ assertEquals('o', newText.charAt(0));
+ assertEquals("o Wor", newText.toString());
+
+ assertEquals(" Wo", newText.subSequence(1, 4));
+
+ char[] c2 = new char[7];
+ ((GetChars) newText).getChars(1, 4, c2, 2);
+ assertEquals('\0', c2[1]);
+ assertEquals(' ', c2[2]);
+ assertEquals('W', c2[3]);
+ assertEquals('o', c2[4]);
+ assertEquals('\0', c2[5]);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/util/BitwiseStreamsTest.java b/core/tests/coretests/src/com/android/internal/util/BitwiseStreamsTest.java
new file mode 100644
index 0000000..a304b68
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/BitwiseStreamsTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2006 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.util;
+
+import com.android.internal.util.BitwiseInputStream;
+import com.android.internal.util.BitwiseOutputStream;
+import com.android.internal.util.HexDump;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import android.util.Log;
+
+import java.util.Random;
+
+public class BitwiseStreamsTest extends AndroidTestCase {
+ private final static String LOG_TAG = "BitwiseStreamsTest";
+
+ @SmallTest
+ public void testOne() throws Exception {
+ int offset = 3;
+ byte[] inBuf = HexDump.hexStringToByteArray("FFDD");
+ BitwiseOutputStream outStream = new BitwiseOutputStream(30);
+ outStream.skip(offset);
+ for (int i = 0; i < inBuf.length; i++) outStream.write(8, inBuf[i]);
+ byte[] outBuf = outStream.toByteArray();
+ BitwiseInputStream inStream = new BitwiseInputStream(outBuf);
+ byte[] inBufDup = new byte[inBuf.length];
+ inStream.skip(offset);
+ for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8);
+ assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup));
+ }
+
+ @SmallTest
+ public void testTwo() throws Exception {
+ int offset = 3;
+ byte[] inBuf = HexDump.hexStringToByteArray("11d4f29c0e9ad3c36e72584e064d9b53");
+ BitwiseOutputStream outStream = new BitwiseOutputStream(30);
+ outStream.skip(offset);
+ for (int i = 0; i < inBuf.length; i++) outStream.write(8, inBuf[i]);
+ BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray());
+ inStream.skip(offset);
+ byte[] inBufDup = new byte[inBuf.length];
+ for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8);
+ assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup));
+ }
+
+ @SmallTest
+ public void testThree() throws Exception {
+ int offset = 4;
+ byte[] inBuf = HexDump.hexStringToByteArray("00031040900112488ea794e0");
+ BitwiseOutputStream outStream = new BitwiseOutputStream(30);
+ outStream.skip(offset);
+ for (int i = 0; i < inBuf.length; i++) outStream.write(8, inBuf[i]);
+ BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray());
+ inStream.skip(offset);
+ byte[] inBufDup = new byte[inBuf.length];
+ for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8);
+ assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup));
+ }
+
+ @SmallTest
+ public void testFour() throws Exception {
+ int offset = 7;
+ byte[] inBuf = HexDump.hexStringToByteArray("00031040900112488ea794e0");
+ BitwiseOutputStream outStream = new BitwiseOutputStream(30);
+ outStream.skip(offset);
+ for (int i = 0; i < inBuf.length; i++) {
+ outStream.write(5, inBuf[i] >>> 3);
+ outStream.write(3, inBuf[i] & 0x07);
+ }
+ BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray());
+ inStream.skip(offset);
+ byte[] inBufDup = new byte[inBuf.length];
+ for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8);
+ assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup));
+ }
+
+ @SmallTest
+ public void testFive() throws Exception {
+ Random random = new Random();
+ int iterations = 10000;
+ int[] sizeArr = new int[iterations];
+ int[] valueArr = new int[iterations];
+ BitwiseOutputStream outStream = new BitwiseOutputStream(iterations * 4);
+ for (int i = 0; i < iterations; i++) {
+ int x = random.nextInt();
+ int size = (x & 0x07) + 1;
+ int value = x & (-1 >>> (32 - size));
+ sizeArr[i] = size;
+ valueArr[i] = value;
+ outStream.write(size, value);
+ }
+ BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray());
+ for (int i = 0; i < iterations; i++) {
+ assertEquals(valueArr[i], inStream.read(sizeArr[i]));
+ }
+ }
+
+ @SmallTest
+ public void testSix() throws Exception {
+ int num_runs = 10;
+ long start = android.os.SystemClock.elapsedRealtime();
+ for (int run = 0; run < num_runs; run++) {
+ int offset = run % 8;
+ byte[] inBuf = HexDump.hexStringToByteArray("00031040900112488ea794e0");
+ BitwiseOutputStream outStream = new BitwiseOutputStream(30);
+ outStream.skip(offset);
+ for (int i = 0; i < inBuf.length; i++) {
+ outStream.write(5, inBuf[i] >>> 3);
+ outStream.write(3, inBuf[i] & 0x07);
+ }
+ BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray());
+ inStream.skip(offset);
+ byte[] inBufDup = new byte[inBuf.length];
+ for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8);
+ assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup));
+ }
+ long end = android.os.SystemClock.elapsedRealtime();
+ Log.d(LOG_TAG, "repeated encode-decode took " + (end - start) + " ms");
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/util/CharSequencesTest.java b/core/tests/coretests/src/com/android/internal/util/CharSequencesTest.java
new file mode 100644
index 0000000..55d186c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/CharSequencesTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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 com.android.internal.util;
+
+import com.android.internal.util.CharSequences;
+import static com.android.internal.util.CharSequences.forAsciiBytes;
+import junit.framework.TestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+public class CharSequencesTest extends TestCase {
+
+ @SmallTest
+ public void testCharSequences() {
+ String s = "Crazy Bob";
+ byte[] bytes = s.getBytes();
+
+ String copy = toString(forAsciiBytes(bytes));
+ assertTrue(s.equals(copy));
+
+ copy = toString(forAsciiBytes(bytes, 0, s.length()));
+ assertTrue(s.equals(copy));
+
+ String crazy = toString(forAsciiBytes(bytes, 0, 5));
+ assertTrue("Crazy".equals(crazy));
+
+ String a = toString(forAsciiBytes(bytes, 0, 3).subSequence(2, 3));
+ assertTrue("a".equals(a));
+
+ String empty = toString(forAsciiBytes(bytes, 0, 3).subSequence(3, 3));
+ assertTrue("".equals(empty));
+
+ assertTrue(CharSequences.equals("bob", "bob"));
+ assertFalse(CharSequences.equals("b", "bob"));
+ assertFalse(CharSequences.equals("", "bob"));
+ }
+
+ /**
+ * Converts a CharSequence to a string the slow way. Useful for testing
+ * a CharSequence implementation.
+ */
+ static String toString(CharSequence charSequence) {
+ return new StringBuilder().append(charSequence).toString();
+ }
+}
diff --git a/core/tests/coretests/src/com/google/android/net/ParentalControlTest.java b/core/tests/coretests/src/com/google/android/net/ParentalControlTest.java
new file mode 100644
index 0000000..d8ffeab
--- /dev/null
+++ b/core/tests/coretests/src/com/google/android/net/ParentalControlTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.google.android.net;
+
+import com.google.android.net.ParentalControl;
+import com.google.android.net.ParentalControlState;
+
+import android.os.SystemClock;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+public class ParentalControlTest extends AndroidTestCase {
+
+ private boolean mOnResultCalled = false;
+
+ public class Callback implements ParentalControl.Callback {
+ public void onResult(ParentalControlState state) {
+ synchronized (ParentalControlTest.class) {
+ mOnResultCalled = true;
+ ParentalControlTest.class.notifyAll();
+ }
+ }
+ }
+
+ @SmallTest
+ public void testParentalControlCallback() {
+ synchronized (ParentalControlTest.class) {
+ ParentalControl.getParentalControlState(new Callback(), null);
+ try {
+ long start = SystemClock.uptimeMillis();
+ ParentalControlTest.class.wait(20 * 1000);
+ long end = SystemClock.uptimeMillis();
+ Log.d("AndroidTests", "ParentalControlTest callback took " + (end-start) + " ms.");
+ } catch (InterruptedException ex) {
+ }
+ }
+
+ Assert.assertTrue(mOnResultCalled);
+ }
+}