1 /* Any copyright is dedicated to the Public Domain.
2    http://creativecommons.org/publicdomain/zero/1.0/ */
3 
4 package org.mozilla.gecko.fxa.devices;
5 
6 import android.content.ContentProvider;
7 import android.content.ContentProviderClient;
8 import android.content.ContentResolver;
9 import android.content.ContentValues;
10 import android.database.Cursor;
11 import android.net.Uri;
12 import android.os.Bundle;
13 import android.os.RemoteException;
14 
15 import org.junit.Before;
16 import org.junit.Test;
17 import org.junit.runner.RunWith;
18 import org.mockito.ArgumentCaptor;
19 import org.mockito.Mock;
20 import org.mockito.MockitoAnnotations;
21 import org.mozilla.gecko.background.db.DelegatingTestContentProvider;
22 import org.mozilla.gecko.background.fxa.FxAccountClient;
23 import org.mozilla.gecko.background.testhelpers.TestRunner;
24 import org.mozilla.gecko.db.BrowserContract;
25 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
26 import org.mozilla.gecko.fxa.login.State;
27 import org.robolectric.shadows.ShadowContentResolver;
28 
29 import java.util.List;
30 import java.util.UUID;
31 
32 import static java.util.Objects.deepEquals;
33 import static org.junit.Assert.assertEquals;
34 import static org.junit.Assert.assertTrue;
35 import static org.junit.Assert.fail;
36 import static org.mockito.Matchers.anyString;
37 import static org.mockito.Mockito.any;
38 import static org.mockito.Mockito.doReturn;
39 import static org.mockito.Mockito.eq;
40 import static org.mockito.Mockito.spy;
41 import static org.mockito.Mockito.verify;
42 import static org.mockito.Mockito.when;
43 
44 @RunWith(TestRunner.class)
45 public class TestFxAccountDeviceListUpdater {
46 
47     @Before
init()48     public void init() {
49         // Process Mockito annotations
50         MockitoAnnotations.initMocks(this);
51         fxaDevicesUpdater = spy(new FxAccountDeviceListUpdater(fxAccount, contentResolver));
52     }
53 
54     FxAccountDeviceListUpdater fxaDevicesUpdater;
55 
56     @Mock
57     AndroidFxAccount fxAccount;
58 
59     @Mock
60     State state;
61 
62     @Mock
63     ContentResolver contentResolver;
64 
65     @Mock
66     FxAccountClient fxaClient;
67 
68     @Test
testUpdate()69     public void testUpdate() throws Throwable {
70         byte[] token = "usertoken".getBytes();
71 
72         when(fxAccount.getState()).thenReturn(state);
73         when(state.getSessionToken()).thenReturn(token);
74         doReturn(fxaClient).when(fxaDevicesUpdater).getSynchronousFxaClient();
75 
76         fxaDevicesUpdater.update();
77         verify(fxaClient).deviceList(token, fxaDevicesUpdater);
78     }
79 
80     @Test
testSuccessHandler()81     public void testSuccessHandler() throws Throwable {
82         FxAccountDevice[] result = new FxAccountDevice[2];
83         FxAccountDevice device1 = new FxAccountDevice("Current device", "deviceid1", "mobile", true, System.currentTimeMillis(),
84                 "https://localhost/push/callback1", "abc123", "321cba", false);
85         FxAccountDevice device2 = new FxAccountDevice("Desktop PC", "deviceid2", "desktop", true, System.currentTimeMillis(),
86                 "https://localhost/push/callback2", "abc123", "321cba", false);
87         result[0] = device1;
88         result[1] = device2;
89 
90         when(fxAccount.getProfile()).thenReturn("default");
91 
92         long timeBeforeCall = System.currentTimeMillis();
93         fxaDevicesUpdater.handleSuccess(result);
94 
95         ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
96         verify(contentResolver).call(any(Uri.class), eq(BrowserContract.METHOD_REPLACE_REMOTE_CLIENTS), anyString(), captor.capture());
97         List<Bundle> allArgs = captor.getAllValues();
98         assertTrue(allArgs.size() == 1);
99         ContentValues[] allValues = (ContentValues[]) allArgs.get(0).getParcelableArray(BrowserContract.METHOD_PARAM_DATA);
100 
101         ContentValues firstDevice = allValues[0];
102         checkInsertDeviceContentValues(device1, firstDevice, timeBeforeCall);
103         ContentValues secondDevice = allValues[1];
104         checkInsertDeviceContentValues(device2, secondDevice, timeBeforeCall);
105     }
106 
checkInsertDeviceContentValues(FxAccountDevice device, ContentValues firstDevice, long timeBeforeCall)107     private void checkInsertDeviceContentValues(FxAccountDevice device, ContentValues firstDevice, long timeBeforeCall) {
108         assertEquals(firstDevice.getAsString(BrowserContract.RemoteDevices.GUID), device.id);
109         assertEquals(firstDevice.getAsString(BrowserContract.RemoteDevices.TYPE), device.type);
110         assertEquals(firstDevice.getAsString(BrowserContract.RemoteDevices.NAME), device.name);
111         assertEquals(firstDevice.getAsBoolean(BrowserContract.RemoteDevices.IS_CURRENT_DEVICE), device.isCurrentDevice);
112         deepEquals(firstDevice.getAsString(BrowserContract.RemoteDevices.LAST_ACCESS_TIME), device.lastAccessTime);
113         assertTrue(firstDevice.getAsLong(BrowserContract.RemoteDevices.DATE_CREATED) < timeBeforeCall + 10000); // Give 10 secs of leeway
114         assertTrue(firstDevice.getAsLong(BrowserContract.RemoteDevices.DATE_MODIFIED) < timeBeforeCall + 10000);
115         assertEquals(firstDevice.getAsString(BrowserContract.RemoteDevices.NAME), device.name);
116     }
117 
118     @Test
119     public void testBrowserProvider() {
120         Uri uri = testUri(BrowserContract.RemoteDevices.CONTENT_URI);
121 
122         final ContentProvider provider = DelegatingTestContentProvider.createDelegatingBrowserProvider();
123         Cursor c = null;
124         try {
125             final ShadowContentResolver cr = new ShadowContentResolver();
126             ContentProviderClient remoteDevicesClient = cr.acquireContentProviderClient(BrowserContract.RemoteDevices.CONTENT_URI);
127 
128             // First let's insert a client for initial state.
129 
130             Bundle bundle = new Bundle();
131             ContentValues device1 = createMockRemoteClientValues("device1");
132             bundle.putParcelableArray(BrowserContract.METHOD_PARAM_DATA, new ContentValues[] { device1 });
133 
134             remoteDevicesClient.call(BrowserContract.METHOD_REPLACE_REMOTE_CLIENTS, uri.toString(), bundle);
135 
136             c = remoteDevicesClient.query(uri, null, null, null, "name ASC");
137             assertEquals(c.getCount(), 1);
138             c.moveToFirst();
139             int nameCol = c.getColumnIndexOrThrow("name");
140             assertEquals(c.getString(nameCol), "device1");
141             c.close();
142 
143             // Then we replace our remote clients list with a new one.
144 
145             bundle = new Bundle();
146             ContentValues device2 = createMockRemoteClientValues("device2");
147             ContentValues device3 = createMockRemoteClientValues("device3");
148             bundle.putParcelableArray(BrowserContract.METHOD_PARAM_DATA, new ContentValues[] { device2, device3 });
149 
150             remoteDevicesClient.call(BrowserContract.METHOD_REPLACE_REMOTE_CLIENTS, uri.toString(), bundle);
151 
152             c = remoteDevicesClient.query(uri, null, null, null, "name ASC");
153             assertEquals(c.getCount(), 2);
154             c.moveToFirst();
155             nameCol = c.getColumnIndexOrThrow("name");
156             assertEquals(c.getString(nameCol), "device2");
157             c.moveToNext();
158             assertEquals(c.getString(nameCol), "device3");
159             c.close();
160         } catch (RemoteException e) {
161             fail(e.getMessage());
162         } finally {
163             if (c != null && !c.isClosed()) {
164                 c.close();
165             }
166             provider.shutdown();
167         }
168     }
169 
170     private Uri testUri(Uri baseUri) {
171         return baseUri.buildUpon().appendQueryParameter(BrowserContract.PARAM_IS_TEST, "1").build();
172     }
173 
174     private ContentValues createMockRemoteClientValues(String name) {
175         final long now = System.currentTimeMillis();
176         ContentValues cli = new ContentValues();
177         cli.put(BrowserContract.RemoteDevices.GUID, UUID.randomUUID().toString());
178         cli.put(BrowserContract.RemoteDevices.NAME, name);
179         cli.put(BrowserContract.RemoteDevices.TYPE, "mobile");
180         cli.put(BrowserContract.RemoteDevices.IS_CURRENT_DEVICE, false);
181         cli.put(BrowserContract.RemoteDevices.LAST_ACCESS_TIME, System.currentTimeMillis());
182         cli.put(BrowserContract.RemoteDevices.DATE_CREATED, now);
183         cli.put(BrowserContract.RemoteDevices.DATE_MODIFIED, now);
184         return cli;
185     }
186 }
187