in components/places/src/storage/history.rs [2116:2307]
fn test_delete_visits_for() -> Result<()> {
use crate::storage::bookmarks::{
self, BookmarkPosition, BookmarkRootGuid, InsertableBookmark,
};
let db = PlacesDb::open_in_memory(ConnectionType::ReadWrite)?;
struct TestPage {
href: &'static str,
synced: bool,
bookmark_title: Option<&'static str>,
keyword: Option<&'static str>,
}
fn page_has_tombstone(conn: &PlacesDb, guid: &SyncGuid) -> Result<bool> {
let exists = conn
.try_query_one::<bool, _>(
"SELECT EXISTS(SELECT 1 FROM moz_places_tombstones
WHERE guid = :guid)",
rusqlite::named_params! { ":guid" : guid },
false,
)?
.unwrap_or_default();
Ok(exists)
}
fn page_has_visit_tombstones(conn: &PlacesDb, page_id: RowId) -> Result<bool> {
let exists = conn
.try_query_one::<bool, _>(
"SELECT EXISTS(SELECT 1 FROM moz_historyvisit_tombstones
WHERE place_id = :page_id)",
rusqlite::named_params! { ":page_id": page_id },
false,
)?
.unwrap_or_default();
Ok(exists)
}
let pages = &[
// A is synced and has a bookmark, so we should insert tombstones
// for all its visits.
TestPage {
href: "http://example.com/a",
synced: true,
bookmark_title: Some("A"),
keyword: None,
},
// B is synced but only has visits, so we should insert a tombstone
// for the page.
TestPage {
href: "http://example.com/b",
synced: true,
bookmark_title: None,
keyword: None,
},
// C isn't synced but has a keyword, so we should delete all its
// visits, but not the page.
TestPage {
href: "http://example.com/c",
synced: false,
bookmark_title: None,
keyword: Some("one"),
},
// D isn't synced and only has visits, so we should delete it
// entirely.
TestPage {
href: "http://example.com/d",
synced: false,
bookmark_title: None,
keyword: None,
},
];
for page in pages {
let url = Url::parse(page.href)?;
let obs = VisitObservation::new(url.clone())
.with_visit_type(VisitType::Link)
.with_at(Some(SystemTime::now().into()));
apply_observation(&db, obs)?;
if page.synced {
db.execute_cached(
&format!(
"UPDATE moz_places
SET sync_status = {}
WHERE url_hash = hash(:url) AND
url = :url",
(SyncStatus::Normal as u8)
),
&[(":url", &url.as_str())],
)?;
}
if let Some(title) = page.bookmark_title {
bookmarks::insert_bookmark(
&db,
InsertableBookmark {
parent_guid: BookmarkRootGuid::Unfiled.into(),
position: BookmarkPosition::Append,
date_added: None,
last_modified: None,
guid: None,
url: url.clone(),
title: Some(title.to_owned()),
}
.into(),
)?;
}
if let Some(keyword) = page.keyword {
// We don't have a public API for inserting keywords, so just
// write to the database directly.
db.execute_cached(
"INSERT INTO moz_keywords(place_id, keyword)
SELECT id, :keyword
FROM moz_places
WHERE url_hash = hash(:url) AND
url = :url",
&[(":url", &url.as_str()), (":keyword", &keyword)],
)?;
}
// Now delete all visits.
let (info, _) =
fetch_visits(&db, &url, 0)?.expect("Should return visits for test page");
delete_visits_for(&db, &info.guid)?;
match (
page.synced,
page.bookmark_title.is_some() || page.keyword.is_some(),
) {
(true, true) => {
let (_, visits) = fetch_visits(&db, &url, 0)?
.expect("Shouldn't delete synced page with foreign count");
assert!(
visits.is_empty(),
"Should delete all visits from synced page with foreign count"
);
assert!(
!page_has_tombstone(&db, &info.guid)?,
"Shouldn't insert tombstone for synced page with foreign count"
);
assert!(
page_has_visit_tombstones(&db, info.row_id)?,
"Should insert visit tombstones for synced page with foreign count"
);
}
(true, false) => {
assert!(
fetch_visits(&db, &url, 0)?.is_none(),
"Should delete synced page"
);
assert!(
page_has_tombstone(&db, &info.guid)?,
"Should insert tombstone for synced page"
);
assert!(
!page_has_visit_tombstones(&db, info.row_id)?,
"Shouldn't insert visit tombstones for synced page"
);
}
(false, true) => {
let (_, visits) = fetch_visits(&db, &url, 0)?
.expect("Shouldn't delete page with foreign count");
assert!(
visits.is_empty(),
"Should delete all visits from page with foreign count"
);
assert!(
!page_has_tombstone(&db, &info.guid)?,
"Shouldn't insert tombstone for page with foreign count"
);
assert!(
!page_has_visit_tombstones(&db, info.row_id)?,
"Shouldn't insert visit tombstones for page with foreign count"
);
}
(false, false) => {
assert!(fetch_visits(&db, &url, 0)?.is_none(), "Should delete page");
assert!(
!page_has_tombstone(&db, &info.guid)?,
"Shouldn't insert tombstone for page"
);
assert!(
!page_has_visit_tombstones(&db, info.row_id)?,
"Shouldn't insert visit tombstones for page"
);
}
}
}
Ok(())
}