|
1 /* Any copyright is dedicated to the Public Domain. |
|
2 http://creativecommons.org/publicdomain/zero/1.0/ |
|
3 |
|
4 /** |
|
5 * This test case populates the profile with some fake stored |
|
6 * pings, and checks that: |
|
7 * |
|
8 * 1) Pings that are considered "expired" are deleted and never sent. |
|
9 * 2) Pings that are considered "overdue" trigger a send of all |
|
10 * overdue and recent pings. |
|
11 */ |
|
12 |
|
13 "use strict" |
|
14 |
|
15 const Cc = Components.classes; |
|
16 const Ci = Components.interfaces; |
|
17 const Cr = Components.results; |
|
18 const Cu = Components.utils; |
|
19 |
|
20 Cu.import("resource://gre/modules/Services.jsm", this); |
|
21 Cu.import("resource://testing-common/httpd.js", this); |
|
22 Cu.import("resource://gre/modules/Promise.jsm", this); |
|
23 Cu.import("resource://gre/modules/TelemetryFile.jsm", this); |
|
24 Cu.import("resource://gre/modules/TelemetryPing.jsm", this); |
|
25 Cu.import("resource://gre/modules/Task.jsm", this); |
|
26 let {OS: {File, Path, Constants}} = Cu.import("resource://gre/modules/osfile.jsm", {}); |
|
27 |
|
28 // We increment TelemetryFile's MAX_PING_FILE_AGE and |
|
29 // OVERDUE_PING_FILE_AGE by 1 minute so that our test pings exceed |
|
30 // those points in time, even taking into account file system imprecision. |
|
31 const ONE_MINUTE_MS = 60 * 1000; |
|
32 const EXPIRED_PING_FILE_AGE = TelemetryFile.MAX_PING_FILE_AGE + ONE_MINUTE_MS; |
|
33 const OVERDUE_PING_FILE_AGE = TelemetryFile.OVERDUE_PING_FILE_AGE + ONE_MINUTE_MS; |
|
34 |
|
35 const PING_SAVE_FOLDER = "saved-telemetry-pings"; |
|
36 const PING_TIMEOUT_LENGTH = 5000; |
|
37 const EXPIRED_PINGS = 5; |
|
38 const OVERDUE_PINGS = 6; |
|
39 const RECENT_PINGS = 4; |
|
40 |
|
41 const TOTAL_EXPECTED_PINGS = OVERDUE_PINGS + RECENT_PINGS; |
|
42 |
|
43 let gHttpServer = new HttpServer(); |
|
44 let gCreatedPings = 0; |
|
45 let gSeenPings = 0; |
|
46 |
|
47 /** |
|
48 * Creates some TelemetryPings for the current session and |
|
49 * saves them to disk. Each ping gets a unique ID slug based on |
|
50 * an incrementor. |
|
51 * |
|
52 * @param aNum the number of pings to create. |
|
53 * @param aAge the age in milliseconds to offset from now. A value |
|
54 * of 10 would make the ping 10ms older than now, for |
|
55 * example. |
|
56 * @returns Promise |
|
57 * @resolve an Array with the created pings. |
|
58 */ |
|
59 function createSavedPings(aNum, aAge) { |
|
60 return Task.spawn(function*(){ |
|
61 // Create a TelemetryPing service that we can generate payloads from. |
|
62 // Luckily, the TelemetryPing constructor does nothing that we need to |
|
63 // clean up. |
|
64 let pings = []; |
|
65 let age = Date.now() - aAge; |
|
66 |
|
67 for (let i = 0; i < aNum; ++i) { |
|
68 let payload = TelemetryPing.getPayload(); |
|
69 let ping = { slug: "test-ping-" + gCreatedPings, reason: "test", payload: payload }; |
|
70 |
|
71 yield TelemetryFile.savePing(ping); |
|
72 |
|
73 if (aAge) { |
|
74 // savePing writes to the file synchronously, so we're good to |
|
75 // modify the lastModifedTime now. |
|
76 let file = getSavePathForPing(ping); |
|
77 yield File.setDates(file, null, age); |
|
78 } |
|
79 gCreatedPings++; |
|
80 pings.push(ping); |
|
81 } |
|
82 return pings; |
|
83 }); |
|
84 } |
|
85 |
|
86 /** |
|
87 * Deletes locally saved pings in aPings if they |
|
88 * exist. |
|
89 * |
|
90 * @param aPings an Array of pings to delete. |
|
91 * @returns Promise |
|
92 */ |
|
93 function clearPings(aPings) { |
|
94 return Task.spawn(function*() { |
|
95 for (let ping of aPings) { |
|
96 let path = getSavePathForPing(ping); |
|
97 yield File.remove(path); |
|
98 } |
|
99 }); |
|
100 } |
|
101 |
|
102 /** |
|
103 * Returns a handle for the file that aPing should be |
|
104 * stored in locally. |
|
105 * |
|
106 * @returns path |
|
107 */ |
|
108 function getSavePathForPing(aPing) { |
|
109 return Path.join(Constants.Path.profileDir, PING_SAVE_FOLDER, aPing.slug); |
|
110 } |
|
111 |
|
112 /** |
|
113 * Check if the number of TelemetryPings received by the |
|
114 * HttpServer is not equal to aExpectedNum. |
|
115 * |
|
116 * @param aExpectedNum the number of pings we expect to receive. |
|
117 */ |
|
118 function assertReceivedPings(aExpectedNum) { |
|
119 do_check_eq(gSeenPings, aExpectedNum); |
|
120 } |
|
121 |
|
122 /** |
|
123 * Throws if any pings in aPings is saved locally. |
|
124 * |
|
125 * @param aPings an Array of pings to check. |
|
126 * @returns Promise |
|
127 */ |
|
128 function assertNotSaved(aPings) { |
|
129 return Task.spawn(function*() { |
|
130 let saved = 0; |
|
131 for (let ping of aPings) { |
|
132 let file = getSavePathForPing(ping); |
|
133 if (yield File.exists()) { |
|
134 saved++; |
|
135 } |
|
136 } |
|
137 if (saved > 0) { |
|
138 do_throw("Found " + saved + " unexpected saved pings."); |
|
139 } |
|
140 }); |
|
141 } |
|
142 |
|
143 /** |
|
144 * Our handler function for the HttpServer that simply |
|
145 * increments the gSeenPings global when it successfully |
|
146 * receives and decodes a TelemetryPing payload. |
|
147 * |
|
148 * @param aRequest the HTTP request sent from HttpServer. |
|
149 */ |
|
150 function pingHandler(aRequest) { |
|
151 gSeenPings++; |
|
152 } |
|
153 |
|
154 /** |
|
155 * Returns a Promise that resolves when gHttpServer has been |
|
156 * successfully shut down. |
|
157 * |
|
158 * @returns Promise |
|
159 */ |
|
160 function stopHttpServer() { |
|
161 let deferred = Promise.defer(); |
|
162 gHttpServer.stop(function() { |
|
163 deferred.resolve(); |
|
164 }) |
|
165 return deferred.promise; |
|
166 } |
|
167 |
|
168 /** |
|
169 * Teardown a TelemetryPing instance and clear out any pending |
|
170 * pings to put as back in the starting state. |
|
171 */ |
|
172 function resetTelemetry() { |
|
173 TelemetryPing.uninstall(); |
|
174 // Quick and dirty way to clear TelemetryFile's pendingPings |
|
175 // collection, and put it back in its initial state. |
|
176 let gen = TelemetryFile.popPendingPings(); |
|
177 for (let item of gen) {}; |
|
178 } |
|
179 |
|
180 /** |
|
181 * Creates and returns a TelemetryPing instance in "testing" |
|
182 * mode. |
|
183 */ |
|
184 function startTelemetry() { |
|
185 return TelemetryPing.setup(); |
|
186 } |
|
187 |
|
188 function run_test() { |
|
189 gHttpServer.registerPrefixHandler("/submit/telemetry/", pingHandler); |
|
190 gHttpServer.start(-1); |
|
191 do_get_profile(); |
|
192 Services.prefs.setBoolPref(TelemetryPing.Constants.PREF_ENABLED, true); |
|
193 Services.prefs.setCharPref(TelemetryPing.Constants.PREF_SERVER, |
|
194 "http://localhost:" + gHttpServer.identity.primaryPort); |
|
195 run_next_test(); |
|
196 } |
|
197 |
|
198 /** |
|
199 * Test that pings that are considered too old are just chucked out |
|
200 * immediately and never sent. |
|
201 */ |
|
202 add_task(function test_expired_pings_are_deleted() { |
|
203 let expiredPings = yield createSavedPings(EXPIRED_PINGS, EXPIRED_PING_FILE_AGE); |
|
204 yield startTelemetry(); |
|
205 assertReceivedPings(0); |
|
206 yield assertNotSaved(expiredPings); |
|
207 yield resetTelemetry(); |
|
208 }); |
|
209 |
|
210 /** |
|
211 * Test that really recent pings are not sent on Telemetry initialization. |
|
212 */ |
|
213 add_task(function test_recent_pings_not_sent() { |
|
214 let recentPings = yield createSavedPings(RECENT_PINGS); |
|
215 yield startTelemetry(); |
|
216 assertReceivedPings(0); |
|
217 yield resetTelemetry(); |
|
218 yield clearPings(recentPings); |
|
219 }); |
|
220 |
|
221 /** |
|
222 * Create some recent, expired and overdue pings. The overdue pings should |
|
223 * trigger a send of all recent and overdue pings, but the expired pings |
|
224 * should just be deleted. |
|
225 */ |
|
226 add_task(function test_overdue_pings_trigger_send() { |
|
227 let recentPings = yield createSavedPings(RECENT_PINGS); |
|
228 let expiredPings = yield createSavedPings(EXPIRED_PINGS, EXPIRED_PING_FILE_AGE); |
|
229 let overduePings = yield createSavedPings(OVERDUE_PINGS, OVERDUE_PING_FILE_AGE); |
|
230 |
|
231 yield startTelemetry(); |
|
232 assertReceivedPings(TOTAL_EXPECTED_PINGS); |
|
233 |
|
234 yield assertNotSaved(recentPings); |
|
235 yield assertNotSaved(expiredPings); |
|
236 yield assertNotSaved(overduePings); |
|
237 yield resetTelemetry(); |
|
238 }); |
|
239 |
|
240 add_task(function teardown() { |
|
241 yield stopHttpServer(); |
|
242 }); |