|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 this.EXPORTED_SYMBOLS = [ |
|
8 "getAppInfo", |
|
9 "updateAppInfo", |
|
10 "createFakeCrash", |
|
11 "InspectedHealthReporter", |
|
12 "getHealthReporter", |
|
13 ]; |
|
14 |
|
15 |
|
16 const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components; |
|
17 |
|
18 Cu.import("resource://gre/modules/Preferences.jsm"); |
|
19 Cu.import("resource://gre/modules/Promise.jsm"); |
|
20 Cu.import("resource://gre/modules/FileUtils.jsm"); |
|
21 Cu.import("resource://gre/modules/osfile.jsm"); |
|
22 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
23 Cu.import("resource://gre/modules/services-common/utils.js"); |
|
24 Cu.import("resource://gre/modules/services/datareporting/policy.jsm"); |
|
25 Cu.import("resource://gre/modules/services/healthreport/healthreporter.jsm"); |
|
26 Cu.import("resource://gre/modules/services/healthreport/providers.jsm"); |
|
27 |
|
28 |
|
29 let APP_INFO = { |
|
30 vendor: "Mozilla", |
|
31 name: "xpcshell", |
|
32 ID: "xpcshell@tests.mozilla.org", |
|
33 version: "1", |
|
34 appBuildID: "20121107", |
|
35 platformVersion: "p-ver", |
|
36 platformBuildID: "20121106", |
|
37 inSafeMode: false, |
|
38 logConsoleErrors: true, |
|
39 OS: "XPCShell", |
|
40 XPCOMABI: "noarch-spidermonkey", |
|
41 QueryInterface: XPCOMUtils.generateQI([Ci.nsIXULAppInfo, Ci.nsIXULRuntime]), |
|
42 invalidateCachesOnRestart: function() {}, |
|
43 }; |
|
44 |
|
45 |
|
46 /** |
|
47 * Obtain a reference to the current object used to define XULAppInfo. |
|
48 */ |
|
49 this.getAppInfo = function () { return APP_INFO; } |
|
50 |
|
51 /** |
|
52 * Update the current application info. |
|
53 * |
|
54 * If the argument is defined, it will be the object used. Else, APP_INFO is |
|
55 * used. |
|
56 * |
|
57 * To change the current XULAppInfo, simply call this function. If there was |
|
58 * a previously registered app info object, it will be unloaded and replaced. |
|
59 */ |
|
60 this.updateAppInfo = function (obj) { |
|
61 obj = obj || APP_INFO; |
|
62 APP_INFO = obj; |
|
63 |
|
64 let id = Components.ID("{fbfae60b-64a4-44ef-a911-08ceb70b9f31}"); |
|
65 let cid = "@mozilla.org/xre/app-info;1"; |
|
66 let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); |
|
67 |
|
68 // Unregister an existing factory if one exists. |
|
69 try { |
|
70 let existing = Components.manager.getClassObjectByContractID(cid, Ci.nsIFactory); |
|
71 registrar.unregisterFactory(id, existing); |
|
72 } catch (ex) {} |
|
73 |
|
74 let factory = { |
|
75 createInstance: function (outer, iid) { |
|
76 if (outer != null) { |
|
77 throw Cr.NS_ERROR_NO_AGGREGATION; |
|
78 } |
|
79 |
|
80 return obj.QueryInterface(iid); |
|
81 }, |
|
82 }; |
|
83 |
|
84 registrar.registerFactory(id, "XULAppInfo", cid, factory); |
|
85 }; |
|
86 |
|
87 /** |
|
88 * Creates a fake crash in the Crash Reports directory. |
|
89 * |
|
90 * Currently, we just create a dummy file. A more robust implementation would |
|
91 * create something that actually resembles a crash report file. |
|
92 * |
|
93 * This is very similar to code in crashreporter/tests/browser/head.js. |
|
94 * |
|
95 * FUTURE consolidate code in a shared JSM. |
|
96 */ |
|
97 this.createFakeCrash = function (submitted=false, date=new Date()) { |
|
98 let id = CommonUtils.generateUUID(); |
|
99 let filename; |
|
100 |
|
101 let paths = ["Crash Reports"]; |
|
102 let mode; |
|
103 |
|
104 if (submitted) { |
|
105 paths.push("submitted"); |
|
106 filename = "bp-" + id + ".txt"; |
|
107 mode = OS.Constants.libc.S_IRUSR | OS.Constants.libc.S_IWUSR | |
|
108 OS.Constants.libc.S_IRGRP | OS.Constants.libc.S_IROTH; |
|
109 } else { |
|
110 paths.push("pending"); |
|
111 filename = id + ".dmp"; |
|
112 mode = OS.Constants.libc.S_IRUSR | OS.Constants.libc.S_IWUSR; |
|
113 } |
|
114 |
|
115 paths.push(filename); |
|
116 |
|
117 let file = FileUtils.getFile("UAppData", paths, true); |
|
118 file.create(file.NORMAL_FILE_TYPE, mode); |
|
119 file.lastModifiedTime = date.getTime(); |
|
120 dump("Created fake crash: " + id + "\n"); |
|
121 |
|
122 return id; |
|
123 }; |
|
124 |
|
125 |
|
126 /** |
|
127 * A HealthReporter that is probed with various callbacks and counters. |
|
128 * |
|
129 * The purpose of this type is to aid testing of startup and shutdown. |
|
130 */ |
|
131 this.InspectedHealthReporter = function (branch, policy, recorder, stateLeaf) { |
|
132 HealthReporter.call(this, branch, policy, recorder, stateLeaf); |
|
133 |
|
134 this.onStorageCreated = null; |
|
135 this.onProviderManagerInitialized = null; |
|
136 this.providerManagerShutdownCount = 0; |
|
137 this.storageCloseCount = 0; |
|
138 } |
|
139 |
|
140 InspectedHealthReporter.prototype = { |
|
141 __proto__: HealthReporter.prototype, |
|
142 |
|
143 _onStorageCreated: function (storage) { |
|
144 if (this.onStorageCreated) { |
|
145 this.onStorageCreated(storage); |
|
146 } |
|
147 |
|
148 return HealthReporter.prototype._onStorageCreated.call(this, storage); |
|
149 }, |
|
150 |
|
151 _initializeProviderManager: function () { |
|
152 for (let result of HealthReporter.prototype._initializeProviderManager.call(this)) { |
|
153 yield result; |
|
154 } |
|
155 |
|
156 if (this.onInitializeProviderManagerFinished) { |
|
157 this.onInitializeProviderManagerFinished(); |
|
158 } |
|
159 }, |
|
160 |
|
161 _onProviderManagerInitialized: function () { |
|
162 if (this.onProviderManagerInitialized) { |
|
163 this.onProviderManagerInitialized(); |
|
164 } |
|
165 |
|
166 return HealthReporter.prototype._onProviderManagerInitialized.call(this); |
|
167 }, |
|
168 |
|
169 _onProviderManagerShutdown: function () { |
|
170 this.providerManagerShutdownCount++; |
|
171 |
|
172 return HealthReporter.prototype._onProviderManagerShutdown.call(this); |
|
173 }, |
|
174 |
|
175 _onStorageClose: function () { |
|
176 this.storageCloseCount++; |
|
177 |
|
178 return HealthReporter.prototype._onStorageClose.call(this); |
|
179 }, |
|
180 }; |
|
181 |
|
182 const DUMMY_URI="http://localhost:62013/"; |
|
183 |
|
184 this.getHealthReporter = function (name, uri=DUMMY_URI, inspected=false) { |
|
185 let branch = "healthreport.testing." + name + "."; |
|
186 |
|
187 let prefs = new Preferences(branch + "healthreport."); |
|
188 prefs.set("documentServerURI", uri); |
|
189 prefs.set("dbName", name); |
|
190 |
|
191 let reporter; |
|
192 |
|
193 let policyPrefs = new Preferences(branch + "policy."); |
|
194 let policy = new DataReportingPolicy(policyPrefs, prefs, { |
|
195 onRequestDataUpload: function (request) { |
|
196 reporter.requestDataUpload(request); |
|
197 }, |
|
198 |
|
199 onNotifyDataPolicy: function (request) { }, |
|
200 |
|
201 onRequestRemoteDelete: function (request) { |
|
202 reporter.deleteRemoteData(request); |
|
203 }, |
|
204 }); |
|
205 |
|
206 let type = inspected ? InspectedHealthReporter : HealthReporter; |
|
207 reporter = new type(branch + "healthreport.", policy, null, |
|
208 "state-" + name + ".json"); |
|
209 |
|
210 return reporter; |
|
211 }; |