TestStartupCache.cpp (5320B)
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 "gtest/gtest.h" 7 8 #include "mozilla/scache/StartupCache.h" 9 #include "mozilla/scache/StartupCacheUtils.h" 10 11 #include "nsDirectoryServiceDefs.h" 12 #include "nsIOutputStream.h" 13 #include "nsISupports.h" 14 #include "nsIStorageStream.h" 15 #include "nsIObjectInputStream.h" 16 #include "nsIObjectOutputStream.h" 17 #include "nsIURI.h" 18 #include "nsThreadUtils.h" 19 #include "prenv.h" 20 #include "prio.h" 21 #include "prprf.h" 22 #include "mozilla/gtest/MozAssertions.h" 23 #include "mozilla/Printf.h" 24 #include "mozilla/UniquePtr.h" 25 #include "mozilla/UniquePtrExtensions.h" 26 #include "nsNetCID.h" 27 #include "nsIURIMutator.h" 28 29 using namespace JS; 30 31 using namespace mozilla::scache; 32 using mozilla::UniquePtr; 33 34 void WaitForStartupTimer() { 35 StartupCache* sc = StartupCache::GetSingleton(); 36 PR_Sleep(3 * PR_TicksPerSecond()); 37 38 while (true) { 39 NS_ProcessPendingEvents(nullptr); 40 if (sc->StartupWriteComplete()) { 41 return; 42 } 43 PR_Sleep(1 * PR_TicksPerSecond()); 44 } 45 } 46 47 class TestStartupCache : public ::testing::Test { 48 protected: 49 TestStartupCache(); 50 ~TestStartupCache(); 51 52 nsCOMPtr<nsIFile> mSCFile; 53 }; 54 55 TestStartupCache::TestStartupCache() { 56 NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(mSCFile)); 57 mSCFile->AppendNative("test-startupcache.tmp"_ns); 58 #ifdef XP_WIN 59 nsAutoString env(u"MOZ_STARTUP_CACHE="_ns); 60 env.Append(mSCFile->NativePath()); 61 _wputenv(env.get()); 62 #else 63 nsAutoCString path; 64 mSCFile->GetNativePath(path); 65 char* env = mozilla::Smprintf("MOZ_STARTUP_CACHE=%s", path.get()).release(); 66 PR_SetEnv(env); 67 // We intentionally leak `env` here because it is required by PR_SetEnv 68 MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(env); 69 #endif 70 StartupCache::GetSingleton()->InvalidateCache(); 71 } 72 TestStartupCache::~TestStartupCache() { 73 PR_SetEnv("MOZ_STARTUP_CACHE="); 74 StartupCache::GetSingleton()->InvalidateCache(); 75 } 76 77 TEST_F(TestStartupCache, StartupWriteRead) { 78 nsresult rv; 79 StartupCache* sc = StartupCache::GetSingleton(); 80 81 const char* buf = "Market opportunities for BeardBook"; 82 const char* id = "id"; 83 const char* outbuf; 84 uint32_t len; 85 86 rv = sc->PutBuffer(id, mozilla::UniqueFreePtr<char[]>(strdup(buf)), 87 strlen(buf) + 1); 88 EXPECT_NS_SUCCEEDED(rv); 89 90 rv = sc->GetBuffer(id, &outbuf, &len); 91 EXPECT_NS_SUCCEEDED(rv); 92 EXPECT_STREQ(buf, outbuf); 93 94 rv = sc->ResetStartupWriteTimerAndLock(); 95 EXPECT_NS_SUCCEEDED(rv); 96 WaitForStartupTimer(); 97 98 rv = sc->GetBuffer(id, &outbuf, &len); 99 EXPECT_NS_SUCCEEDED(rv); 100 EXPECT_STREQ(buf, outbuf); 101 } 102 103 TEST_F(TestStartupCache, WriteInvalidateRead) { 104 nsresult rv; 105 const char* buf = "BeardBook competitive analysis"; 106 const char* id = "id"; 107 const char* outbuf; 108 uint32_t len; 109 StartupCache* sc = StartupCache::GetSingleton(); 110 ASSERT_TRUE(sc); 111 112 rv = sc->PutBuffer(id, mozilla::UniqueFreePtr<char[]>(strdup(buf)), 113 strlen(buf) + 1); 114 EXPECT_NS_SUCCEEDED(rv); 115 116 sc->InvalidateCache(); 117 118 rv = sc->GetBuffer(id, &outbuf, &len); 119 EXPECT_EQ(rv, NS_ERROR_NOT_AVAILABLE); 120 } 121 122 TEST_F(TestStartupCache, WriteObject) { 123 nsresult rv; 124 125 nsCOMPtr<nsIURI> obj; 126 127 constexpr auto spec = "http://www.mozilla.org"_ns; 128 rv = NS_MutateURI(NS_SIMPLEURIMUTATOR_CONTRACTID).SetSpec(spec).Finalize(obj); 129 EXPECT_NS_SUCCEEDED(rv); 130 131 StartupCache* sc = StartupCache::GetSingleton(); 132 133 // Create an object stream. Usually this is done with 134 // NewObjectOutputWrappedStorageStream, but that uses 135 // StartupCache::GetSingleton in debug builds, and we 136 // don't have access to that here. Obviously. 137 const char* id = "id"; 138 nsCOMPtr<nsIStorageStream> storageStream = 139 do_CreateInstance("@mozilla.org/storagestream;1"); 140 ASSERT_TRUE(storageStream); 141 142 rv = storageStream->Init(256, (uint32_t)-1); 143 EXPECT_NS_SUCCEEDED(rv); 144 145 nsCOMPtr<nsIObjectOutputStream> objectOutput = 146 do_CreateInstance("@mozilla.org/binaryoutputstream;1"); 147 ASSERT_TRUE(objectOutput); 148 149 nsCOMPtr<nsIOutputStream> outputStream = do_QueryInterface(storageStream); 150 151 rv = objectOutput->SetOutputStream(outputStream); 152 EXPECT_NS_SUCCEEDED(rv); 153 154 nsCOMPtr<nsISupports> objQI(do_QueryInterface(obj)); 155 rv = objectOutput->WriteObject(objQI, true); 156 EXPECT_NS_SUCCEEDED(rv); 157 158 mozilla::UniqueFreePtr<char[]> buf; 159 uint32_t len; 160 NewBufferFromStorageStream(storageStream, &buf, &len); 161 162 // Since this is a post-startup write, it should be written and 163 // available. 164 rv = sc->PutBuffer(id, std::move(buf), len); 165 EXPECT_NS_SUCCEEDED(rv); 166 167 const char* buf2; 168 uint32_t len2; 169 nsCOMPtr<nsIObjectInputStream> objectInput; 170 rv = sc->GetBuffer(id, &buf2, &len2); 171 EXPECT_NS_SUCCEEDED(rv); 172 173 rv = NewObjectInputStreamFromBuffer(buf2, len2, getter_AddRefs(objectInput)); 174 EXPECT_NS_SUCCEEDED(rv); 175 176 nsCOMPtr<nsISupports> deserialized; 177 rv = objectInput->ReadObject(true, getter_AddRefs(deserialized)); 178 EXPECT_NS_SUCCEEDED(rv); 179 180 nsCOMPtr<nsIURI> uri(do_QueryInterface(deserialized)); 181 ASSERT_TRUE(uri); 182 183 nsCString outSpec; 184 rv = uri->GetSpec(outSpec); 185 EXPECT_NS_SUCCEEDED(rv); 186 ASSERT_TRUE(outSpec.Equals(spec)); 187 }