mirror of
https://github.com/servo/servo.git
synced 2025-09-27 15:20:09 +01:00
Implement getAll and getAllKeys for IDBObjectStore. Testing: WPT & Unit testing Fixes: Part of #6963. --------- Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
328 lines
11 KiB
Rust
328 lines
11 KiB
Rust
/* 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::collections::VecDeque;
|
|
use std::sync::Arc;
|
|
|
|
use net::indexeddb::engines::{KvsEngine, KvsOperation, KvsTransaction, SqliteEngine};
|
|
use net::indexeddb::idb_thread::IndexedDBDescription;
|
|
use net::resource_thread::CoreResourceThreadPool;
|
|
use net_traits::indexeddb_thread::{
|
|
AsyncOperation, AsyncReadOnlyOperation, AsyncReadWriteOperation, CreateObjectResult,
|
|
IndexedDBKeyRange, IndexedDBKeyType, IndexedDBTxnMode, KeyPath, PutItemResult,
|
|
};
|
|
use serde::{Deserialize, Serialize};
|
|
use servo_url::ImmutableOrigin;
|
|
use url::Host;
|
|
|
|
fn test_origin() -> ImmutableOrigin {
|
|
ImmutableOrigin::Tuple(
|
|
"test_origin".to_string(),
|
|
Host::Domain("localhost".to_string()),
|
|
80,
|
|
)
|
|
}
|
|
|
|
fn get_pool() -> Arc<CoreResourceThreadPool> {
|
|
Arc::new(CoreResourceThreadPool::new(1, "test".to_string()))
|
|
}
|
|
|
|
#[test]
|
|
fn test_cycle() {
|
|
let base_dir = tempfile::tempdir().expect("Failed to create temp dir");
|
|
let thread_pool = get_pool();
|
|
// Test create
|
|
let _ = SqliteEngine::new(
|
|
base_dir.path(),
|
|
&IndexedDBDescription {
|
|
name: "test_db".to_string(),
|
|
origin: test_origin(),
|
|
},
|
|
thread_pool.clone(),
|
|
)
|
|
.unwrap();
|
|
// Test open
|
|
let db = SqliteEngine::new(
|
|
base_dir.path(),
|
|
&IndexedDBDescription {
|
|
name: "test_db".to_string(),
|
|
origin: test_origin(),
|
|
},
|
|
thread_pool.clone(),
|
|
)
|
|
.unwrap();
|
|
let version = db.version().expect("Failed to get version");
|
|
assert_eq!(version, 0);
|
|
db.set_version(5).unwrap();
|
|
let new_version = db.version().expect("Failed to get new version");
|
|
assert_eq!(new_version, 5);
|
|
db.delete_database().expect("Failed to delete database");
|
|
}
|
|
|
|
#[test]
|
|
fn test_create_store() {
|
|
let base_dir = tempfile::tempdir().expect("Failed to create temp dir");
|
|
let thread_pool = get_pool();
|
|
let db = SqliteEngine::new(
|
|
base_dir.path(),
|
|
&IndexedDBDescription {
|
|
name: "test_db".to_string(),
|
|
origin: test_origin(),
|
|
},
|
|
thread_pool,
|
|
)
|
|
.unwrap();
|
|
let store_name = "test_store";
|
|
let result = db.create_store(store_name, None, true);
|
|
assert!(result.is_ok());
|
|
let create_result = result.unwrap();
|
|
assert_eq!(create_result, CreateObjectResult::Created);
|
|
// Try to create the same store again
|
|
let result = db.create_store(store_name, None, false);
|
|
assert!(result.is_ok());
|
|
let create_result = result.unwrap();
|
|
assert_eq!(create_result, CreateObjectResult::AlreadyExists);
|
|
// Ensure store was not overwritten
|
|
assert!(db.has_key_generator(store_name));
|
|
}
|
|
|
|
#[test]
|
|
fn test_create_store_empty_name() {
|
|
let base_dir = tempfile::tempdir().expect("Failed to create temp dir");
|
|
let thread_pool = get_pool();
|
|
let db = SqliteEngine::new(
|
|
base_dir.path(),
|
|
&IndexedDBDescription {
|
|
name: "test_db".to_string(),
|
|
origin: test_origin(),
|
|
},
|
|
thread_pool,
|
|
)
|
|
.unwrap();
|
|
let store_name = "";
|
|
let result = db.create_store(store_name, None, true);
|
|
assert!(result.is_ok());
|
|
let create_result = result.unwrap();
|
|
assert_eq!(create_result, CreateObjectResult::Created);
|
|
}
|
|
|
|
#[test]
|
|
fn test_injection() {
|
|
let base_dir = tempfile::tempdir().expect("Failed to create temp dir");
|
|
let thread_pool = get_pool();
|
|
let db = SqliteEngine::new(
|
|
base_dir.path(),
|
|
&IndexedDBDescription {
|
|
name: "test_db".to_string(),
|
|
origin: test_origin(),
|
|
},
|
|
thread_pool,
|
|
)
|
|
.unwrap();
|
|
// Create a normal store
|
|
let store_name1 = "test_store";
|
|
let result = db.create_store(store_name1, None, true);
|
|
assert!(result.is_ok());
|
|
let create_result = result.unwrap();
|
|
assert_eq!(create_result, CreateObjectResult::Created);
|
|
// Injection
|
|
let store_name2 = "' OR 1=1 -- -";
|
|
let result = db.create_store(store_name2, None, false);
|
|
assert!(result.is_ok());
|
|
let create_result = result.unwrap();
|
|
assert_eq!(create_result, CreateObjectResult::Created);
|
|
}
|
|
|
|
#[test]
|
|
fn test_key_path() {
|
|
let base_dir = tempfile::tempdir().expect("Failed to create temp dir");
|
|
let thread_pool = get_pool();
|
|
let db = SqliteEngine::new(
|
|
base_dir.path(),
|
|
&IndexedDBDescription {
|
|
name: "test_db".to_string(),
|
|
origin: test_origin(),
|
|
},
|
|
thread_pool,
|
|
)
|
|
.unwrap();
|
|
let store_name = "test_store";
|
|
let result = db.create_store(store_name, Some(KeyPath::String("test".to_string())), true);
|
|
assert!(result.is_ok());
|
|
assert_eq!(
|
|
db.key_path(store_name),
|
|
Some(KeyPath::String("test".to_string()))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_delete_store() {
|
|
let base_dir = tempfile::tempdir().expect("Failed to create temp dir");
|
|
let thread_pool = get_pool();
|
|
let db = SqliteEngine::new(
|
|
base_dir.path(),
|
|
&IndexedDBDescription {
|
|
name: "test_db".to_string(),
|
|
origin: test_origin(),
|
|
},
|
|
thread_pool,
|
|
)
|
|
.unwrap();
|
|
db.create_store("test_store", None, false)
|
|
.expect("Failed to create store");
|
|
// Delete the store
|
|
db.delete_store("test_store")
|
|
.expect("Failed to delete store");
|
|
// Try to delete the same store again
|
|
let result = db.delete_store("test_store");
|
|
assert!(result.is_err());
|
|
// Try to delete a non-existing store
|
|
let result = db.delete_store("test_store");
|
|
// Should work as per spec
|
|
assert!(result.is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_async_operations() {
|
|
fn get_channel<T>() -> (
|
|
ipc_channel::ipc::IpcSender<T>,
|
|
ipc_channel::ipc::IpcReceiver<T>,
|
|
)
|
|
where
|
|
T: for<'de> Deserialize<'de> + Serialize,
|
|
{
|
|
ipc_channel::ipc::channel().unwrap()
|
|
}
|
|
|
|
let base_dir = tempfile::tempdir().expect("Failed to create temp dir");
|
|
let thread_pool = get_pool();
|
|
let db = SqliteEngine::new(
|
|
base_dir.path(),
|
|
&IndexedDBDescription {
|
|
name: "test_db".to_string(),
|
|
origin: test_origin(),
|
|
},
|
|
thread_pool,
|
|
)
|
|
.unwrap();
|
|
let store_name = "test_store";
|
|
db.create_store(store_name, None, false)
|
|
.expect("Failed to create store");
|
|
let put = get_channel();
|
|
let put2 = get_channel();
|
|
let put3 = get_channel();
|
|
let put_dup = get_channel();
|
|
let get_item_some = get_channel();
|
|
let get_item_none = get_channel();
|
|
let get_all_items = get_channel();
|
|
let count = get_channel();
|
|
let remove = get_channel();
|
|
let clear = get_channel();
|
|
let rx = db.process_transaction(KvsTransaction {
|
|
mode: IndexedDBTxnMode::Readwrite,
|
|
requests: VecDeque::from(vec![
|
|
KvsOperation {
|
|
store_name: store_name.to_owned(),
|
|
operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
|
|
sender: put.0,
|
|
key: Some(IndexedDBKeyType::Number(1.0)),
|
|
value: vec![1, 2, 3],
|
|
should_overwrite: false,
|
|
}),
|
|
},
|
|
KvsOperation {
|
|
store_name: store_name.to_owned(),
|
|
operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
|
|
sender: put2.0,
|
|
key: Some(IndexedDBKeyType::String("2.0".to_string())),
|
|
value: vec![4, 5, 6],
|
|
should_overwrite: false,
|
|
}),
|
|
},
|
|
KvsOperation {
|
|
store_name: store_name.to_owned(),
|
|
operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
|
|
sender: put3.0,
|
|
key: Some(IndexedDBKeyType::Array(vec![
|
|
IndexedDBKeyType::String("3".to_string()),
|
|
IndexedDBKeyType::Number(0.0),
|
|
])),
|
|
value: vec![7, 8, 9],
|
|
should_overwrite: false,
|
|
}),
|
|
},
|
|
// Try to put a duplicate key without overwrite
|
|
KvsOperation {
|
|
store_name: store_name.to_owned(),
|
|
operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
|
|
sender: put_dup.0,
|
|
key: Some(IndexedDBKeyType::Number(1.0)),
|
|
value: vec![10, 11, 12],
|
|
should_overwrite: false,
|
|
}),
|
|
},
|
|
KvsOperation {
|
|
store_name: store_name.to_owned(),
|
|
operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem {
|
|
sender: get_item_some.0,
|
|
key_range: IndexedDBKeyRange::only(IndexedDBKeyType::Number(1.0)),
|
|
}),
|
|
},
|
|
KvsOperation {
|
|
store_name: store_name.to_owned(),
|
|
operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem {
|
|
sender: get_item_none.0,
|
|
key_range: IndexedDBKeyRange::only(IndexedDBKeyType::Number(5.0)),
|
|
}),
|
|
},
|
|
KvsOperation {
|
|
store_name: store_name.to_owned(),
|
|
operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetAllItems {
|
|
sender: get_all_items.0,
|
|
key_range: IndexedDBKeyRange::lower_bound(IndexedDBKeyType::Number(0.0), false),
|
|
count: None,
|
|
}),
|
|
},
|
|
KvsOperation {
|
|
store_name: store_name.to_owned(),
|
|
operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Count {
|
|
sender: count.0,
|
|
key_range: IndexedDBKeyRange::only(IndexedDBKeyType::Number(1.0)),
|
|
}),
|
|
},
|
|
KvsOperation {
|
|
store_name: store_name.to_owned(),
|
|
operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::RemoveItem {
|
|
sender: remove.0,
|
|
key: IndexedDBKeyType::Number(1.0),
|
|
}),
|
|
},
|
|
KvsOperation {
|
|
store_name: store_name.to_owned(),
|
|
operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::Clear(clear.0)),
|
|
},
|
|
]),
|
|
});
|
|
let _ = rx.blocking_recv().unwrap();
|
|
put.1.recv().unwrap().unwrap();
|
|
put2.1.recv().unwrap().unwrap();
|
|
put3.1.recv().unwrap().unwrap();
|
|
let err = put_dup.1.recv().unwrap().unwrap();
|
|
assert_eq!(err, PutItemResult::CannotOverwrite);
|
|
let get_result = get_item_some.1.recv().unwrap();
|
|
let value = get_result.unwrap();
|
|
assert_eq!(value, Some(vec![1, 2, 3]));
|
|
let get_result = get_item_none.1.recv().unwrap();
|
|
let value = get_result.unwrap();
|
|
assert_eq!(value, None);
|
|
let all_items = get_all_items.1.recv().unwrap().unwrap();
|
|
assert_eq!(all_items.len(), 3);
|
|
// Check that all three items are present
|
|
assert!(all_items.contains(&vec![1, 2, 3]));
|
|
assert!(all_items.contains(&vec![4, 5, 6]));
|
|
assert!(all_items.contains(&vec![7, 8, 9]));
|
|
let amount = count.1.recv().unwrap().unwrap();
|
|
assert_eq!(amount, 1);
|
|
remove.1.recv().unwrap().unwrap();
|
|
clear.1.recv().unwrap().unwrap();
|
|
}
|