1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/startupcache/test/TestStartupCache.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,462 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "TestHarness.h" 1.10 + 1.11 +#include "nsThreadUtils.h" 1.12 +#include "nsIClassInfo.h" 1.13 +#include "nsIOutputStream.h" 1.14 +#include "nsIObserver.h" 1.15 +#include "nsISerializable.h" 1.16 +#include "nsISupports.h" 1.17 +#include "nsIStartupCache.h" 1.18 +#include "nsIStringStream.h" 1.19 +#include "nsIStorageStream.h" 1.20 +#include "nsIObjectInputStream.h" 1.21 +#include "nsIObjectOutputStream.h" 1.22 +#include "nsIURI.h" 1.23 +#include "nsStringAPI.h" 1.24 +#include "nsIPrefBranch.h" 1.25 +#include "nsIPrefService.h" 1.26 +#include "nsIXPConnect.h" 1.27 +#include "prio.h" 1.28 +#include "mozilla/Maybe.h" 1.29 + 1.30 +using namespace JS; 1.31 + 1.32 +namespace mozilla { 1.33 +namespace scache { 1.34 + 1.35 +NS_IMPORT nsresult 1.36 +NewObjectInputStreamFromBuffer(char* buffer, uint32_t len, 1.37 + nsIObjectInputStream** stream); 1.38 + 1.39 +// We can't retrieve the wrapped stream from the objectOutputStream later, 1.40 +// so we return it here. 1.41 +NS_IMPORT nsresult 1.42 +NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream, 1.43 + nsIStorageStream** stream); 1.44 + 1.45 +NS_IMPORT nsresult 1.46 +NewBufferFromStorageStream(nsIStorageStream *storageStream, 1.47 + char** buffer, uint32_t* len); 1.48 +} 1.49 +} 1.50 + 1.51 +using namespace mozilla::scache; 1.52 + 1.53 +#define NS_ENSURE_STR_MATCH(str1, str2, testname) \ 1.54 +PR_BEGIN_MACRO \ 1.55 +if (0 != strcmp(str1, str2)) { \ 1.56 + fail("failed " testname); \ 1.57 + return NS_ERROR_FAILURE; \ 1.58 +} \ 1.59 +passed("passed " testname); \ 1.60 +PR_END_MACRO 1.61 + 1.62 +nsresult 1.63 +WaitForStartupTimer() { 1.64 + nsresult rv; 1.65 + nsCOMPtr<nsIStartupCache> sc 1.66 + = do_GetService("@mozilla.org/startupcache/cache;1"); 1.67 + PR_Sleep(10 * PR_TicksPerSecond()); 1.68 + 1.69 + bool complete; 1.70 + while (true) { 1.71 + 1.72 + NS_ProcessPendingEvents(nullptr); 1.73 + rv = sc->StartupWriteComplete(&complete); 1.74 + if (NS_FAILED(rv) || complete) 1.75 + break; 1.76 + PR_Sleep(1 * PR_TicksPerSecond()); 1.77 + } 1.78 + return rv; 1.79 +} 1.80 + 1.81 +nsresult 1.82 +TestStartupWriteRead() { 1.83 + nsresult rv; 1.84 + nsCOMPtr<nsIStartupCache> sc 1.85 + = do_GetService("@mozilla.org/startupcache/cache;1", &rv); 1.86 + if (!sc) { 1.87 + fail("didn't get a pointer..."); 1.88 + return NS_ERROR_FAILURE; 1.89 + } else { 1.90 + passed("got a pointer?"); 1.91 + } 1.92 + sc->InvalidateCache(); 1.93 + 1.94 + const char* buf = "Market opportunities for BeardBook"; 1.95 + const char* id = "id"; 1.96 + char* outbufPtr = nullptr; 1.97 + nsAutoArrayPtr<char> outbuf; 1.98 + uint32_t len; 1.99 + 1.100 + rv = sc->PutBuffer(id, buf, strlen(buf) + 1); 1.101 + NS_ENSURE_SUCCESS(rv, rv); 1.102 + 1.103 + rv = sc->GetBuffer(id, &outbufPtr, &len); 1.104 + NS_ENSURE_SUCCESS(rv, rv); 1.105 + outbuf = outbufPtr; 1.106 + NS_ENSURE_STR_MATCH(buf, outbuf, "pre-write read"); 1.107 + 1.108 + rv = sc->ResetStartupWriteTimer(); 1.109 + rv = WaitForStartupTimer(); 1.110 + NS_ENSURE_SUCCESS(rv, rv); 1.111 + 1.112 + rv = sc->GetBuffer(id, &outbufPtr, &len); 1.113 + NS_ENSURE_SUCCESS(rv, rv); 1.114 + outbuf = outbufPtr; 1.115 + NS_ENSURE_STR_MATCH(buf, outbuf, "simple write/read"); 1.116 + 1.117 + return NS_OK; 1.118 +} 1.119 + 1.120 +nsresult 1.121 +TestWriteInvalidateRead() { 1.122 + nsresult rv; 1.123 + const char* buf = "BeardBook competitive analysis"; 1.124 + const char* id = "id"; 1.125 + char* outbuf = nullptr; 1.126 + uint32_t len; 1.127 + nsCOMPtr<nsIStartupCache> sc 1.128 + = do_GetService("@mozilla.org/startupcache/cache;1", &rv); 1.129 + sc->InvalidateCache(); 1.130 + 1.131 + rv = sc->PutBuffer(id, buf, strlen(buf) + 1); 1.132 + NS_ENSURE_SUCCESS(rv, rv); 1.133 + 1.134 + sc->InvalidateCache(); 1.135 + 1.136 + rv = sc->GetBuffer(id, &outbuf, &len); 1.137 + delete[] outbuf; 1.138 + if (rv == NS_ERROR_NOT_AVAILABLE) { 1.139 + passed("buffer not available after invalidate"); 1.140 + } else if (NS_SUCCEEDED(rv)) { 1.141 + fail("GetBuffer succeeded unexpectedly after invalidate"); 1.142 + return NS_ERROR_UNEXPECTED; 1.143 + } else { 1.144 + fail("GetBuffer gave an unexpected failure, expected NOT_AVAILABLE"); 1.145 + return rv; 1.146 + } 1.147 + 1.148 + sc->InvalidateCache(); 1.149 + return NS_OK; 1.150 +} 1.151 + 1.152 +nsresult 1.153 +TestWriteObject() { 1.154 + nsresult rv; 1.155 + 1.156 + nsCOMPtr<nsIURI> obj 1.157 + = do_CreateInstance("@mozilla.org/network/simple-uri;1"); 1.158 + if (!obj) { 1.159 + fail("did not create object in test write object"); 1.160 + return NS_ERROR_UNEXPECTED; 1.161 + } 1.162 + NS_NAMED_LITERAL_CSTRING(spec, "http://www.mozilla.org"); 1.163 + obj->SetSpec(spec); 1.164 + nsCOMPtr<nsIStartupCache> sc = do_GetService("@mozilla.org/startupcache/cache;1", &rv); 1.165 + 1.166 + sc->InvalidateCache(); 1.167 + 1.168 + // Create an object stream. Usually this is done with 1.169 + // NewObjectOutputWrappedStorageStream, but that uses 1.170 + // StartupCache::GetSingleton in debug builds, and we 1.171 + // don't have access to that here. Obviously. 1.172 + const char* id = "id"; 1.173 + nsCOMPtr<nsIStorageStream> storageStream 1.174 + = do_CreateInstance("@mozilla.org/storagestream;1"); 1.175 + NS_ENSURE_ARG_POINTER(storageStream); 1.176 + 1.177 + rv = storageStream->Init(256, (uint32_t) -1); 1.178 + NS_ENSURE_SUCCESS(rv, rv); 1.179 + 1.180 + nsCOMPtr<nsIObjectOutputStream> objectOutput 1.181 + = do_CreateInstance("@mozilla.org/binaryoutputstream;1"); 1.182 + if (!objectOutput) 1.183 + return NS_ERROR_OUT_OF_MEMORY; 1.184 + 1.185 + nsCOMPtr<nsIOutputStream> outputStream 1.186 + = do_QueryInterface(storageStream); 1.187 + 1.188 + rv = objectOutput->SetOutputStream(outputStream); 1.189 + 1.190 + if (NS_FAILED(rv)) { 1.191 + fail("failed to create output stream"); 1.192 + return rv; 1.193 + } 1.194 + nsCOMPtr<nsISupports> objQI(do_QueryInterface(obj)); 1.195 + rv = objectOutput->WriteObject(objQI, true); 1.196 + if (NS_FAILED(rv)) { 1.197 + fail("failed to write object"); 1.198 + return rv; 1.199 + } 1.200 + 1.201 + char* bufPtr = nullptr; 1.202 + nsAutoArrayPtr<char> buf; 1.203 + uint32_t len; 1.204 + NewBufferFromStorageStream(storageStream, &bufPtr, &len); 1.205 + buf = bufPtr; 1.206 + 1.207 + // Since this is a post-startup write, it should be written and 1.208 + // available. 1.209 + rv = sc->PutBuffer(id, buf, len); 1.210 + if (NS_FAILED(rv)) { 1.211 + fail("failed to insert input stream"); 1.212 + return rv; 1.213 + } 1.214 + 1.215 + char* buf2Ptr = nullptr; 1.216 + nsAutoArrayPtr<char> buf2; 1.217 + uint32_t len2; 1.218 + nsCOMPtr<nsIObjectInputStream> objectInput; 1.219 + rv = sc->GetBuffer(id, &buf2Ptr, &len2); 1.220 + if (NS_FAILED(rv)) { 1.221 + fail("failed to retrieve buffer"); 1.222 + return rv; 1.223 + } 1.224 + buf2 = buf2Ptr; 1.225 + 1.226 + rv = NewObjectInputStreamFromBuffer(buf2, len2, getter_AddRefs(objectInput)); 1.227 + if (NS_FAILED(rv)) { 1.228 + fail("failed to created input stream"); 1.229 + return rv; 1.230 + } 1.231 + buf2.forget(); 1.232 + 1.233 + nsCOMPtr<nsISupports> deserialized; 1.234 + rv = objectInput->ReadObject(true, getter_AddRefs(deserialized)); 1.235 + if (NS_FAILED(rv)) { 1.236 + fail("failed to read object"); 1.237 + return rv; 1.238 + } 1.239 + 1.240 + bool match = false; 1.241 + nsCOMPtr<nsIURI> uri(do_QueryInterface(deserialized)); 1.242 + if (uri) { 1.243 + nsCString outSpec; 1.244 + uri->GetSpec(outSpec); 1.245 + match = outSpec.Equals(spec); 1.246 + } 1.247 + if (!match) { 1.248 + fail("deserialized object has incorrect information"); 1.249 + return rv; 1.250 + } 1.251 + 1.252 + passed("write object"); 1.253 + return NS_OK; 1.254 +} 1.255 + 1.256 +nsresult 1.257 +LockCacheFile(bool protect, nsIFile* profileDir) { 1.258 + NS_ENSURE_ARG(profileDir); 1.259 + 1.260 + nsCOMPtr<nsIFile> startupCache; 1.261 + profileDir->Clone(getter_AddRefs(startupCache)); 1.262 + NS_ENSURE_STATE(startupCache); 1.263 + startupCache->AppendNative(NS_LITERAL_CSTRING("startupCache")); 1.264 + 1.265 + nsresult rv; 1.266 +#ifndef XP_WIN 1.267 + static uint32_t oldPermissions; 1.268 +#else 1.269 + static PRFileDesc* fd = nullptr; 1.270 +#endif 1.271 + 1.272 + // To prevent deletion of the startupcache file, we change the containing 1.273 + // directory's permissions on Linux/Mac, and hold the file open on Windows 1.274 + if (protect) { 1.275 +#ifndef XP_WIN 1.276 + rv = startupCache->GetPermissions(&oldPermissions); 1.277 + NS_ENSURE_SUCCESS(rv, rv); 1.278 + rv = startupCache->SetPermissions(0555); 1.279 + NS_ENSURE_SUCCESS(rv, rv); 1.280 +#else 1.281 + // Filename logic from StartupCache.cpp 1.282 + #ifdef IS_BIG_ENDIAN 1.283 + #define SC_ENDIAN "big" 1.284 + #else 1.285 + #define SC_ENDIAN "little" 1.286 + #endif 1.287 + 1.288 + #if PR_BYTES_PER_WORD == 4 1.289 + #define SC_WORDSIZE "4" 1.290 + #else 1.291 + #define SC_WORDSIZE "8" 1.292 + #endif 1.293 + char sStartupCacheName[] = "startupCache." SC_WORDSIZE "." SC_ENDIAN; 1.294 + startupCache->AppendNative(NS_LITERAL_CSTRING(sStartupCacheName)); 1.295 + 1.296 + rv = startupCache->OpenNSPRFileDesc(PR_RDONLY, 0, &fd); 1.297 + NS_ENSURE_SUCCESS(rv, rv); 1.298 +#endif 1.299 + } else { 1.300 +#ifndef XP_WIN 1.301 + rv = startupCache->SetPermissions(oldPermissions); 1.302 + NS_ENSURE_SUCCESS(rv, rv); 1.303 +#else 1.304 + PR_Close(fd); 1.305 +#endif 1.306 + } 1.307 + 1.308 + return NS_OK; 1.309 +} 1.310 + 1.311 +nsresult 1.312 +TestIgnoreDiskCache(nsIFile* profileDir) { 1.313 + nsresult rv; 1.314 + nsCOMPtr<nsIStartupCache> sc 1.315 + = do_GetService("@mozilla.org/startupcache/cache;1", &rv); 1.316 + sc->InvalidateCache(); 1.317 + 1.318 + const char* buf = "Get a Beardbook app for your smartphone"; 1.319 + const char* id = "id"; 1.320 + char* outbuf = nullptr; 1.321 + uint32_t len; 1.322 + 1.323 + rv = sc->PutBuffer(id, buf, strlen(buf) + 1); 1.324 + NS_ENSURE_SUCCESS(rv, rv); 1.325 + rv = sc->ResetStartupWriteTimer(); 1.326 + rv = WaitForStartupTimer(); 1.327 + NS_ENSURE_SUCCESS(rv, rv); 1.328 + 1.329 + // Prevent StartupCache::InvalidateCache from deleting the disk file 1.330 + rv = LockCacheFile(true, profileDir); 1.331 + NS_ENSURE_SUCCESS(rv, rv); 1.332 + 1.333 + sc->IgnoreDiskCache(); 1.334 + 1.335 + rv = sc->GetBuffer(id, &outbuf, &len); 1.336 + 1.337 + nsresult r = LockCacheFile(false, profileDir); 1.338 + NS_ENSURE_SUCCESS(r, r); 1.339 + 1.340 + delete[] outbuf; 1.341 + 1.342 + if (rv == NS_ERROR_NOT_AVAILABLE) { 1.343 + passed("buffer not available after ignoring disk cache"); 1.344 + } else if (NS_SUCCEEDED(rv)) { 1.345 + fail("GetBuffer succeeded unexpectedly after ignoring disk cache"); 1.346 + return NS_ERROR_UNEXPECTED; 1.347 + } else { 1.348 + fail("GetBuffer gave an unexpected failure, expected NOT_AVAILABLE"); 1.349 + return rv; 1.350 + } 1.351 + 1.352 + sc->InvalidateCache(); 1.353 + return NS_OK; 1.354 +} 1.355 + 1.356 +nsresult 1.357 +TestEarlyShutdown() { 1.358 + nsresult rv; 1.359 + nsCOMPtr<nsIStartupCache> sc 1.360 + = do_GetService("@mozilla.org/startupcache/cache;1", &rv); 1.361 + sc->InvalidateCache(); 1.362 + 1.363 + const char* buf = "Find your soul beardmate on BeardBook"; 1.364 + const char* id = "id"; 1.365 + uint32_t len; 1.366 + char* outbuf = nullptr; 1.367 + 1.368 + sc->ResetStartupWriteTimer(); 1.369 + rv = sc->PutBuffer(id, buf, strlen(buf) + 1); 1.370 + NS_ENSURE_SUCCESS(rv, rv); 1.371 + 1.372 + nsCOMPtr<nsIObserver> obs; 1.373 + sc->GetObserver(getter_AddRefs(obs)); 1.374 + obs->Observe(nullptr, "xpcom-shutdown", nullptr); 1.375 + rv = WaitForStartupTimer(); 1.376 + NS_ENSURE_SUCCESS(rv, rv); 1.377 + 1.378 + rv = sc->GetBuffer(id, &outbuf, &len); 1.379 + delete[] outbuf; 1.380 + 1.381 + if (NS_SUCCEEDED(rv)) { 1.382 + passed("GetBuffer succeeded after early shutdown"); 1.383 + } else { 1.384 + fail("GetBuffer failed after early shutdown"); 1.385 + return rv; 1.386 + } 1.387 + 1.388 + const char* other_id = "other_id"; 1.389 + rv = sc->PutBuffer(other_id, buf, strlen(buf) + 1); 1.390 + 1.391 + if (rv == NS_ERROR_NOT_AVAILABLE) { 1.392 + passed("PutBuffer not available after early shutdown"); 1.393 + } else if (NS_SUCCEEDED(rv)) { 1.394 + fail("PutBuffer succeeded unexpectedly after early shutdown"); 1.395 + return NS_ERROR_UNEXPECTED; 1.396 + } else { 1.397 + fail("PutBuffer gave an unexpected failure, expected NOT_AVAILABLE"); 1.398 + return rv; 1.399 + } 1.400 + 1.401 + return NS_OK; 1.402 +} 1.403 + 1.404 +int main(int argc, char** argv) 1.405 +{ 1.406 + ScopedXPCOM xpcom("Startup Cache"); 1.407 + if (xpcom.failed()) 1.408 + return 1; 1.409 + 1.410 + nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); 1.411 + prefs->SetIntPref("hangmonitor.timeout", 0); 1.412 + 1.413 + int rv = 0; 1.414 + nsresult scrv; 1.415 + 1.416 + // Register TestStartupCacheTelemetry 1.417 + nsCOMPtr<nsIFile> manifest; 1.418 + scrv = NS_GetSpecialDirectory(NS_GRE_DIR, 1.419 + getter_AddRefs(manifest)); 1.420 + if (NS_FAILED(scrv)) { 1.421 + fail("NS_XPCOM_CURRENT_PROCESS_DIR"); 1.422 + return 1; 1.423 + } 1.424 + 1.425 + manifest->AppendNative(NS_LITERAL_CSTRING("TestStartupCacheTelemetry.manifest")); 1.426 + XRE_AddManifestLocation(NS_COMPONENT_LOCATION, manifest); 1.427 + 1.428 + nsCOMPtr<nsIObserver> telemetryThing = 1.429 + do_GetService("@mozilla.org/testing/startup-cache-telemetry.js"); 1.430 + if (!telemetryThing) { 1.431 + fail("telemetryThing"); 1.432 + return 1; 1.433 + } 1.434 + scrv = telemetryThing->Observe(nullptr, "save-initial", nullptr); 1.435 + if (NS_FAILED(scrv)) { 1.436 + fail("save-initial"); 1.437 + rv = 1; 1.438 + } 1.439 + 1.440 + nsCOMPtr<nsIStartupCache> sc 1.441 + = do_GetService("@mozilla.org/startupcache/cache;1", &scrv); 1.442 + if (NS_FAILED(scrv)) 1.443 + rv = 1; 1.444 + else 1.445 + sc->RecordAgesAlways(); 1.446 + if (NS_FAILED(TestStartupWriteRead())) 1.447 + rv = 1; 1.448 + if (NS_FAILED(TestWriteInvalidateRead())) 1.449 + rv = 1; 1.450 + if (NS_FAILED(TestWriteObject())) 1.451 + rv = 1; 1.452 + nsCOMPtr<nsIFile> profileDir = xpcom.GetProfileDirectory(); 1.453 + if (NS_FAILED(TestIgnoreDiskCache(profileDir))) 1.454 + rv = 1; 1.455 + if (NS_FAILED(TestEarlyShutdown())) 1.456 + rv = 1; 1.457 + 1.458 + scrv = telemetryThing->Observe(nullptr, "save-initial", nullptr); 1.459 + if (NS_FAILED(scrv)) { 1.460 + fail("check-final"); 1.461 + rv = 1; 1.462 + } 1.463 + 1.464 + return rv; 1.465 +}