startupcache/test/TestStartupCache.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:7e7650391b5d
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "TestHarness.h"
7
8 #include "nsThreadUtils.h"
9 #include "nsIClassInfo.h"
10 #include "nsIOutputStream.h"
11 #include "nsIObserver.h"
12 #include "nsISerializable.h"
13 #include "nsISupports.h"
14 #include "nsIStartupCache.h"
15 #include "nsIStringStream.h"
16 #include "nsIStorageStream.h"
17 #include "nsIObjectInputStream.h"
18 #include "nsIObjectOutputStream.h"
19 #include "nsIURI.h"
20 #include "nsStringAPI.h"
21 #include "nsIPrefBranch.h"
22 #include "nsIPrefService.h"
23 #include "nsIXPConnect.h"
24 #include "prio.h"
25 #include "mozilla/Maybe.h"
26
27 using namespace JS;
28
29 namespace mozilla {
30 namespace scache {
31
32 NS_IMPORT nsresult
33 NewObjectInputStreamFromBuffer(char* buffer, uint32_t len,
34 nsIObjectInputStream** stream);
35
36 // We can't retrieve the wrapped stream from the objectOutputStream later,
37 // so we return it here.
38 NS_IMPORT nsresult
39 NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream,
40 nsIStorageStream** stream);
41
42 NS_IMPORT nsresult
43 NewBufferFromStorageStream(nsIStorageStream *storageStream,
44 char** buffer, uint32_t* len);
45 }
46 }
47
48 using namespace mozilla::scache;
49
50 #define NS_ENSURE_STR_MATCH(str1, str2, testname) \
51 PR_BEGIN_MACRO \
52 if (0 != strcmp(str1, str2)) { \
53 fail("failed " testname); \
54 return NS_ERROR_FAILURE; \
55 } \
56 passed("passed " testname); \
57 PR_END_MACRO
58
59 nsresult
60 WaitForStartupTimer() {
61 nsresult rv;
62 nsCOMPtr<nsIStartupCache> sc
63 = do_GetService("@mozilla.org/startupcache/cache;1");
64 PR_Sleep(10 * PR_TicksPerSecond());
65
66 bool complete;
67 while (true) {
68
69 NS_ProcessPendingEvents(nullptr);
70 rv = sc->StartupWriteComplete(&complete);
71 if (NS_FAILED(rv) || complete)
72 break;
73 PR_Sleep(1 * PR_TicksPerSecond());
74 }
75 return rv;
76 }
77
78 nsresult
79 TestStartupWriteRead() {
80 nsresult rv;
81 nsCOMPtr<nsIStartupCache> sc
82 = do_GetService("@mozilla.org/startupcache/cache;1", &rv);
83 if (!sc) {
84 fail("didn't get a pointer...");
85 return NS_ERROR_FAILURE;
86 } else {
87 passed("got a pointer?");
88 }
89 sc->InvalidateCache();
90
91 const char* buf = "Market opportunities for BeardBook";
92 const char* id = "id";
93 char* outbufPtr = nullptr;
94 nsAutoArrayPtr<char> outbuf;
95 uint32_t len;
96
97 rv = sc->PutBuffer(id, buf, strlen(buf) + 1);
98 NS_ENSURE_SUCCESS(rv, rv);
99
100 rv = sc->GetBuffer(id, &outbufPtr, &len);
101 NS_ENSURE_SUCCESS(rv, rv);
102 outbuf = outbufPtr;
103 NS_ENSURE_STR_MATCH(buf, outbuf, "pre-write read");
104
105 rv = sc->ResetStartupWriteTimer();
106 rv = WaitForStartupTimer();
107 NS_ENSURE_SUCCESS(rv, rv);
108
109 rv = sc->GetBuffer(id, &outbufPtr, &len);
110 NS_ENSURE_SUCCESS(rv, rv);
111 outbuf = outbufPtr;
112 NS_ENSURE_STR_MATCH(buf, outbuf, "simple write/read");
113
114 return NS_OK;
115 }
116
117 nsresult
118 TestWriteInvalidateRead() {
119 nsresult rv;
120 const char* buf = "BeardBook competitive analysis";
121 const char* id = "id";
122 char* outbuf = nullptr;
123 uint32_t len;
124 nsCOMPtr<nsIStartupCache> sc
125 = do_GetService("@mozilla.org/startupcache/cache;1", &rv);
126 sc->InvalidateCache();
127
128 rv = sc->PutBuffer(id, buf, strlen(buf) + 1);
129 NS_ENSURE_SUCCESS(rv, rv);
130
131 sc->InvalidateCache();
132
133 rv = sc->GetBuffer(id, &outbuf, &len);
134 delete[] outbuf;
135 if (rv == NS_ERROR_NOT_AVAILABLE) {
136 passed("buffer not available after invalidate");
137 } else if (NS_SUCCEEDED(rv)) {
138 fail("GetBuffer succeeded unexpectedly after invalidate");
139 return NS_ERROR_UNEXPECTED;
140 } else {
141 fail("GetBuffer gave an unexpected failure, expected NOT_AVAILABLE");
142 return rv;
143 }
144
145 sc->InvalidateCache();
146 return NS_OK;
147 }
148
149 nsresult
150 TestWriteObject() {
151 nsresult rv;
152
153 nsCOMPtr<nsIURI> obj
154 = do_CreateInstance("@mozilla.org/network/simple-uri;1");
155 if (!obj) {
156 fail("did not create object in test write object");
157 return NS_ERROR_UNEXPECTED;
158 }
159 NS_NAMED_LITERAL_CSTRING(spec, "http://www.mozilla.org");
160 obj->SetSpec(spec);
161 nsCOMPtr<nsIStartupCache> sc = do_GetService("@mozilla.org/startupcache/cache;1", &rv);
162
163 sc->InvalidateCache();
164
165 // Create an object stream. Usually this is done with
166 // NewObjectOutputWrappedStorageStream, but that uses
167 // StartupCache::GetSingleton in debug builds, and we
168 // don't have access to that here. Obviously.
169 const char* id = "id";
170 nsCOMPtr<nsIStorageStream> storageStream
171 = do_CreateInstance("@mozilla.org/storagestream;1");
172 NS_ENSURE_ARG_POINTER(storageStream);
173
174 rv = storageStream->Init(256, (uint32_t) -1);
175 NS_ENSURE_SUCCESS(rv, rv);
176
177 nsCOMPtr<nsIObjectOutputStream> objectOutput
178 = do_CreateInstance("@mozilla.org/binaryoutputstream;1");
179 if (!objectOutput)
180 return NS_ERROR_OUT_OF_MEMORY;
181
182 nsCOMPtr<nsIOutputStream> outputStream
183 = do_QueryInterface(storageStream);
184
185 rv = objectOutput->SetOutputStream(outputStream);
186
187 if (NS_FAILED(rv)) {
188 fail("failed to create output stream");
189 return rv;
190 }
191 nsCOMPtr<nsISupports> objQI(do_QueryInterface(obj));
192 rv = objectOutput->WriteObject(objQI, true);
193 if (NS_FAILED(rv)) {
194 fail("failed to write object");
195 return rv;
196 }
197
198 char* bufPtr = nullptr;
199 nsAutoArrayPtr<char> buf;
200 uint32_t len;
201 NewBufferFromStorageStream(storageStream, &bufPtr, &len);
202 buf = bufPtr;
203
204 // Since this is a post-startup write, it should be written and
205 // available.
206 rv = sc->PutBuffer(id, buf, len);
207 if (NS_FAILED(rv)) {
208 fail("failed to insert input stream");
209 return rv;
210 }
211
212 char* buf2Ptr = nullptr;
213 nsAutoArrayPtr<char> buf2;
214 uint32_t len2;
215 nsCOMPtr<nsIObjectInputStream> objectInput;
216 rv = sc->GetBuffer(id, &buf2Ptr, &len2);
217 if (NS_FAILED(rv)) {
218 fail("failed to retrieve buffer");
219 return rv;
220 }
221 buf2 = buf2Ptr;
222
223 rv = NewObjectInputStreamFromBuffer(buf2, len2, getter_AddRefs(objectInput));
224 if (NS_FAILED(rv)) {
225 fail("failed to created input stream");
226 return rv;
227 }
228 buf2.forget();
229
230 nsCOMPtr<nsISupports> deserialized;
231 rv = objectInput->ReadObject(true, getter_AddRefs(deserialized));
232 if (NS_FAILED(rv)) {
233 fail("failed to read object");
234 return rv;
235 }
236
237 bool match = false;
238 nsCOMPtr<nsIURI> uri(do_QueryInterface(deserialized));
239 if (uri) {
240 nsCString outSpec;
241 uri->GetSpec(outSpec);
242 match = outSpec.Equals(spec);
243 }
244 if (!match) {
245 fail("deserialized object has incorrect information");
246 return rv;
247 }
248
249 passed("write object");
250 return NS_OK;
251 }
252
253 nsresult
254 LockCacheFile(bool protect, nsIFile* profileDir) {
255 NS_ENSURE_ARG(profileDir);
256
257 nsCOMPtr<nsIFile> startupCache;
258 profileDir->Clone(getter_AddRefs(startupCache));
259 NS_ENSURE_STATE(startupCache);
260 startupCache->AppendNative(NS_LITERAL_CSTRING("startupCache"));
261
262 nsresult rv;
263 #ifndef XP_WIN
264 static uint32_t oldPermissions;
265 #else
266 static PRFileDesc* fd = nullptr;
267 #endif
268
269 // To prevent deletion of the startupcache file, we change the containing
270 // directory's permissions on Linux/Mac, and hold the file open on Windows
271 if (protect) {
272 #ifndef XP_WIN
273 rv = startupCache->GetPermissions(&oldPermissions);
274 NS_ENSURE_SUCCESS(rv, rv);
275 rv = startupCache->SetPermissions(0555);
276 NS_ENSURE_SUCCESS(rv, rv);
277 #else
278 // Filename logic from StartupCache.cpp
279 #ifdef IS_BIG_ENDIAN
280 #define SC_ENDIAN "big"
281 #else
282 #define SC_ENDIAN "little"
283 #endif
284
285 #if PR_BYTES_PER_WORD == 4
286 #define SC_WORDSIZE "4"
287 #else
288 #define SC_WORDSIZE "8"
289 #endif
290 char sStartupCacheName[] = "startupCache." SC_WORDSIZE "." SC_ENDIAN;
291 startupCache->AppendNative(NS_LITERAL_CSTRING(sStartupCacheName));
292
293 rv = startupCache->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
294 NS_ENSURE_SUCCESS(rv, rv);
295 #endif
296 } else {
297 #ifndef XP_WIN
298 rv = startupCache->SetPermissions(oldPermissions);
299 NS_ENSURE_SUCCESS(rv, rv);
300 #else
301 PR_Close(fd);
302 #endif
303 }
304
305 return NS_OK;
306 }
307
308 nsresult
309 TestIgnoreDiskCache(nsIFile* profileDir) {
310 nsresult rv;
311 nsCOMPtr<nsIStartupCache> sc
312 = do_GetService("@mozilla.org/startupcache/cache;1", &rv);
313 sc->InvalidateCache();
314
315 const char* buf = "Get a Beardbook app for your smartphone";
316 const char* id = "id";
317 char* outbuf = nullptr;
318 uint32_t len;
319
320 rv = sc->PutBuffer(id, buf, strlen(buf) + 1);
321 NS_ENSURE_SUCCESS(rv, rv);
322 rv = sc->ResetStartupWriteTimer();
323 rv = WaitForStartupTimer();
324 NS_ENSURE_SUCCESS(rv, rv);
325
326 // Prevent StartupCache::InvalidateCache from deleting the disk file
327 rv = LockCacheFile(true, profileDir);
328 NS_ENSURE_SUCCESS(rv, rv);
329
330 sc->IgnoreDiskCache();
331
332 rv = sc->GetBuffer(id, &outbuf, &len);
333
334 nsresult r = LockCacheFile(false, profileDir);
335 NS_ENSURE_SUCCESS(r, r);
336
337 delete[] outbuf;
338
339 if (rv == NS_ERROR_NOT_AVAILABLE) {
340 passed("buffer not available after ignoring disk cache");
341 } else if (NS_SUCCEEDED(rv)) {
342 fail("GetBuffer succeeded unexpectedly after ignoring disk cache");
343 return NS_ERROR_UNEXPECTED;
344 } else {
345 fail("GetBuffer gave an unexpected failure, expected NOT_AVAILABLE");
346 return rv;
347 }
348
349 sc->InvalidateCache();
350 return NS_OK;
351 }
352
353 nsresult
354 TestEarlyShutdown() {
355 nsresult rv;
356 nsCOMPtr<nsIStartupCache> sc
357 = do_GetService("@mozilla.org/startupcache/cache;1", &rv);
358 sc->InvalidateCache();
359
360 const char* buf = "Find your soul beardmate on BeardBook";
361 const char* id = "id";
362 uint32_t len;
363 char* outbuf = nullptr;
364
365 sc->ResetStartupWriteTimer();
366 rv = sc->PutBuffer(id, buf, strlen(buf) + 1);
367 NS_ENSURE_SUCCESS(rv, rv);
368
369 nsCOMPtr<nsIObserver> obs;
370 sc->GetObserver(getter_AddRefs(obs));
371 obs->Observe(nullptr, "xpcom-shutdown", nullptr);
372 rv = WaitForStartupTimer();
373 NS_ENSURE_SUCCESS(rv, rv);
374
375 rv = sc->GetBuffer(id, &outbuf, &len);
376 delete[] outbuf;
377
378 if (NS_SUCCEEDED(rv)) {
379 passed("GetBuffer succeeded after early shutdown");
380 } else {
381 fail("GetBuffer failed after early shutdown");
382 return rv;
383 }
384
385 const char* other_id = "other_id";
386 rv = sc->PutBuffer(other_id, buf, strlen(buf) + 1);
387
388 if (rv == NS_ERROR_NOT_AVAILABLE) {
389 passed("PutBuffer not available after early shutdown");
390 } else if (NS_SUCCEEDED(rv)) {
391 fail("PutBuffer succeeded unexpectedly after early shutdown");
392 return NS_ERROR_UNEXPECTED;
393 } else {
394 fail("PutBuffer gave an unexpected failure, expected NOT_AVAILABLE");
395 return rv;
396 }
397
398 return NS_OK;
399 }
400
401 int main(int argc, char** argv)
402 {
403 ScopedXPCOM xpcom("Startup Cache");
404 if (xpcom.failed())
405 return 1;
406
407 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
408 prefs->SetIntPref("hangmonitor.timeout", 0);
409
410 int rv = 0;
411 nsresult scrv;
412
413 // Register TestStartupCacheTelemetry
414 nsCOMPtr<nsIFile> manifest;
415 scrv = NS_GetSpecialDirectory(NS_GRE_DIR,
416 getter_AddRefs(manifest));
417 if (NS_FAILED(scrv)) {
418 fail("NS_XPCOM_CURRENT_PROCESS_DIR");
419 return 1;
420 }
421
422 manifest->AppendNative(NS_LITERAL_CSTRING("TestStartupCacheTelemetry.manifest"));
423 XRE_AddManifestLocation(NS_COMPONENT_LOCATION, manifest);
424
425 nsCOMPtr<nsIObserver> telemetryThing =
426 do_GetService("@mozilla.org/testing/startup-cache-telemetry.js");
427 if (!telemetryThing) {
428 fail("telemetryThing");
429 return 1;
430 }
431 scrv = telemetryThing->Observe(nullptr, "save-initial", nullptr);
432 if (NS_FAILED(scrv)) {
433 fail("save-initial");
434 rv = 1;
435 }
436
437 nsCOMPtr<nsIStartupCache> sc
438 = do_GetService("@mozilla.org/startupcache/cache;1", &scrv);
439 if (NS_FAILED(scrv))
440 rv = 1;
441 else
442 sc->RecordAgesAlways();
443 if (NS_FAILED(TestStartupWriteRead()))
444 rv = 1;
445 if (NS_FAILED(TestWriteInvalidateRead()))
446 rv = 1;
447 if (NS_FAILED(TestWriteObject()))
448 rv = 1;
449 nsCOMPtr<nsIFile> profileDir = xpcom.GetProfileDirectory();
450 if (NS_FAILED(TestIgnoreDiskCache(profileDir)))
451 rv = 1;
452 if (NS_FAILED(TestEarlyShutdown()))
453 rv = 1;
454
455 scrv = telemetryThing->Observe(nullptr, "save-initial", nullptr);
456 if (NS_FAILED(scrv)) {
457 fail("check-final");
458 rv = 1;
459 }
460
461 return rv;
462 }

mercurial