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