From 79fae3d93ccc5056f221c741f8c7976773b846a6 Mon Sep 17 00:00:00 2001 From: gwenn <45554+gwenn@users.noreply.github.com> Date: Mon, 1 Apr 2024 10:11:07 +0200 Subject: [PATCH] Improve test coverage (#1490) Improve test coverage --- libsqlite3-sys/src/error.rs | 39 ++++++++++++++++++++ src/blob/mod.rs | 11 ++++++ src/collation.rs | 9 +++-- src/functions.rs | 7 ++++ src/row.rs | 27 +++++++++++++- src/transaction.rs | 23 ++++++++++++ src/types/to_sql.rs | 16 ++++++++- src/types/value.rs | 22 ++++++++++++ src/types/value_ref.rs | 72 +++++++++++++++++++++++++++++++++++++ 9 files changed, 222 insertions(+), 4 deletions(-) diff --git a/libsqlite3-sys/src/error.rs b/libsqlite3-sys/src/error.rs index 6c6a29f..9583d23 100644 --- a/libsqlite3-sys/src/error.rs +++ b/libsqlite3-sys/src/error.rs @@ -302,3 +302,42 @@ impl ::std::fmt::Display for InitError { } #[cfg(feature = "loadable_extension")] impl error::Error for InitError {} + +#[cfg(test)] +mod test { + use crate::*; + + #[test] + pub fn error_new() { + let assoc = vec![ + (SQLITE_INTERNAL, ErrorCode::InternalMalfunction), + (SQLITE_PERM, ErrorCode::PermissionDenied), + (SQLITE_ABORT_ROLLBACK, ErrorCode::OperationAborted), + (SQLITE_BUSY_RECOVERY, ErrorCode::DatabaseBusy), + (SQLITE_LOCKED_SHAREDCACHE, ErrorCode::DatabaseLocked), + (SQLITE_NOMEM, ErrorCode::OutOfMemory), + (SQLITE_IOERR_READ, ErrorCode::SystemIoFailure), + (SQLITE_NOTFOUND, ErrorCode::NotFound), + (SQLITE_FULL, ErrorCode::DiskFull), + (SQLITE_PROTOCOL, ErrorCode::FileLockingProtocolFailed), + (SQLITE_SCHEMA, ErrorCode::SchemaChanged), + (SQLITE_TOOBIG, ErrorCode::TooBig), + (SQLITE_MISMATCH, ErrorCode::TypeMismatch), + (SQLITE_NOLFS, ErrorCode::NoLargeFileSupport), + (SQLITE_RANGE, ErrorCode::ParameterOutOfRange), + (SQLITE_NOTADB, ErrorCode::NotADatabase), + ]; + for (sqlite_code, rust_code) in assoc { + let err = Error::new(sqlite_code); + assert_eq!( + err, + Error { + code: rust_code, + extended_code: sqlite_code + } + ); + let s = format!("{}", err); + assert!(!s.is_empty()); + } + } +} diff --git a/src/blob/mod.rs b/src/blob/mod.rs index c9e797b..cf4e24e 100644 --- a/src/blob/mod.rs +++ b/src/blob/mod.rs @@ -439,9 +439,12 @@ mod test { let (db, rowid) = db_with_test_blob()?; let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)?; + assert!(!blob.is_empty()); + assert_eq!(10, blob.len()); assert_eq!(4, blob.write(b"Clob").unwrap()); assert_eq!(6, blob.write(b"567890xxxxxx").unwrap()); // cannot write past 10 assert_eq!(0, blob.write(b"5678").unwrap()); // still cannot write past 10 + blob.flush().unwrap(); blob.reopen(rowid)?; blob.close()?; @@ -547,4 +550,12 @@ mod test { Ok(()) } } + + #[test] + fn zero_blob() -> Result<()> { + use crate::types::ToSql; + let zb = super::ZeroBlob(1); + assert!(zb.to_sql().is_ok()); + Ok(()) + } } diff --git a/src/collation.rs b/src/collation.rs index cf4226b..1319db4 100644 --- a/src/collation.rs +++ b/src/collation.rs @@ -201,9 +201,7 @@ mod test { #[test] fn test_unicase() -> Result<()> { let db = Connection::open_in_memory()?; - db.create_collation("unicase", unicase_compare)?; - collate(db) } @@ -233,4 +231,11 @@ mod test { db.collation_needed(collation_needed)?; collate(db) } + + #[test] + fn remove_collation() -> Result<()> { + let db = Connection::open_in_memory()?; + db.create_collation("unicase", unicase_compare)?; + db.remove_collation("unicase") + } } diff --git a/src/functions.rs b/src/functions.rs index e6ab3f2..4e81f33 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -855,7 +855,14 @@ mod test { use crate::{Connection, Error, Result}; fn half(ctx: &Context<'_>) -> Result { + assert!(!ctx.is_empty()); assert_eq!(ctx.len(), 1, "called with unexpected number of arguments"); + assert!(unsafe { + ctx.get_connection() + .as_ref() + .map(::std::ops::Deref::deref) + .is_ok() + }); let value = ctx.get::(0)?; Ok(value / 2f64) } diff --git a/src/row.rs b/src/row.rs index 3f20154..f2f6b1d 100644 --- a/src/row.rs +++ b/src/row.rs @@ -359,7 +359,7 @@ impl<'stmt> std::fmt::Debug for Row<'stmt> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut dm = f.debug_map(); for c in 0..self.stmt.column_count() { - let name = self.stmt.column_name(c); + let name = self.stmt.column_name(c).expect("valid column index"); dm.key(&name); let value = self.get_ref(c); match value { @@ -613,4 +613,29 @@ mod tests { } Ok(()) } + + #[test] + fn as_ref() -> Result<()> { + let conn = Connection::open_in_memory()?; + let mut stmt = conn.prepare("SELECT 'Lisa' as name, 1 as id")?; + let rows = stmt.query([])?; + assert_eq!(rows.as_ref().unwrap().column_count(), 2); + Ok(()) + } + + #[test] + fn debug() -> Result<()> { + let conn = Connection::open_in_memory()?; + let mut stmt = conn.prepare( + "SELECT 'Lisa' as name, 1 as id, 3.14 as pi, X'53514C697465' as blob, NULL as void", + )?; + let mut rows = stmt.query([])?; + let row = rows.next()?.unwrap(); + let s = format!("{:?}", row); + assert_eq!( + s, + r#"{"name": (Text, "Lisa"), "id": (Integer, 1), "pi": (Real, 3.14), "blob": (Blob, 6), "void": (Null, ())}"# + ); + Ok(()) + } } diff --git a/src/transaction.rs b/src/transaction.rs index 2e90528..218d026 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -775,4 +775,27 @@ mod test { db.execute_batch("ROLLBACK")?; Ok(()) } + + #[test] + #[cfg(feature = "modern_sqlite")] + fn auto_commit() -> Result<()> { + use super::TransactionState; + let db = Connection::open_in_memory()?; + db.execute_batch("CREATE TABLE t(i UNIQUE);")?; + assert!(db.is_autocommit()); + let mut stmt = db.prepare("SELECT name FROM sqlite_master")?; + assert_eq!(TransactionState::None, db.transaction_state(None)?); + { + let mut rows = stmt.query([])?; + assert!(rows.next()?.is_some()); // start reading + assert_eq!(TransactionState::Read, db.transaction_state(None)?); + db.execute("INSERT INTO t VALUES (1)", [])?; // auto-commit + assert_eq!(TransactionState::Read, db.transaction_state(None)?); + assert!(rows.next()?.is_some()); // still reading + assert_eq!(TransactionState::Read, db.transaction_state(None)?); + assert!(rows.next()?.is_none()); // end + assert_eq!(TransactionState::None, db.transaction_state(None)?); + } + Ok(()) + } } diff --git a/src/types/to_sql.rs b/src/types/to_sql.rs index 47927e4..7ccbc0a 100644 --- a/src/types/to_sql.rs +++ b/src/types/to_sql.rs @@ -310,10 +310,24 @@ impl ToSql for Option { #[cfg(test)] mod test { - use super::ToSql; + use super::{ToSql, ToSqlOutput}; + use crate::{types::Value, types::ValueRef, Result}; fn is_to_sql() {} + #[test] + fn to_sql() -> Result<()> { + assert_eq!( + ToSqlOutput::Borrowed(ValueRef::Null).to_sql()?, + ToSqlOutput::Borrowed(ValueRef::Null) + ); + assert_eq!( + ToSqlOutput::Owned(Value::Null).to_sql()?, + ToSqlOutput::Borrowed(ValueRef::Null) + ); + Ok(()) + } + #[test] fn test_integral_types() { is_to_sql::(); diff --git a/src/types/value.rs b/src/types/value.rs index ca3ee9f..6f0ee46 100644 --- a/src/types/value.rs +++ b/src/types/value.rs @@ -140,3 +140,25 @@ impl Value { } } } + +#[cfg(test)] +mod test { + use super::Value; + use crate::types::Type; + + #[test] + fn from() { + assert_eq!(Value::from(2f32), Value::Real(2f64)); + assert_eq!(Value::from(3.), Value::Real(3.)); + assert_eq!(Value::from(vec![0u8]), Value::Blob(vec![0u8])); + } + + #[test] + fn data_type() { + assert_eq!(Value::Null.data_type(), Type::Null); + assert_eq!(Value::Integer(0).data_type(), Type::Integer); + assert_eq!(Value::Real(0.).data_type(), Type::Real); + assert_eq!(Value::Text("".to_owned()).data_type(), Type::Text); + assert_eq!(Value::Blob(vec![]).data_type(), Type::Blob); + } +} diff --git a/src/types/value_ref.rs b/src/types/value_ref.rs index 7c3e020..64cb6c9 100644 --- a/src/types/value_ref.rs +++ b/src/types/value_ref.rs @@ -261,3 +261,75 @@ impl<'a> ValueRef<'a> { // TODO sqlite3_value_nochange // 3.22.0 & VTab xUpdate // TODO sqlite3_value_frombind // 3.28.0 } + +#[cfg(test)] +mod test { + use super::ValueRef; + use crate::types::FromSqlResult; + + #[test] + fn as_i64() -> FromSqlResult<()> { + assert!(ValueRef::Real(1.0).as_i64().is_err()); + assert_eq!(ValueRef::Integer(1).as_i64(), Ok(1)); + Ok(()) + } + #[test] + fn as_i64_or_null() -> FromSqlResult<()> { + assert_eq!(ValueRef::Null.as_i64_or_null(), Ok(None)); + assert!(ValueRef::Real(1.0).as_i64_or_null().is_err()); + assert_eq!(ValueRef::Integer(1).as_i64_or_null(), Ok(Some(1))); + Ok(()) + } + #[test] + fn as_f64() -> FromSqlResult<()> { + assert!(ValueRef::Integer(1).as_f64().is_err()); + assert_eq!(ValueRef::Real(1.0).as_f64(), Ok(1.0)); + Ok(()) + } + #[test] + fn as_f64_or_null() -> FromSqlResult<()> { + assert_eq!(ValueRef::Null.as_f64_or_null(), Ok(None)); + assert!(ValueRef::Integer(1).as_f64_or_null().is_err()); + assert_eq!(ValueRef::Real(1.0).as_f64_or_null(), Ok(Some(1.0))); + Ok(()) + } + #[test] + fn as_str() -> FromSqlResult<()> { + assert!(ValueRef::Null.as_str().is_err()); + assert_eq!(ValueRef::Text(b"").as_str(), Ok("")); + Ok(()) + } + #[test] + fn as_str_or_null() -> FromSqlResult<()> { + assert_eq!(ValueRef::Null.as_str_or_null(), Ok(None)); + assert!(ValueRef::Integer(1).as_str_or_null().is_err()); + assert_eq!(ValueRef::Text(b"").as_str_or_null(), Ok(Some(""))); + Ok(()) + } + #[test] + fn as_blob() -> FromSqlResult<()> { + assert!(ValueRef::Null.as_blob().is_err()); + assert_eq!(ValueRef::Blob(b"").as_blob(), Ok(&b""[..])); + Ok(()) + } + #[test] + fn as_blob_or_null() -> FromSqlResult<()> { + assert_eq!(ValueRef::Null.as_blob_or_null(), Ok(None)); + assert!(ValueRef::Integer(1).as_blob_or_null().is_err()); + assert_eq!(ValueRef::Blob(b"").as_blob_or_null(), Ok(Some(&b""[..]))); + Ok(()) + } + #[test] + fn as_bytes() -> FromSqlResult<()> { + assert!(ValueRef::Null.as_bytes().is_err()); + assert_eq!(ValueRef::Blob(b"").as_bytes(), Ok(&b""[..])); + Ok(()) + } + #[test] + fn as_bytes_or_null() -> FromSqlResult<()> { + assert_eq!(ValueRef::Null.as_bytes_or_null(), Ok(None)); + assert!(ValueRef::Integer(1).as_bytes_or_null().is_err()); + assert_eq!(ValueRef::Blob(b"").as_bytes_or_null(), Ok(Some(&b""[..]))); + Ok(()) + } +}