1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/places/tests/cpp/places_test_harness.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,405 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "TestHarness.h" 1.11 +#include "nsMemory.h" 1.12 +#include "nsThreadUtils.h" 1.13 +#include "nsNetUtil.h" 1.14 +#include "nsDocShellCID.h" 1.15 + 1.16 +#include "nsToolkitCompsCID.h" 1.17 +#include "nsINavHistoryService.h" 1.18 +#include "nsIObserverService.h" 1.19 +#include "mozilla/IHistory.h" 1.20 +#include "mozIStorageConnection.h" 1.21 +#include "mozIStorageStatement.h" 1.22 +#include "mozIStorageAsyncStatement.h" 1.23 +#include "mozIStorageStatementCallback.h" 1.24 +#include "mozIStoragePendingStatement.h" 1.25 +#include "nsPIPlacesDatabase.h" 1.26 +#include "nsIObserver.h" 1.27 +#include "prinrval.h" 1.28 +#include "mozilla/Attributes.h" 1.29 + 1.30 +#define WAITFORTOPIC_TIMEOUT_SECONDS 5 1.31 + 1.32 + 1.33 +static size_t gTotalTests = 0; 1.34 +static size_t gPassedTests = 0; 1.35 + 1.36 +#define do_check_true(aCondition) \ 1.37 + PR_BEGIN_MACRO \ 1.38 + gTotalTests++; \ 1.39 + if (aCondition) { \ 1.40 + gPassedTests++; \ 1.41 + } else { \ 1.42 + fail("%s | Expected true, got false at line %d", __FILE__, __LINE__); \ 1.43 + } \ 1.44 + PR_END_MACRO 1.45 + 1.46 +#define do_check_false(aCondition) \ 1.47 + PR_BEGIN_MACRO \ 1.48 + gTotalTests++; \ 1.49 + if (!aCondition) { \ 1.50 + gPassedTests++; \ 1.51 + } else { \ 1.52 + fail("%s | Expected false, got true at line %d", __FILE__, __LINE__); \ 1.53 + } \ 1.54 + PR_END_MACRO 1.55 + 1.56 +#define do_check_success(aResult) \ 1.57 + do_check_true(NS_SUCCEEDED(aResult)) 1.58 + 1.59 +#ifdef LINUX 1.60 +// XXX Linux opt builds on tinderbox are orange due to linking with stdlib. 1.61 +// This is sad and annoying, but it's a workaround that works. 1.62 +#define do_check_eq(aExpected, aActual) \ 1.63 + do_check_true(aExpected == aActual) 1.64 +#else 1.65 +#include <sstream> 1.66 + 1.67 +#define do_check_eq(aActual, aExpected) \ 1.68 + PR_BEGIN_MACRO \ 1.69 + gTotalTests++; \ 1.70 + if (aExpected == aActual) { \ 1.71 + gPassedTests++; \ 1.72 + } else { \ 1.73 + std::ostringstream temp; \ 1.74 + temp << __FILE__ << " | Expected '" << aExpected << "', got '"; \ 1.75 + temp << aActual <<"' at line " << __LINE__; \ 1.76 + fail(temp.str().c_str()); \ 1.77 + } \ 1.78 + PR_END_MACRO 1.79 +#endif 1.80 + 1.81 +struct Test 1.82 +{ 1.83 + void (*func)(void); 1.84 + const char* const name; 1.85 +}; 1.86 +#define TEST(aName) \ 1.87 + {aName, #aName} 1.88 + 1.89 +/** 1.90 + * Runs the next text. 1.91 + */ 1.92 +void run_next_test(); 1.93 + 1.94 +/** 1.95 + * To be used around asynchronous work. 1.96 + */ 1.97 +void do_test_pending(); 1.98 +void do_test_finished(); 1.99 + 1.100 +/** 1.101 + * Spins current thread until a topic is received. 1.102 + */ 1.103 +class WaitForTopicSpinner MOZ_FINAL : public nsIObserver 1.104 +{ 1.105 +public: 1.106 + NS_DECL_ISUPPORTS 1.107 + 1.108 + WaitForTopicSpinner(const char* const aTopic) 1.109 + : mTopicReceived(false) 1.110 + , mStartTime(PR_IntervalNow()) 1.111 + { 1.112 + nsCOMPtr<nsIObserverService> observerService = 1.113 + do_GetService(NS_OBSERVERSERVICE_CONTRACTID); 1.114 + do_check_true(observerService); 1.115 + (void)observerService->AddObserver(this, aTopic, false); 1.116 + } 1.117 + 1.118 + void Spin() { 1.119 + while (!mTopicReceived) { 1.120 + if ((PR_IntervalNow() - mStartTime) > (WAITFORTOPIC_TIMEOUT_SECONDS * PR_USEC_PER_SEC)) { 1.121 + // Timed out waiting for the topic. 1.122 + do_check_true(false); 1.123 + break; 1.124 + } 1.125 + (void)NS_ProcessNextEvent(); 1.126 + } 1.127 + } 1.128 + 1.129 + NS_IMETHOD Observe(nsISupports* aSubject, 1.130 + const char* aTopic, 1.131 + const char16_t* aData) 1.132 + { 1.133 + mTopicReceived = true; 1.134 + nsCOMPtr<nsIObserverService> observerService = 1.135 + do_GetService(NS_OBSERVERSERVICE_CONTRACTID); 1.136 + do_check_true(observerService); 1.137 + (void)observerService->RemoveObserver(this, aTopic); 1.138 + return NS_OK; 1.139 + } 1.140 + 1.141 +private: 1.142 + bool mTopicReceived; 1.143 + PRIntervalTime mStartTime; 1.144 +}; 1.145 +NS_IMPL_ISUPPORTS( 1.146 + WaitForTopicSpinner, 1.147 + nsIObserver 1.148 +) 1.149 + 1.150 +/** 1.151 + * Spins current thread until an async statement is executed. 1.152 + */ 1.153 +class AsyncStatementSpinner MOZ_FINAL : public mozIStorageStatementCallback 1.154 +{ 1.155 +public: 1.156 + NS_DECL_ISUPPORTS 1.157 + NS_DECL_MOZISTORAGESTATEMENTCALLBACK 1.158 + 1.159 + AsyncStatementSpinner(); 1.160 + void SpinUntilCompleted(); 1.161 + uint16_t completionReason; 1.162 + 1.163 +protected: 1.164 + volatile bool mCompleted; 1.165 +}; 1.166 + 1.167 +NS_IMPL_ISUPPORTS(AsyncStatementSpinner, 1.168 + mozIStorageStatementCallback) 1.169 + 1.170 +AsyncStatementSpinner::AsyncStatementSpinner() 1.171 +: completionReason(0) 1.172 +, mCompleted(false) 1.173 +{ 1.174 +} 1.175 + 1.176 +NS_IMETHODIMP 1.177 +AsyncStatementSpinner::HandleResult(mozIStorageResultSet *aResultSet) 1.178 +{ 1.179 + return NS_OK; 1.180 +} 1.181 + 1.182 +NS_IMETHODIMP 1.183 +AsyncStatementSpinner::HandleError(mozIStorageError *aError) 1.184 +{ 1.185 + return NS_OK; 1.186 +} 1.187 + 1.188 +NS_IMETHODIMP 1.189 +AsyncStatementSpinner::HandleCompletion(uint16_t aReason) 1.190 +{ 1.191 + completionReason = aReason; 1.192 + mCompleted = true; 1.193 + return NS_OK; 1.194 +} 1.195 + 1.196 +void AsyncStatementSpinner::SpinUntilCompleted() 1.197 +{ 1.198 + nsCOMPtr<nsIThread> thread(::do_GetCurrentThread()); 1.199 + nsresult rv = NS_OK; 1.200 + bool processed = true; 1.201 + while (!mCompleted && NS_SUCCEEDED(rv)) { 1.202 + rv = thread->ProcessNextEvent(true, &processed); 1.203 + } 1.204 +} 1.205 + 1.206 +struct PlaceRecord 1.207 +{ 1.208 + int64_t id; 1.209 + int32_t hidden; 1.210 + int32_t typed; 1.211 + int32_t visitCount; 1.212 + nsCString guid; 1.213 +}; 1.214 + 1.215 +struct VisitRecord 1.216 +{ 1.217 + int64_t id; 1.218 + int64_t lastVisitId; 1.219 + int32_t transitionType; 1.220 +}; 1.221 + 1.222 +already_AddRefed<mozilla::IHistory> 1.223 +do_get_IHistory() 1.224 +{ 1.225 + nsCOMPtr<mozilla::IHistory> history = do_GetService(NS_IHISTORY_CONTRACTID); 1.226 + do_check_true(history); 1.227 + return history.forget(); 1.228 +} 1.229 + 1.230 +already_AddRefed<nsINavHistoryService> 1.231 +do_get_NavHistory() 1.232 +{ 1.233 + nsCOMPtr<nsINavHistoryService> serv = 1.234 + do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID); 1.235 + do_check_true(serv); 1.236 + return serv.forget(); 1.237 +} 1.238 + 1.239 +already_AddRefed<mozIStorageConnection> 1.240 +do_get_db() 1.241 +{ 1.242 + nsCOMPtr<nsINavHistoryService> history = do_get_NavHistory(); 1.243 + nsCOMPtr<nsPIPlacesDatabase> database = do_QueryInterface(history); 1.244 + do_check_true(database); 1.245 + 1.246 + nsCOMPtr<mozIStorageConnection> dbConn; 1.247 + nsresult rv = database->GetDBConnection(getter_AddRefs(dbConn)); 1.248 + do_check_success(rv); 1.249 + return dbConn.forget(); 1.250 +} 1.251 + 1.252 +/** 1.253 + * Get the place record from the database. 1.254 + * 1.255 + * @param aURI The unique URI of the place we are looking up 1.256 + * @param result Out parameter where the result is stored 1.257 + */ 1.258 +void 1.259 +do_get_place(nsIURI* aURI, PlaceRecord& result) 1.260 +{ 1.261 + nsCOMPtr<mozIStorageConnection> dbConn = do_get_db(); 1.262 + nsCOMPtr<mozIStorageStatement> stmt; 1.263 + 1.264 + nsCString spec; 1.265 + nsresult rv = aURI->GetSpec(spec); 1.266 + do_check_success(rv); 1.267 + 1.268 + rv = dbConn->CreateStatement(NS_LITERAL_CSTRING( 1.269 + "SELECT id, hidden, typed, visit_count, guid FROM moz_places " 1.270 + "WHERE url=?1 " 1.271 + ), getter_AddRefs(stmt)); 1.272 + do_check_success(rv); 1.273 + 1.274 + rv = stmt->BindUTF8StringByIndex(0, spec); 1.275 + do_check_success(rv); 1.276 + 1.277 + bool hasResults; 1.278 + rv = stmt->ExecuteStep(&hasResults); 1.279 + do_check_success(rv); 1.280 + if (!hasResults) { 1.281 + result.id = 0; 1.282 + return; 1.283 + } 1.284 + 1.285 + rv = stmt->GetInt64(0, &result.id); 1.286 + do_check_success(rv); 1.287 + rv = stmt->GetInt32(1, &result.hidden); 1.288 + do_check_success(rv); 1.289 + rv = stmt->GetInt32(2, &result.typed); 1.290 + do_check_success(rv); 1.291 + rv = stmt->GetInt32(3, &result.visitCount); 1.292 + do_check_success(rv); 1.293 + rv = stmt->GetUTF8String(4, result.guid); 1.294 + do_check_success(rv); 1.295 +} 1.296 + 1.297 +/** 1.298 + * Gets the most recent visit to a place. 1.299 + * 1.300 + * @param placeID ID from the moz_places table 1.301 + * @param result Out parameter where visit is stored 1.302 + */ 1.303 +void 1.304 +do_get_lastVisit(int64_t placeId, VisitRecord& result) 1.305 +{ 1.306 + nsCOMPtr<mozIStorageConnection> dbConn = do_get_db(); 1.307 + nsCOMPtr<mozIStorageStatement> stmt; 1.308 + 1.309 + nsresult rv = dbConn->CreateStatement(NS_LITERAL_CSTRING( 1.310 + "SELECT id, from_visit, visit_type FROM moz_historyvisits " 1.311 + "WHERE place_id=?1 " 1.312 + "LIMIT 1" 1.313 + ), getter_AddRefs(stmt)); 1.314 + do_check_success(rv); 1.315 + 1.316 + rv = stmt->BindInt64ByIndex(0, placeId); 1.317 + do_check_success(rv); 1.318 + 1.319 + bool hasResults; 1.320 + rv = stmt->ExecuteStep(&hasResults); 1.321 + do_check_success(rv); 1.322 + 1.323 + if (!hasResults) { 1.324 + result.id = 0; 1.325 + return; 1.326 + } 1.327 + 1.328 + rv = stmt->GetInt64(0, &result.id); 1.329 + do_check_success(rv); 1.330 + rv = stmt->GetInt64(1, &result.lastVisitId); 1.331 + do_check_success(rv); 1.332 + rv = stmt->GetInt32(2, &result.transitionType); 1.333 + do_check_success(rv); 1.334 +} 1.335 + 1.336 +void 1.337 +do_wait_async_updates() { 1.338 + nsCOMPtr<mozIStorageConnection> db = do_get_db(); 1.339 + nsCOMPtr<mozIStorageAsyncStatement> stmt; 1.340 + 1.341 + db->CreateAsyncStatement(NS_LITERAL_CSTRING("BEGIN EXCLUSIVE"), 1.342 + getter_AddRefs(stmt)); 1.343 + nsCOMPtr<mozIStoragePendingStatement> pending; 1.344 + (void)stmt->ExecuteAsync(nullptr, getter_AddRefs(pending)); 1.345 + 1.346 + db->CreateAsyncStatement(NS_LITERAL_CSTRING("COMMIT"), 1.347 + getter_AddRefs(stmt)); 1.348 + nsRefPtr<AsyncStatementSpinner> spinner = new AsyncStatementSpinner(); 1.349 + (void)stmt->ExecuteAsync(spinner, getter_AddRefs(pending)); 1.350 + 1.351 + spinner->SpinUntilCompleted(); 1.352 +} 1.353 + 1.354 +/** 1.355 + * Adds a URI to the database. 1.356 + * 1.357 + * @param aURI 1.358 + * The URI to add to the database. 1.359 + */ 1.360 +void 1.361 +addURI(nsIURI* aURI) 1.362 +{ 1.363 + nsCOMPtr<mozilla::IHistory> history = do_GetService(NS_IHISTORY_CONTRACTID); 1.364 + do_check_true(history); 1.365 + nsresult rv = history->VisitURI(aURI, nullptr, mozilla::IHistory::TOP_LEVEL); 1.366 + do_check_success(rv); 1.367 + 1.368 + do_wait_async_updates(); 1.369 +} 1.370 + 1.371 +static const char TOPIC_PROFILE_CHANGE[] = "profile-before-change"; 1.372 +static const char TOPIC_PLACES_CONNECTION_CLOSED[] = "places-connection-closed"; 1.373 + 1.374 +class WaitForConnectionClosed MOZ_FINAL : public nsIObserver 1.375 +{ 1.376 + nsRefPtr<WaitForTopicSpinner> mSpinner; 1.377 +public: 1.378 + NS_DECL_ISUPPORTS 1.379 + 1.380 + WaitForConnectionClosed() 1.381 + { 1.382 + nsCOMPtr<nsIObserverService> os = 1.383 + do_GetService(NS_OBSERVERSERVICE_CONTRACTID); 1.384 + MOZ_ASSERT(os); 1.385 + if (os) { 1.386 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(os->AddObserver(this, TOPIC_PROFILE_CHANGE, false))); 1.387 + } 1.388 + mSpinner = new WaitForTopicSpinner(TOPIC_PLACES_CONNECTION_CLOSED); 1.389 + } 1.390 + 1.391 + NS_IMETHOD Observe(nsISupports* aSubject, 1.392 + const char* aTopic, 1.393 + const char16_t* aData) 1.394 + { 1.395 + nsCOMPtr<nsIObserverService> os = 1.396 + do_GetService(NS_OBSERVERSERVICE_CONTRACTID); 1.397 + MOZ_ASSERT(os); 1.398 + if (os) { 1.399 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(os->RemoveObserver(this, aTopic))); 1.400 + } 1.401 + 1.402 + mSpinner->Spin(); 1.403 + 1.404 + return NS_OK; 1.405 + } 1.406 +}; 1.407 + 1.408 +NS_IMPL_ISUPPORTS(WaitForConnectionClosed, nsIObserver)