Backed out changeset 4585a7b6f3ce (bug 1314378)
authorIris Hsiao <ihsiao@mozilla.com>
Wed, 16 Nov 2016 15:30:52 +0800
changeset 322695 03614ff20ee0880f6c5ee538bf0a0072f1c683d4
parent 322694 4ab3e15efcfeecd9a9a53bc6669546d4ea2c7f4b
child 322696 87b4c375fe750de1df65036619da70e7d151228c
push id83959
push userihsiao@mozilla.com
push dateWed, 16 Nov 2016 07:34:02 +0000
treeherdermozilla-inbound@87b4c375fe75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1314378
milestone53.0a1
backs out4585a7b6f3ce8c8dc7b7e043e0fae86f8e5ee392
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset 4585a7b6f3ce (bug 1314378)
startupcache/StartupCache.cpp
startupcache/StartupCache.h
startupcache/StartupCacheUtils.cpp
startupcache/StartupCacheUtils.h
startupcache/nsIStartupCache.idl
startupcache/test/TestStartupCache.cpp
startupcache/test/moz.build
testing/cppunittest.ini
--- a/startupcache/StartupCache.cpp
+++ b/startupcache/StartupCache.cpp
@@ -148,17 +148,17 @@ StartupCache::Init()
   // workaround for bug 653936
   nsCOMPtr<nsIProtocolHandler> jarInitializer(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "jar"));
 
   nsresult rv;
 
   // This allows to override the startup cache filename
   // which is useful from xpcshell, when there is no ProfLDS directory to keep cache in.
   char *env = PR_GetEnv("MOZ_STARTUP_CACHE");
