ContactsProvider2Test.java revision 6cffee46a1334d2b3ed19f436b27638451541044
1d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov/*
2d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * Copyright (C) 2009 The Android Open Source Project
3d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov *
4d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * Licensed under the Apache License, Version 2.0 (the "License");
5d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * you may not use this file except in compliance with the License.
6d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * You may obtain a copy of the License at
7d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov *
8d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov *      http://www.apache.org/licenses/LICENSE-2.0
9d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov *
10d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * Unless required by applicable law or agreed to in writing, software
11d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * distributed under the License is distributed on an "AS IS" BASIS,
12d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * See the License for the specific language governing permissions and
14d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * limitations under the License.
15d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov */
1628f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarpackage com.android.providers.contacts;
17d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
18d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport com.android.internal.util.ArrayUtils;
1928f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.BaseContactsProvider2Test;
20d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
21d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport android.content.ContentUris;
22d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport android.content.ContentValues;
239261b2141aa90a4fed632fd6da03026d4c216280Fred Quintanaimport android.content.EntityIterator;
249261b2141aa90a4fed632fd6da03026d4c216280Fred Quintanaimport android.content.Entity;
25d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport android.database.Cursor;
26d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport android.net.Uri;
27d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport android.provider.ContactsContract.Aggregates;
28d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
296cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
309261b2141aa90a4fed632fd6da03026d4c216280Fred Quintanaimport android.provider.ContactsContract.Data;
31bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millarimport android.provider.ContactsContract.Presence;
329261b2141aa90a4fed632fd6da03026d4c216280Fred Quintanaimport android.provider.ContactsContract.CommonDataKinds.Email;
339261b2141aa90a4fed632fd6da03026d4c216280Fred Quintanaimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
34bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millarimport android.provider.ContactsContract.CommonDataKinds.Im;
359261b2141aa90a4fed632fd6da03026d4c216280Fred Quintanaimport android.provider.ContactsContract.CommonDataKinds.Phone;
364097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
37d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovimport android.test.suitebuilder.annotation.LargeTest;
389261b2141aa90a4fed632fd6da03026d4c216280Fred Quintanaimport android.os.RemoteException;
39d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
40d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov/**
41d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * Unit tests for {@link ContactsProvider2}.
42d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov *
43d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * Run the test like this:
44d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * <code>
45d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * adb shell am instrument -w \
4628f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar *         com.android.providers.contacts.tests/android.test.InstrumentationTestRunner
47d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov * </code>
48d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov */
49d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov@LargeTest
50d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikovpublic class ContactsProvider2Test extends BaseContactsProvider2Test {
51d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
524097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    public void testDisplayNameParsingWhenPartsUnspecified() {
534097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        long contactId = createContact();
544097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        ContentValues values = new ContentValues();
554097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr.");
564097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        insertStructuredName(contactId, values);
574097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
584097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        assertStructuredName(contactId, "Mr", "John", "Kevin", "von Smith", "Jr");
594097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    }
604097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
614097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    public void testDisplayNameParsingWhenPartsSpecified() {
624097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        long contactId = createContact();
634097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        ContentValues values = new ContentValues();
644097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr.");
654097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        values.put(StructuredName.FAMILY_NAME, "Johnson");
664097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        insertStructuredName(contactId, values);
674097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
684097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        assertStructuredName(contactId, null, null, null, "Johnson", null);
694097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    }
704097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
71d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    public void testSendToVoicemailDefault() {
72d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        long contactId = createContact();
73d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        long aggregateId = queryAggregateId(contactId);
74d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
75d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        Cursor c = queryAggregate(aggregateId);
76d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        assertTrue(c.moveToNext());
77d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        int sendToVoicemail = c.getInt(c.getColumnIndex(Aggregates.SEND_TO_VOICEMAIL));
78d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        assertEquals(0, sendToVoicemail);
79d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        c.close();
80d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
81d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
82d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    public void testSetSendToVoicemailAndRingtone() {
83d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        long contactId = createContact();
84d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        long aggregateId = queryAggregateId(contactId);
85d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
86d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        updateSendToVoicemailAndRingtone(aggregateId, true, "foo");
87d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        assertSendToVoicemailAndRingtone(aggregateId, true, "foo");
88d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
89d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
90d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    public void testSendToVoicemailAndRingtoneAfterAggregation() {
91d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        long contactId1 = createContact();
92d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        long aggregateId1 = queryAggregateId(contactId1);
93d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        updateSendToVoicemailAndRingtone(aggregateId1, true, "foo");
94d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
95d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        long contactId2 = createContact();
96d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        long aggregateId2 = queryAggregateId(contactId2);
97d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        updateSendToVoicemailAndRingtone(aggregateId2, true, "bar");
98d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
99d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Aggregate them
100d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        setAggregationException(AggregationExceptions.TYPE_KEEP_IN, aggregateId1, contactId2);
101d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
102d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Both contacts had "send to VM", the aggregate now has the same value
103d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        assertSendToVoicemailAndRingtone(aggregateId1, true, "foo,bar"); // Either foo or bar
104d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
105d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
106d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    public void testDoNotSendToVoicemailAfterAggregation() {
107d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        long contactId1 = createContact();
108d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        long aggregateId1 = queryAggregateId(contactId1);
109d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        updateSendToVoicemailAndRingtone(aggregateId1, true, null);
110d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
111d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        long contactId2 = createContact();
112d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        long aggregateId2 = queryAggregateId(contactId2);
113d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        updateSendToVoicemailAndRingtone(aggregateId2, false, null);
114d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
115d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Aggregate them
116d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        setAggregationException(AggregationExceptions.TYPE_KEEP_IN, aggregateId1, contactId2);
117d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
118d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Since one of the contacts had "don't send to VM" that setting wins for the aggregate
119d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        assertSendToVoicemailAndRingtone(aggregateId1, false, null);
120d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
121d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
122d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    public void testSetSendToVoicemailAndRingtonePreservedAfterJoinAndSplit() {
123d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        long contactId1 = createContact();
124d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        long aggregateId1 = queryAggregateId(contactId1);
125d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        updateSendToVoicemailAndRingtone(aggregateId1, true, "foo");
126d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
127d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        long contactId2 = createContact();
128d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        long aggregateId2 = queryAggregateId(contactId2);
129d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        updateSendToVoicemailAndRingtone(aggregateId2, false, "bar");
130d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
131d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Aggregate them
132d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        setAggregationException(AggregationExceptions.TYPE_KEEP_IN, aggregateId1, contactId2);
133d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
134d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Split them
135d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        setAggregationException(AggregationExceptions.TYPE_KEEP_OUT, aggregateId1, contactId2);
136d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
137d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        assertSendToVoicemailAndRingtone(aggregateId1, true, "foo");
138d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        assertSendToVoicemailAndRingtone(queryAggregateId(contactId2), false, "bar");
139d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
140d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
141bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar    public void testSinglePresenceRowPerAggregate() {
142bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        int protocol1 = Im.PROTOCOL_GOOGLE_TALK;
143bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        String handle1 = "test@gmail.com";
144bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar
145bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        long contactId1 = createContact();
146bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        insertImHandle(contactId1, protocol1, handle1);
147bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar
148bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        insertPresence(protocol1, handle1, Presence.AVAILABLE);
149bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        insertPresence(protocol1, handle1, Presence.AWAY);
150bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        insertPresence(protocol1, handle1, Presence.INVISIBLE);
151bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar
152bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        Cursor c = queryAggregateSummary(queryAggregateId(contactId1),
153bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                new String[] {Presence.PRESENCE_STATUS});
154bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        assertEquals(c.getCount(), 1);
155bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar
156bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        c.moveToFirst();
157bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        assertEquals(c.getInt(0), Presence.AVAILABLE);
158bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar
159bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar    }
160bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar
161d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    private void updateSendToVoicemailAndRingtone(long aggregateId, boolean sendToVoicemail,
162d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            String ringtone) {
163d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        ContentValues values = new ContentValues();
164d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        values.put(Aggregates.SEND_TO_VOICEMAIL, sendToVoicemail);
165d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (ringtone != null) {
166d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            values.put(Aggregates.CUSTOM_RINGTONE, ringtone);
167d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
168d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
169d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        final Uri uri = ContentUris.withAppendedId(Aggregates.CONTENT_URI, aggregateId);
170d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        int count = mResolver.update(uri, values, null, null);
171d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        assertEquals(1, count);
172d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
173d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
174d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    private void assertSendToVoicemailAndRingtone(long aggregateId, boolean expectedSendToVoicemail,
175d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            String expectedRingtone) {
176d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        Cursor c = queryAggregate(aggregateId);
177d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        assertTrue(c.moveToNext());
178d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        int sendToVoicemail = c.getInt(c.getColumnIndex(Aggregates.SEND_TO_VOICEMAIL));
179d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        assertEquals(expectedSendToVoicemail ? 1 : 0, sendToVoicemail);
180d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        String ringtone = c.getString(c.getColumnIndex(Aggregates.CUSTOM_RINGTONE));
181d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (expectedRingtone == null) {
182d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            assertNull(ringtone);
183d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        } else {
184d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            assertTrue(ArrayUtils.contains(expectedRingtone.split(","), ringtone));
185d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
186d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        c.close();
187d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
1889261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
1899261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    public void testGroupCreationAfterMembershipInsert() {
1909261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        long contactId1 = createContact(mAccount);
1919261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Uri groupMembershipUri = insertGroupMembership(contactId1, "gsid1");
1929261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
1939261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        long groupId = assertSingleGroup(NO_LONG, mAccount, "gsid1", null);
1949261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertSingleGroupMembership(ContentUris.parseId(groupMembershipUri),
1959261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                contactId1, groupId, "gsid1");
1969261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
1979261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
1989261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    public void testGroupReuseAfterMembershipInsert() {
1999261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        long contactId1 = createContact(mAccount);
2009261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        long groupId1 = createGroup(mAccount, "gsid1", "title1");
2019261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Uri groupMembershipUri = insertGroupMembership(contactId1, "gsid1");
2029261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2039261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertSingleGroup(groupId1, mAccount, "gsid1", "title1");
2049261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertSingleGroupMembership(ContentUris.parseId(groupMembershipUri),
2059261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                contactId1, groupId1, "gsid1");
2069261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
2079261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2089261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    public void testGroupInsertFailureOnGroupIdConflict() {
2099261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        long contactId1 = createContact(mAccount);
2109261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        long groupId1 = createGroup(mAccount, "gsid1", "title1");
2119261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2129261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        ContentValues values = new ContentValues();
2139261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        values.put(GroupMembership.CONTACT_ID, contactId1);
2149261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        values.put(GroupMembership.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
2159261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        values.put(GroupMembership.GROUP_SOURCE_ID, "gsid1");
2169261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        values.put(GroupMembership.GROUP_ROW_ID, groupId1);
2179261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
2189261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            mResolver.insert(Data.CONTENT_URI, values);
2199261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            fail("the insert was expected to fail, but it succeeded");
2209261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } catch (IllegalArgumentException e) {
2219261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            // this was expected
2229261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
2239261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
2249261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2259261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    public void testContentEntityIterator() throws RemoteException {
2269261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // create multiple contacts and check that the selected ones are returned
2279261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        long id;
2289261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2299261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        long groupId1 = createGroup(mAccount, "gsid1", "title1");
2309261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        long groupId2 = createGroup(mAccount, "gsid2", "title2");
2319261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2329261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        long c0 = id = createContact(mAccount);
2339261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Uri id_0_0 = insertGroupMembership(id, "gsid1");
2349261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Uri id_0_1 = insertEmail(id, "c0@email.com");
2359261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Uri id_0_2 = insertPhoneNumber(id, "5551212c0");
2369261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2379261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        long c1 = id = createContact(mAccount);
2389261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Uri id_1_0 = insertGroupMembership(id, "gsid1");
2399261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Uri id_1_1 = insertGroupMembership(id, "gsid2");
2409261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Uri id_1_2 = insertEmail(id, "c1@email.com");
2419261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Uri id_1_3 = insertPhoneNumber(id, "5551212c1");
2429261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2439261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        long c2 = id = createContact(mAccount);
2449261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Uri id_2_0 = insertGroupMembership(id, "gsid1");
2459261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Uri id_2_1 = insertEmail(id, "c2@email.com");
2469261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Uri id_2_2 = insertPhoneNumber(id, "5551212c2");
2479261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2489261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        long c3 = id = createContact(mAccount);
2499261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Uri id_3_0 = insertGroupMembership(id, groupId2);
2509261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Uri id_3_1 = insertEmail(id, "c3@email.com");
2519261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Uri id_3_2 = insertPhoneNumber(id, "5551212c3");
2529261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2536cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        EntityIterator iterator = mResolver.queryEntities(RawContacts.CONTENT_URI,
2549261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                "contact_id in (" + c1 + "," + c2 + "," + c3 + ")", null, null);
2559261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Entity entity;
2569261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        ContentValues[] subValues;
2579261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        entity = iterator.next();
2586cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        assertEquals(c1, (long) entity.getEntityValues().getAsLong(RawContacts._ID));
2599261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        subValues = asSortedContentValuesArray(entity.getSubValues());
2609261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertEquals(4, subValues.length);
2619261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE,
2629261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Data._ID, id_1_0,
2639261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                GroupMembership.GROUP_ROW_ID, groupId1,
2649261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                GroupMembership.GROUP_SOURCE_ID, "gsid1");
2659261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertDataRow(subValues[1], GroupMembership.CONTENT_ITEM_TYPE,
2669261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Data._ID, id_1_1,
2679261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                GroupMembership.GROUP_ROW_ID, groupId2,
2689261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                GroupMembership.GROUP_SOURCE_ID, "gsid2");
2699261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertDataRow(subValues[2], Email.CONTENT_ITEM_TYPE,
2709261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Data._ID, id_1_2,
2719261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Email.DATA, "c1@email.com");
2729261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertDataRow(subValues[3], Phone.CONTENT_ITEM_TYPE,
2739261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Data._ID, id_1_3,
2749261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Email.DATA, "5551212c1");
2759261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2769261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        entity = iterator.next();
2776cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        assertEquals(c2, (long) entity.getEntityValues().getAsLong(RawContacts._ID));
2789261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        subValues = asSortedContentValuesArray(entity.getSubValues());
2799261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertEquals(3, subValues.length);
2809261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE,
2819261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Data._ID, id_2_0,
2829261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                GroupMembership.GROUP_ROW_ID, groupId1,
2839261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                GroupMembership.GROUP_SOURCE_ID, "gsid1");
2849261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertDataRow(subValues[1], Email.CONTENT_ITEM_TYPE,
2859261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Data._ID, id_2_1,
2869261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Email.DATA, "c2@email.com");
2879261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertDataRow(subValues[2], Phone.CONTENT_ITEM_TYPE,
2889261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Data._ID, id_2_2,
2899261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Email.DATA, "5551212c2");
2909261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
2919261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        entity = iterator.next();
2926cffee46a1334d2b3ed19f436b27638451541044Dmitri Plotnikov        assertEquals(c3, (long) entity.getEntityValues().getAsLong(RawContacts._ID));
2939261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        subValues = asSortedContentValuesArray(entity.getSubValues());
2949261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertEquals(3, subValues.length);
2959261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE,
2969261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Data._ID, id_3_0,
2979261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                GroupMembership.GROUP_ROW_ID, groupId2,
2989261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                GroupMembership.GROUP_SOURCE_ID, "gsid2");
2999261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertDataRow(subValues[1], Email.CONTENT_ITEM_TYPE,
3009261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Data._ID, id_3_1,
3019261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Email.DATA, "c3@email.com");
3029261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertDataRow(subValues[2], Phone.CONTENT_ITEM_TYPE,
3039261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Data._ID, id_3_2,
3049261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Email.DATA, "5551212c3");
3059261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
3069261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        assertFalse(iterator.hasNext());
3079261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
30820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
30920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    public void testDataCreateUpdateDeleteByMimeType() throws Exception {
31020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        long contactId = createContact();
31120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
31220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        ContentValues values = new ContentValues();
31320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.CONTACT_ID, contactId);
31420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.MIMETYPE, "testmimetype");
31520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.RES_PACKAGE, "oldpackage");
31620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.IS_PRIMARY, 1);
31720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.IS_SUPER_PRIMARY, 1);
31820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA1, "old1");
31920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA2, "old2");
32020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA3, "old3");
32120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA4, "old4");
32220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA5, "old5");
32320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA6, "old6");
32420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA7, "old7");
32520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA8, "old8");
32620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA9, "old9");
32720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA10, "old10");
32820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA11, "old11");
32920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA12, "old12");
33020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA13, "old13");
33120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA14, "old14");
33220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA15, "old15");
33320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        Uri uri = mResolver.insert(Data.CONTENT_URI, values);
33420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        assertStoredValues(uri, values);
33520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
33620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.clear();
33720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.RES_PACKAGE, "newpackage");
33820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.IS_PRIMARY, 0);
33920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.IS_SUPER_PRIMARY, 0);
34020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA1, "new1");
34120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA2, "new2");
34220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA3, "new3");
34320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA4, "new4");
34420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA5, "new5");
34520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA6, "new6");
34620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA7, "new7");
34720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA8, "new8");
34820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA9, "new9");
34920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA10, "new10");
35020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA11, "new11");
35120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA12, "new12");
35220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA13, "new13");
35320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA14, "new14");
35420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        values.put(Data.DATA15, "new15");
35520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        mResolver.update(Data.CONTENT_URI, values, Data.CONTACT_ID + "=" + contactId +
35620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                " AND " + Data.MIMETYPE + "='testmimetype'", null);
35770b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov
35870b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        // Should not be able to change IS_PRIMARY and IS_SUPER_PRIMARY by the above update
35970b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        values.put(Data.IS_PRIMARY, 1);
36070b5ee6864cb3368d24a9e876fb93008997b12dfDmitri Plotnikov        values.put(Data.IS_SUPER_PRIMARY, 1);
36120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        assertStoredValues(uri, values);
36220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
36320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        int count = mResolver.delete(Data.CONTENT_URI, Data.CONTACT_ID + "=" + contactId
36420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                + " AND " + Data.MIMETYPE + "='testmimetype'", null);
36520a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        assertEquals(1, count);
36620a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov
36720a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        Cursor c = mResolver.query(Data.CONTENT_URI, null, Data.CONTACT_ID + "=" + contactId
36820a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov                + " AND " + Data.MIMETYPE + "='testmimetype'", null, null);
36920a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        try {
37020a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            assertEquals(0, c.getCount());
37120a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        } finally {
37220a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov            c.close();
37320a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov        }
37420a94c86ede7380c8dd8df2f6a72b3c00ac1bed8Dmitri Plotnikov    }
375d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov}
376d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
377