indexeddb: Initialize DB version to zero in unit tests (#38911)

Continuation of https://github.com/servo/servo/pull/38819

Testing: Fixes indexeddb unit tests

---------

Signed-off-by: Rodion Borovyk <rodion.borovyk@gmail.com>
This commit is contained in:
Rodion Borovyk 2025-08-26 10:02:40 +02:00 committed by GitHub
parent 814fa8ff2b
commit 3f6e2679dc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 227 additions and 235 deletions

View file

@ -69,7 +69,6 @@ pub trait KvsEngine {
fn delete_store(&self, store_name: SanitizedName) -> Result<(), Self::Error>; fn delete_store(&self, store_name: SanitizedName) -> Result<(), Self::Error>;
#[expect(dead_code)]
fn close_store(&self, store_name: SanitizedName) -> Result<(), Self::Error>; fn close_store(&self, store_name: SanitizedName) -> Result<(), Self::Error>;
fn delete_database(self) -> Result<(), Self::Error>; fn delete_database(self) -> Result<(), Self::Error>;

View file

@ -536,234 +536,3 @@ impl KvsEngine for SqliteEngine {
Ok(()) Ok(())
} }
} }
#[cfg(test)]
mod tests {
use std::collections::VecDeque;
use std::sync::Arc;
use net_traits::indexeddb_thread::{
AsyncOperation, AsyncReadOnlyOperation, AsyncReadWriteOperation, CreateObjectResult,
IndexedDBKeyType, IndexedDBTxnMode, KeyPath,
};
use servo_url::ImmutableOrigin;
use url::Host;
use crate::indexeddb::engines::{KvsEngine, KvsOperation, KvsTransaction, SanitizedName};
use crate::indexeddb::idb_thread::IndexedDBDescription;
use crate::resource_thread::CoreResourceThreadPool;
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 _ = super::SqliteEngine::new(
base_dir.path(),
&IndexedDBDescription {
name: "test_db".to_string(),
origin: test_origin(),
},
1,
thread_pool.clone(),
)
.unwrap();
// Test open
let db = super::SqliteEngine::new(
base_dir.path(),
&IndexedDBDescription {
name: "test_db".to_string(),
origin: test_origin(),
},
1,
thread_pool.clone(),
)
.unwrap();
let version = db.version().expect("Failed to get version");
assert_eq!(version, 1);
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 = super::SqliteEngine::new(
base_dir.path(),
&IndexedDBDescription {
name: "test_db".to_string(),
origin: test_origin(),
},
1,
thread_pool,
)
.unwrap();
let store_name = SanitizedName::new("test_store".to_string());
let result = db.create_store(store_name.clone(), 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.clone(), 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_key_path() {
let base_dir = tempfile::tempdir().expect("Failed to create temp dir");
let thread_pool = get_pool();
let db = super::SqliteEngine::new(
base_dir.path(),
&IndexedDBDescription {
name: "test_db".to_string(),
origin: test_origin(),
},
1,
thread_pool,
)
.unwrap();
let store_name = SanitizedName::new("test_store".to_string());
let result = db.create_store(
store_name.clone(),
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 = super::SqliteEngine::new(
base_dir.path(),
&IndexedDBDescription {
name: "test_db".to_string(),
origin: test_origin(),
},
1,
thread_pool,
)
.unwrap();
db.create_store(SanitizedName::new("test_store".to_string()), None, false)
.expect("Failed to create store");
// Delete the store
db.delete_store(SanitizedName::new("test_store".to_string()))
.expect("Failed to delete store");
// Try to delete the same store again
let result = db.delete_store(SanitizedName::new("test_store".into()));
assert!(result.is_err());
// Try to delete a non-existing store
let result = db.delete_store(SanitizedName::new("test_store".into()));
// Should work as per spec
assert!(result.is_err());
}
#[test]
fn test_async_operations() {
let base_dir = tempfile::tempdir().expect("Failed to create temp dir");
let thread_pool = get_pool();
let db = super::SqliteEngine::new(
base_dir.path(),
&IndexedDBDescription {
name: "test_db".to_string(),
origin: test_origin(),
},
1,
thread_pool,
)
.unwrap();
let store_name = SanitizedName::new("test_store".to_string());
db.create_store(store_name.clone(), None, false)
.expect("Failed to create store");
let channel = ipc_channel::ipc::channel().unwrap();
let channel2 = ipc_channel::ipc::channel().unwrap();
let channel3 = ipc_channel::ipc::channel().unwrap();
let channel4 = ipc_channel::ipc::channel().unwrap();
let channel5 = ipc_channel::ipc::channel().unwrap();
let channel6 = ipc_channel::ipc::channel().unwrap();
let rx = db.process_transaction(KvsTransaction {
mode: IndexedDBTxnMode::Readwrite,
requests: VecDeque::from(vec![
KvsOperation {
store_name: store_name.clone(),
operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
sender: channel.0,
key: IndexedDBKeyType::Number(1.0),
value: vec![1, 2, 3],
should_overwrite: false,
}),
},
KvsOperation {
store_name: store_name.clone(),
operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem {
sender: channel2.0,
key_range: super::IndexedDBKeyRange::only(IndexedDBKeyType::Number(1.0)),
}),
},
KvsOperation {
store_name: store_name.clone(),
operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem {
sender: channel3.0,
key_range: super::IndexedDBKeyRange::only(IndexedDBKeyType::Number(5.0)),
}),
},
KvsOperation {
store_name: store_name.clone(),
operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Count {
sender: channel4.0,
key_range: super::IndexedDBKeyRange::only(IndexedDBKeyType::Number(1.0)),
}),
},
KvsOperation {
store_name: store_name.clone(),
operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::RemoveItem {
sender: channel5.0,
key: IndexedDBKeyType::Number(1.0),
}),
},
KvsOperation {
store_name: store_name.clone(),
operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::Clear(
channel6.0,
)),
},
]),
});
let _ = rx.blocking_recv().unwrap();
channel.1.recv().unwrap().unwrap();
let get_result = channel2.1.recv().unwrap();
let value = get_result.unwrap();
assert_eq!(value, Some(vec![1, 2, 3]));
let get_result = channel3.1.recv().unwrap();
let value = get_result.unwrap();
assert_eq!(value, None);
let amount = channel4.1.recv().unwrap().unwrap();
assert_eq!(amount, 1);
channel5.1.recv().unwrap().unwrap();
channel6.1.recv().unwrap().unwrap();
}
}

