Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "places_test_harness.h"
8 #include "nsIPrefService.h"
9 #include "nsIPrefBranch.h"
10 #include "mozilla/Attributes.h"
12 #include "mock_Link.h"
13 using namespace mozilla;
14 using namespace mozilla::dom;
16 /**
17 * This file tests the IHistory interface.
18 */
20 ////////////////////////////////////////////////////////////////////////////////
21 //// Helper Methods
23 void
24 expect_visit(nsLinkState aState)
25 {
26 do_check_true(aState == eLinkState_Visited);
27 }
29 void
30 expect_no_visit(nsLinkState aState)
31 {
32 do_check_true(aState == eLinkState_Unvisited);
33 }
35 already_AddRefed<nsIURI>
36 new_test_uri()
37 {
38 // Create a unique spec.
39 static int32_t specNumber = 0;
40 nsAutoCString spec = NS_LITERAL_CSTRING("http://mozilla.org/");
41 spec.AppendInt(specNumber++);
43 // Create the URI for the spec.
44 nsCOMPtr<nsIURI> testURI;
45 nsresult rv = NS_NewURI(getter_AddRefs(testURI), spec);
46 do_check_success(rv);
47 return testURI.forget();
48 }
50 class VisitURIObserver MOZ_FINAL : public nsIObserver
51 {
52 public:
53 NS_DECL_ISUPPORTS
55 VisitURIObserver(int aExpectedVisits = 1) :
56 mVisits(0),
57 mExpectedVisits(aExpectedVisits)
58 {
59 nsCOMPtr<nsIObserverService> observerService =
60 do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
61 do_check_true(observerService);
62 (void)observerService->AddObserver(this,
63 "uri-visit-saved",
64 false);
65 }
67 void WaitForNotification()
68 {
69 while (mVisits < mExpectedVisits) {
70 (void)NS_ProcessNextEvent();
71 }
72 }
74 NS_IMETHOD Observe(nsISupports* aSubject,
75 const char* aTopic,
76 const char16_t* aData)
77 {
78 mVisits++;
80 if (mVisits == mExpectedVisits) {
81 nsCOMPtr<nsIObserverService> observerService =
82 do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
83 (void)observerService->RemoveObserver(this, "uri-visit-saved");
84 }
86 return NS_OK;
87 }
88 private:
89 int mVisits;
90 int mExpectedVisits;
91 };
92 NS_IMPL_ISUPPORTS(
93 VisitURIObserver,
94 nsIObserver
95 )
97 ////////////////////////////////////////////////////////////////////////////////
98 //// Test Functions
100 void
101 test_set_places_enabled()
102 {
103 // Ensure places is enabled for everyone.
104 nsresult rv;
105 nsCOMPtr<nsIPrefBranch> prefBranch =
106 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
107 do_check_success(rv);
109 rv = prefBranch->SetBoolPref("places.history.enabled", true);
110 do_check_success(rv);
112 // Run the next test.
113 run_next_test();
114 }
117 void
118 test_wait_checkpoint()
119 {
120 // This "fake" test is here to wait for the initial WAL checkpoint we force
121 // after creating the database schema, since that may happen at any time,
122 // and cause concurrent readers to access an older checkpoint.
123 nsCOMPtr<mozIStorageConnection> db = do_get_db();
124 nsCOMPtr<mozIStorageAsyncStatement> stmt;
125 db->CreateAsyncStatement(NS_LITERAL_CSTRING("SELECT 1"),
126 getter_AddRefs(stmt));
127 nsRefPtr<AsyncStatementSpinner> spinner = new AsyncStatementSpinner();
128 nsCOMPtr<mozIStoragePendingStatement> pending;
129 (void)stmt->ExecuteAsync(spinner, getter_AddRefs(pending));
130 spinner->SpinUntilCompleted();
132 // Run the next test.
133 run_next_test();
134 }
136 // These variables are shared between part 1 and part 2 of the test. Part 2
137 // sets the nsCOMPtr's to nullptr, freeing the reference.
138 namespace test_unvisited_does_not_notify {
139 nsCOMPtr<nsIURI> testURI;
140 nsRefPtr<Link> testLink;
141 }
142 void
143 test_unvisited_does_not_notify_part1()
144 {
145 using namespace test_unvisited_does_not_notify;
147 // This test is done in two parts. The first part registers for a URI that
148 // should not be visited. We then run another test that will also do a
149 // lookup and will be notified. Since requests are answered in the order they
150 // are requested (at least as long as the same URI isn't asked for later), we
151 // will know that the Link was not notified.
153 // First, we need a test URI.
154 testURI = new_test_uri();
156 // Create our test Link.
157 testLink = new mock_Link(expect_no_visit);
159 // Now, register our Link to be notified.
160 nsCOMPtr<IHistory> history = do_get_IHistory();
161 nsresult rv = history->RegisterVisitedCallback(testURI, testLink);
162 do_check_success(rv);
164 // Run the next test.
165 run_next_test();
166 }
168 void
169 test_visited_notifies()
170 {
171 // First, we add our test URI to history.
172 nsCOMPtr<nsIURI> testURI = new_test_uri();
173 addURI(testURI);
175 // Create our test Link. The callback function will release the reference we
176 // have on the Link.
177 nsRefPtr<Link> link = new mock_Link(expect_visit);
179 // Now, register our Link to be notified.
180 nsCOMPtr<IHistory> history = do_get_IHistory();
181 nsresult rv = history->RegisterVisitedCallback(testURI, link);
182 do_check_success(rv);
184 // Note: test will continue upon notification.
185 }
187 void
188 test_unvisited_does_not_notify_part2()
189 {
190 using namespace test_unvisited_does_not_notify;
192 // We would have had a failure at this point had the content node been told it
193 // was visited. Therefore, it is safe to unregister our content node.
194 nsCOMPtr<IHistory> history = do_get_IHistory();
195 nsresult rv = history->UnregisterVisitedCallback(testURI, testLink);
196 do_check_success(rv);
198 // Clear the stored variables now.
199 testURI = nullptr;
200 testLink = nullptr;
202 // Run the next test.
203 run_next_test();
204 }
206 void
207 test_same_uri_notifies_both()
208 {
209 // First, we add our test URI to history.
210 nsCOMPtr<nsIURI> testURI = new_test_uri();
211 addURI(testURI);
213 // Create our two test Links. The callback function will release the
214 // reference we have on the Links. Only the second Link should run the next
215 // test!
216 nsRefPtr<Link> link1 = new mock_Link(expect_visit, false);
217 nsRefPtr<Link> link2 = new mock_Link(expect_visit);
219 // Now, register our Link to be notified.
220 nsCOMPtr<IHistory> history = do_get_IHistory();
221 nsresult rv = history->RegisterVisitedCallback(testURI, link1);
222 do_check_success(rv);
223 rv = history->RegisterVisitedCallback(testURI, link2);
224 do_check_success(rv);
226 // Note: test will continue upon notification.
227 }
229 void
230 test_unregistered_visited_does_not_notify()
231 {
232 // This test must have a test that has a successful notification after it.
233 // The Link would have been notified by now if we were buggy and notified
234 // unregistered Links (due to request serialization).
236 nsCOMPtr<nsIURI> testURI = new_test_uri();
237 nsRefPtr<Link> link = new mock_Link(expect_no_visit);
239 // Now, register our Link to be notified.
240 nsCOMPtr<IHistory> history(do_get_IHistory());
241 nsresult rv = history->RegisterVisitedCallback(testURI, link);
242 do_check_success(rv);
244 // Unregister the Link.
245 rv = history->UnregisterVisitedCallback(testURI, link);
246 do_check_success(rv);
248 // And finally add a visit for the URI.
249 addURI(testURI);
251 // If history tries to notify us, we'll either crash because the Link will
252 // have been deleted (we are the only thing holding a reference to it), or our
253 // expect_no_visit call back will produce a failure. Either way, the test
254 // will be reported as a failure.
256 // Run the next test.
257 run_next_test();
258 }
260 void
261 test_new_visit_notifies_waiting_Link()
262 {
263 // Create our test Link. The callback function will release the reference we
264 // have on the link.
265 nsRefPtr<Link> link = new mock_Link(expect_visit);
267 // Now, register our content node to be notified.
268 nsCOMPtr<nsIURI> testURI = new_test_uri();
269 nsCOMPtr<IHistory> history = do_get_IHistory();
270 nsresult rv = history->RegisterVisitedCallback(testURI, link);
271 do_check_success(rv);
273 // Add ourselves to history.
274 addURI(testURI);
276 // Note: test will continue upon notification.
277 }
279 void
280 test_RegisterVisitedCallback_returns_before_notifying()
281 {
282 // Add a URI so that it's already in history.
283 nsCOMPtr<nsIURI> testURI = new_test_uri();
284 addURI(testURI);
286 // Create our test Link.
287 nsRefPtr<Link> link = new mock_Link(expect_no_visit);
289 // Now, register our content node to be notified. It should not be notified.
290 nsCOMPtr<IHistory> history = do_get_IHistory();
291 nsresult rv = history->RegisterVisitedCallback(testURI, link);
292 do_check_success(rv);
294 // Remove ourselves as an observer. We would have failed if we had been
295 // notified.
296 rv = history->UnregisterVisitedCallback(testURI, link);
297 do_check_success(rv);
299 run_next_test();
300 }
302 namespace test_observer_topic_dispatched_helpers {
303 #define URI_VISITED "visited"
304 #define URI_NOT_VISITED "not visited"
305 #define URI_VISITED_RESOLUTION_TOPIC "visited-status-resolution"
306 class statusObserver MOZ_FINAL : public nsIObserver
307 {
308 public:
309 NS_DECL_ISUPPORTS
311 statusObserver(nsIURI* aURI,
312 const bool aExpectVisit,
313 bool& _notified)
314 : mURI(aURI)
315 , mExpectVisit(aExpectVisit)
316 , mNotified(_notified)
317 {
318 nsCOMPtr<nsIObserverService> observerService =
319 do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
320 do_check_true(observerService);
321 (void)observerService->AddObserver(this,
322 URI_VISITED_RESOLUTION_TOPIC,
323 false);
324 }
326 NS_IMETHOD Observe(nsISupports* aSubject,
327 const char* aTopic,
328 const char16_t* aData)
329 {
330 // Make sure we got notified of the right topic.
331 do_check_false(strcmp(aTopic, URI_VISITED_RESOLUTION_TOPIC));
333 // If this isn't for our URI, do not do anything.
334 nsCOMPtr<nsIURI> notifiedURI = do_QueryInterface(aSubject);
335 do_check_true(notifiedURI);
337 bool isOurURI;
338 nsresult rv = notifiedURI->Equals(mURI, &isOurURI);
339 do_check_success(rv);
340 if (!isOurURI) {
341 return NS_OK;
342 }
344 // Check that we have either the visited or not visited string.
345 bool visited = !!NS_LITERAL_STRING(URI_VISITED).Equals(aData);
346 bool notVisited = !!NS_LITERAL_STRING(URI_NOT_VISITED).Equals(aData);
347 do_check_true(visited || notVisited);
349 // Check to make sure we got the state we expected.
350 do_check_eq(visited, mExpectVisit);
352 // Indicate that we've been notified.
353 mNotified = true;
355 // Remove ourselves as an observer.
356 nsCOMPtr<nsIObserverService> observerService =
357 do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
358 (void)observerService->RemoveObserver(this,
359 URI_VISITED_RESOLUTION_TOPIC);
360 return NS_OK;
361 }
362 private:
363 nsCOMPtr<nsIURI> mURI;
364 const bool mExpectVisit;
365 bool& mNotified;
366 };
367 NS_IMPL_ISUPPORTS(
368 statusObserver,
369 nsIObserver
370 )
371 }
372 void
373 test_observer_topic_dispatched()
374 {
375 using namespace test_observer_topic_dispatched_helpers;
377 // Create two URIs, making sure only one is in history.
378 nsCOMPtr<nsIURI> visitedURI = new_test_uri();
379 nsCOMPtr<nsIURI> notVisitedURI = new_test_uri();
380 bool urisEqual;
381 nsresult rv = visitedURI->Equals(notVisitedURI, &urisEqual);
382 do_check_success(rv);
383 do_check_false(urisEqual);
384 addURI(visitedURI);
386 // Need two Link objects as well - one for each URI.
387 nsRefPtr<Link> visitedLink = new mock_Link(expect_visit, false);
388 nsRefPtr<Link> visitedLinkCopy = visitedLink;
389 nsRefPtr<Link> notVisitedLink = new mock_Link(expect_no_visit);
391 // Add the right observers for the URIs to check results.
392 bool visitedNotified = false;
393 nsCOMPtr<nsIObserver> visitedObs =
394 new statusObserver(visitedURI, true, visitedNotified);
395 bool notVisitedNotified = false;
396 nsCOMPtr<nsIObserver> unvisitedObs =
397 new statusObserver(notVisitedURI, false, notVisitedNotified);
399 // Register our Links to be notified.
400 nsCOMPtr<IHistory> history = do_get_IHistory();
401 rv = history->RegisterVisitedCallback(visitedURI, visitedLink);
402 do_check_success(rv);
403 rv = history->RegisterVisitedCallback(notVisitedURI, notVisitedLink);
404 do_check_success(rv);
406 // Spin the event loop as long as we have not been properly notified.
407 while (!visitedNotified || !notVisitedNotified) {
408 (void)NS_ProcessNextEvent();
409 }
411 // Unregister our observer that would not have been released.
412 rv = history->UnregisterVisitedCallback(notVisitedURI, notVisitedLink);
413 do_check_success(rv);
415 run_next_test();
416 }
418 void
419 test_visituri_inserts()
420 {
421 nsCOMPtr<IHistory> history = do_get_IHistory();
422 nsCOMPtr<nsIURI> lastURI = new_test_uri();
423 nsCOMPtr<nsIURI> visitedURI = new_test_uri();
425 history->VisitURI(visitedURI, lastURI, mozilla::IHistory::TOP_LEVEL);
427 nsRefPtr<VisitURIObserver> finisher = new VisitURIObserver();
428 finisher->WaitForNotification();
430 PlaceRecord place;
431 do_get_place(visitedURI, place);
433 do_check_true(place.id > 0);
434 do_check_false(place.hidden);
435 do_check_false(place.typed);
436 do_check_eq(place.visitCount, 1);
438 run_next_test();
439 }
441 void
442 test_visituri_updates()
443 {
444 nsCOMPtr<IHistory> history = do_get_IHistory();
445 nsCOMPtr<nsIURI> lastURI = new_test_uri();
446 nsCOMPtr<nsIURI> visitedURI = new_test_uri();
447 nsRefPtr<VisitURIObserver> finisher;
449 history->VisitURI(visitedURI, lastURI, mozilla::IHistory::TOP_LEVEL);
450 finisher = new VisitURIObserver();
451 finisher->WaitForNotification();
453 history->VisitURI(visitedURI, lastURI, mozilla::IHistory::TOP_LEVEL);
454 finisher = new VisitURIObserver();
455 finisher->WaitForNotification();
457 PlaceRecord place;
458 do_get_place(visitedURI, place);
460 do_check_eq(place.visitCount, 2);
462 run_next_test();
463 }
465 void
466 test_visituri_preserves_shown_and_typed()
467 {
468 nsCOMPtr<IHistory> history = do_get_IHistory();
469 nsCOMPtr<nsIURI> lastURI = new_test_uri();
470 nsCOMPtr<nsIURI> visitedURI = new_test_uri();
472 history->VisitURI(visitedURI, lastURI, mozilla::IHistory::TOP_LEVEL);
473 // this simulates the uri visit happening in a frame. Normally frame
474 // transitions would be hidden unless it was previously loaded top-level
475 history->VisitURI(visitedURI, lastURI, 0);
477 nsRefPtr<VisitURIObserver> finisher = new VisitURIObserver(2);
478 finisher->WaitForNotification();
480 PlaceRecord place;
481 do_get_place(visitedURI, place);
482 do_check_false(place.hidden);
484 run_next_test();
485 }
487 void
488 test_visituri_creates_visit()
489 {
490 nsCOMPtr<IHistory> history = do_get_IHistory();
491 nsCOMPtr<nsIURI> lastURI = new_test_uri();
492 nsCOMPtr<nsIURI> visitedURI = new_test_uri();
494 history->VisitURI(visitedURI, lastURI, mozilla::IHistory::TOP_LEVEL);
495 nsRefPtr<VisitURIObserver> finisher = new VisitURIObserver();
496 finisher->WaitForNotification();
498 PlaceRecord place;
499 VisitRecord visit;
500 do_get_place(visitedURI, place);
501 do_get_lastVisit(place.id, visit);
503 do_check_true(visit.id > 0);
504 do_check_eq(visit.lastVisitId, 0);
505 do_check_eq(visit.transitionType, nsINavHistoryService::TRANSITION_LINK);
507 run_next_test();
508 }
510 void
511 test_visituri_transition_typed()
512 {
513 nsCOMPtr<nsINavHistoryService> navHistory = do_get_NavHistory();
514 nsCOMPtr<IHistory> history = do_get_IHistory();
515 nsCOMPtr<nsIURI> lastURI = new_test_uri();
516 nsCOMPtr<nsIURI> visitedURI = new_test_uri();
518 navHistory->MarkPageAsTyped(visitedURI);
519 history->VisitURI(visitedURI, lastURI, mozilla::IHistory::TOP_LEVEL);
520 nsRefPtr<VisitURIObserver> finisher = new VisitURIObserver();
521 finisher->WaitForNotification();
523 PlaceRecord place;
524 VisitRecord visit;
525 do_get_place(visitedURI, place);
526 do_get_lastVisit(place.id, visit);
528 do_check_true(visit.transitionType == nsINavHistoryService::TRANSITION_TYPED);
530 run_next_test();
531 }
533 void
534 test_visituri_transition_embed()
535 {
536 nsCOMPtr<IHistory> history = do_get_IHistory();
537 nsCOMPtr<nsIURI> lastURI = new_test_uri();
538 nsCOMPtr<nsIURI> visitedURI = new_test_uri();
540 history->VisitURI(visitedURI, lastURI, 0);
541 nsRefPtr<VisitURIObserver> finisher = new VisitURIObserver();
542 finisher->WaitForNotification();
544 PlaceRecord place;
545 VisitRecord visit;
546 do_get_place(visitedURI, place);
547 do_get_lastVisit(place.id, visit);
549 do_check_eq(place.id, 0);
550 do_check_eq(visit.id, 0);
552 run_next_test();
553 }
555 void
556 test_new_visit_adds_place_guid()
557 {
558 // First, add a visit and wait. This will also add a place.
559 nsCOMPtr<nsIURI> visitedURI = new_test_uri();
560 nsCOMPtr<IHistory> history = do_get_IHistory();
561 nsresult rv = history->VisitURI(visitedURI, nullptr,
562 mozilla::IHistory::TOP_LEVEL);
563 do_check_success(rv);
564 nsRefPtr<VisitURIObserver> finisher = new VisitURIObserver();
565 finisher->WaitForNotification();
567 // Check that we have a guid for our visit.
568 PlaceRecord place;
569 do_get_place(visitedURI, place);
570 do_check_eq(place.visitCount, 1);
571 do_check_eq(place.guid.Length(), 12);
573 run_next_test();
574 }
576 ////////////////////////////////////////////////////////////////////////////////
577 //// IPC-only Tests
579 void
580 test_two_null_links_same_uri()
581 {
582 // Tests that we do not crash when we have had two nullptr Links passed to
583 // RegisterVisitedCallback and then the visit occurs (bug 607469). This only
584 // happens in IPC builds.
585 nsCOMPtr<nsIURI> testURI = new_test_uri();
587 nsCOMPtr<IHistory> history = do_get_IHistory();
588 nsresult rv = history->RegisterVisitedCallback(testURI, nullptr);
589 do_check_success(rv);
590 rv = history->RegisterVisitedCallback(testURI, nullptr);
591 do_check_success(rv);
593 rv = history->VisitURI(testURI, nullptr, mozilla::IHistory::TOP_LEVEL);
594 do_check_success(rv);
596 nsRefPtr<VisitURIObserver> finisher = new VisitURIObserver();
597 finisher->WaitForNotification();
599 run_next_test();
600 }
602 ////////////////////////////////////////////////////////////////////////////////
603 //// Test Harness
605 /**
606 * Note: for tests marked "Order Important!", please see the test for details.
607 */
608 Test gTests[] = {
609 TEST(test_set_places_enabled), // Must come first!
610 TEST(test_wait_checkpoint), // Must come second!
611 TEST(test_unvisited_does_not_notify_part1), // Order Important!
612 TEST(test_visited_notifies),
613 TEST(test_unvisited_does_not_notify_part2), // Order Important!
614 TEST(test_same_uri_notifies_both),
615 TEST(test_unregistered_visited_does_not_notify), // Order Important!
616 TEST(test_new_visit_notifies_waiting_Link),
617 TEST(test_RegisterVisitedCallback_returns_before_notifying),
618 TEST(test_observer_topic_dispatched),
619 TEST(test_visituri_inserts),
620 TEST(test_visituri_updates),
621 TEST(test_visituri_preserves_shown_and_typed),
622 TEST(test_visituri_creates_visit),
623 TEST(test_visituri_transition_typed),
624 TEST(test_visituri_transition_embed),
625 TEST(test_new_visit_adds_place_guid),
627 // The rest of these tests are tests that are only run in IPC builds.
628 TEST(test_two_null_links_same_uri),
629 };
631 const char* file = __FILE__;
632 #define TEST_NAME "IHistory"
633 #define TEST_FILE file
634 #include "places_test_harness_tail.h"