Bumping manifests a=b2g-bump
[gecko.git] / startupcache / test / TestStartupCache.cpp
blob122950c82b699a7a757b8cef0a56f7997d456009
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/. */
6 #include "TestHarness.h"
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"
27 using namespace JS;
29 namespace mozilla {
30 namespace scache {
32 NS_IMPORT nsresult
33 NewObjectInputStreamFromBuffer(char* buffer, uint32_t len,
34 nsIObjectInputStream** stream);
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);
42 NS_IMPORT nsresult
43 NewBufferFromStorageStream(nsIStorageStream *storageStream,
44 char** buffer, uint32_t* len);
48 using namespace mozilla::scache;
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
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());
66 bool complete;
67 while (true) {
69 NS_ProcessPendingEvents(nullptr);
70 rv = sc->StartupWriteComplete(&complete);
71 if (NS_FAILED(rv) || complete)
72 break;
73 PR_Sleep(1 * PR_TicksPerSecond());
75 return rv;
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?");
89 sc->InvalidateCache();
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;
97 rv = sc->PutBuffer(id, buf, strlen(buf) + 1);
98 NS_ENSURE_SUCCESS(rv, rv);
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");
105 rv = sc->ResetStartupWriteTimer();
106 rv = WaitForStartupTimer();
107 NS_ENSURE_SUCCESS(rv, rv);
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");
114 return NS_OK;
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();
128 rv = sc->PutBuffer(id, buf, strlen(buf) + 1);
129 NS_ENSURE_SUCCESS(rv, rv);
131 sc->InvalidateCache();
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;
145 sc->InvalidateCache();
146 return NS_OK;
149 nsresult
150 TestWriteObject() {
151 nsresult rv;
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;
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);
163 sc->InvalidateCache();
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);
174 rv = storageStream->Init(256, (uint32_t) -1);
175 NS_ENSURE_SUCCESS(rv, rv);
177 nsCOMPtr<nsIObjectOutputStream> objectOutput
178 = do_CreateInstance("@mozilla.org/binaryoutputstream;1");
179 if (!objectOutput)
180 return NS_ERROR_OUT_OF_MEMORY;
182 nsCOMPtr<nsIOutputStream> outputStream
183 = do_QueryInterface(storageStream);
185 rv = objectOutput->SetOutputStream(outputStream);
187 if (NS_FAILED(rv)) {
188 fail("failed to create output stream");
189 return rv;
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;
198 char* bufPtr = nullptr;
199 nsAutoArrayPtr<char> buf;
200 uint32_t len;
201 NewBufferFromStorageStream(storageStream, &bufPtr, &len);
202 buf = bufPtr;
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;
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;
221 buf2 = buf2Ptr;
223 rv = NewObjectInputStreamFromBuffer(buf2, len2, getter_AddRefs(objectInput));
224 if (NS_FAILED(rv)) {
225 fail("failed to created input stream");
226 return rv;
228 buf2.forget();
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;
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);
244 if (!match) {
245 fail("deserialized object has incorrect information");
246 return rv;
249 passed("write object");
250 return NS_OK;
253 nsresult
254 LockCacheFile(bool protect, nsIFile* profileDir) {
255 NS_ENSURE_ARG(profileDir);
257 nsCOMPtr<nsIFile> startupCache;
258 profileDir->Clone(getter_AddRefs(startupCache));
259 NS_ENSURE_STATE(startupCache);
260 startupCache->AppendNative(NS_LITERAL_CSTRING("startupCache"));
262 nsresult rv;
263 #ifndef XP_WIN
264 static uint32_t oldPermissions;
265 #else
266 static PRFileDesc* fd = nullptr;
267 #endif
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
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));
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
305 return NS_OK;
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();
315 const char* buf = "Get a Beardbook app for your smartphone";
316 const char* id = "id";
317 char* outbuf = nullptr;
318 uint32_t len;
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);
326 // Prevent StartupCache::InvalidateCache from deleting the disk file
327 rv = LockCacheFile(true, profileDir);
328 NS_ENSURE_SUCCESS(rv, rv);
330 sc->IgnoreDiskCache();
332 rv = sc->GetBuffer(id, &outbuf, &len);
334 nsresult r = LockCacheFile(false, profileDir);
335 NS_ENSURE_SUCCESS(r, r);
337 delete[] outbuf;
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;
349 sc->InvalidateCache();
350 return NS_OK;
353 nsresult
354 TestEarlyShutdown() {
355 nsresult rv;
356 nsCOMPtr<nsIStartupCache> sc
357 = do_GetService("@mozilla.org/startupcache/cache;1", &rv);
358 sc->InvalidateCache();
360 const char* buf = "Find your soul beardmate on BeardBook";
361 const char* id = "id";
362 uint32_t len;
363 char* outbuf = nullptr;
365 sc->ResetStartupWriteTimer();
366 rv = sc->PutBuffer(id, buf, strlen(buf) + 1);
367 NS_ENSURE_SUCCESS(rv, rv);
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);
375 rv = sc->GetBuffer(id, &outbuf, &len);
376 delete[] outbuf;
378 if (NS_SUCCEEDED(rv)) {
379 passed("GetBuffer succeeded after early shutdown");
380 } else {
381 fail("GetBuffer failed after early shutdown");
382 return rv;
385 const char* other_id = "other_id";
386 rv = sc->PutBuffer(other_id, buf, strlen(buf) + 1);
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;
398 return NS_OK;
401 int main(int argc, char** argv)
403 ScopedXPCOM xpcom("Startup Cache");
404 if (xpcom.failed())
405 return 1;
407 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
408 prefs->SetIntPref("hangmonitor.timeout", 0);
410 int rv = 0;
411 nsresult scrv;
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;
422 #ifdef XP_MACOSX
423 nsCOMPtr<nsIFile> tempManifest;
424 manifest->Clone(getter_AddRefs(tempManifest));
425 manifest->AppendNative(
426 NS_LITERAL_CSTRING("TestStartupCacheTelemetry.manifest"));
427 bool exists;
428 manifest->Exists(&exists);
429 if (!exists) {
430 // Workaround for bug 1080338 in mozharness.
431 manifest = tempManifest.forget();
432 manifest->SetNativeLeafName(NS_LITERAL_CSTRING("MacOS"));
433 manifest->AppendNative(
434 NS_LITERAL_CSTRING("TestStartupCacheTelemetry.manifest"));
436 #else
437 manifest->AppendNative(
438 NS_LITERAL_CSTRING("TestStartupCacheTelemetry.manifest"));
439 #endif
441 XRE_AddManifestLocation(NS_COMPONENT_LOCATION, manifest);
443 nsCOMPtr<nsIObserver> telemetryThing =
444 do_GetService("@mozilla.org/testing/startup-cache-telemetry.js");
445 if (!telemetryThing) {
446 fail("telemetryThing");
447 return 1;
449 scrv = telemetryThing->Observe(nullptr, "save-initial", nullptr);
450 if (NS_FAILED(scrv)) {
451 fail("save-initial");
452 rv = 1;
455 nsCOMPtr<nsIStartupCache> sc
456 = do_GetService("@mozilla.org/startupcache/cache;1", &scrv);
457 if (NS_FAILED(scrv))
458 rv = 1;
459 else
460 sc->RecordAgesAlways();
461 if (NS_FAILED(TestStartupWriteRead()))
462 rv = 1;
463 if (NS_FAILED(TestWriteInvalidateRead()))
464 rv = 1;
465 if (NS_FAILED(TestWriteObject()))
466 rv = 1;
467 nsCOMPtr<nsIFile> profileDir = xpcom.GetProfileDirectory();
468 if (NS_FAILED(TestIgnoreDiskCache(profileDir)))
469 rv = 1;
470 if (NS_FAILED(TestEarlyShutdown()))
471 rv = 1;
473 scrv = telemetryThing->Observe(nullptr, "save-initial", nullptr);
474 if (NS_FAILED(scrv)) {
475 fail("check-final");
476 rv = 1;
479 return rv;