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