/
githubmirror
/
servo
Обзор
Документация
Войти
/
githubmirror
/
servo
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
components/storage/tests/client_storage.rs
317 строк
11 KB
Gregory Terzian
storage: use client storage in indexeddb (#44374)
29 апр 2026, 15:15
Не верифицирован
29 апр 2026, 15:15
8aa2008
Код
Авторство
О чём код?
/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::path::PathBuf; use rusqlite::Connection; use servo_base::generic_channel; use servo_base::generic_channel::GenericCallback; use servo_base::id::{ BrowsingContextId, PIPELINE_NAMESPACE, PipelineNamespace, PipelineNamespaceId, WebViewId, }; use servo_url::ServoUrl; use storage::ClientStorageThreadFactory; use storage_traits::client_storage::{ ClientStorageThreadHandle, ClientStorageThreadMessage, StorageIdentifier, StorageProxyMap, StorageType, }; fn install_test_namespace() { if PIPELINE_NAMESPACE.get().is_none() { PipelineNamespace::install(PipelineNamespaceId(1)); } } fn registry_db_path(tmp_dir: &tempfile::TempDir) -> PathBuf { tmp_dir .path() .join("clientstorage") .join("default_v1") .join("reg.sqlite") } fn open_registry(tmp_dir: &tempfile::TempDir) -> Connection { Connection::open(registry_db_path(tmp_dir)).expect("Registry database should exist") } fn obtain_bottle_map( handle: &ClientStorageThreadHandle, storage_type: StorageType, webview: Option<WebViewId>, storage_identifier: StorageIdentifier, origin: servo_url::ImmutableOrigin, ) -> StorageProxyMap { handle .obtain_a_storage_bottle_map(storage_type, webview, storage_identifier, origin) .recv() .unwrap() .unwrap() } #[test] fn test_exit() { let handle: ClientStorageThreadHandle = ClientStorageThreadFactory::new(None, false); let (sender, receiver) = generic_channel::channel().unwrap(); handle .send(ClientStorageThreadMessage::Exit(sender)) .unwrap(); receiver.recv().unwrap(); // Workaround for https://github.com/servo/servo/issues/32912 #[cfg(windows)] std::thread::sleep(std::time::Duration::from_millis(1000)); } #[test] fn test_workflow() { install_test_namespace(); let tmp_dir = tempfile::tempdir().unwrap(); let handle: ClientStorageThreadHandle = ClientStorageThreadFactory::new(Some(tmp_dir.path().to_path_buf()), false); let url = ServoUrl::parse("https://example.com").unwrap(); // Obtain a first storage proxy map. let storage_proxy_map = handle .obtain_a_storage_bottle_map( StorageType::Local, Some(WebViewId::new(servo_base::id::TEST_PAINTER_ID)), StorageIdentifier::IndexedDB, url.origin(), ) .recv() .unwrap() .unwrap(); // Create a db. let receiver = handle.create_database(storage_proxy_map.bottle_id, "test1".to_string()); let (path, created) = receiver.recv().unwrap().expect("Path should be created"); assert!(created); assert!(std::fs::read_dir(path.clone()).is_ok()); // Create another db with the same name. let receiver = handle.create_database(storage_proxy_map.bottle_id, "test1".to_string()); let (path, created) = receiver.recv().unwrap().expect("Path should be created"); assert!(!created); // Create another db with a different same. let receiver = handle.create_database(storage_proxy_map.bottle_id, "test2".to_string()); let (yet_another_path, created) = receiver.recv().unwrap().expect("Path should be created"); assert_ne!(path, yet_another_path); assert!(created); // Delete the dbs. let receiver = handle.delete_database(storage_proxy_map.bottle_id, "test1".to_string()); receiver.recv().unwrap().expect("Db should be deleted"); let receiver = handle.delete_database(storage_proxy_map.bottle_id, "test2".to_string()); receiver.recv().unwrap().expect("Db should be deleted"); assert!(std::fs::read_dir(path).is_err()); // Get another proxy map fro the same parameters. let second_proxy_map = handle .obtain_a_storage_bottle_map( StorageType::Local, Some(WebViewId::new(servo_base::id::TEST_PAINTER_ID)), StorageIdentifier::IndexedDB, url.origin(), ) .recv() .unwrap() .unwrap(); // Bottle id should be the same, because the manager returned the existing id. assert_eq!(second_proxy_map.bottle_id, storage_proxy_map.bottle_id); // Workaround for https://github.com/servo/servo/issues/32912 #[cfg(windows)] std::thread::sleep(std::time::Duration::from_millis(1000)); } #[test] fn test_repeated_local_obtain_reuses_same_logical_rows() { install_test_namespace(); let tmp_dir = tempfile::tempdir().unwrap(); let handle: ClientStorageThreadHandle = ClientStorageThreadFactory::new(Some(tmp_dir.path().to_path_buf()), false); let origin = ServoUrl::parse("https://example.com").unwrap().origin(); let webview = Some(WebViewId::new(servo_base::id::TEST_PAINTER_ID)); let first = obtain_bottle_map( &handle, StorageType::Local, webview, StorageIdentifier::IndexedDB, origin.clone(), ); let second = obtain_bottle_map( &handle, StorageType::Local, webview, StorageIdentifier::IndexedDB, origin.clone(), ); assert_eq!(first.bottle_id, second.bottle_id); let registry = open_registry(&tmp_dir); let local_shed_count: i64 = registry .query_row( "SELECT COUNT(*) FROM sheds WHERE storage_type = 'local' AND browsing_context IS NULL;", [], |row| row.get(0), ) .unwrap(); let shelf_count: i64 = registry .query_row( "SELECT COUNT(*) FROM shelves WHERE origin = ?1;", [origin.ascii_serialization()], |row| row.get(0), ) .unwrap(); let bucket_count: i64 = registry .query_row("SELECT COUNT(*) FROM buckets;", [], |row| row.get(0)) .unwrap(); let bottle_count: i64 = registry .query_row("SELECT COUNT(*) FROM bottles;", [], |row| row.get(0)) .unwrap(); assert_eq!(local_shed_count, 1); assert_eq!(shelf_count, 1); assert_eq!(bucket_count, 1); assert_eq!(bottle_count, 4); } #[test] fn test_repeated_session_obtain_reuses_same_logical_rows() { install_test_namespace(); let tmp_dir = tempfile::tempdir().unwrap(); let handle: ClientStorageThreadHandle = ClientStorageThreadFactory::new(Some(tmp_dir.path().to_path_buf()), false); let origin = ServoUrl::parse("https://example.com").unwrap().origin(); let webview = Some(WebViewId::new(servo_base::id::TEST_PAINTER_ID)); let browsing_context = Into::<BrowsingContextId>::into(webview.clone().unwrap()).to_string(); let first = obtain_bottle_map( &handle, StorageType::Session, webview, StorageIdentifier::SessionStorage, origin.clone(), ); let second = obtain_bottle_map( &handle, StorageType::Session, webview, StorageIdentifier::SessionStorage, origin.clone(), ); assert_eq!(first.bottle_id, second.bottle_id); let registry = open_registry(&tmp_dir); let session_shed_count: i64 = registry .query_row( "SELECT COUNT(*) FROM sheds WHERE storage_type = 'session' AND browsing_context = ?1;", [browsing_context], |row| row.get(0), ) .unwrap(); let shelf_count: i64 = registry .query_row( "SELECT COUNT(*) FROM shelves WHERE origin = ?1;", [origin.ascii_serialization()], |row| row.get(0), ) .unwrap(); let bucket_count: i64 = registry .query_row("SELECT COUNT(*) FROM buckets;", [], |row| row.get(0)) .unwrap(); let bottle_count: i64 = registry .query_row("SELECT COUNT(*) FROM bottles;", [], |row| row.get(0)) .unwrap(); assert_eq!(session_shed_count, 1); assert_eq!(shelf_count, 1); assert_eq!(bucket_count, 1); assert_eq!(bottle_count, 1); } #[test] fn test_local_persistence_and_estimate() { install_test_namespace(); let tmp_dir = tempfile::tempdir().unwrap(); let handle: ClientStorageThreadHandle = ClientStorageThreadFactory::new(Some(tmp_dir.path().to_path_buf()), false); let origin = ServoUrl::parse("https://example.com").unwrap().origin(); let webview = WebViewId::new(servo_base::id::TEST_PAINTER_ID); let storage_proxy_map = obtain_bottle_map( &handle, StorageType::Local, Some(webview), StorageIdentifier::IndexedDB, origin.clone(), ); let (cb, rx) = GenericCallback::new_blocking().unwrap(); handle.persisted(origin.clone(), cb).unwrap(); assert!(!rx.recv().unwrap().unwrap()); let (cb, rx) = GenericCallback::new_blocking().unwrap(); handle.persist(origin.clone(), false, cb).unwrap(); assert!(!rx.recv().unwrap().unwrap()); let (cb, rx) = GenericCallback::new_blocking().unwrap(); handle.persisted(origin.clone(), cb).unwrap(); assert!(!rx.recv().unwrap().unwrap()); let (cb, rx) = GenericCallback::new_blocking().unwrap(); handle.persist(origin.clone(), true, cb).unwrap(); assert!(rx.recv().unwrap().unwrap()); let (cb, rx) = GenericCallback::new_blocking().unwrap(); handle.persisted(origin.clone(), cb).unwrap(); assert!(rx.recv().unwrap().unwrap()); let (path, _created) = handle .create_database(storage_proxy_map.bottle_id, "estimate".to_string()) .recv() .unwrap() .unwrap(); let payload = vec![0x5a; 8192]; std::fs::write(path.join("payload.bin"), &payload).unwrap(); let (cb, rx) = GenericCallback::new_blocking().unwrap(); handle.estimate(origin, cb).unwrap(); let (usage, quota) = rx.recv().unwrap().unwrap(); assert!(usage >= payload.len() as u64); assert!(quota > usage); } #[test] fn test_storage_manager_operations_fail_for_opaque_origins() { install_test_namespace(); let tmp_dir = tempfile::tempdir().unwrap(); let handle: ClientStorageThreadHandle = ClientStorageThreadFactory::new(Some(tmp_dir.path().to_path_buf()), false); let origin = ServoUrl::parse("data:text/plain,hello").unwrap().origin(); let (cb, rx) = GenericCallback::new_blocking().unwrap(); handle.persisted(origin.clone(), cb).unwrap(); assert!(rx.recv().unwrap().is_err()); let (cb, rx) = GenericCallback::new_blocking().unwrap(); handle.persist(origin.clone(), true, cb).unwrap(); assert!(rx.recv().unwrap().is_err()); let (cb, rx) = GenericCallback::new_blocking().unwrap(); handle.estimate(origin, cb).unwrap(); assert!(rx.recv().unwrap().is_err()); }