View file

@ -49,8 +49,8 @@ impl IndexedDBThreadFactory for IpcSender<IndexedDBThreadMsg> {
#[derive(Clone, Eq, Hash, PartialEq)] #[derive(Clone, Eq, Hash, PartialEq)]
pub struct IndexedDBDescription { pub struct IndexedDBDescription {
pub(super) origin: ImmutableOrigin, pub origin: ImmutableOrigin,
pub(super) name: String, pub name: String,
} }
impl IndexedDBDescription { impl IndexedDBDescription {

View file

@ -4,6 +4,6 @@
pub use self::idb_thread::IndexedDBThreadFactory; pub use self::idb_thread::IndexedDBThreadFactory;
mod engines; pub mod engines;
pub mod idb_thread; pub mod idb_thread;

View file

@ -15,6 +15,7 @@ mod hsts;
mod http_cache; mod http_cache;
mod http_loader; mod http_loader;
mod resource_thread; mod resource_thread;
mod sqlite;
mod subresource_integrity; mod subresource_integrity;
use core::convert::Infallible; use core::convert::Infallible;

View file

@ -0,0 +1,223 @@
/* 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, SanitizedName, 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,
};
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 = SanitizedName::new("test_store".to_string());
let result = db.create_store(store_name.clone(), 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.clone(), 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_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 = SanitizedName::new("test_store".to_string());
let result = db.create_store(
store_name.clone(),
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(SanitizedName::new("test_store".to_string()), None, false)
.expect("Failed to create store");
// Delete the store
db.delete_store(SanitizedName::new("test_store".to_string()))
.expect("Failed to delete store");
// Try to delete the same store again
let result = db.delete_store(SanitizedName::new("test_store".into()));
assert!(result.is_err());
// Try to delete a non-existing store
let result = db.delete_store(SanitizedName::new("test_store".into()));
// Should work as per spec
assert!(result.is_err());
}
#[test]
fn test_async_operations() {
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 = SanitizedName::new("test_store".to_string());
db.create_store(store_name.clone(), None, false)
.expect("Failed to create store");
let channel = ipc_channel::ipc::channel().unwrap();
let channel2 = ipc_channel::ipc::channel().unwrap();
let channel3 = ipc_channel::ipc::channel().unwrap();
let channel4 = ipc_channel::ipc::channel().unwrap();
let channel5 = ipc_channel::ipc::channel().unwrap();
let channel6 = ipc_channel::ipc::channel().unwrap();
let rx = db.process_transaction(KvsTransaction {
mode: IndexedDBTxnMode::Readwrite,
requests: VecDeque::from(vec![
KvsOperation {
store_name: store_name.clone(),
operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::PutItem {
sender: channel.0,
key: IndexedDBKeyType::Number(1.0),
value: vec![1, 2, 3],
should_overwrite: false,
}),
},
KvsOperation {
store_name: store_name.clone(),
operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem {
sender: channel2.0,
key_range: IndexedDBKeyRange::only(IndexedDBKeyType::Number(1.0)),
}),
},
KvsOperation {
store_name: store_name.clone(),
operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::GetItem {
sender: channel3.0,
key_range: IndexedDBKeyRange::only(IndexedDBKeyType::Number(5.0)),
}),
},
KvsOperation {
store_name: store_name.clone(),
operation: AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Count {
sender: channel4.0,
key_range: IndexedDBKeyRange::only(IndexedDBKeyType::Number(1.0)),
}),
},
KvsOperation {
store_name: store_name.clone(),
operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::RemoveItem {
sender: channel5.0,
key: IndexedDBKeyType::Number(1.0),
}),
},
KvsOperation {
store_name: store_name.clone(),
operation: AsyncOperation::ReadWrite(AsyncReadWriteOperation::Clear(channel6.0)),
},
]),
});
let _ = rx.blocking_recv().unwrap();
channel.1.recv().unwrap().unwrap();
let get_result = channel2.1.recv().unwrap();
let value = get_result.unwrap();
assert_eq!(value, Some(vec![1, 2, 3]));
let get_result = channel3.1.recv().unwrap();
let value = get_result.unwrap();
assert_eq!(value, None);
let amount = channel4.1.recv().unwrap().unwrap();
assert_eq!(amount, 1);
channel5.1.recv().unwrap().unwrap();
channel6.1.recv().unwrap().unwrap();
}