LCOV - code coverage report
Current view: directory - startupcache/test - TestStartupCache.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 245 188 76.7 %
Date: 2012-06-02 Functions: 9 9 100.0 %

       1                 : /* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Startup Cache.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * The Mozilla Foundation <http://www.mozilla.org/>.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2009
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *  Benedict Hsieh <bhsieh@mozilla.com>
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #include "TestHarness.h"
      40                 : 
      41                 : #include "nsThreadUtils.h"
      42                 : #include "nsIClassInfo.h"
      43                 : #include "nsIOutputStream.h"
      44                 : #include "nsIObserver.h"
      45                 : #include "nsISerializable.h"
      46                 : #include "nsISupports.h"
      47                 : #include "nsIStartupCache.h"
      48                 : #include "nsIStringStream.h"
      49                 : #include "nsIStorageStream.h"
      50                 : #include "nsIObjectInputStream.h"
      51                 : #include "nsIObjectOutputStream.h"
      52                 : #include "nsIURI.h"
      53                 : #include "nsStringAPI.h"
      54                 : #include "nsIPrefBranch.h"
      55                 : #include "nsIPrefService.h"
      56                 : #include "nsITelemetry.h"
      57                 : #include "jsapi.h"
      58                 : 
      59                 : namespace mozilla {
      60                 : namespace scache {
      61                 : 
      62                 : NS_IMPORT nsresult
      63                 : NewObjectInputStreamFromBuffer(char* buffer, PRUint32 len, 
      64                 :                                nsIObjectInputStream** stream);
      65                 : 
      66                 : // We can't retrieve the wrapped stream from the objectOutputStream later,
      67                 : // so we return it here.
      68                 : NS_IMPORT nsresult
      69                 : NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream,
      70                 :                                     nsIStorageStream** stream);
      71                 : 
      72                 : NS_IMPORT nsresult
      73                 : NewBufferFromStorageStream(nsIStorageStream *storageStream, 
      74                 :                            char** buffer, PRUint32* len);
      75                 : }
      76                 : }
      77                 : 
      78                 : using namespace mozilla::scache;
      79                 : 
      80                 : #define NS_ENSURE_STR_MATCH(str1, str2, testname)  \
      81                 : PR_BEGIN_MACRO                                     \
      82                 : if (0 != strcmp(str1, str2)) {                     \
      83                 :   fail("failed " testname);                        \
      84                 :   return NS_ERROR_FAILURE;                         \
      85                 : }                                                  \
      86                 : passed("passed " testname);                        \
      87                 : PR_END_MACRO
      88                 : 
      89                 : nsresult
      90               2 : WaitForStartupTimer() {
      91                 :   nsresult rv;
      92                 :   nsCOMPtr<nsIStartupCache> sc 
      93               4 :     = do_GetService("@mozilla.org/startupcache/cache;1");
      94               2 :   PR_Sleep(10 * PR_TicksPerSecond());
      95                 :   
      96                 :   bool complete;
      97             100 :   while (true) {
      98                 :     
      99             102 :     NS_ProcessPendingEvents(nsnull);
     100             102 :     rv = sc->StartupWriteComplete(&complete);
     101             102 :     if (NS_FAILED(rv) || complete)
     102                 :       break;
     103             100 :     PR_Sleep(1 * PR_TicksPerSecond());
     104                 :   }
     105               2 :   return rv;
     106                 : }
     107                 : 
     108                 : nsresult
     109               1 : TestStartupWriteRead() {
     110                 :   nsresult rv;
     111                 :   nsCOMPtr<nsIStartupCache> sc 
     112               2 :     = do_GetService("@mozilla.org/startupcache/cache;1", &rv);
     113               1 :   if (!sc) {
     114               0 :     fail("didn't get a pointer...");
     115               0 :     return NS_ERROR_FAILURE;
     116                 :   } else {
     117               1 :     passed("got a pointer?");
     118                 :   }
     119               1 :   sc->InvalidateCache();
     120                 :   
     121               1 :   const char* buf = "Market opportunities for BeardBook";
     122               1 :   const char* id = "id";
     123               1 :   char* outbufPtr = NULL;
     124               2 :   nsAutoArrayPtr<char> outbuf;  
     125                 :   PRUint32 len;
     126                 :   
     127               1 :   rv = sc->PutBuffer(id, buf, strlen(buf) + 1);
     128               1 :   NS_ENSURE_SUCCESS(rv, rv);
     129                 :   
     130               1 :   rv = sc->GetBuffer(id, &outbufPtr, &len);
     131               1 :   NS_ENSURE_SUCCESS(rv, rv);
     132               1 :   outbuf = outbufPtr;
     133               1 :   NS_ENSURE_STR_MATCH(buf, outbuf, "pre-write read");
     134                 : 
     135               1 :   rv = sc->ResetStartupWriteTimer();
     136               1 :   rv = WaitForStartupTimer();
     137               1 :   NS_ENSURE_SUCCESS(rv, rv);
     138                 :   
     139               1 :   rv = sc->GetBuffer(id, &outbufPtr, &len);
     140               1 :   NS_ENSURE_SUCCESS(rv, rv);
     141               1 :   outbuf = outbufPtr;
     142               1 :   NS_ENSURE_STR_MATCH(buf, outbuf, "simple write/read");
     143                 : 
     144               1 :   return NS_OK;
     145                 : }
     146                 : 
     147                 : nsresult
     148               1 : TestWriteInvalidateRead() {
     149                 :   nsresult rv;
     150               1 :   const char* buf = "BeardBook competitive analysis";
     151               1 :   const char* id = "id";
     152               1 :   char* outbuf = NULL;
     153                 :   PRUint32 len;
     154                 :   nsCOMPtr<nsIStartupCache> sc 
     155               2 :     = do_GetService("@mozilla.org/startupcache/cache;1", &rv);
     156               1 :   sc->InvalidateCache();
     157                 : 
     158               1 :   rv = sc->PutBuffer(id, buf, strlen(buf) + 1);
     159               1 :   NS_ENSURE_SUCCESS(rv, rv);
     160                 : 
     161               1 :   sc->InvalidateCache();
     162                 : 
     163               1 :   rv = sc->GetBuffer(id, &outbuf, &len);
     164               1 :   delete[] outbuf;
     165               1 :   if (rv == NS_ERROR_NOT_AVAILABLE) {
     166               1 :     passed("buffer not available after invalidate");
     167               0 :   } else if (NS_SUCCEEDED(rv)) {
     168               0 :     fail("GetBuffer succeeded unexpectedly after invalidate");
     169               0 :     return NS_ERROR_UNEXPECTED;
     170                 :   } else {
     171               0 :     fail("GetBuffer gave an unexpected failure, expected NOT_AVAILABLE");
     172               0 :     return rv;
     173                 :   }
     174                 : 
     175               1 :   sc->InvalidateCache();
     176               1 :   return NS_OK;
     177                 : }
     178                 : 
     179                 : nsresult
     180               1 : TestWriteObject() {
     181                 :   nsresult rv;
     182                 : 
     183                 :   nsCOMPtr<nsIURI> obj
     184               2 :     = do_CreateInstance("@mozilla.org/network/simple-uri;1");
     185               1 :   if (!obj) {
     186               0 :     fail("did not create object in test write object");
     187               0 :     return NS_ERROR_UNEXPECTED;
     188                 :   }
     189               2 :   NS_NAMED_LITERAL_CSTRING(spec, "http://www.mozilla.org");
     190               1 :   obj->SetSpec(spec);
     191               2 :   nsCOMPtr<nsIStartupCache> sc = do_GetService("@mozilla.org/startupcache/cache;1", &rv);
     192                 : 
     193               1 :   sc->InvalidateCache();
     194                 :   
     195                 :   // Create an object stream. Usually this is done with
     196                 :   // NewObjectOutputWrappedStorageStream, but that uses
     197                 :   // StartupCache::GetSingleton in debug builds, and we
     198                 :   // don't have access to that here. Obviously.
     199               1 :   const char* id = "id";
     200                 :   nsCOMPtr<nsIStorageStream> storageStream
     201               2 :     = do_CreateInstance("@mozilla.org/storagestream;1");
     202               1 :   NS_ENSURE_ARG_POINTER(storageStream);
     203                 :   
     204               1 :   rv = storageStream->Init(256, (PRUint32) -1, nsnull);
     205               1 :   NS_ENSURE_SUCCESS(rv, rv);
     206                 :   
     207                 :   nsCOMPtr<nsIObjectOutputStream> objectOutput
     208               2 :     = do_CreateInstance("@mozilla.org/binaryoutputstream;1");
     209               1 :   if (!objectOutput)
     210               0 :     return NS_ERROR_OUT_OF_MEMORY;
     211                 :   
     212                 :   nsCOMPtr<nsIOutputStream> outputStream
     213               2 :     = do_QueryInterface(storageStream);
     214                 :   
     215               1 :   rv = objectOutput->SetOutputStream(outputStream);
     216                 : 
     217               1 :   if (NS_FAILED(rv)) {
     218               0 :     fail("failed to create output stream");
     219               0 :     return rv;
     220                 :   }
     221               2 :   nsCOMPtr<nsISupports> objQI(do_QueryInterface(obj));
     222               1 :   rv = objectOutput->WriteObject(objQI, true);
     223               1 :   if (NS_FAILED(rv)) {
     224               0 :     fail("failed to write object");
     225               0 :     return rv;
     226                 :   }
     227                 : 
     228               1 :   char* bufPtr = NULL;
     229               2 :   nsAutoArrayPtr<char> buf;
     230                 :   PRUint32 len;
     231               1 :   NewBufferFromStorageStream(storageStream, &bufPtr, &len);
     232               1 :   buf = bufPtr;
     233                 : 
     234                 :   // Since this is a post-startup write, it should be written and
     235                 :   // available.
     236               1 :   rv = sc->PutBuffer(id, buf, len);
     237               1 :   if (NS_FAILED(rv)) {
     238               0 :     fail("failed to insert input stream");
     239               0 :     return rv;
     240                 :   }
     241                 :     
     242               1 :   char* buf2Ptr = NULL;
     243               2 :   nsAutoArrayPtr<char> buf2;
     244                 :   PRUint32 len2;
     245               2 :   nsCOMPtr<nsIObjectInputStream> objectInput;
     246               1 :   rv = sc->GetBuffer(id, &buf2Ptr, &len2);
     247               1 :   if (NS_FAILED(rv)) {
     248               0 :     fail("failed to retrieve buffer");
     249               0 :     return rv;
     250                 :   }
     251               1 :   buf2 = buf2Ptr;
     252                 : 
     253               1 :   rv = NewObjectInputStreamFromBuffer(buf2, len2, getter_AddRefs(objectInput));
     254               1 :   if (NS_FAILED(rv)) {
     255               0 :     fail("failed to created input stream");
     256               0 :     return rv;
     257                 :   }  
     258               1 :   buf2.forget();
     259                 : 
     260               2 :   nsCOMPtr<nsISupports> deserialized;
     261               1 :   rv = objectInput->ReadObject(true, getter_AddRefs(deserialized));
     262               1 :   if (NS_FAILED(rv)) {
     263               0 :     fail("failed to read object");
     264               0 :     return rv;
     265                 :   }
     266                 :   
     267               1 :   bool match = false;
     268               2 :   nsCOMPtr<nsIURI> uri(do_QueryInterface(deserialized));
     269               1 :   if (uri) {
     270               2 :     nsCString outSpec;
     271               1 :     uri->GetSpec(outSpec);
     272               1 :     match = outSpec.Equals(spec);
     273                 :   }
     274               1 :   if (!match) {
     275               0 :     fail("deserialized object has incorrect information");
     276               0 :     return rv;
     277                 :   }
     278                 :   
     279               1 :   passed("write object");
     280               1 :   return NS_OK;
     281                 : }
     282                 : 
     283                 : nsresult
     284               1 : TestEarlyShutdown() {
     285                 :   nsresult rv;
     286                 :   nsCOMPtr<nsIStartupCache> sc 
     287               2 :     = do_GetService("@mozilla.org/startupcache/cache;1", &rv);
     288               1 :   sc->InvalidateCache();
     289                 : 
     290               1 :   const char* buf = "Find your soul beardmate on BeardBook";
     291               1 :   const char* id = "id";
     292                 :   PRUint32 len;
     293               1 :   char* outbuf = NULL;
     294                 :   
     295               1 :   sc->ResetStartupWriteTimer();
     296               1 :   rv = sc->PutBuffer(buf, id, strlen(buf) + 1);
     297               1 :   NS_ENSURE_SUCCESS(rv, rv);
     298                 : 
     299               2 :   nsCOMPtr<nsIObserver> obs;
     300               1 :   sc->GetObserver(getter_AddRefs(obs));
     301               1 :   obs->Observe(nsnull, "xpcom-shutdown", nsnull);
     302               1 :   rv = WaitForStartupTimer();
     303               1 :   NS_ENSURE_SUCCESS(rv, rv);
     304                 :   
     305               1 :   rv = sc->GetBuffer(id, &outbuf, &len);
     306               1 :   delete[] outbuf;
     307                 : 
     308               1 :   if (rv == NS_ERROR_NOT_AVAILABLE) {
     309               1 :     passed("buffer not available after early shutdown");
     310               0 :   } else if (NS_SUCCEEDED(rv)) {
     311               0 :     fail("GetBuffer succeeded unexpectedly after early shutdown");
     312               0 :     return NS_ERROR_UNEXPECTED;
     313                 :   } else {
     314               0 :     fail("GetBuffer gave an unexpected failure, expected NOT_AVAILABLE");
     315               0 :     return rv;
     316                 :   }
     317                 :  
     318               1 :   return NS_OK;
     319                 : }
     320                 : 
     321                 : bool
     322               1 : SetupJS(JSContext **cxp)
     323                 : {
     324               1 :   JSRuntime *rt = JS_NewRuntime(32 * 1024 * 1024);
     325               1 :   if (!rt)
     326               0 :     return false;
     327               1 :   JSContext *cx = JS_NewContext(rt, 8192);
     328               1 :   if (!cx)
     329               0 :     return false;
     330               1 :   *cxp = cx;
     331               1 :   return true;
     332                 : }
     333                 : 
     334                 : bool
     335               2 : GetHistogramCounts(const char *testmsg, JSContext *cx, jsval *counts)
     336                 : {
     337               4 :   nsCOMPtr<nsITelemetry> telemetry = do_GetService("@mozilla.org/base/telemetry;1");
     338               4 :   NS_NAMED_LITERAL_CSTRING(histogram_id, "STARTUP_CACHE_AGE_HOURS");
     339               4 :   JS::AutoValueRooter h(cx);
     340               2 :   nsresult trv = telemetry->GetHistogramById(histogram_id, cx, h.addr());
     341               2 :   if (NS_FAILED(trv)) {
     342               0 :     fail("%s: couldn't get histogram", testmsg);
     343               0 :     return false;
     344                 :   }
     345               2 :   passed(testmsg);
     346                 : 
     347               4 :   JS::AutoValueRooter snapshot_val(cx);
     348               2 :   JSFunction *snapshot_fn = NULL;
     349               4 :   JS::AutoValueRooter ss(cx);
     350               2 :   return (JS_GetProperty(cx, JSVAL_TO_OBJECT(h.value()), "snapshot",
     351               4 :                          snapshot_val.addr())
     352               2 :           && (snapshot_fn = JS_ValueToFunction(cx, snapshot_val.value()))
     353               2 :           && JS::Call(cx, JSVAL_TO_OBJECT(h.value()),
     354               4 :                       snapshot_fn, 0, NULL, ss.addr())
     355               2 :           && JS_GetProperty(cx, JSVAL_TO_OBJECT(ss.value()),
     356               8 :                             "counts", counts));
     357                 : }
     358                 : 
     359                 : nsresult
     360               1 : CompareCountArrays(JSContext *cx, JSObject *before, JSObject *after)
     361                 : {
     362                 :   uint32_t before_size, after_size;
     363               1 :   if (!(JS_GetArrayLength(cx, before, &before_size)
     364               1 :         && JS_GetArrayLength(cx, after, &after_size))) {
     365               0 :     return NS_ERROR_UNEXPECTED;
     366                 :   }
     367                 : 
     368               1 :   if (before_size != after_size) {
     369               0 :     return NS_ERROR_UNEXPECTED;
     370                 :   }
     371                 : 
     372               2 :   for (uint32_t i = 0; i < before_size; ++i) {
     373                 :     jsval before_num, after_num;
     374                 : 
     375               1 :     if (!(JS_GetElement(cx, before, i, &before_num)
     376               1 :           && JS_GetElement(cx, after, i, &after_num))) {
     377               0 :       return NS_ERROR_UNEXPECTED;
     378                 :     }
     379                 : 
     380               1 :     JSBool same = JS_TRUE;
     381               1 :     if (!JS_LooselyEqual(cx, before_num, after_num, &same)) {
     382               0 :       return NS_ERROR_UNEXPECTED;
     383                 :     } else {
     384               1 :       if (same) {
     385               0 :         continue;
     386                 :       } else {
     387                 :         // Some element of the histograms's count arrays differed.
     388                 :         // That's a good thing!
     389               1 :         return NS_OK;
     390                 :       }
     391                 :     }
     392                 :   }
     393                 : 
     394                 :   // All of the elements of the histograms's count arrays differed.
     395                 :   // Not good, we should have recorded something.
     396               0 :   return NS_ERROR_FAILURE;
     397                 : }
     398                 : 
     399               1 : int main(int argc, char** argv)
     400                 : {
     401               2 :   ScopedXPCOM xpcom("Startup Cache");
     402               1 :   if (xpcom.failed())
     403               0 :     return 1;
     404                 : 
     405               2 :   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     406               1 :   prefs->SetIntPref("hangmonitor.timeout", 0);
     407                 :   
     408               1 :   int rv = 0;
     409                 :   // nsITelemetry doesn't have a nice C++ interface.
     410                 :   JSContext *cx;
     411               1 :   bool use_js = true;
     412               1 :   if (!SetupJS(&cx))
     413               0 :     use_js = false;
     414                 : 
     415               2 :   JSAutoRequest req(cx);
     416                 :   static JSClass global_class = {
     417                 :     "global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE,
     418                 :     JS_PropertyStub,  JS_PropertyStub,
     419                 :     JS_PropertyStub,  JS_StrictPropertyStub,
     420                 :     JS_EnumerateStub, JS_ResolveStub,
     421                 :     JS_ConvertStub,   JS_FinalizeStub,
     422                 :     JSCLASS_NO_OPTIONAL_MEMBERS
     423                 :   };
     424               1 :   JSObject *glob = nsnull;
     425               1 :   if (use_js)
     426               1 :     glob = JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL);
     427               1 :   if (!glob)
     428               0 :     use_js = false;
     429               1 :   JSCrossCompartmentCall *compartment = nsnull;
     430               1 :   if (use_js)
     431               1 :     compartment = JS_EnterCrossCompartmentCall(cx, glob);
     432               1 :   if (!compartment)
     433               0 :     use_js = false;
     434               1 :   if (use_js && !JS_InitStandardClasses(cx, glob))
     435               0 :     use_js = false;
     436                 : 
     437               2 :   JS::AutoValueRooter before_counts(cx);
     438               2 :   if (use_js && !GetHistogramCounts("STARTUP_CACHE_AGE_HOURS histogram before test",
     439               1 :                                  cx, before_counts.addr()))
     440               0 :     use_js = false;
     441                 :   
     442                 :   nsresult scrv;
     443                 :   nsCOMPtr<nsIStartupCache> sc 
     444               2 :     = do_GetService("@mozilla.org/startupcache/cache;1", &scrv);
     445               1 :   if (NS_FAILED(scrv))
     446               0 :     rv = 1;
     447                 :   else
     448               1 :     sc->RecordAgesAlways();
     449               1 :   if (NS_FAILED(TestStartupWriteRead()))
     450               0 :     rv = 1;
     451               1 :   if (NS_FAILED(TestWriteInvalidateRead()))
     452               0 :     rv = 1;
     453               1 :   if (NS_FAILED(TestWriteObject()))
     454               0 :     rv = 1;
     455               1 :   if (NS_FAILED(TestEarlyShutdown()))
     456               0 :     rv = 1;
     457                 : 
     458               2 :   JS::AutoValueRooter after_counts(cx);
     459               2 :   if (use_js && !GetHistogramCounts("STARTUP_CACHE_AGE_HOURS histogram after test",
     460               1 :                                     cx, after_counts.addr()))
     461               0 :     use_js = false;
     462                 : 
     463               1 :   if (!use_js) {
     464               0 :     fail("couldn't check histogram recording");
     465               0 :     rv = 1;
     466                 :   } else {
     467                 :     nsresult compare = CompareCountArrays(cx,
     468               1 :                                           JSVAL_TO_OBJECT(before_counts.value()),
     469               2 :                                           JSVAL_TO_OBJECT(after_counts.value()));
     470               1 :     if (compare == NS_ERROR_UNEXPECTED) {
     471               0 :       fail("count comparison error");
     472               0 :       rv = 1;
     473               1 :     } else if (compare == NS_ERROR_FAILURE) {
     474               0 :       fail("histogram didn't record samples");
     475               0 :       rv = 1;
     476                 :     } else {
     477               1 :       passed("histogram records samples");
     478                 :     }
     479                 :   }
     480                 : 
     481               1 :   if (use_js)
     482               1 :     JS_LeaveCrossCompartmentCall(compartment);
     483                 : 
     484               1 :   return rv;
     485                 : }

Generated by: LCOV version 1.7