mirror of
https://github.com/chatmail/core.git
synced 2026-04-17 21:46:35 +03:00
Remove dc_open() from the Rust API
This means the Context becomes a struct following the normal Rust conventions where when it is created it is usable. This will over time allow removing a lot of runtime checks and simplify code. Many members will no longer need to be Options or similar. The C API needs to remain compatible so hides the implementation of this behind another struct which can be opened and closed.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -396,8 +396,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
|||||||
_ => println!(
|
_ => println!(
|
||||||
"==========================Database commands==\n\
|
"==========================Database commands==\n\
|
||||||
info\n\
|
info\n\
|
||||||
open <file to open or create>\n\
|
|
||||||
close\n\
|
|
||||||
set <configuration-key> [<value>]\n\
|
set <configuration-key> [<value>]\n\
|
||||||
get <configuration-key>\n\
|
get <configuration-key>\n\
|
||||||
oauth2\n\
|
oauth2\n\
|
||||||
@@ -474,14 +472,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
|||||||
println!("Already authorized.");
|
println!("Already authorized.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"open" => {
|
|
||||||
ensure!(!arg1.is_empty(), "Argument <file> missing");
|
|
||||||
dc_close(context);
|
|
||||||
ensure!(dc_open(context, arg1, None), "Open failed");
|
|
||||||
}
|
|
||||||
"close" => {
|
|
||||||
dc_close(context);
|
|
||||||
}
|
|
||||||
"initiate-key-transfer" => {
|
"initiate-key-transfer" => {
|
||||||
let setup_code = dc_initiate_key_transfer(context);
|
let setup_code = dc_initiate_key_transfer(context);
|
||||||
if !setup_code.is_null() {
|
if !setup_code.is_null() {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ extern crate lazy_static;
|
|||||||
extern crate rusqlite;
|
extern crate rusqlite;
|
||||||
|
|
||||||
use std::borrow::Cow::{self, Borrowed, Owned};
|
use std::borrow::Cow::{self, Borrowed, Owned};
|
||||||
|
use std::path::Path;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
|
|
||||||
@@ -41,7 +42,7 @@ use self::cmdline::*;
|
|||||||
|
|
||||||
// Event Handler
|
// Event Handler
|
||||||
|
|
||||||
unsafe extern "C" fn receive_event(
|
fn receive_event(
|
||||||
_context: &Context,
|
_context: &Context,
|
||||||
event: Event,
|
event: Event,
|
||||||
data1: uintptr_t,
|
data1: uintptr_t,
|
||||||
@@ -384,21 +385,16 @@ impl Highlighter for DcHelper {
|
|||||||
impl Helper for DcHelper {}
|
impl Helper for DcHelper {}
|
||||||
|
|
||||||
fn main_0(args: Vec<String>) -> Result<(), failure::Error> {
|
fn main_0(args: Vec<String>) -> Result<(), failure::Error> {
|
||||||
let mut context = dc_context_new(
|
|
||||||
Some(receive_event),
|
|
||||||
0 as *mut libc::c_void,
|
|
||||||
Some("CLI".into()),
|
|
||||||
);
|
|
||||||
|
|
||||||
unsafe { dc_cmdline_skip_auth() };
|
unsafe { dc_cmdline_skip_auth() };
|
||||||
|
if args.len() != 2 {
|
||||||
if args.len() == 2 {
|
|
||||||
if unsafe { !dc_open(&mut context, &args[1], None) } {
|
|
||||||
println!("Error: Cannot open {}.", args[0],);
|
|
||||||
}
|
|
||||||
} else if args.len() != 1 {
|
|
||||||
println!("Error: Bad arguments, expected [db-name].");
|
println!("Error: Bad arguments, expected [db-name].");
|
||||||
|
return Err(format_err!("No db-name specified"));
|
||||||
}
|
}
|
||||||
|
let context = Context::new(
|
||||||
|
Box::new(receive_event),
|
||||||
|
"CLI".into(),
|
||||||
|
Path::new(&args[1]).to_path_buf(),
|
||||||
|
)?;
|
||||||
|
|
||||||
println!("Delta Chat Core is awaiting your commands.");
|
println!("Delta Chat Core is awaiting your commands.");
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,8 @@ use deltachat::job::{
|
|||||||
perform_imap_fetch, perform_imap_idle, perform_imap_jobs, perform_smtp_idle, perform_smtp_jobs,
|
perform_imap_fetch, perform_imap_idle, perform_imap_jobs, perform_smtp_idle, perform_smtp_jobs,
|
||||||
};
|
};
|
||||||
|
|
||||||
extern "C" fn cb(_ctx: &Context, event: Event, data1: usize, data2: usize) -> usize {
|
fn cb(_ctx: &Context, event: Event, data1: usize, data2: usize) -> usize {
|
||||||
println!("[{:?}]", event);
|
println!("[{:?}]", event);
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
Event::CONFIGURE_PROGRESS => {
|
Event::CONFIGURE_PROGRESS => {
|
||||||
println!(" progress: {}", data1);
|
println!(" progress: {}", data1);
|
||||||
@@ -39,7 +38,11 @@ extern "C" fn cb(_ctx: &Context, event: Event, data1: usize, data2: usize) -> us
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None);
|
let dir = tempdir().unwrap();
|
||||||
|
let dbfile = dir.path().join("db.sqlite");
|
||||||
|
println!("creating database {:?}", dbfile);
|
||||||
|
let ctx =
|
||||||
|
Context::new(Box::new(cb), "FakeOs".into(), dbfile).expect("Failed to create context");
|
||||||
let running = Arc::new(RwLock::new(true));
|
let running = Arc::new(RwLock::new(true));
|
||||||
let info = dc_get_info(&ctx);
|
let info = dc_get_info(&ctx);
|
||||||
let info_s = CStr::from_ptr(info);
|
let info_s = CStr::from_ptr(info);
|
||||||
@@ -73,13 +76,6 @@ fn main() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let dir = tempdir().unwrap();
|
|
||||||
let dbfile = dir.path().join("db.sqlite");
|
|
||||||
|
|
||||||
println!("opening database {:?}", dbfile);
|
|
||||||
|
|
||||||
assert!(dc_open(&ctx, dbfile.to_str().unwrap(), None));
|
|
||||||
|
|
||||||
println!("configuring");
|
println!("configuring");
|
||||||
let args = std::env::args().collect::<Vec<String>>();
|
let args = std::env::args().collect::<Vec<String>>();
|
||||||
assert_eq!(args.len(), 2, "missing password");
|
assert_eq!(args.len(), 2, "missing password");
|
||||||
|
|||||||
@@ -17,11 +17,19 @@ def test_callback_None2int():
|
|||||||
clear_context_callback(ctx)
|
clear_context_callback(ctx)
|
||||||
|
|
||||||
|
|
||||||
def test_dc_close_events():
|
def test_dc_close_events(tmpdir):
|
||||||
ctx = capi.lib.dc_context_new(capi.lib.py_dc_callback, ffi.NULL, ffi.NULL)
|
ctx = ffi.gc(
|
||||||
|
capi.lib.dc_context_new(capi.lib.py_dc_callback, ffi.NULL, ffi.NULL),
|
||||||
|
lib.dc_context_unref,
|
||||||
|
)
|
||||||
evlog = EventLogger(ctx)
|
evlog = EventLogger(ctx)
|
||||||
evlog.set_timeout(5)
|
evlog.set_timeout(5)
|
||||||
set_context_callback(ctx, lambda ctx, evt_name, data1, data2: evlog(evt_name, data1, data2))
|
set_context_callback(
|
||||||
|
ctx,
|
||||||
|
lambda ctx, evt_name, data1, data2: evlog(evt_name, data1, data2),
|
||||||
|
)
|
||||||
|
p = tmpdir.join("hello.db")
|
||||||
|
lib.dc_open(ctx, p.strpath.encode("ascii"), ffi.NULL)
|
||||||
capi.lib.dc_close(ctx)
|
capi.lib.dc_close(ctx)
|
||||||
|
|
||||||
def find(info_string):
|
def find(info_string):
|
||||||
|
|||||||
236
src/context.rs
236
src/context.rs
@@ -1,3 +1,5 @@
|
|||||||
|
use std::ffi::OsString;
|
||||||
|
use std::path::Path;
|
||||||
use std::sync::{Arc, Condvar, Mutex, RwLock};
|
use std::sync::{Arc, Condvar, Mutex, RwLock};
|
||||||
|
|
||||||
use crate::chat::*;
|
use crate::chat::*;
|
||||||
@@ -7,6 +9,7 @@ use crate::dc_loginparam::*;
|
|||||||
use crate::dc_move::*;
|
use crate::dc_move::*;
|
||||||
use crate::dc_receive_imf::*;
|
use crate::dc_receive_imf::*;
|
||||||
use crate::dc_tools::*;
|
use crate::dc_tools::*;
|
||||||
|
use crate::error::*;
|
||||||
use crate::imap::*;
|
use crate::imap::*;
|
||||||
use crate::job::*;
|
use crate::job::*;
|
||||||
use crate::job_thread::JobThread;
|
use crate::job_thread::JobThread;
|
||||||
@@ -22,6 +25,15 @@ use std::ptr;
|
|||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
/// Callback function type for [Context].
|
||||||
|
///
|
||||||
|
/// @param context The context object as returned by dc_context_new().
|
||||||
|
/// @param event one of the @ref DC_EVENT constants
|
||||||
|
/// @param data1 depends on the event parameter
|
||||||
|
/// @param data2 depends on the event parameter
|
||||||
|
/// @return return 0 unless stated otherwise in the event parameter documentation
|
||||||
|
pub type ContextCallback = dyn Fn(&Context, Event, uintptr_t, uintptr_t) -> uintptr_t;
|
||||||
|
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
pub userdata: *mut libc::c_void,
|
pub userdata: *mut libc::c_void,
|
||||||
pub dbfile: Arc<RwLock<Option<PathBuf>>>,
|
pub dbfile: Arc<RwLock<Option<PathBuf>>>,
|
||||||
@@ -35,7 +47,7 @@ pub struct Context {
|
|||||||
pub smtp: Arc<Mutex<Smtp>>,
|
pub smtp: Arc<Mutex<Smtp>>,
|
||||||
pub smtp_state: Arc<(Mutex<SmtpState>, Condvar)>,
|
pub smtp_state: Arc<(Mutex<SmtpState>, Condvar)>,
|
||||||
pub oauth2_critical: Arc<Mutex<()>>,
|
pub oauth2_critical: Arc<Mutex<()>>,
|
||||||
pub cb: Option<dc_callback_t>,
|
pub cb: Box<ContextCallback>,
|
||||||
pub os_name: Option<String>,
|
pub os_name: Option<String>,
|
||||||
pub cmdline_sel_chat_id: Arc<RwLock<u32>>,
|
pub cmdline_sel_chat_id: Arc<RwLock<u32>>,
|
||||||
pub bob: Arc<RwLock<BobStatus>>,
|
pub bob: Arc<RwLock<BobStatus>>,
|
||||||
@@ -55,6 +67,76 @@ pub struct RunningState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
|
pub fn new(cb: Box<ContextCallback>, os_name: String, dbfile: PathBuf) -> Result<Context> {
|
||||||
|
let mut blob_fname = OsString::new();
|
||||||
|
blob_fname.push(dbfile.file_name().unwrap_or_default());
|
||||||
|
blob_fname.push("-blobs");
|
||||||
|
let blobdir = dbfile.with_file_name(blob_fname);
|
||||||
|
if !blobdir.exists() {
|
||||||
|
std::fs::create_dir_all(&blobdir)?;
|
||||||
|
}
|
||||||
|
Context::with_blobdir(cb, os_name, dbfile, &blobdir)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_blobdir(
|
||||||
|
cb: Box<ContextCallback>,
|
||||||
|
os_name: String,
|
||||||
|
dbfile: PathBuf,
|
||||||
|
blobdir: &Path,
|
||||||
|
) -> Result<Context> {
|
||||||
|
if !blobdir.is_dir() {
|
||||||
|
return Err(format_err!("Blobdir does not exist: {}", blobdir.display()));
|
||||||
|
}
|
||||||
|
let ctx = Context {
|
||||||
|
blobdir: Arc::new(RwLock::new(unsafe { blobdir.strdup() })),
|
||||||
|
dbfile: Arc::new(RwLock::new(Some(dbfile))),
|
||||||
|
inbox: Arc::new(RwLock::new({
|
||||||
|
Imap::new(
|
||||||
|
cb_get_config,
|
||||||
|
cb_set_config,
|
||||||
|
cb_precheck_imf,
|
||||||
|
cb_receive_imf,
|
||||||
|
)
|
||||||
|
})),
|
||||||
|
userdata: std::ptr::null_mut(),
|
||||||
|
cb,
|
||||||
|
os_name: Some(os_name),
|
||||||
|
running_state: Arc::new(RwLock::new(Default::default())),
|
||||||
|
sql: Sql::new(),
|
||||||
|
smtp: Arc::new(Mutex::new(Smtp::new())),
|
||||||
|
smtp_state: Arc::new((Mutex::new(Default::default()), Condvar::new())),
|
||||||
|
oauth2_critical: Arc::new(Mutex::new(())),
|
||||||
|
bob: Arc::new(RwLock::new(Default::default())),
|
||||||
|
last_smeared_timestamp: Arc::new(RwLock::new(0)),
|
||||||
|
cmdline_sel_chat_id: Arc::new(RwLock::new(0)),
|
||||||
|
sentbox_thread: Arc::new(RwLock::new(JobThread::new(
|
||||||
|
"SENTBOX",
|
||||||
|
"configured_sentbox_folder",
|
||||||
|
Imap::new(
|
||||||
|
cb_get_config,
|
||||||
|
cb_set_config,
|
||||||
|
cb_precheck_imf,
|
||||||
|
cb_receive_imf,
|
||||||
|
),
|
||||||
|
))),
|
||||||
|
mvbox_thread: Arc::new(RwLock::new(JobThread::new(
|
||||||
|
"MVBOX",
|
||||||
|
"configured_mvbox_folder",
|
||||||
|
Imap::new(
|
||||||
|
cb_get_config,
|
||||||
|
cb_set_config,
|
||||||
|
cb_precheck_imf,
|
||||||
|
cb_receive_imf,
|
||||||
|
),
|
||||||
|
))),
|
||||||
|
probe_imap_network: Arc::new(RwLock::new(false)),
|
||||||
|
perform_inbox_jobs_needed: Arc::new(RwLock::new(false)),
|
||||||
|
generating_key_mutex: Mutex::new(()),
|
||||||
|
};
|
||||||
|
ctx.sql.open(&ctx, &ctx.dbfile.read().unwrap().as_ref().unwrap(), 0)?;
|
||||||
|
Ok(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn has_dbfile(&self) -> bool {
|
pub fn has_dbfile(&self) -> bool {
|
||||||
self.get_dbfile().is_some()
|
self.get_dbfile().is_some()
|
||||||
}
|
}
|
||||||
@@ -74,11 +156,7 @@ impl Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn call_cb(&self, event: Event, data1: uintptr_t, data2: uintptr_t) -> uintptr_t {
|
pub fn call_cb(&self, event: Event, data1: uintptr_t, data2: uintptr_t) -> uintptr_t {
|
||||||
if let Some(cb) = self.cb {
|
(*self.cb)(self, event, data1, data2)
|
||||||
unsafe { cb(self, event, data1, data2) }
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,60 +193,6 @@ pub struct SmtpState {
|
|||||||
pub probe_network: bool,
|
pub probe_network: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// create/open/config/information
|
|
||||||
pub fn dc_context_new(
|
|
||||||
cb: Option<dc_callback_t>,
|
|
||||||
userdata: *mut libc::c_void,
|
|
||||||
os_name: Option<String>,
|
|
||||||
) -> Context {
|
|
||||||
Context {
|
|
||||||
blobdir: Arc::new(RwLock::new(std::ptr::null_mut())),
|
|
||||||
dbfile: Arc::new(RwLock::new(None)),
|
|
||||||
inbox: Arc::new(RwLock::new({
|
|
||||||
Imap::new(
|
|
||||||
cb_get_config,
|
|
||||||
cb_set_config,
|
|
||||||
cb_precheck_imf,
|
|
||||||
cb_receive_imf,
|
|
||||||
)
|
|
||||||
})),
|
|
||||||
userdata,
|
|
||||||
cb,
|
|
||||||
os_name,
|
|
||||||
running_state: Arc::new(RwLock::new(Default::default())),
|
|
||||||
sql: Sql::new(),
|
|
||||||
smtp: Arc::new(Mutex::new(Smtp::new())),
|
|
||||||
smtp_state: Arc::new((Mutex::new(Default::default()), Condvar::new())),
|
|
||||||
oauth2_critical: Arc::new(Mutex::new(())),
|
|
||||||
bob: Arc::new(RwLock::new(Default::default())),
|
|
||||||
last_smeared_timestamp: Arc::new(RwLock::new(0)),
|
|
||||||
cmdline_sel_chat_id: Arc::new(RwLock::new(0)),
|
|
||||||
sentbox_thread: Arc::new(RwLock::new(JobThread::new(
|
|
||||||
"SENTBOX",
|
|
||||||
"configured_sentbox_folder",
|
|
||||||
Imap::new(
|
|
||||||
cb_get_config,
|
|
||||||
cb_set_config,
|
|
||||||
cb_precheck_imf,
|
|
||||||
cb_receive_imf,
|
|
||||||
),
|
|
||||||
))),
|
|
||||||
mvbox_thread: Arc::new(RwLock::new(JobThread::new(
|
|
||||||
"MVBOX",
|
|
||||||
"configured_mvbox_folder",
|
|
||||||
Imap::new(
|
|
||||||
cb_get_config,
|
|
||||||
cb_set_config,
|
|
||||||
cb_precheck_imf,
|
|
||||||
cb_receive_imf,
|
|
||||||
),
|
|
||||||
))),
|
|
||||||
probe_imap_network: Arc::new(RwLock::new(false)),
|
|
||||||
perform_inbox_jobs_needed: Arc::new(RwLock::new(false)),
|
|
||||||
generating_key_mutex: Mutex::new(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn cb_receive_imf(
|
unsafe fn cb_receive_imf(
|
||||||
context: &Context,
|
context: &Context,
|
||||||
imf_raw_not_terminated: *const libc::c_char,
|
imf_raw_not_terminated: *const libc::c_char,
|
||||||
@@ -287,39 +311,10 @@ pub unsafe fn dc_close(context: &Context) {
|
|||||||
*blobdir = ptr::null_mut();
|
*blobdir = ptr::null_mut();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn dc_is_open(context: &Context) -> libc::c_int {
|
|
||||||
context.sql.is_open() as libc::c_int
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn dc_get_userdata(context: &mut Context) -> *mut libc::c_void {
|
pub unsafe fn dc_get_userdata(context: &mut Context) -> *mut libc::c_void {
|
||||||
context.userdata as *mut _
|
context.userdata as *mut _
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn dc_open(context: &Context, dbfile: &str, blobdir: Option<&str>) -> bool {
|
|
||||||
let mut success = false;
|
|
||||||
if 0 != dc_is_open(context) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*context.dbfile.write().unwrap() = Some(PathBuf::from(dbfile));
|
|
||||||
if blobdir.is_some() && !blobdir.unwrap().is_empty() {
|
|
||||||
let dir = dc_ensure_no_slash_safe(blobdir.unwrap()).strdup();
|
|
||||||
*context.blobdir.write().unwrap() = dir;
|
|
||||||
} else {
|
|
||||||
let dir = dbfile.to_string() + "-blobs";
|
|
||||||
dc_create_folder(context, &dir);
|
|
||||||
*context.blobdir.write().unwrap() = dir.strdup();
|
|
||||||
}
|
|
||||||
// Create/open sqlite database, this may already use the blobdir
|
|
||||||
let dbfile_path = std::path::Path::new(dbfile);
|
|
||||||
if context.sql.open(context, dbfile_path, 0) {
|
|
||||||
success = true
|
|
||||||
}
|
|
||||||
if !success {
|
|
||||||
dc_close(context);
|
|
||||||
}
|
|
||||||
success
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn dc_get_blobdir(context: &Context) -> *mut libc::c_char {
|
pub unsafe fn dc_get_blobdir(context: &Context) -> *mut libc::c_char {
|
||||||
dc_strdup(*context.blobdir.clone().read().unwrap())
|
dc_strdup(*context.blobdir.clone().read().unwrap())
|
||||||
}
|
}
|
||||||
@@ -577,19 +572,70 @@ pub fn dc_is_mvbox(context: &Context, folder_name: impl AsRef<str>) -> bool {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
use crate::test_utils::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_wrong_db() {
|
||||||
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
|
let dbfile = tmp.path().join("db.sqlite");
|
||||||
|
std::fs::write(&dbfile, b"123").unwrap();
|
||||||
|
let res = Context::new(Box::new(|_, _, _, _| 0), "FakeOs".into(), dbfile);
|
||||||
|
assert!(res.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_blobdir_exists() {
|
||||||
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
|
let dbfile = tmp.path().join("db.sqlite");
|
||||||
|
Context::new(Box::new(|_, _, _, _| 0), "FakeOS".into(), dbfile).unwrap();
|
||||||
|
let blobdir = tmp.path().join("db.sqlite-blobs");
|
||||||
|
assert!(blobdir.is_dir());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_wrong_blogdir() {
|
||||||
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
|
let dbfile = tmp.path().join("db.sqlite");
|
||||||
|
let blobdir = tmp.path().join("db.sqlite-blobs");
|
||||||
|
std::fs::write(&blobdir, b"123").unwrap();
|
||||||
|
let res = Context::new(Box::new(|_, _, _, _| 0), "FakeOS".into(), dbfile);
|
||||||
|
assert!(res.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sqlite_parent_not_exists() {
|
||||||
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
|
let subdir = tmp.path().join("subdir");
|
||||||
|
let dbfile = subdir.join("db.sqlite");
|
||||||
|
let dbfile2 = dbfile.clone();
|
||||||
|
Context::new(Box::new(|_, _, _, _| 0), "FakeOS".into(), dbfile).unwrap();
|
||||||
|
assert!(subdir.is_dir());
|
||||||
|
assert!(dbfile2.is_file());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_with_blobdir_not_exists() {
|
||||||
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
|
let dbfile = tmp.path().join("db.sqlite");
|
||||||
|
let blobdir = tmp.path().join("blobs");
|
||||||
|
let res =
|
||||||
|
Context::with_blobdir(Box::new(|_, _, _, _| 0), "FakeOS".into(), dbfile, &blobdir);
|
||||||
|
assert!(res.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_crashes_on_context_deref() {
|
fn no_crashes_on_context_deref() {
|
||||||
let ctx = dc_context_new(None, std::ptr::null_mut(), Some("Test OS".into()));
|
let t = dummy_context();
|
||||||
std::mem::drop(ctx);
|
std::mem::drop(t.ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_context_double_close() {
|
fn test_context_double_close() {
|
||||||
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
|
let t = dummy_context();
|
||||||
unsafe {
|
unsafe {
|
||||||
dc_close(&ctx);
|
dc_close(&t.ctx);
|
||||||
dc_close(&ctx);
|
dc_close(&t.ctx);
|
||||||
}
|
}
|
||||||
std::mem::drop(ctx);
|
std::mem::drop(t.ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ pub unsafe fn dc_imex_has_backup(
|
|||||||
let name = name.to_string_lossy();
|
let name = name.to_string_lossy();
|
||||||
if name.starts_with("delta-chat") && name.ends_with(".bak") {
|
if name.starts_with("delta-chat") && name.ends_with(".bak") {
|
||||||
let sql = Sql::new();
|
let sql = Sql::new();
|
||||||
if sql.open(context, &path, 0x1) {
|
if sql.open(context, &path, 0x1).is_ok() {
|
||||||
let curr_backup_time =
|
let curr_backup_time =
|
||||||
sql.get_config_int(context, "backup_time")
|
sql.get_config_int(context, "backup_time")
|
||||||
.unwrap_or_default() as u64;
|
.unwrap_or_default() as u64;
|
||||||
@@ -617,9 +617,10 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
|
|||||||
}
|
}
|
||||||
/* error already logged */
|
/* error already logged */
|
||||||
/* re-open copied database file */
|
/* re-open copied database file */
|
||||||
if !context
|
if context
|
||||||
.sql
|
.sql
|
||||||
.open(&context, &context.get_dbfile().unwrap(), 0)
|
.open(&context, &context.get_dbfile().unwrap(), 0)
|
||||||
|
.is_err()
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -710,7 +711,7 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
|
|||||||
/* the FILE_PROGRESS macro calls the callback with the permille of files processed.
|
/* the FILE_PROGRESS macro calls the callback with the permille of files processed.
|
||||||
The macro avoids weird values of 0% or 100% while still working. */
|
The macro avoids weird values of 0% or 100% while still working. */
|
||||||
// TODO should return bool /rtn
|
// TODO should return bool /rtn
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case, unused_must_use)]
|
||||||
unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_int {
|
unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_int {
|
||||||
let mut current_block: u64;
|
let mut current_block: u64;
|
||||||
let mut success: libc::c_int = 0;
|
let mut success: libc::c_int = 0;
|
||||||
@@ -757,7 +758,7 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
|
|||||||
/* add all files as blobs to the database copy (this does not require the source to be locked, neigher the destination as it is used only here) */
|
/* add all files as blobs to the database copy (this does not require the source to be locked, neigher the destination as it is used only here) */
|
||||||
/*for logging only*/
|
/*for logging only*/
|
||||||
let sql = Sql::new();
|
let sql = Sql::new();
|
||||||
if sql.open(context, as_path(dest_pathNfilename), 0) {
|
if sql.open(context, as_path(dest_pathNfilename), 0).is_ok() {
|
||||||
if !sql.table_exists("backup_blobs") {
|
if !sql.table_exists("backup_blobs") {
|
||||||
if sql::execute(
|
if sql::execute(
|
||||||
context,
|
context,
|
||||||
@@ -1159,7 +1160,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_render_setup_file() {
|
fn test_render_setup_file() {
|
||||||
let t = test_context(Some(logging_cb));
|
let t = test_context(Some(Box::new(logging_cb)));
|
||||||
|
|
||||||
configure_alice_keypair(&t.ctx);
|
configure_alice_keypair(&t.ctx);
|
||||||
let msg = dc_render_setup_file(&t.ctx, "hello").unwrap();
|
let msg = dc_render_setup_file(&t.ctx, "hello").unwrap();
|
||||||
@@ -1177,14 +1178,9 @@ mod tests {
|
|||||||
assert!(msg.contains("-----END PGP MESSAGE-----\n"));
|
assert!(msg.contains("-----END PGP MESSAGE-----\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn ac_setup_msg_cb(
|
fn ac_setup_msg_cb(ctx: &Context, evt: Event, d1: uintptr_t, d2: uintptr_t) -> uintptr_t {
|
||||||
ctx: &Context,
|
|
||||||
evt: Event,
|
|
||||||
d1: uintptr_t,
|
|
||||||
d2: uintptr_t,
|
|
||||||
) -> uintptr_t {
|
|
||||||
if evt == Event::GET_STRING && d1 == StockMessage::AcSetupMsgBody.to_usize().unwrap() {
|
if evt == Event::GET_STRING && d1 == StockMessage::AcSetupMsgBody.to_usize().unwrap() {
|
||||||
"hello\r\nthere".strdup() as usize
|
unsafe { "hello\r\nthere".strdup() as usize }
|
||||||
} else {
|
} else {
|
||||||
logging_cb(ctx, evt, d1, d2)
|
logging_cb(ctx, evt, d1, d2)
|
||||||
}
|
}
|
||||||
@@ -1192,7 +1188,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_render_setup_file_newline_replace() {
|
fn test_render_setup_file_newline_replace() {
|
||||||
let t = test_context(Some(ac_setup_msg_cb));
|
let t = test_context(Some(Box::new(ac_setup_msg_cb)));
|
||||||
configure_alice_keypair(&t.ctx);
|
configure_alice_keypair(&t.ctx);
|
||||||
let msg = dc_render_setup_file(&t.ctx, "pw").unwrap();
|
let msg = dc_render_setup_file(&t.ctx, "pw").unwrap();
|
||||||
println!("{}", &msg);
|
println!("{}", &msg);
|
||||||
|
|||||||
@@ -8,14 +8,13 @@ use std::{fmt, fs, ptr};
|
|||||||
use chrono::{Local, TimeZone};
|
use chrono::{Local, TimeZone};
|
||||||
use mmime::mailimf_types::*;
|
use mmime::mailimf_types::*;
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
|
use itertools::max;
|
||||||
|
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
use crate::x::*;
|
use crate::x::*;
|
||||||
|
|
||||||
use itertools::max;
|
|
||||||
|
|
||||||
/* Some tools and enhancements to the used libraries, there should be
|
/* Some tools and enhancements to the used libraries, there should be
|
||||||
no references to Context and other "larger" classes here. */
|
no references to Context and other "larger" classes here. */
|
||||||
/* ** library-private **********************************************************/
|
/* ** library-private **********************************************************/
|
||||||
@@ -1228,7 +1227,7 @@ impl CStringExt for CString {}
|
|||||||
/// Rust strings to raw C strings. This can be clumsy to do correctly
|
/// Rust strings to raw C strings. This can be clumsy to do correctly
|
||||||
/// and the compiler sometimes allows it in an unsafe way. These
|
/// and the compiler sometimes allows it in an unsafe way. These
|
||||||
/// methods make it more succinct and help you get it right.
|
/// methods make it more succinct and help you get it right.
|
||||||
pub trait StrExt {
|
pub trait Strdup {
|
||||||
/// Allocate a new raw C `*char` version of this string.
|
/// Allocate a new raw C `*char` version of this string.
|
||||||
///
|
///
|
||||||
/// This allocates a new raw C string which must be freed using
|
/// This allocates a new raw C string which must be freed using
|
||||||
@@ -1245,13 +1244,26 @@ pub trait StrExt {
|
|||||||
unsafe fn strdup(&self) -> *mut libc::c_char;
|
unsafe fn strdup(&self) -> *mut libc::c_char;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsRef<str>> StrExt for T {
|
impl<T: AsRef<str>> Strdup for T {
|
||||||
unsafe fn strdup(&self) -> *mut libc::c_char {
|
unsafe fn strdup(&self) -> *mut libc::c_char {
|
||||||
let tmp = CString::yolo(self.as_ref());
|
let tmp = CString::yolo(self.as_ref());
|
||||||
dc_strdup(tmp.as_ptr())
|
dc_strdup(tmp.as_ptr())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Strdup for CStr {
|
||||||
|
unsafe fn strdup(&self) -> *mut libc::c_char {
|
||||||
|
dc_strdup(self.as_ptr())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Strdup for std::path::Path {
|
||||||
|
unsafe fn strdup(&self) -> *mut libc::c_char {
|
||||||
|
let tmp = self.to_c_string().unwrap();
|
||||||
|
dc_strdup(tmp.as_ptr())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_string(s: *const libc::c_char) -> String {
|
pub fn to_string(s: *const libc::c_char) -> String {
|
||||||
if s.is_null() {
|
if s.is_null() {
|
||||||
return "".into();
|
return "".into();
|
||||||
|
|||||||
22
src/sql.rs
22
src/sql.rs
@@ -38,16 +38,13 @@ impl Sql {
|
|||||||
info!(context, 0, "Database closed.");
|
info!(context, 0, "Database closed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// return true on success, false on failure
|
pub fn open(
|
||||||
pub fn open(&self, context: &Context, dbfile: &std::path::Path, flags: libc::c_int) -> bool {
|
&self,
|
||||||
match open(context, self, dbfile, flags) {
|
context: &Context,
|
||||||
Ok(_) => true,
|
dbfile: &std::path::Path,
|
||||||
Err(Error::SqlAlreadyOpen) => false,
|
flags: libc::c_int,
|
||||||
Err(_) => {
|
) -> Result<()> {
|
||||||
self.close(context);
|
open(context, self, dbfile, flags)
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute<P>(&self, sql: &str, params: P) -> Result<usize>
|
pub fn execute<P>(&self, sql: &str, params: P) -> Result<usize>
|
||||||
@@ -227,7 +224,10 @@ impl Sql {
|
|||||||
match res {
|
match res {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!(context, 0, "set_config(): Cannot change value. {:?}", &err);
|
error!(
|
||||||
|
context,
|
||||||
|
0, "set_config(): Cannot change value for {}: {:?}", key, &err
|
||||||
|
);
|
||||||
Err(err.into())
|
Err(err.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
36
src/stock.rs
36
src/stock.rs
@@ -232,10 +232,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::test_utils::*;
|
use crate::test_utils::*;
|
||||||
|
|
||||||
use std::ffi::CString;
|
|
||||||
|
|
||||||
use crate::constants::DC_CONTACT_ID_SELF;
|
use crate::constants::DC_CONTACT_ID_SELF;
|
||||||
use crate::context::dc_context_new;
|
|
||||||
use crate::types::uintptr_t;
|
use crate::types::uintptr_t;
|
||||||
|
|
||||||
use num_traits::ToPrimitive;
|
use num_traits::ToPrimitive;
|
||||||
@@ -253,19 +250,18 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stock_str() {
|
fn test_stock_str() {
|
||||||
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
|
let t = dummy_context();
|
||||||
assert_eq!(ctx.stock_str(StockMessage::NoMessages), "No messages.");
|
assert_eq!(t.ctx.stock_str(StockMessage::NoMessages), "No messages.");
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn test_stock_str_no_fallback_cb(
|
fn test_stock_str_no_fallback_cb(
|
||||||
_ctx: &Context,
|
_ctx: &Context,
|
||||||
evt: Event,
|
evt: Event,
|
||||||
d1: uintptr_t,
|
d1: uintptr_t,
|
||||||
_d2: uintptr_t,
|
_d2: uintptr_t,
|
||||||
) -> uintptr_t {
|
) -> uintptr_t {
|
||||||
if evt == Event::GET_STRING && d1 == StockMessage::NoMessages.to_usize().unwrap() {
|
if evt == Event::GET_STRING && d1 == StockMessage::NoMessages.to_usize().unwrap() {
|
||||||
let tmp = CString::new("Hello there").unwrap();
|
unsafe { "Hello there".strdup() as usize }
|
||||||
dc_strdup(tmp.as_ptr()) as usize
|
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
@@ -273,16 +269,16 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stock_str_no_fallback() {
|
fn test_stock_str_no_fallback() {
|
||||||
let t = test_context(Some(test_stock_str_no_fallback_cb));
|
let t = test_context(Some(Box::new(test_stock_str_no_fallback_cb)));
|
||||||
assert_eq!(t.ctx.stock_str(StockMessage::NoMessages), "Hello there");
|
assert_eq!(t.ctx.stock_str(StockMessage::NoMessages), "Hello there");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stock_string_repl_str() {
|
fn test_stock_string_repl_str() {
|
||||||
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
|
let t = dummy_context();
|
||||||
// uses %1$s substitution
|
// uses %1$s substitution
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ctx.stock_string_repl_str(StockMessage::Member, "42"),
|
t.ctx.stock_string_repl_str(StockMessage::Member, "42"),
|
||||||
"42 member(s)"
|
"42 member(s)"
|
||||||
);
|
);
|
||||||
// We have no string using %1$d to test...
|
// We have no string using %1$d to test...
|
||||||
@@ -290,36 +286,38 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stock_string_repl_int() {
|
fn test_stock_string_repl_int() {
|
||||||
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
|
let t = dummy_context();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ctx.stock_string_repl_int(StockMessage::Member, 42),
|
t.ctx.stock_string_repl_int(StockMessage::Member, 42),
|
||||||
"42 member(s)"
|
"42 member(s)"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stock_string_repl_str2() {
|
fn test_stock_string_repl_str2() {
|
||||||
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
|
let t = dummy_context();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ctx.stock_string_repl_str2(StockMessage::ServerResponse, "foo", "bar"),
|
t.ctx
|
||||||
|
.stock_string_repl_str2(StockMessage::ServerResponse, "foo", "bar"),
|
||||||
"Response from foo: bar"
|
"Response from foo: bar"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stock_system_msg_simple() {
|
fn test_stock_system_msg_simple() {
|
||||||
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
|
let t = dummy_context();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ctx.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0),
|
t.ctx
|
||||||
|
.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0),
|
||||||
"Location streaming enabled."
|
"Location streaming enabled."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stock_system_msg_add_member_by_me() {
|
fn test_stock_system_msg_add_member_by_me() {
|
||||||
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
|
let t = dummy_context();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ctx.stock_system_msg(
|
t.ctx.stock_system_msg(
|
||||||
StockMessage::MsgAddMember,
|
StockMessage::MsgAddMember,
|
||||||
"alice@example.com",
|
"alice@example.com",
|
||||||
"",
|
"",
|
||||||
|
|||||||
@@ -9,9 +9,8 @@ use tempfile::{tempdir, TempDir};
|
|||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::constants::{Event, KeyType};
|
use crate::constants::{Event, KeyType};
|
||||||
use crate::context::{dc_context_new, dc_open, Context};
|
use crate::context::{Context, ContextCallback};
|
||||||
use crate::key;
|
use crate::key;
|
||||||
use crate::types::dc_callback_t;
|
|
||||||
|
|
||||||
/// A Context and temporary directory.
|
/// A Context and temporary directory.
|
||||||
///
|
///
|
||||||
@@ -28,18 +27,15 @@ pub struct TestContext {
|
|||||||
/// "db.sqlite" in the [TestContext.dir] directory.
|
/// "db.sqlite" in the [TestContext.dir] directory.
|
||||||
///
|
///
|
||||||
/// [Context]: crate::context::Context
|
/// [Context]: crate::context::Context
|
||||||
pub fn test_context(cb: Option<dc_callback_t>) -> TestContext {
|
pub fn test_context(callback: Option<Box<ContextCallback>>) -> TestContext {
|
||||||
unsafe {
|
let dir = tempdir().unwrap();
|
||||||
let mut ctx = dc_context_new(cb, std::ptr::null_mut(), None);
|
let dbfile = dir.path().join("db.sqlite");
|
||||||
let dir = tempdir().unwrap();
|
let cb: Box<ContextCallback> = match callback {
|
||||||
let dbfile = dir.path().join("db.sqlite");
|
Some(cb) => cb,
|
||||||
assert!(
|
None => Box::new(|_, _, _, _| 0),
|
||||||
dc_open(&mut ctx, dbfile.to_str().unwrap(), None),
|
};
|
||||||
"Failed to open {}",
|
let ctx = Context::new(cb, "FakeOs".into(), dbfile).unwrap();
|
||||||
dbfile.display(),
|
TestContext { ctx: ctx, dir: dir }
|
||||||
);
|
|
||||||
TestContext { ctx: ctx, dir: dir }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a dummy [TestContext].
|
/// Return a dummy [TestContext].
|
||||||
@@ -51,13 +47,8 @@ pub fn dummy_context() -> TestContext {
|
|||||||
test_context(None)
|
test_context(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe extern "C" fn logging_cb(
|
pub fn logging_cb(_ctx: &Context, evt: Event, _d1: uintptr_t, d2: uintptr_t) -> uintptr_t {
|
||||||
_ctx: &Context,
|
let to_str = unsafe { |x| CStr::from_ptr(x as *const libc::c_char).to_str().unwrap() };
|
||||||
evt: Event,
|
|
||||||
_d1: uintptr_t,
|
|
||||||
d2: uintptr_t,
|
|
||||||
) -> uintptr_t {
|
|
||||||
let to_str = |x| CStr::from_ptr(x as *const libc::c_char).to_str().unwrap();
|
|
||||||
match evt {
|
match evt {
|
||||||
Event::INFO => println!("I: {}", to_str(d2)),
|
Event::INFO => println!("I: {}", to_str(d2)),
|
||||||
Event::WARNING => println!("W: {}", to_str(d2)),
|
Event::WARNING => println!("W: {}", to_str(d2)),
|
||||||
|
|||||||
12
src/types.rs
12
src/types.rs
@@ -1,21 +1,9 @@
|
|||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
use crate::constants::Event;
|
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
|
|
||||||
pub use mmime::clist::*;
|
pub use mmime::clist::*;
|
||||||
pub use rusqlite::ffi::*;
|
pub use rusqlite::ffi::*;
|
||||||
|
|
||||||
/// Callback function that should be given to dc_context_new().
|
|
||||||
///
|
|
||||||
/// @memberof Context
|
|
||||||
/// @param context The context object as returned by dc_context_new().
|
|
||||||
/// @param event one of the @ref DC_EVENT constants
|
|
||||||
/// @param data1 depends on the event parameter
|
|
||||||
/// @param data2 depends on the event parameter
|
|
||||||
/// @return return 0 unless stated otherwise in the event parameter documentation
|
|
||||||
pub type dc_callback_t =
|
|
||||||
unsafe extern "C" fn(_: &Context, _: Event, _: uintptr_t, _: uintptr_t) -> uintptr_t;
|
|
||||||
|
|
||||||
pub type dc_receive_imf_t = unsafe fn(
|
pub type dc_receive_imf_t = unsafe fn(
|
||||||
_: &Context,
|
_: &Context,
|
||||||
_: *const libc::c_char,
|
_: *const libc::c_char,
|
||||||
|
|||||||
244
tests/stress.rs
244
tests/stress.rs
@@ -33,103 +33,100 @@ static mut S_EM_SETUPFILE: *const libc::c_char =
|
|||||||
as *const u8 as *const libc::c_char;
|
as *const u8 as *const libc::c_char;
|
||||||
|
|
||||||
unsafe fn stress_functions(context: &Context) {
|
unsafe fn stress_functions(context: &Context) {
|
||||||
if 0 != dc_is_open(context) {
|
if dc_file_exist(context, "$BLOBDIR/foobar")
|
||||||
if dc_file_exist(context, "$BLOBDIR/foobar")
|
|| dc_file_exist(context, "$BLOBDIR/dada")
|
||||||
|| dc_file_exist(context, "$BLOBDIR/dada")
|
|| dc_file_exist(context, "$BLOBDIR/foobar.dadada")
|
||||||
|| dc_file_exist(context, "$BLOBDIR/foobar.dadada")
|
|| dc_file_exist(context, "$BLOBDIR/foobar-folder")
|
||||||
|| dc_file_exist(context, "$BLOBDIR/foobar-folder")
|
{
|
||||||
{
|
dc_delete_file(context, "$BLOBDIR/foobar");
|
||||||
dc_delete_file(context, "$BLOBDIR/foobar");
|
dc_delete_file(context, "$BLOBDIR/dada");
|
||||||
dc_delete_file(context, "$BLOBDIR/dada");
|
dc_delete_file(context, "$BLOBDIR/foobar.dadada");
|
||||||
dc_delete_file(context, "$BLOBDIR/foobar.dadada");
|
dc_delete_file(context, "$BLOBDIR/foobar-folder");
|
||||||
dc_delete_file(context, "$BLOBDIR/foobar-folder");
|
|
||||||
}
|
|
||||||
dc_write_file(
|
|
||||||
context,
|
|
||||||
b"$BLOBDIR/foobar\x00" as *const u8 as *const libc::c_char,
|
|
||||||
b"content\x00" as *const u8 as *const libc::c_char as *const libc::c_void,
|
|
||||||
7i32 as size_t,
|
|
||||||
);
|
|
||||||
assert!(dc_file_exist(context, "$BLOBDIR/foobar",));
|
|
||||||
assert!(!dc_file_exist(context, "$BLOBDIR/foobarx"));
|
|
||||||
assert_eq!(
|
|
||||||
dc_get_filebytes(context, "$BLOBDIR/foobar",),
|
|
||||||
7i32 as libc::c_ulonglong
|
|
||||||
);
|
|
||||||
|
|
||||||
let abs_path: *mut libc::c_char = dc_mprintf(
|
|
||||||
b"%s/%s\x00" as *const u8 as *const libc::c_char,
|
|
||||||
context.get_blobdir(),
|
|
||||||
b"foobar\x00" as *const u8 as *const libc::c_char,
|
|
||||||
);
|
|
||||||
assert!(dc_is_blobdir_path(context, as_str(abs_path)));
|
|
||||||
assert!(dc_is_blobdir_path(context, "$BLOBDIR/fofo",));
|
|
||||||
assert!(!dc_is_blobdir_path(context, "/BLOBDIR/fofo",));
|
|
||||||
assert!(dc_file_exist(context, as_path(abs_path)));
|
|
||||||
free(abs_path as *mut libc::c_void);
|
|
||||||
assert!(dc_copy_file(context, "$BLOBDIR/foobar", "$BLOBDIR/dada",));
|
|
||||||
assert_eq!(dc_get_filebytes(context, "$BLOBDIR/dada",), 7);
|
|
||||||
|
|
||||||
let mut buf: *mut libc::c_void = 0 as *mut libc::c_void;
|
|
||||||
let mut buf_bytes: size_t = 0;
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
dc_read_file(
|
|
||||||
context,
|
|
||||||
b"$BLOBDIR/dada\x00" as *const u8 as *const libc::c_char,
|
|
||||||
&mut buf,
|
|
||||||
&mut buf_bytes,
|
|
||||||
),
|
|
||||||
1
|
|
||||||
);
|
|
||||||
assert_eq!(buf_bytes, 7);
|
|
||||||
assert_eq!(
|
|
||||||
std::str::from_utf8(std::slice::from_raw_parts(buf as *const u8, buf_bytes)).unwrap(),
|
|
||||||
"content"
|
|
||||||
);
|
|
||||||
|
|
||||||
free(buf as *mut _);
|
|
||||||
assert!(dc_delete_file(context, "$BLOBDIR/foobar"));
|
|
||||||
assert!(dc_delete_file(context, "$BLOBDIR/dada"));
|
|
||||||
assert!(dc_create_folder(context, "$BLOBDIR/foobar-folder"));
|
|
||||||
assert!(dc_file_exist(context, "$BLOBDIR/foobar-folder",));
|
|
||||||
assert!(dc_delete_file(context, "$BLOBDIR/foobar-folder"));
|
|
||||||
let fn0: *mut libc::c_char = dc_get_fine_pathNfilename(
|
|
||||||
context,
|
|
||||||
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
|
|
||||||
b"foobar.dadada\x00" as *const u8 as *const libc::c_char,
|
|
||||||
);
|
|
||||||
assert!(!fn0.is_null());
|
|
||||||
assert_eq!(
|
|
||||||
strcmp(
|
|
||||||
fn0,
|
|
||||||
b"$BLOBDIR/foobar.dadada\x00" as *const u8 as *const libc::c_char,
|
|
||||||
),
|
|
||||||
0
|
|
||||||
);
|
|
||||||
dc_write_file(
|
|
||||||
context,
|
|
||||||
fn0,
|
|
||||||
b"content\x00" as *const u8 as *const libc::c_char as *const libc::c_void,
|
|
||||||
7i32 as size_t,
|
|
||||||
);
|
|
||||||
let fn1: *mut libc::c_char = dc_get_fine_pathNfilename(
|
|
||||||
context,
|
|
||||||
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
|
|
||||||
b"foobar.dadada\x00" as *const u8 as *const libc::c_char,
|
|
||||||
);
|
|
||||||
assert!(!fn1.is_null());
|
|
||||||
assert_eq!(
|
|
||||||
strcmp(
|
|
||||||
fn1,
|
|
||||||
b"$BLOBDIR/foobar-1.dadada\x00" as *const u8 as *const libc::c_char,
|
|
||||||
),
|
|
||||||
0
|
|
||||||
);
|
|
||||||
assert!(dc_delete_file(context, as_path(fn0)));
|
|
||||||
free(fn0 as *mut libc::c_void);
|
|
||||||
free(fn1 as *mut libc::c_void);
|
|
||||||
}
|
}
|
||||||
|
dc_write_file(
|
||||||
|
context,
|
||||||
|
b"$BLOBDIR/foobar\x00" as *const u8 as *const libc::c_char,
|
||||||
|
b"content\x00" as *const u8 as *const libc::c_char as *const libc::c_void,
|
||||||
|
7i32 as size_t,
|
||||||
|
);
|
||||||
|
assert!(dc_file_exist(context, "$BLOBDIR/foobar",));
|
||||||
|
assert!(!dc_file_exist(context, "$BLOBDIR/foobarx"));
|
||||||
|
assert_eq!(
|
||||||
|
dc_get_filebytes(context, "$BLOBDIR/foobar",),
|
||||||
|
7i32 as libc::c_ulonglong
|
||||||
|
);
|
||||||
|
let abs_path: *mut libc::c_char = dc_mprintf(
|
||||||
|
b"%s/%s\x00" as *const u8 as *const libc::c_char,
|
||||||
|
context.get_blobdir(),
|
||||||
|
b"foobar\x00" as *const u8 as *const libc::c_char,
|
||||||
|
);
|
||||||
|
assert!(dc_is_blobdir_path(context, as_str(abs_path)));
|
||||||
|
assert!(dc_is_blobdir_path(context, "$BLOBDIR/fofo"));
|
||||||
|
assert!(!dc_is_blobdir_path(context, "/BLOBDIR/fofo"));
|
||||||
|
assert!(dc_file_exist(context, as_path(abs_path)));
|
||||||
|
free(abs_path as *mut libc::c_void);
|
||||||
|
assert!(dc_copy_file(context, "$BLOBDIR/foobar", "$BLOBDIR/dada",));
|
||||||
|
assert_eq!(dc_get_filebytes(context, "$BLOBDIR/dada",), 7);
|
||||||
|
|
||||||
|
let mut buf: *mut libc::c_void = 0 as *mut libc::c_void;
|
||||||
|
let mut buf_bytes: size_t = 0;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
dc_read_file(
|
||||||
|
context,
|
||||||
|
b"$BLOBDIR/dada\x00" as *const u8 as *const libc::c_char,
|
||||||
|
&mut buf,
|
||||||
|
&mut buf_bytes,
|
||||||
|
),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
assert_eq!(buf_bytes, 7);
|
||||||
|
assert_eq!(
|
||||||
|
std::str::from_utf8(std::slice::from_raw_parts(buf as *const u8, buf_bytes)).unwrap(),
|
||||||
|
"content"
|
||||||
|
);
|
||||||
|
|
||||||
|
free(buf as *mut _);
|
||||||
|
assert!(dc_delete_file(context, "$BLOBDIR/foobar"));
|
||||||
|
assert!(dc_delete_file(context, "$BLOBDIR/dada"));
|
||||||
|
assert!(dc_create_folder(context, "$BLOBDIR/foobar-folder"));
|
||||||
|
assert!(dc_file_exist(context, "$BLOBDIR/foobar-folder",));
|
||||||
|
assert!(dc_delete_file(context, "$BLOBDIR/foobar-folder"));
|
||||||
|
let fn0: *mut libc::c_char = dc_get_fine_pathNfilename(
|
||||||
|
context,
|
||||||
|
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
|
||||||
|
b"foobar.dadada\x00" as *const u8 as *const libc::c_char,
|
||||||
|
);
|
||||||
|
assert!(!fn0.is_null());
|
||||||
|
assert_eq!(
|
||||||
|
strcmp(
|
||||||
|
fn0,
|
||||||
|
b"$BLOBDIR/foobar.dadada\x00" as *const u8 as *const libc::c_char,
|
||||||
|
),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
dc_write_file(
|
||||||
|
context,
|
||||||
|
fn0,
|
||||||
|
b"content\x00" as *const u8 as *const libc::c_char as *const libc::c_void,
|
||||||
|
7i32 as size_t,
|
||||||
|
);
|
||||||
|
let fn1: *mut libc::c_char = dc_get_fine_pathNfilename(
|
||||||
|
context,
|
||||||
|
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
|
||||||
|
b"foobar.dadada\x00" as *const u8 as *const libc::c_char,
|
||||||
|
);
|
||||||
|
assert!(!fn1.is_null());
|
||||||
|
assert_eq!(
|
||||||
|
strcmp(
|
||||||
|
fn1,
|
||||||
|
b"$BLOBDIR/foobar-1.dadada\x00" as *const u8 as *const libc::c_char,
|
||||||
|
),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
assert!(dc_delete_file(context, as_path(fn0)));
|
||||||
|
free(fn0 as *mut libc::c_void);
|
||||||
|
free(fn1 as *mut libc::c_void);
|
||||||
|
|
||||||
let res = context.get_config(config::Config::SysConfigKeys).unwrap();
|
let res = context.get_config(config::Config::SysConfigKeys).unwrap();
|
||||||
|
|
||||||
@@ -626,12 +623,7 @@ fn test_encryption_decryption() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn cb(
|
fn cb(_context: &Context, _event: Event, _data1: uintptr_t, _data2: uintptr_t) -> uintptr_t {
|
||||||
_context: &Context,
|
|
||||||
_event: Event,
|
|
||||||
_data1: uintptr_t,
|
|
||||||
_data2: uintptr_t,
|
|
||||||
) -> uintptr_t {
|
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -641,15 +633,10 @@ struct TestContext {
|
|||||||
dir: TempDir,
|
dir: TempDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn create_test_context() -> TestContext {
|
fn create_test_context() -> TestContext {
|
||||||
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None);
|
|
||||||
let dir = tempdir().unwrap();
|
let dir = tempdir().unwrap();
|
||||||
let dbfile = dir.path().join("db.sqlite");
|
let dbfile = dir.path().join("db.sqlite");
|
||||||
assert!(
|
let ctx = Context::new(Box::new(cb), "FakeOs".into(), dbfile).unwrap();
|
||||||
dc_open(&mut ctx, dbfile.to_str().unwrap(), None),
|
|
||||||
"Failed to open {}",
|
|
||||||
dbfile.display()
|
|
||||||
);
|
|
||||||
TestContext { ctx: ctx, dir: dir }
|
TestContext { ctx: ctx, dir: dir }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -686,7 +673,7 @@ fn test_dc_mimeparser_with_context() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dc_get_oauth2_url() {
|
fn test_dc_get_oauth2_url() {
|
||||||
let ctx = unsafe { create_test_context() };
|
let ctx = create_test_context();
|
||||||
let addr = "dignifiedquire@gmail.com";
|
let addr = "dignifiedquire@gmail.com";
|
||||||
let redirect_uri = "chat.delta:/com.b44t.messenger";
|
let redirect_uri = "chat.delta:/com.b44t.messenger";
|
||||||
let res = dc_get_oauth2_url(&ctx.ctx, addr, redirect_uri);
|
let res = dc_get_oauth2_url(&ctx.ctx, addr, redirect_uri);
|
||||||
@@ -696,7 +683,7 @@ fn test_dc_get_oauth2_url() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dc_get_oauth2_addr() {
|
fn test_dc_get_oauth2_addr() {
|
||||||
let ctx = unsafe { create_test_context() };
|
let ctx = create_test_context();
|
||||||
let addr = "dignifiedquire@gmail.com";
|
let addr = "dignifiedquire@gmail.com";
|
||||||
let code = "fail";
|
let code = "fail";
|
||||||
let res = dc_get_oauth2_addr(&ctx.ctx, addr, code);
|
let res = dc_get_oauth2_addr(&ctx.ctx, addr, code);
|
||||||
@@ -706,7 +693,7 @@ fn test_dc_get_oauth2_addr() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dc_get_oauth2_token() {
|
fn test_dc_get_oauth2_token() {
|
||||||
let ctx = unsafe { create_test_context() };
|
let ctx = create_test_context();
|
||||||
let addr = "dignifiedquire@gmail.com";
|
let addr = "dignifiedquire@gmail.com";
|
||||||
let code = "fail";
|
let code = "fail";
|
||||||
let res = dc_get_oauth2_access_token(&ctx.ctx, addr, code, 0);
|
let res = dc_get_oauth2_access_token(&ctx.ctx, addr, code, 0);
|
||||||
@@ -724,20 +711,18 @@ fn test_stress_tests() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_contacts() {
|
fn test_get_contacts() {
|
||||||
unsafe {
|
let context = create_test_context();
|
||||||
let context = create_test_context();
|
let contacts = Contact::get_all(&context.ctx, 0, Some("some2")).unwrap();
|
||||||
let contacts = Contact::get_all(&context.ctx, 0, Some("some2")).unwrap();
|
assert_eq!(contacts.len(), 0);
|
||||||
assert_eq!(contacts.len(), 0);
|
|
||||||
|
|
||||||
let id = Contact::create(&context.ctx, "bob", "bob@mail.de").unwrap();
|
let id = Contact::create(&context.ctx, "bob", "bob@mail.de").unwrap();
|
||||||
assert_ne!(id, 0);
|
assert_ne!(id, 0);
|
||||||
|
|
||||||
let contacts = Contact::get_all(&context.ctx, 0, Some("bob")).unwrap();
|
let contacts = Contact::get_all(&context.ctx, 0, Some("bob")).unwrap();
|
||||||
assert_eq!(contacts.len(), 1);
|
assert_eq!(contacts.len(), 1);
|
||||||
|
|
||||||
let contacts = Contact::get_all(&context.ctx, 0, Some("alice")).unwrap();
|
let contacts = Contact::get_all(&context.ctx, 0, Some("alice")).unwrap();
|
||||||
assert_eq!(contacts.len(), 0);
|
assert_eq!(contacts.len(), 0);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -758,16 +743,3 @@ fn test_chat() {
|
|||||||
assert_eq!(chat2.name, chat.name);
|
assert_eq!(chat2.name, chat.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_wrong_db() {
|
|
||||||
unsafe {
|
|
||||||
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None);
|
|
||||||
let dir = tempdir().unwrap();
|
|
||||||
let dbfile = dir.path().join("db.sqlite");
|
|
||||||
std::fs::write(&dbfile, b"123").unwrap();
|
|
||||||
|
|
||||||
let res = dc_open(&mut ctx, dbfile.to_str().unwrap(), None);
|
|
||||||
assert!(!res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user