-  if (env && *env) {
+  if (env) {
     rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), false, getter_AddRefs(mFile));
   } else {
     nsCOMPtr<nsIFile> file;
     rv = NS_GetSpecialDirectory("ProfLDS",
                                 getter_AddRefs(file));
     if (NS_FAILED(rv)) {
       // return silently, this will fail in mochitests's xpcshell process.
       return rv;
@@ -590,23 +590,16 @@ StartupCache::ResetStartupWriteTimer()
     rv = mTimer->Cancel();
   NS_ENSURE_SUCCESS(rv, rv);
   // Wait for 10 seconds, then write out the cache.
   mTimer->InitWithFuncCallback(StartupCache::WriteTimeout, this, 60000,
                                nsITimer::TYPE_ONE_SHOT);
   return NS_OK;
 }
 
-bool
-StartupCache::StartupWriteComplete()
-{
-  WaitOnWriteThread();
-  return mStartupWriteInitiated && mTable.Count() == 0;
-}
-
 // StartupCacheDebugOutputStream implementation
 #ifdef DEBUG
 NS_IMPL_ISUPPORTS(StartupCacheDebugOutputStream, nsIObjectOutputStream, 
                   nsIBinaryOutputStream, nsIOutputStream)
 
 bool
 StartupCacheDebugOutputStream::CheckReferences(nsISupports* aObject)
 {
@@ -739,28 +732,54 @@ StartupCacheWrapper::InvalidateCache()
   StartupCache* sc = StartupCache::GetSingleton();
   if (!sc) {
     return NS_ERROR_NOT_INITIALIZED;
   }
   sc->InvalidateCache();
   return NS_OK;
 }
 
+nsresult
+StartupCacheWrapper::IgnoreDiskCache()
+{
+  StartupCache::IgnoreDiskCache();
+  return NS_OK;
+}
+
 nsresult 
 StartupCacheWrapper::GetDebugObjectOutputStream(nsIObjectOutputStream* stream,
                                                 nsIObjectOutputStream** outStream) 
 {
   StartupCache* sc = StartupCache::GetSingleton();
   if (!sc) {
     return NS_ERROR_NOT_INITIALIZED;
   }
   return sc->GetDebugObjectOutputStream(stream, outStream);
 }
 
 nsresult
+StartupCacheWrapper::StartupWriteComplete(bool *complete)
+{
+  StartupCache* sc = StartupCache::GetSingleton();
+  if (!sc) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+  sc->WaitOnWriteThread();
+  *complete = sc->mStartupWriteInitiated && sc->mTable.Count() == 0;
+  return NS_OK;
+}
+
+nsresult
+StartupCacheWrapper::ResetStartupWriteTimer()
+{
+  StartupCache* sc = StartupCache::GetSingleton();
+  return sc ? sc->ResetStartupWriteTimer() : NS_ERROR_NOT_INITIALIZED;
+}
+
+nsresult
 StartupCacheWrapper::GetObserver(nsIObserver** obv) {
   StartupCache* sc = StartupCache::GetSingleton();
   if (!sc) {
     return NS_ERROR_NOT_INITIALIZED;
   }
   NS_ADDREF(*obv = sc->mListener);
   return NS_OK;
 }
--- a/startupcache/StartupCache.h
+++ b/startupcache/StartupCache.h
@@ -133,26 +133,24 @@ public:
   static void DeleteSingleton();
 
   // This measures all the heap memory used by the StartupCache, i.e. it
   // excludes the mapping.
   size_t HeapSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
   size_t SizeOfMapping();
 
-  // FOR TESTING ONLY
-  nsresult ResetStartupWriteTimer();
-  bool StartupWriteComplete();
 private:
   StartupCache();
   virtual ~StartupCache();
 
   nsresult LoadArchive();
   nsresult Init();
   void WriteToDisk();
+  nsresult ResetStartupWriteTimer();
   void WaitOnWriteThread();
 
   static nsresult InitSingleton();
   static void WriteTimeout(nsITimer *aTimer, void *aClosure);
   static void ThreadedWrite(void *aClosure);
 
   nsClassHashtable<nsCStringHashKey, CacheEntry> mTable;
   nsTArray<nsCString> mPendingWrites;
--- a/startupcache/StartupCacheUtils.cpp
+++ b/startupcache/StartupCacheUtils.cpp
@@ -13,18 +13,18 @@
 #include "nsAutoPtr.h"
 #include "StartupCacheUtils.h"
 #include "mozilla/scache/StartupCache.h"
 #include "mozilla/Omnijar.h"
 
 namespace mozilla {
 namespace scache {
 
-nsresult
-NewObjectInputStreamFromBuffer(UniquePtr<char[]> buffer, uint32_t len,
+NS_EXPORT nsresult
+NewObjectInputStreamFromBuffer(UniquePtr<char[]> buffer, uint32_t len, 
                                nsIObjectInputStream** stream)
 {
   nsCOMPtr<nsIStringInputStream> stringStream =
     do_CreateInstance("@mozilla.org/io/string-input-stream;1");
   NS_ENSURE_TRUE(stringStream, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIObjectInputStream> objectInput =
     do_CreateInstance("@mozilla.org/binaryinputstream;1");
@@ -32,17 +32,17 @@ NewObjectInputStreamFromBuffer(UniquePtr
 
   stringStream->AdoptData(buffer.release(), len);
   objectInput->SetInputStream(stringStream);
 
   objectInput.forget(stream);
   return NS_OK;
 }
 
-nsresult
+NS_EXPORT nsresult
 NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream,
                                     nsIStorageStream** stream,
                                     bool wantDebugStream)
 {
   nsCOMPtr<nsIStorageStream> storageStream;
 
   nsresult rv = NS_NewStorageStream(256, UINT32_MAX, getter_AddRefs(storageStream));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -69,19 +69,19 @@ NewObjectOutputWrappedStorageStream(nsIO
 #else
   objectOutput.forget(wrapperStream);
 #endif
   
   storageStream.forget(stream);
   return NS_OK;
 }
 
-nsresult
-NewBufferFromStorageStream(nsIStorageStream *storageStream,
-                           UniquePtr<char[]>* buffer, uint32_t* len)
+NS_EXPORT nsresult
+NewBufferFromStorageStream(nsIStorageStream *storageStream, 
+                           UniquePtr<char[]>* buffer, uint32_t* len) 
 {
   nsresult rv;
   nsCOMPtr<nsIInputStream> inputStream;
   rv = storageStream->NewInputStream(0, getter_AddRefs(inputStream));
   NS_ENSURE_SUCCESS(rv, rv);
   
   uint64_t avail64;
   rv = inputStream->Available(&avail64);
@@ -165,17 +165,17 @@ canonicalizeBase(nsAutoCString &spec,
  *  file://$GRE_DIR/modules/XPCOMUtils.jsm or
  *  jar:file://$GRE_DIR/omni.jar!/modules/XPCOMUtils.jsm becomes
  *     jsloader/resource/gre/modules/XPCOMUtils.jsm
  *  file://$PROFILE_DIR/extensions/{uuid}/components/component.js becomes
  *     jsloader/$PROFILE_DIR/extensions/%7Buuid%7D/components/component.js
  *  jar:file://$PROFILE_DIR/extensions/some.xpi!/components/component.js becomes
  *     jsloader/$PROFILE_DIR/extensions/some.xpi/components/component.js
  */
-nsresult
+NS_EXPORT nsresult
 PathifyURI(nsIURI *in, nsACString &out)
 {
     bool equals;
     nsresult rv;
     nsCOMPtr<nsIURI> uri = in;
     nsAutoCString spec;
 
     // Resolve resource:// URIs. At the end of this if/else block, we
--- a/startupcache/StartupCacheUtils.h
+++ b/startupcache/StartupCacheUtils.h
@@ -1,46 +1,43 @@
 /* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef nsStartupCacheUtils_h_
 #define nsStartupCacheUtils_h_
 
-#include "nsString.h"
 #include "nsIStorageStream.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "mozilla/UniquePtr.h"
 
-class nsIURI;
-
 namespace mozilla {
 namespace scache {
 
-nsresult
+NS_EXPORT nsresult
 NewObjectInputStreamFromBuffer(UniquePtr<char[]> buffer, uint32_t len, 
                                nsIObjectInputStream** stream);
 
 // We can't retrieve the wrapped stream from the objectOutputStream later,
 // so we return it here. We give callers in debug builds the option 
 // to wrap the outputstream in a debug stream, which will detect if
 // non-singleton objects are written out multiple times during a serialization.
 // This could cause them to be deserialized incorrectly (as multiple copies
 // instead of references).
-nsresult
+NS_EXPORT nsresult
 NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream,
                                     nsIStorageStream** stream,
                                     bool wantDebugStream);
 
 // Creates a buffer for storing the stream into the cache. The buffer is
 // allocated with 'new []'.  After calling this function, the caller would
 // typically call nsIStartupCache::PutBuffer with the returned buffer.
-nsresult
+NS_EXPORT nsresult
 NewBufferFromStorageStream(nsIStorageStream *storageStream, 
                            UniquePtr<char[]>* buffer, uint32_t* len);
 
-nsresult
+NS_EXPORT nsresult
 PathifyURI(nsIURI *in, nsACString &out);
 } // namespace scache
 } // namespace mozilla
 
 #endif //nsStartupCacheUtils_h_
--- a/startupcache/nsIStartupCache.idl
+++ b/startupcache/nsIStartupCache.idl
@@ -35,19 +35,27 @@ interface nsIStartupCache : nsISupports
   }
 %}
 
   void putBuffer(in string aID, in string aBuffer, 
                             in uint32_t aLength);
  
   void invalidateCache();
   
+  void ignoreDiskCache();
 
   /** In debug builds, wraps this object output stream with a stream that will 
    *  detect and prevent the write of a multiply-referenced non-singleton object 
    *  during serialization. In non-debug, returns an add-ref'd pointer to
    *  original stream, unwrapped. */
   nsIObjectOutputStream getDebugObjectOutputStream(in nsIObjectOutputStream aStream);
 
+  /* Allows clients to check whether the one-time writeout after startup 
+   * has finished yet, and also to set this variable as needed (so test
+   * code can fire mulitple startup writes if needed).
+   */
+  boolean startupWriteComplete();
+  void resetStartupWriteTimer();
+
   /* Allows clients to simulate the behavior of ObserverService. */
   readonly attribute nsIObserver observer;
 };
 
--- a/startupcache/test/TestStartupCache.cpp
+++ b/startupcache/test/TestStartupCache.cpp
@@ -1,195 +1,432 @@
 /* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "gtest/gtest.h"
+#include "TestHarness.h"
 
-#include "mozilla/scache/StartupCache.h"
-#include "mozilla/scache/StartupCacheUtils.h"
-
-#include "nsDirectoryServiceDefs.h"
+#include "nsThreadUtils.h"
 #include "nsIClassInfo.h"
 #include "nsIOutputStream.h"
 #include "nsIObserver.h"
 #include "nsISerializable.h"
 #include "nsISupports.h"
+#include "nsIStartupCache.h"
 #include "nsIStringStream.h"
 #include "nsIStorageStream.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIURI.h"
+#include "nsStringAPI.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "nsIXPConnect.h"
-#include "nsThreadUtils.h"
-#include "prenv.h"
 #include "prio.h"
-#include "prprf.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/UniquePtr.h"
 
 using namespace JS;
 
+namespace mozilla {
+namespace scache {
+
+NS_IMPORT nsresult
+NewObjectInputStreamFromBuffer(UniquePtr<char[]> buffer, uint32_t len, 
+                               nsIObjectInputStream** stream);
+
+// We can't retrieve the wrapped stream from the objectOutputStream later,
+// so we return it here.
+NS_IMPORT nsresult
+NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream,
+                                    nsIStorageStream** stream);
+
+NS_IMPORT nsresult
+NewBufferFromStorageStream(nsIStorageStream *storageStream, 
+                           UniquePtr<char[]>* buffer, uint32_t* len);
+} // namespace scache
+} // namespace mozilla
+
 using namespace mozilla::scache;
 using mozilla::UniquePtr;
 
-void
-WaitForStartupTimer()
-{
-  StartupCache* sc = StartupCache::GetSingleton();
-  PR_Sleep(10 * PR_TicksPerSecond());
+#define NS_ENSURE_STR_MATCH(str1, str2, testname)  \
+PR_BEGIN_MACRO                                     \
+if (0 != strcmp(str1, str2)) {                     \
+  fail("failed " testname);                        \
+  return NS_ERROR_FAILURE;                         \
+}                                                  \
+passed("passed " testname);                        \
+PR_END_MACRO
 
+nsresult
+WaitForStartupTimer() {
+  nsresult rv;
+  nsCOMPtr<nsIStartupCache> sc
+    = do_GetService("@mozilla.org/startupcache/cache;1");
+  PR_Sleep(10 * PR_TicksPerSecond());
+  
+  bool complete;
   while (true) {
+    
     NS_ProcessPendingEvents(nullptr);
-    if (sc->StartupWriteComplete()) {
-      return;
-    }
+    rv = sc->StartupWriteComplete(&complete);
+    if (NS_FAILED(rv) || complete)
+      break;
     PR_Sleep(1 * PR_TicksPerSecond());
   }
+  return rv;
 }
 
-class TestStartupCache : public ::testing::Test
-{
-protected:
-  TestStartupCache();
-  ~TestStartupCache();
-
-  nsCOMPtr<nsIFile> mSCFile;
-};
+nsresult
+TestStartupWriteRead() {
+  nsresult rv;
+  nsCOMPtr<nsIStartupCache> sc
+    = do_GetService("@mozilla.org/startupcache/cache;1", &rv);
+  if (!sc) {
+    fail("didn't get a pointer...");
+    return NS_ERROR_FAILURE;
+  } else {
+    passed("got a pointer?");
+  }
+  sc->InvalidateCache();
+  
+  const char* buf = "Market opportunities for BeardBook";
+  const char* id = "id";
+  UniquePtr<char[]> outbuf;  
+  uint32_t len;
+  
+  rv = sc->PutBuffer(id, buf, strlen(buf) + 1);
+  NS_ENSURE_SUCCESS(rv, rv);
+  
+  rv = sc->GetBuffer(id, &outbuf, &len);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_STR_MATCH(buf, outbuf.get(), "pre-write read");
 
-TestStartupCache::TestStartupCache()
-{
-  NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(mSCFile));
-  mSCFile->AppendNative(NS_LITERAL_CSTRING("test-startupcache.tmp"));
-  nsAutoCString path;
-  mSCFile->GetNativePath(path);
-  char* env = PR_smprintf("MOZ_STARTUP_CACHE=%s", path.get());
-  PR_SetEnv(env);
-  // We intentionally leak `env` here because it is required by PR_SetEnv
-  MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(env);
-  StartupCache::GetSingleton()->InvalidateCache();
-}
-TestStartupCache::~TestStartupCache()
-{
-  PR_SetEnv("MOZ_STARTUP_CACHE=");
-  StartupCache::GetSingleton()->InvalidateCache();
+  rv = sc->ResetStartupWriteTimer();
+  rv = WaitForStartupTimer();
+  NS_ENSURE_SUCCESS(rv, rv);
+  
+  rv = sc->GetBuffer(id, &outbuf, &len);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_STR_MATCH(buf, outbuf.get(), "simple write/read");
+
+  return NS_OK;
 }
 
-
-TEST_F(TestStartupCache, StartupWriteRead)
-{
-  nsresult rv;
-  StartupCache* sc = StartupCache::GetSingleton();
-
-  const char* buf = "Market opportunities for BeardBook";
-  const char* id = "id";
-  UniquePtr<char[]> outbuf;
-  uint32_t len;
-
-  rv = sc->PutBuffer(id, buf, strlen(buf) + 1);
-  EXPECT_TRUE(NS_SUCCEEDED(rv));
-
-  rv = sc->GetBuffer(id, &outbuf, &len);
-  EXPECT_TRUE(NS_SUCCEEDED(rv));
-  EXPECT_STREQ(buf, outbuf.get());
-
-  rv = sc->ResetStartupWriteTimer();
-  EXPECT_TRUE(NS_SUCCEEDED(rv));
-  WaitForStartupTimer();
-
-  rv = sc->GetBuffer(id, &outbuf, &len);
-  EXPECT_TRUE(NS_SUCCEEDED(rv));
-  EXPECT_STREQ(buf, outbuf.get());
-}
-
-TEST_F(TestStartupCache, WriteInvalidateRead)
-{
+nsresult
+TestWriteInvalidateRead() {
   nsresult rv;
   const char* buf = "BeardBook competitive analysis";
   const char* id = "id";
   UniquePtr<char[]> outbuf;
   uint32_t len;
-  StartupCache* sc = StartupCache::GetSingleton();
-  ASSERT_TRUE(sc);
+  nsCOMPtr<nsIStartupCache> sc
+    = do_GetService("@mozilla.org/startupcache/cache;1", &rv);
+  sc->InvalidateCache();
 
   rv = sc->PutBuffer(id, buf, strlen(buf) + 1);
-  EXPECT_TRUE(NS_SUCCEEDED(rv));
+  NS_ENSURE_SUCCESS(rv, rv);
 
   sc->InvalidateCache();
 
   rv = sc->GetBuffer(id, &outbuf, &len);
-  EXPECT_EQ(rv, NS_ERROR_NOT_AVAILABLE);
+  if (rv == NS_ERROR_NOT_AVAILABLE) {
+    passed("buffer not available after invalidate");
+  } else if (NS_SUCCEEDED(rv)) {
+    fail("GetBuffer succeeded unexpectedly after invalidate");
+    return NS_ERROR_UNEXPECTED;
+  } else {
+    fail("GetBuffer gave an unexpected failure, expected NOT_AVAILABLE");
+    return rv;
+  }
+
+  sc->InvalidateCache();
+  return NS_OK;
 }
 
-TEST_F(TestStartupCache, WriteObject)
-{
+nsresult
+TestWriteObject() {
   nsresult rv;
 
   nsCOMPtr<nsIURI> obj
     = do_CreateInstance("@mozilla.org/network/simple-uri;1");
-  ASSERT_TRUE(obj);
-
+  if (!obj) {
+    fail("did not create object in test write object");
+    return NS_ERROR_UNEXPECTED;
+  }
   NS_NAMED_LITERAL_CSTRING(spec, "http://www.mozilla.org");
   rv = obj->SetSpec(spec);
-  EXPECT_TRUE(NS_SUCCEEDED(rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsIStartupCache> sc = do_GetService("@mozilla.org/startupcache/cache;1", &rv);
 
-  StartupCache* sc = StartupCache::GetSingleton();
-
+  sc->InvalidateCache();
+  
   // Create an object stream. Usually this is done with
   // NewObjectOutputWrappedStorageStream, but that uses
   // StartupCache::GetSingleton in debug builds, and we
   // don't have access to that here. Obviously.
   const char* id = "id";
   nsCOMPtr<nsIStorageStream> storageStream
     = do_CreateInstance("@mozilla.org/storagestream;1");
-  ASSERT_TRUE(storageStream);
-
+  NS_ENSURE_ARG_POINTER(storageStream);
+  
   rv = storageStream->Init(256, (uint32_t) -1);
-  EXPECT_TRUE(NS_SUCCEEDED(rv));
-
+  NS_ENSURE_SUCCESS(rv, rv);
+  
   nsCOMPtr<nsIObjectOutputStream> objectOutput
     = do_CreateInstance("@mozilla.org/binaryoutputstream;1");
-  ASSERT_TRUE(objectOutput);
-
+  if (!objectOutput)
+    return NS_ERROR_OUT_OF_MEMORY;
+  
   nsCOMPtr<nsIOutputStream> outputStream
     = do_QueryInterface(storageStream);
-
+  
   rv = objectOutput->SetOutputStream(outputStream);
-  EXPECT_TRUE(NS_SUCCEEDED(rv));
 
+  if (NS_FAILED(rv)) {
+    fail("failed to create output stream");
+    return rv;
+  }
   nsCOMPtr<nsISupports> objQI(do_QueryInterface(obj));
   rv = objectOutput->WriteObject(objQI, true);
-  EXPECT_TRUE(NS_SUCCEEDED(rv));
+  if (NS_FAILED(rv)) {
+    fail("failed to write object");
+    return rv;
+  }
 
   UniquePtr<char[]> buf;
   uint32_t len;
   NewBufferFromStorageStream(storageStream, &buf, &len);
 
   // Since this is a post-startup write, it should be written and
   // available.
   rv = sc->PutBuffer(id, buf.get(), len);
-  EXPECT_TRUE(NS_SUCCEEDED(rv));
-
+  if (NS_FAILED(rv)) {
+    fail("failed to insert input stream");
+    return rv;
+  }
+    
   UniquePtr<char[]> buf2;
   uint32_t len2;
   nsCOMPtr<nsIObjectInputStream> objectInput;
   rv = sc->GetBuffer(id, &buf2, &len2);
-  EXPECT_TRUE(NS_SUCCEEDED(rv));
+  if (NS_FAILED(rv)) {
+    fail("failed to retrieve buffer");
+    return rv;
+  }
 
   rv = NewObjectInputStreamFromBuffer(Move(buf2), len2,
                                       getter_AddRefs(objectInput));
-  EXPECT_TRUE(NS_SUCCEEDED(rv));
+  if (NS_FAILED(rv)) {
+    fail("failed to created input stream");
+    return rv;
+  }  
 
   nsCOMPtr<nsISupports> deserialized;
   rv = objectInput->ReadObject(true, getter_AddRefs(deserialized));
-  EXPECT_TRUE(NS_SUCCEEDED(rv));
+  if (NS_FAILED(rv)) {
+    fail("failed to read object");
+    return rv;
+  }
+  
+  bool match = false;
+  nsCOMPtr<nsIURI> uri(do_QueryInterface(deserialized));
+  if (uri) {
+    nsCString outSpec;
+    rv = uri->GetSpec(outSpec);
+    if (NS_FAILED(rv)) {
+      fail("failed to get spec");
+      return rv;
+    }
+    match = outSpec.Equals(spec);
+  }
+  if (!match) {
+    fail("deserialized object has incorrect information");
+    return rv;
+  }
+  
+  passed("write object");
+  return NS_OK;
+}
+
+nsresult
+LockCacheFile(bool protect, nsIFile* profileDir) {
+  NS_ENSURE_ARG(profileDir);
+
+  nsCOMPtr<nsIFile> startupCache;
+  profileDir->Clone(getter_AddRefs(startupCache));
+  NS_ENSURE_STATE(startupCache);
+  startupCache->AppendNative(NS_LITERAL_CSTRING("startupCache"));
+
+  nsresult rv;
+#ifndef XP_WIN
+  static uint32_t oldPermissions;
+#else
+  static PRFileDesc* fd = nullptr;
+#endif
 
-  nsCOMPtr<nsIURI> uri(do_QueryInterface(deserialized));
-  ASSERT_TRUE(uri);
+  // To prevent deletion of the startupcache file, we change the containing
+  // directory's permissions on Linux/Mac, and hold the file open on Windows
+  if (protect) {
+#ifndef XP_WIN
+    rv = startupCache->GetPermissions(&oldPermissions);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = startupCache->SetPermissions(0555);
+    NS_ENSURE_SUCCESS(rv, rv);
+#else
+    // Filename logic from StartupCache.cpp
+    #ifdef IS_BIG_ENDIAN
+    #define SC_ENDIAN "big"
+    #else
+    #define SC_ENDIAN "little"
+    #endif
+
+    #if PR_BYTES_PER_WORD == 4
+    #define SC_WORDSIZE "4"
+    #else
+    #define SC_WORDSIZE "8"
+    #endif
+    char sStartupCacheName[] = "startupCache." SC_WORDSIZE "." SC_ENDIAN;
+    startupCache->AppendNative(NS_LITERAL_CSTRING(sStartupCacheName));
+
+    rv = startupCache->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
+    NS_ENSURE_SUCCESS(rv, rv);
+#endif
+  } else {
+#ifndef XP_WIN
+    rv = startupCache->SetPermissions(oldPermissions);
+    NS_ENSURE_SUCCESS(rv, rv);
+#else
+   PR_Close(fd);
+#endif
+  }
+
+  return NS_OK;
+}
+
+nsresult
+TestIgnoreDiskCache(nsIFile* profileDir) {
+  nsresult rv;
+  nsCOMPtr<nsIStartupCache> sc
+    = do_GetService("@mozilla.org/startupcache/cache;1", &rv);
+  sc->InvalidateCache();
+  
+  const char* buf = "Get a Beardbook app for your smartphone";
+  const char* id = "id";
+  UniquePtr<char[]> outbuf;
+  uint32_t len;
+  
+  rv = sc->PutBuffer(id, buf, strlen(buf) + 1);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = sc->ResetStartupWriteTimer();
+  rv = WaitForStartupTimer();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Prevent StartupCache::InvalidateCache from deleting the disk file
+  rv = LockCacheFile(true, profileDir);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCString outSpec;
-  rv = uri->GetSpec(outSpec);
-  EXPECT_TRUE(NS_SUCCEEDED(rv));
-  ASSERT_TRUE(outSpec.Equals(spec));
+  sc->IgnoreDiskCache();
+
+  rv = sc->GetBuffer(id, &outbuf, &len);
+
+  nsresult r = LockCacheFile(false, profileDir);
+  NS_ENSURE_SUCCESS(r, r);
+
+  if (rv == NS_ERROR_NOT_AVAILABLE) {
+    passed("buffer not available after ignoring disk cache");
+  } else if (NS_SUCCEEDED(rv)) {
+    fail("GetBuffer succeeded unexpectedly after ignoring disk cache");
+    return NS_ERROR_UNEXPECTED;
+  } else {
+    fail("GetBuffer gave an unexpected failure, expected NOT_AVAILABLE");
+    return rv;
+  }
+
+  sc->InvalidateCache();
+  return NS_OK;
 }
+
+nsresult
+TestEarlyShutdown() {
+  nsresult rv;
+  nsCOMPtr<nsIStartupCache> sc
+    = do_GetService("@mozilla.org/startupcache/cache;1", &rv);
+  sc->InvalidateCache();
+
+  const char* buf = "Find your soul beardmate on BeardBook";
+  const char* id = "id";
+  uint32_t len;
+  UniquePtr<char[]> outbuf;
+  
+  sc->ResetStartupWriteTimer();
+  rv = sc->PutBuffer(id, buf, strlen(buf) + 1);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIObserver> obs;
+  sc->GetObserver(getter_AddRefs(obs));
+  obs->Observe(nullptr, "xpcom-shutdown", nullptr);
+  rv = WaitForStartupTimer();
+  NS_ENSURE_SUCCESS(rv, rv);
+  
+  rv = sc->GetBuffer(id, &outbuf, &len);
+
+  if (NS_SUCCEEDED(rv)) {
+    passed("GetBuffer succeeded after early shutdown");
+  } else {
+    fail("GetBuffer failed after early shutdown");
+    return rv;
+  }
+
+  const char* other_id = "other_id";
+  rv = sc->PutBuffer(other_id, buf, strlen(buf) + 1);
+
+  if (rv == NS_ERROR_NOT_AVAILABLE) {
+    passed("PutBuffer not available after early shutdown");
+  } else if (NS_SUCCEEDED(rv)) {
+    fail("PutBuffer succeeded unexpectedly after early shutdown");
+    return NS_ERROR_UNEXPECTED;
+  } else {
+    fail("PutBuffer gave an unexpected failure, expected NOT_AVAILABLE");
+    return rv;
+  }
+ 
+  return NS_OK;
+}
+
+int main(int argc, char** argv)
+{
+  ScopedXPCOM xpcom("Startup Cache");
+  if (xpcom.failed())
+    return 1;
+
+  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+  if (!prefs) {
+    fail("prefs");
+    return 1;
+  }
+  prefs->SetIntPref("hangmonitor.timeout", 0);
+
+  int rv = 0;
+  nsresult scrv;
+
+  nsCOMPtr<nsIStartupCache> sc 
+    = do_GetService("@mozilla.org/startupcache/cache;1", &scrv);
+  if (NS_FAILED(scrv))
+    rv = 1;
+  else
+    sc->RecordAgesAlways();
+  if (NS_FAILED(TestStartupWriteRead()))
+    rv = 1;
+  if (NS_FAILED(TestWriteInvalidateRead()))
+    rv = 1;
+  if (NS_FAILED(TestWriteObject()))
+    rv = 1;
+  nsCOMPtr<nsIFile> profileDir = xpcom.GetProfileDirectory();
+  if (NS_FAILED(TestIgnoreDiskCache(profileDir)))
+    rv = 1;
+  if (NS_FAILED(TestEarlyShutdown()))
+    rv = 1;
+
+  return rv;
+}
--- a/startupcache/test/moz.build
+++ b/startupcache/test/moz.build
@@ -1,10 +1,9 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-UNIFIED_SOURCES += [
-    'TestStartupCache.cpp',
-]
-FINAL_LIBRARY = 'xul-gtest'
+GeckoCppUnitTests([
+    'TestStartupCache',
+])
--- a/testing/cppunittest.ini
+++ b/testing/cppunittest.ini
@@ -36,16 +36,19 @@ skip-if = os == 'b2g'  #Bug 1038197
 skip-if = os == 'android' # Bug 1147630
 [TestRefPtr]
 [TestRollingMean]
 [TestScopeExit]
 [TestSegmentedVector]
 [TestSHA1]
 [TestSaturate]
 [TestSplayTree]
+[TestStartupCache]
+skip-if = os == 'b2g' || os == 'android'  # Bug 929655
+support-files = TestStartupCacheTelemetry.js TestStartupCacheTelemetry.manifest
 [TestSyncRunnable]
 [TestTXMgr]
 skip-if = os == 'b2g'  #Bug 919595
 [TestTemplateLib]
 [TestTuple]
 [TestTypeTraits]
 [TestTypedEnum]
 [TestUDPSocket]