Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
michael@0 | 1 | /* Any copyright is dedicated to the Public Domain. |
michael@0 | 2 | http://creativecommons.org/publicdomain/zero/1.0/ */ |
michael@0 | 3 | |
michael@0 | 4 | package org.mozilla.gecko.background.healthreport; |
michael@0 | 5 | |
michael@0 | 6 | import org.mozilla.gecko.background.helpers.DBHelpers; |
michael@0 | 7 | import org.mozilla.gecko.background.helpers.DBProviderTestCase; |
michael@0 | 8 | |
michael@0 | 9 | import android.content.ContentResolver; |
michael@0 | 10 | import android.content.ContentValues; |
michael@0 | 11 | import android.database.Cursor; |
michael@0 | 12 | import android.net.Uri; |
michael@0 | 13 | import android.test.mock.MockContentResolver; |
michael@0 | 14 | |
michael@0 | 15 | public class TestHealthReportProvider extends DBProviderTestCase<HealthReportProvider> { |
michael@0 | 16 | protected static final int MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000; |
michael@0 | 17 | |
michael@0 | 18 | public TestHealthReportProvider() { |
michael@0 | 19 | super(HealthReportProvider.class, HealthReportProvider.HEALTH_AUTHORITY); |
michael@0 | 20 | } |
michael@0 | 21 | |
michael@0 | 22 | public TestHealthReportProvider(Class<HealthReportProvider> providerClass, |
michael@0 | 23 | String providerAuthority) { |
michael@0 | 24 | super(providerClass, providerAuthority); |
michael@0 | 25 | } |
michael@0 | 26 | |
michael@0 | 27 | private Uri getCompleteUri(String rest) { |
michael@0 | 28 | return Uri.parse("content://" + HealthReportProvider.HEALTH_AUTHORITY + rest + |
michael@0 | 29 | (rest.indexOf('?') == -1 ? "?" : "&") + |
michael@0 | 30 | "profilePath=" + Uri.encode(fakeProfileDirectory.getAbsolutePath())); |
michael@0 | 31 | } |
michael@0 | 32 | |
michael@0 | 33 | private void ensureCount(int expected, Uri uri) { |
michael@0 | 34 | final MockContentResolver resolver = getMockContentResolver(); |
michael@0 | 35 | Cursor cursor = resolver.query(uri, null, null, null, null); |
michael@0 | 36 | assertNotNull(cursor); |
michael@0 | 37 | assertEquals(expected, cursor.getCount()); |
michael@0 | 38 | cursor.close(); |
michael@0 | 39 | } |
michael@0 | 40 | |
michael@0 | 41 | private void ensureMeasurementCount(int expected) { |
michael@0 | 42 | final Uri measurements = getCompleteUri("/measurements/"); |
michael@0 | 43 | ensureCount(expected, measurements); |
michael@0 | 44 | } |
michael@0 | 45 | |
michael@0 | 46 | private void ensureFieldCount(int expected) { |
michael@0 | 47 | final Uri fields = getCompleteUri("/fields/"); |
michael@0 | 48 | ensureCount(expected, fields); |
michael@0 | 49 | } |
michael@0 | 50 | |
michael@0 | 51 | public void testNonExistentMeasurement() { |
michael@0 | 52 | assertNotNull(getContext()); |
michael@0 | 53 | Uri u = getCompleteUri("/events/" + 0 + "/" + "testm" + "/" + 3 + "/" + "testf"); |
michael@0 | 54 | ContentValues v = new ContentValues(); |
michael@0 | 55 | v.put("value", 5); |
michael@0 | 56 | ContentResolver r = getMockContentResolver(); |
michael@0 | 57 | assertNotNull(r); |
michael@0 | 58 | try { |
michael@0 | 59 | r.insert(u, v); |
michael@0 | 60 | fail("Should throw."); |
michael@0 | 61 | } catch (IllegalStateException e) { |
michael@0 | 62 | assertTrue(e.getMessage().contains("No field with name testf")); |
michael@0 | 63 | } |
michael@0 | 64 | } |
michael@0 | 65 | |
michael@0 | 66 | public void testEnsureMeasurements() { |
michael@0 | 67 | ensureMeasurementCount(0); |
michael@0 | 68 | |
michael@0 | 69 | final MockContentResolver resolver = getMockContentResolver(); |
michael@0 | 70 | |
michael@0 | 71 | // Note that we insert no fields. These are empty measurements. |
michael@0 | 72 | ContentValues values = new ContentValues(); |
michael@0 | 73 | resolver.insert(getCompleteUri("/fields/testm1/1"), values); |
michael@0 | 74 | ensureMeasurementCount(1); |
michael@0 | 75 | |
michael@0 | 76 | resolver.insert(getCompleteUri("/fields/testm1/1"), values); |
michael@0 | 77 | ensureMeasurementCount(1); |
michael@0 | 78 | |
michael@0 | 79 | resolver.insert(getCompleteUri("/fields/testm1/3"), values); |
michael@0 | 80 | ensureMeasurementCount(2); |
michael@0 | 81 | |
michael@0 | 82 | resolver.insert(getCompleteUri("/fields/testm2/1"), values); |
michael@0 | 83 | ensureMeasurementCount(3); |
michael@0 | 84 | |
michael@0 | 85 | Cursor cursor = resolver.query(getCompleteUri("/measurements/"), null, null, null, null); |
michael@0 | 86 | |
michael@0 | 87 | assertTrue(cursor.moveToFirst()); |
michael@0 | 88 | assertEquals("testm1", cursor.getString(1)); // 'id' is column 0. |
michael@0 | 89 | assertEquals(1, cursor.getInt(2)); |
michael@0 | 90 | |
michael@0 | 91 | assertTrue(cursor.moveToNext()); |
michael@0 | 92 | assertEquals("testm1", cursor.getString(1)); |
michael@0 | 93 | assertEquals(3, cursor.getInt(2)); |
michael@0 | 94 | |
michael@0 | 95 | assertTrue(cursor.moveToNext()); |
michael@0 | 96 | assertEquals("testm2", cursor.getString(1)); |
michael@0 | 97 | assertEquals(1, cursor.getInt(2)); |
michael@0 | 98 | assertFalse(cursor.moveToNext()); |
michael@0 | 99 | |
michael@0 | 100 | cursor.close(); |
michael@0 | 101 | |
michael@0 | 102 | resolver.delete(getCompleteUri("/measurements/"), null, null); |
michael@0 | 103 | } |
michael@0 | 104 | |
michael@0 | 105 | /** |
michael@0 | 106 | * Return true if the two times occur on the same UTC day. |
michael@0 | 107 | */ |
michael@0 | 108 | private static boolean sameDay(long start, long end) { |
michael@0 | 109 | return Math.floor(start / MILLISECONDS_PER_DAY) == |
michael@0 | 110 | Math.floor(end / MILLISECONDS_PER_DAY); |
michael@0 | 111 | } |
michael@0 | 112 | |
michael@0 | 113 | private static int getDay(long time) { |
michael@0 | 114 | return (int) Math.floor(time / MILLISECONDS_PER_DAY); |
michael@0 | 115 | } |
michael@0 | 116 | |
michael@0 | 117 | |
michael@0 | 118 | public void testRealData() { |
michael@0 | 119 | ensureMeasurementCount(0); |
michael@0 | 120 | long start = System.currentTimeMillis(); |
michael@0 | 121 | int day = getDay(start); |
michael@0 | 122 | |
michael@0 | 123 | final MockContentResolver resolver = getMockContentResolver(); |
michael@0 | 124 | |
michael@0 | 125 | // Register a provider with four fields. |
michael@0 | 126 | ContentValues values = new ContentValues(); |
michael@0 | 127 | values.put("counter1", 1); |
michael@0 | 128 | values.put("counter2", 4); |
michael@0 | 129 | values.put("last1", 7); |
michael@0 | 130 | values.put("discrete1", 11); |
michael@0 | 131 | |
michael@0 | 132 | resolver.insert(getCompleteUri("/fields/testm1/1"), values); |
michael@0 | 133 | ensureMeasurementCount(1); |
michael@0 | 134 | ensureFieldCount(4); |
michael@0 | 135 | |
michael@0 | 136 | final Uri envURI = resolver.insert(getCompleteUri("/environments/"), getTestEnvContentValues()); |
michael@0 | 137 | String envHash = null; |
michael@0 | 138 | Cursor envCursor = resolver.query(envURI, null, null, null, null); |
michael@0 | 139 | try { |
michael@0 | 140 | assertTrue(envCursor.moveToFirst()); |
michael@0 | 141 | envHash = envCursor.getString(2); // id, version, hash, ... |
michael@0 | 142 | } finally { |
michael@0 | 143 | envCursor.close(); |
michael@0 | 144 | } |
michael@0 | 145 | |
michael@0 | 146 | final Uri eventURI = HealthReportUtils.getEventURI(envURI); |
michael@0 | 147 | |
michael@0 | 148 | Uri discrete1 = eventURI.buildUpon().appendEncodedPath("testm1/1/discrete1").build(); |
michael@0 | 149 | Uri counter1 = eventURI.buildUpon().appendEncodedPath("testm1/1/counter1/counter").build(); |
michael@0 | 150 | Uri counter2 = eventURI.buildUpon().appendEncodedPath("testm1/1/counter2/counter").build(); |
michael@0 | 151 | Uri last1 = eventURI.buildUpon().appendEncodedPath("testm1/1/last1/last").build(); |
michael@0 | 152 | |
michael@0 | 153 | ContentValues discreteS = new ContentValues(); |
michael@0 | 154 | ContentValues discreteI = new ContentValues(); |
michael@0 | 155 | |
michael@0 | 156 | discreteS.put("value", "Some string"); |
michael@0 | 157 | discreteI.put("value", 9); |
michael@0 | 158 | resolver.insert(discrete1, discreteS); |
michael@0 | 159 | resolver.insert(discrete1, discreteI); |
michael@0 | 160 | |
michael@0 | 161 | ContentValues counter = new ContentValues(); |
michael@0 | 162 | resolver.update(counter1, counter, null, null); // Defaults to 1. |
michael@0 | 163 | resolver.update(counter2, counter, null, null); // Defaults to 1. |
michael@0 | 164 | counter.put("value", 3); |
michael@0 | 165 | resolver.update(counter2, counter, null, null); // Increment by 3. |
michael@0 | 166 | |
michael@0 | 167 | // Interleaving. |
michael@0 | 168 | discreteS.put("value", "Some other string"); |
michael@0 | 169 | discreteI.put("value", 3); |
michael@0 | 170 | resolver.insert(discrete1, discreteS); |
michael@0 | 171 | resolver.insert(discrete1, discreteI); |
michael@0 | 172 | |
michael@0 | 173 | // Note that we explicitly do not support last-values transitioning between types. |
michael@0 | 174 | ContentValues last = new ContentValues(); |
michael@0 | 175 | last.put("value", 123); |
michael@0 | 176 | resolver.update(last1, last, null, null); |
michael@0 | 177 | last.put("value", 245); |
michael@0 | 178 | resolver.update(last1, last, null, null); |
michael@0 | 179 | |
michael@0 | 180 | int expectedRows = 2 + 1 + 4; // Two counters, one last, four entries for discrete. |
michael@0 | 181 | |
michael@0 | 182 | // Now let's see what comes up in the query! |
michael@0 | 183 | // We'll do "named" first -- the results include strings. |
michael@0 | 184 | Cursor cursor = resolver.query(getCompleteUri("/events/?time=" + start), null, null, null, null); |
michael@0 | 185 | assertEquals(expectedRows, cursor.getCount()); |
michael@0 | 186 | assertTrue(cursor.moveToFirst()); |
michael@0 | 187 | |
michael@0 | 188 | // Let's be safe in case someone runs this test at midnight. |
michael@0 | 189 | long end = System.currentTimeMillis(); |
michael@0 | 190 | if (!sameDay(start, end)) { |
michael@0 | 191 | System.out.println("Aborting testAddData: spans midnight."); |
michael@0 | 192 | cursor.close(); |
michael@0 | 193 | return; |
michael@0 | 194 | } |
michael@0 | 195 | |
michael@0 | 196 | // "date", "env", m, mv, f, f_flags, "value" |
michael@0 | 197 | Object[][] expected = { |
michael@0 | 198 | {day, envHash, "testm1", 1, "counter1", null, 1}, |
michael@0 | 199 | {day, envHash, "testm1", 1, "counter2", null, 4}, |
michael@0 | 200 | |
michael@0 | 201 | // Discrete values don't preserve order of insertion across types, but |
michael@0 | 202 | // this actually isn't really permitted -- fields have a single type. |
michael@0 | 203 | {day, envHash, "testm1", 1, "discrete1", null, 9}, |
michael@0 | 204 | {day, envHash, "testm1", 1, "discrete1", null, 3}, |
michael@0 | 205 | {day, envHash, "testm1", 1, "discrete1", null, "Some string"}, |
michael@0 | 206 | {day, envHash, "testm1", 1, "discrete1", null, "Some other string"}, |
michael@0 | 207 | {day, envHash, "testm1", 1, "last1", null, 245}, |
michael@0 | 208 | }; |
michael@0 | 209 | |
michael@0 | 210 | |
michael@0 | 211 | DBHelpers.assertCursorContains(expected, cursor); |
michael@0 | 212 | cursor.close(); |
michael@0 | 213 | |
michael@0 | 214 | resolver.delete(getCompleteUri("/measurements/"), null, null); |
michael@0 | 215 | ensureMeasurementCount(0); |
michael@0 | 216 | ensureFieldCount(0); |
michael@0 | 217 | } |
michael@0 | 218 | |
michael@0 | 219 | private ContentValues getTestEnvContentValues() { |
michael@0 | 220 | ContentValues v = new ContentValues(); |
michael@0 | 221 | v.put("profileCreation", 0); |
michael@0 | 222 | v.put("cpuCount", 0); |
michael@0 | 223 | v.put("memoryMB", 0); |
michael@0 | 224 | |
michael@0 | 225 | v.put("isBlocklistEnabled", 0); |
michael@0 | 226 | v.put("isTelemetryEnabled", 0); |
michael@0 | 227 | v.put("extensionCount", 0); |
michael@0 | 228 | v.put("pluginCount", 0); |
michael@0 | 229 | v.put("themeCount", 0); |
michael@0 | 230 | |
michael@0 | 231 | v.put("architecture", ""); |
michael@0 | 232 | v.put("sysName", ""); |
michael@0 | 233 | v.put("sysVersion", ""); |
michael@0 | 234 | v.put("vendor", ""); |
michael@0 | 235 | v.put("appName", ""); |
michael@0 | 236 | v.put("appID", ""); |
michael@0 | 237 | v.put("appVersion", ""); |
michael@0 | 238 | v.put("appBuildID", ""); |
michael@0 | 239 | v.put("platformVersion", ""); |
michael@0 | 240 | v.put("platformBuildID", ""); |
michael@0 | 241 | v.put("os", ""); |
michael@0 | 242 | v.put("xpcomabi", ""); |
michael@0 | 243 | v.put("updateChannel", ""); |
michael@0 | 244 | |
michael@0 | 245 | // v2. |
michael@0 | 246 | v.put("distribution", ""); |
michael@0 | 247 | v.put("osLocale", "en_us"); |
michael@0 | 248 | v.put("appLocale", "en_us"); |
michael@0 | 249 | v.put("acceptLangSet", 0); |
michael@0 | 250 | |
michael@0 | 251 | return v; |
michael@0 | 252 | } |
michael@0 | 253 | } |