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"
20 #include "nsStringAPI.h"
21 #include "nsIPrefBranch.h"
22 #include "nsIPrefService.h"
23 #include "nsIXPConnect.h"
25 #include "mozilla/Maybe.h"
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.
39 NewObjectOutputWrappedStorageStream(nsIObjectOutputStream
**wrapperStream
,
40 nsIStorageStream
** stream
);
43 NewBufferFromStorageStream(nsIStorageStream
*storageStream
,
44 char** buffer
, uint32_t* len
);
48 using namespace mozilla::scache
;
50 #define NS_ENSURE_STR_MATCH(str1, str2, testname) \
52 if (0 != strcmp(str1, str2)) { \
53 fail("failed " testname); \
54 return NS_ERROR_FAILURE; \
56 passed("passed " testname); \
60 WaitForStartupTimer() {
62 nsCOMPtr
<nsIStartupCache
> sc
63 = do_GetService("@mozilla.org/startupcache/cache;1");
64 PR_Sleep(10 * PR_TicksPerSecond());
69 NS_ProcessPendingEvents(nullptr);
70 rv
= sc
->StartupWriteComplete(&complete
);
71 if (NS_FAILED(rv
) || complete
)
73 PR_Sleep(1 * PR_TicksPerSecond());
79 TestStartupWriteRead() {
81 nsCOMPtr
<nsIStartupCache
> sc
82 = do_GetService("@mozilla.org/startupcache/cache;1", &rv
);
84 fail("didn't get a pointer...");
85 return NS_ERROR_FAILURE
;
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
;
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
);
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
);
112 NS_ENSURE_STR_MATCH(buf
, outbuf
, "simple write/read");
118 TestWriteInvalidateRead() {
120 const char* buf
= "BeardBook competitive analysis";
121 const char* id
= "id";
122 char* outbuf
= nullptr;
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
);
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
;
141 fail("GetBuffer gave an unexpected failure, expected NOT_AVAILABLE");
145 sc
->InvalidateCache();
154 = do_CreateInstance("@mozilla.org/network/simple-uri;1");
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");
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");
180 return NS_ERROR_OUT_OF_MEMORY
;
182 nsCOMPtr
<nsIOutputStream
> outputStream
183 = do_QueryInterface(storageStream
);
185 rv
= objectOutput
->SetOutputStream(outputStream
);
188 fail("failed to create output stream");
191 nsCOMPtr
<nsISupports
> objQI(do_QueryInterface(obj
));
192 rv
= objectOutput
->WriteObject(objQI
, true);
194 fail("failed to write object");
198 char* bufPtr
= nullptr;
199 nsAutoArrayPtr
<char> buf
;
201 NewBufferFromStorageStream(storageStream
, &bufPtr
, &len
);
204 // Since this is a post-startup write, it should be written and
206 rv
= sc
->PutBuffer(id
, buf
, len
);
208 fail("failed to insert input stream");
212 char* buf2Ptr
= nullptr;
213 nsAutoArrayPtr
<char> buf2
;
215 nsCOMPtr
<nsIObjectInputStream
> objectInput
;
216 rv
= sc
->GetBuffer(id
, &buf2Ptr
, &len2
);
218 fail("failed to retrieve buffer");
223 rv
= NewObjectInputStreamFromBuffer(buf2
, len2
, getter_AddRefs(objectInput
));
225 fail("failed to created input stream");
230 nsCOMPtr
<nsISupports
> deserialized
;
231 rv
= objectInput
->ReadObject(true, getter_AddRefs(deserialized
));
233 fail("failed to read object");
238 nsCOMPtr
<nsIURI
> uri(do_QueryInterface(deserialized
));
241 uri
->GetSpec(outSpec
);
242 match
= outSpec
.Equals(spec
);
245 fail("deserialized object has incorrect information");
249 passed("write object");
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"));
264 static uint32_t oldPermissions
;
266 static PRFileDesc
* fd
= nullptr;
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
273 rv
= startupCache
->GetPermissions(&oldPermissions
);
274 NS_ENSURE_SUCCESS(rv
, rv
);
275 rv
= startupCache
->SetPermissions(0555);
276 NS_ENSURE_SUCCESS(rv
, rv
);
278 // Filename logic from StartupCache.cpp
280 #define SC_ENDIAN "big"
282 #define SC_ENDIAN "little"
285 #if PR_BYTES_PER_WORD == 4
286 #define SC_WORDSIZE "4"
288 #define SC_WORDSIZE "8"
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
);
298 rv
= startupCache
->SetPermissions(oldPermissions
);
299 NS_ENSURE_SUCCESS(rv
, rv
);
309 TestIgnoreDiskCache(nsIFile
* profileDir
) {
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;
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
);
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
;
345 fail("GetBuffer gave an unexpected failure, expected NOT_AVAILABLE");
349 sc
->InvalidateCache();
354 TestEarlyShutdown() {
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";
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
);
378 if (NS_SUCCEEDED(rv
)) {
379 passed("GetBuffer succeeded after early shutdown");
381 fail("GetBuffer failed after early shutdown");
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
;
394 fail("PutBuffer gave an unexpected failure, expected NOT_AVAILABLE");
401 int main(int argc
, char** argv
)
403 ScopedXPCOM
xpcom("Startup Cache");
407 nsCOMPtr
<nsIPrefBranch
> prefs
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
408 prefs
->SetIntPref("hangmonitor.timeout", 0);
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");
423 nsCOMPtr
<nsIFile
> tempManifest
;
424 manifest
->Clone(getter_AddRefs(tempManifest
));
425 manifest
->AppendNative(
426 NS_LITERAL_CSTRING("TestStartupCacheTelemetry.manifest"));
428 manifest
->Exists(&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"));
437 manifest
->AppendNative(
438 NS_LITERAL_CSTRING("TestStartupCacheTelemetry.manifest"));
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");
449 scrv
= telemetryThing
->Observe(nullptr, "save-initial", nullptr);
450 if (NS_FAILED(scrv
)) {
451 fail("save-initial");
455 nsCOMPtr
<nsIStartupCache
> sc
456 = do_GetService("@mozilla.org/startupcache/cache;1", &scrv
);
460 sc
->RecordAgesAlways();
461 if (NS_FAILED(TestStartupWriteRead()))
463 if (NS_FAILED(TestWriteInvalidateRead()))
465 if (NS_FAILED(TestWriteObject()))
467 nsCOMPtr
<nsIFile
> profileDir
= xpcom
.GetProfileDirectory();
468 if (NS_FAILED(TestIgnoreDiskCache(profileDir
)))
470 if (NS_FAILED(TestEarlyShutdown()))
473 scrv
= telemetryThing
->Observe(nullptr, "save-initial", nullptr);
474 if (NS_FAILED(scrv
)) {