aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml4
-rw-r--r--Cargo.toml12
-rw-r--r--src/database.rs24
-rw-r--r--src/query.rs7
-rw-r--r--src/tags.rs12
-rw-r--r--tests/fixtures.rs216
-rw-r--r--tests/lib.rs11
-rw-r--r--tests/main.rs91
-rw-r--r--tests/test_database.rs337
9 files changed, 669 insertions, 45 deletions
diff --git a/.travis.yml b/.travis.yml
index ab21b0a..b4c0414 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,7 +9,11 @@ addons:
apt:
packages:
- libnotmuch-dev
+ - notmuch
+ - git
script:
- cargo build --no-default-features --verbose --all
+ # clone notmuch to have mail corpora
+ - git clone git://git.notmuchmail.org/git/notmuch /tmp/notmuch
- cargo test --no-default-features --verbose --all
diff --git a/Cargo.toml b/Cargo.toml
index 5b8191c..c88b049 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,6 +7,7 @@ repository = "https://github.com/vhdirk/notmuch-rs"
description = "Rust interface and bindings for notmuch"
license = "GPL-3.0+"
readme = "README.md"
+keywords = ["email", "notmuch"]
[badges]
travis-ci = { repository = "vhdirk/notmuch-rs" }
@@ -18,13 +19,18 @@ supercow = "0.1.0"
[dev-dependencies]
dirs = "1.0"
+tempfile = "3"
+gethostname = "0.2.0"
+maildir = "0.3.2"
+lettre = "0.9.2"
+lettre_email = "0.9.2"
[features]
v0_21 = []
v0_26 = ["v0_21"]
default = ["v0_26"]
-
[[test]]
-name = "main"
-harness = false
+name = "tests"
+path = "tests/lib.rs"
+harness = true \ No newline at end of file
diff --git a/src/database.rs b/src/database.rs
index 53ba76b..44ad040 100644
--- a/src/database.rs
+++ b/src/database.rs
@@ -6,6 +6,7 @@ use std::ptr;
use supercow::Supercow;
use libc;
+use std::cmp::{PartialEq, PartialOrd, Ordering};
use error::{Error, Result};
use ffi;
@@ -25,8 +26,6 @@ use utils::ScopedSupercow;
// Re-exported under database module for pretty namespacin'.
pub use ffi::DatabaseMode;
-#[derive(Copy, Clone, Debug)]
-pub struct Version(libc::c_uint);
#[derive(Clone, Debug)]
pub struct Revision {
@@ -34,6 +33,21 @@ pub struct Revision {
pub uuid: String,
}
+impl PartialEq for Revision {
+ fn eq(&self, other: &Revision) -> bool{
+ self.uuid == other.uuid && self.revision == other.revision
+ }
+}
+
+impl PartialOrd for Revision {
+ fn partial_cmp(&self, other: &Revision) -> Option<Ordering>{
+ if self.uuid != other.uuid {
+ return None;
+ }
+ self.revision.partial_cmp(&other.revision)
+ }
+}
+
#[derive(Debug)]
pub struct Database {
@@ -79,7 +93,7 @@ impl Database {
})
}
- pub fn close(&mut self) -> Result<()> {
+ pub fn close(&self) -> Result<()> {
unsafe { ffi::notmuch_database_close(self.ptr) }.as_result()?;
Ok(())
@@ -143,8 +157,8 @@ impl Database {
)
}
- pub fn version(&self) -> Version {
- Version(unsafe { ffi::notmuch_database_get_version(self.ptr) })
+ pub fn version(&self) -> u32 {
+ unsafe { ffi::notmuch_database_get_version(self.ptr) }
}
#[cfg(feature = "v0_21")]
diff --git a/src/query.rs b/src/query.rs
index 0ea5268..50b56e5 100644
--- a/src/query.rs
+++ b/src/query.rs
@@ -46,6 +46,13 @@ impl<'d> Query<'d> {
<Database as DatabaseExt>::create_query(db, query_string)
}
+ pub fn query_string(self: &Self) -> String {
+ let qstring = unsafe {
+ CStr::from_ptr(ffi::notmuch_query_get_query_string(self.ptr))
+ };
+ qstring.to_str().unwrap().to_string()
+ }
+
/// Specify the sorting desired for this query.
pub fn set_sort(self: &Self, sort: Sort) {
unsafe { ffi::notmuch_query_set_sort(self.ptr, sort.into()) }
diff --git a/src/tags.rs b/src/tags.rs
index 3fb1613..40a45c8 100644
--- a/src/tags.rs
+++ b/src/tags.rs
@@ -1,3 +1,4 @@
+use std::cmp::PartialEq;
use std::ffi::CStr;
use std::iter::Iterator;
use std::ops::Drop;
@@ -11,7 +12,7 @@ pub trait TagsOwner {}
pub struct Tags<'o, O> where
O: TagsOwner + 'o,
{
- ptr: *mut ffi::notmuch_tags_t,
+ pub(crate) ptr: *mut ffi::notmuch_tags_t,
marker: ScopedPhantomcow<'o, O>,
}
@@ -24,6 +25,15 @@ where
}
}
+impl<'o, O> PartialEq for Tags<'o, O>
+where
+ O: TagsOwner + 'o,
+{
+ fn eq(&self, other: &Self) -> bool {
+ self.ptr == other.ptr
+ }
+}
+
impl<'o, O> Tags<'o, O>
where
O: TagsOwner + 'o,
diff --git a/tests/fixtures.rs b/tests/fixtures.rs
new file mode 100644
index 0000000..8887da8
--- /dev/null
+++ b/tests/fixtures.rs
@@ -0,0 +1,216 @@
+extern crate dirs;
+extern crate tempfile;
+extern crate notmuch;
+extern crate gethostname;
+extern crate maildir;
+extern crate lettre;
+extern crate lettre_email;
+
+use std::ffi::OsStr;
+use std::io::{self, Result, Write};
+use std::fs::{self, File};
+use std::rc::Rc;
+use std::path::{Path, PathBuf};
+use tempfile::{tempdir, tempdir_in, Builder, TempDir};
+use std::net::ToSocketAddrs;
+use std::process::Command;
+use std::time::{SystemTime, UNIX_EPOCH};
+use maildir::Maildir;
+use lettre_email::{EmailBuilder, Header};
+use lettre::SendableEmail;
+
+
+pub fn timestamp_ms() -> u128 {
+ let start = SystemTime::now();
+ let time_since_epoch = start.duration_since(UNIX_EPOCH).unwrap();
+ time_since_epoch.as_millis()
+}
+
+// A basic test interface to a valid maildir directory.
+//
+// This creates a valid maildir and provides a simple mechanism to
+// deliver test emails to it. It also writes a notmuch-config file
+// in the top of the maildir.
+pub struct MailBox {
+ root_dir: TempDir,
+ idcount: u32,
+ maildir: Maildir
+}
+
+impl MailBox {
+
+ // Creates a new maildir fixture. Since this is only used for tests,
+ // may just panic of something is wrong
+ pub fn new() -> Self {
+
+ let root_dir = tempdir().unwrap();
+ let root_path = root_dir.path().to_path_buf();
+
+ let tmp_path = root_path.join("tmp");
+ fs::create_dir(&tmp_path).unwrap();
+
+ let cfg_fname = root_path.join("notmuch-config");
+ let mut cfg_file = File::create(cfg_fname).unwrap();
+ write!(cfg_file, r#"
+ [database]
+ path={tmppath}
+ [user]
+ name=Some Hacker
+ primary_email=dst@example.com
+ [new]
+ tags=unread;inbox;
+ ignore=
+ [search]
+ exclude_tags=deleted;spam;
+ [maildir]
+ synchronize_flags=true
+ [crypto]
+ gpg_path=gpg
+ "#, tmppath=root_path.to_string_lossy()).unwrap();
+
+ let maildir = Maildir::from(root_path.to_path_buf());
+ maildir.create_dirs().unwrap();
+
+ Self {
+ root_dir,
+ idcount: 0,
+ maildir
+ }
+ }
+
+ /// Return a new unique message ID
+ // fn next_msgid(&mut self) -> String{
+ // let hostname = gethostname::gethostname();
+ // let msgid = format!("{}@{}", self.idcount, hostname.to_string_lossy());
+ // self.idcount += 1;
+ // msgid
+ // }
+
+ pub fn path(&self) -> PathBuf
+ {
+ self.root_dir.path().into()
+ }
+
+ pub fn hostname(&self) -> String {
+ let hname = gethostname::gethostname();
+ hname.to_string_lossy().into()
+ }
+
+ /// Deliver a new mail message in the mbox.
+ /// This does only adds the message to maildir, does not insert it
+ /// into the notmuch database.
+ /// returns a tuple of (msgid, pathname).
+ pub fn deliver(&self,
+ subject: Option<String>,
+ body: Option<String>,
+ to: Option<String>,
+ from: Option<String>,
+ headers: Vec<(String, String)>,
+ is_new: bool, // Move to new dir or cur dir?
+ keywords: Option<Vec<String>>, // List of keywords or labels
+ seen: bool, // Seen flag (cur dir only)
+ replied: bool, // Replied flag (cur dir only)
+ flagged: bool) // Flagged flag (cur dir only)
+ -> Result<(String, PathBuf)>
+ {
+
+ let mut builder = EmailBuilder::new();
+
+ if let Some(val) = subject {
+ builder = builder.subject(val);
+ }
+ if let Some(val) = body {
+ builder = builder.text(val);
+ }
+ builder = match to {
+ Some(val) => builder.to(val),
+ None => builder.to(format!("to@{}.localhost", self.hostname()))
+ };
+ builder = match from {
+ Some(val) => builder.from(val),
+ None => builder.from(format!("from@{}.localhost", self.hostname()))
+ };
+
+ for h in headers.into_iter(){
+ let hdr: Header = h.into();
+ builder = builder.header(hdr);
+ }
+
+ let msg:SendableEmail = builder.build().unwrap().into();
+
+ // not sure why lettre doesn't add the host suffix itself
+ let msg_id = msg.message_id().to_string() + ".lettre@localhost";
+ let id = if is_new {
+ self.maildir.store_new(&msg.message_to_string().unwrap().as_bytes()).unwrap()
+ }else{
+ let mut flags = String::from("");
+ if flagged {
+ flags += "F";
+ }
+ if replied {
+ flags += "R";
+ }
+ if seen {
+ flags += "S";
+ }
+ self.maildir.store_cur_with_flags(&msg.message_to_string().unwrap().as_bytes(), flags.as_str()).unwrap()
+ };
+
+ let mut msgpath = self.path();
+ msgpath = if is_new {
+ msgpath.join("new")
+ } else {
+ msgpath.join("cur")
+ };
+
+ msgpath = msgpath.join(&id);
+
+ Ok((msg_id, msgpath))
+ }
+}
+
+impl Drop for MailBox {
+ fn drop(&mut self) {
+ }
+}
+
+
+#[derive(Clone, Debug)]
+pub struct NotmuchCommand {
+ maildir_path: PathBuf
+}
+
+impl NotmuchCommand {
+
+ /// Return a function which runs notmuch commands on our test maildir.
+ ///
+ /// This uses the notmuch-config file created by the ``maildir``
+ /// fixture.
+ pub fn new(maildir_path: &PathBuf) -> Self {
+ Self {
+ maildir_path: maildir_path.clone()
+ }
+ }
+
+ /// Run a notmuch comand.
+ ///
+ /// This function runs with a timeout error as many notmuch
+ /// commands may block if multiple processes are trying to open
+ /// the database in write-mode. It is all too easy to
+ /// accidentally do this in the unittests.
+ pub fn run<I, S>(&self, args: I) -> Result<()>
+ where
+ I: IntoIterator<Item=S>,
+ S: AsRef<OsStr>
+ {
+ let cfg_fname = self.maildir_path.join("notmuch-config");
+
+ Command::new("notmuch").env("NOTMUCH_CONFIG", &cfg_fname)
+ .args(args)
+ .status()?;
+ Ok(())
+ }
+
+}
+
+
diff --git a/tests/lib.rs b/tests/lib.rs
new file mode 100644
index 0000000..c5095f4
--- /dev/null
+++ b/tests/lib.rs
@@ -0,0 +1,11 @@
+extern crate dirs;
+extern crate tempfile;
+extern crate notmuch;
+extern crate gethostname;
+extern crate maildir;
+extern crate lettre;
+extern crate lettre_email;
+
+mod fixtures;
+mod test_database;
+
diff --git a/tests/main.rs b/tests/main.rs
index 9ad0a36..17db2bc 100644
--- a/tests/main.rs
+++ b/tests/main.rs
@@ -1,43 +1,62 @@
extern crate dirs;
+extern crate tempfile;
extern crate notmuch;
+extern crate gethostname;
+extern crate maildir;
+extern crate lettre;
+extern crate lettre_email;
use std::sync::Arc;
use notmuch::{Query, QueryExt};
-fn main() {
- let mut mail_path = dirs::home_dir().unwrap();
- mail_path.push(".mail");
-
- match notmuch::Database::open(
- &mail_path.to_str().unwrap().to_string(),
- notmuch::DatabaseMode::ReadOnly,
- ) {
- Ok(db) => {
- #[cfg(feature = "v0_21")]
- {
- let rev = db.revision();
- println!("db revision: {:?}", rev);
- }
- let query = {
- let dbr = Arc::new(db);
-
- notmuch::Query::create(dbr.clone(), &"".to_string()).unwrap()
- };
-
- // let mut threads = query.search_threads().unwrap();
-
- // let mut threads = db.create_query(&"".to_string()).unwrap().search_threads().unwrap();
-
- let mut threads = Arc::new(<Query as QueryExt>::search_threads(query).unwrap());
-
- for thread in Arc::get_mut(&mut threads).unwrap()
- {
- println!("thread {:?} {:?}", thread.subject(), thread.authors());
- }
- }
- Err(err) => {
- println!("Got error while trying to open db: {:?}", err);
- }
- }
-}
+mod fixtures;
+use fixtures::{MailBox, NotmuchCommand};
+
+
+
+
+// fn main() {
+// let mut mail_path = dirs::home_dir().unwrap();
+// mail_path.push(".mail");
+
+// let md = MailBox::new();
+// let nmcmd = NotMuchCommand::new(md.path());
+
+// match notmuch::Database::open(
+// &mail_path.to_str().unwrap().to_string(),
+// notmuch::DatabaseMode::ReadOnly,
+// ) {
+// Ok(db) => {
+// #[cfg(feature = "v0_21")]
+// {
+// let rev = db.revision();
+// println!("db revision: {:?}", rev);
+// }
+// let query = {
+// let dbr = Arc::new(db);
+
+// notmuch::Query::create(dbr.clone(), &"".to_string()).unwrap()
+// };
+
+// // let mut threads = query.search_threads().unwrap();
+
+// // let mut threads = db.create_query(&"".to_string()).unwrap().search_threads().unwrap();
+
+// let mut threads = Arc::new(<Query as QueryExt>::search_threads(query).unwrap());
+
+// for thread in Arc::get_mut(&mut threads).unwrap()
+// {
+// println!("thread {:?} {:?}", thread.subject(), thread.authors());
+// }
+// }
+// Err(err) => {
+// println!("Got error while trying to open db: {:?}", err);
+// }
+// }
+// }
+
+
+
+
+
diff --git a/tests/test_database.rs b/tests/test_database.rs
new file mode 100644
index 0000000..96d9bcc
--- /dev/null
+++ b/tests/test_database.rs
@@ -0,0 +1,337 @@
+use fixtures::{NotmuchCommand, MailBox};
+
+// #[test]
+// // fn test_config_pathname_default(){
+
+// // monkeypatch.delenv('NOTMUCH_CONFIG', raising=False)
+// // user = pathlib.Path('~/.notmuch-config').expanduser()
+// // assert dbmod._config_pathname() == user
+
+// // }
+
+mod database {
+
+ use super::*;
+
+ #[test]
+ fn test_create(){
+ let mailbox = MailBox::new();
+ let db = notmuch::Database::create(&mailbox.path());
+ assert!(db.is_ok());
+
+ assert!(mailbox.path().join(".notmuch/xapian").exists());
+ }
+
+ #[test]
+ fn test_create_already_open(){
+ let mailbox = MailBox::new();
+ let db1 = notmuch::Database::create(&mailbox.path());
+ assert!(db1.is_ok());
+
+ let db2 = notmuch::Database::create(&mailbox.path());
+ assert!(db2.is_err());
+ }
+
+
+ #[test]
+ fn test_create_existing(){
+ let mailbox = MailBox::new();
+ notmuch::Database::create(&mailbox.path()).unwrap();
+
+ let db2 = notmuch::Database::create(&mailbox.path());
+ assert!(db2.is_err());
+ }
+
+
+ #[test]
+ fn test_close(){
+ let mailbox = MailBox::new();
+ let db = notmuch::Database::create(&mailbox.path()).unwrap();
+
+ assert!(db.close().is_ok());
+ }
+
+ #[test]
+ fn test_drop_noclose(){
+ let mailbox = MailBox::new();
+ let db = notmuch::Database::create(&mailbox.path()).unwrap();
+
+ drop(db);
+ }
+
+ #[test]
+ fn test_close_drop(){
+ let mailbox = MailBox::new();
+ let db = notmuch::Database::create(&mailbox.path()).unwrap();
+ db.close().unwrap();
+ drop(db);
+ }
+
+ #[test]
+ fn test_path(){
+ let mailbox = MailBox::new();
+ let db = notmuch::Database::create(&mailbox.path()).unwrap();
+ assert!(db.path() == mailbox.path());
+ }
+
+ #[test]
+ fn test_version(){
+ let mailbox = MailBox::new();
+ let db = notmuch::Database::create(&mailbox.path()).unwrap();
+ assert!(db.version() > 0);
+ }
+
+}
+
+
+mod atomic {
+ use super::*;
+
+ // TODO: how do I test this??
+
+}
+
+
+mod revision {
+ use super::*;
+
+ #[test]
+ fn test_single_rev(){
+ let mailbox = MailBox::new();
+ let db = notmuch::Database::create(&mailbox.path()).unwrap();
+
+ let rev0 = db.revision();
+ let rev1 = db.revision();
+
+ assert!(rev0 == rev1);
+ assert!(rev0 <= rev1);
+ assert!(rev0 >= rev1);
+ assert!(!(rev0 < rev1));
+ assert!(!(rev0 > rev1));
+ }
+
+ #[test]
+ fn test_diff_db(){
+ let mailbox0 = MailBox::new();
+ let db0 = notmuch::Database::create(&mailbox0.path()).unwrap();
+ let rev0 = db0.revision();
+
+
+ let mailbox1 = MailBox::new();
+ let db1 = notmuch::Database::create(&mailbox1.path()).unwrap();
+ let rev1 = db1.revision();
+
+ assert!(rev0 != rev1);
+ assert!(rev0.uuid != rev1.uuid);
+ }
+
+ #[test]
+ fn test_cmp(){
+ let mailbox = MailBox::new();
+ let db = notmuch::Database::create(&mailbox.path()).unwrap();
+
+ let rev0 = db.revision();
+
+ let (_, filename) = mailbox.deliver(None, None, None, None, vec![], true, None, false, false, false).unwrap();
+
+ db.index_file(&filename, None).unwrap();
+
+ let rev1 = db.revision();
+
+ assert!(rev0 < rev1);
+ assert!(rev0 <= rev1);
+ assert!(!(rev0 > rev1));
+ assert!(!(rev0 >= rev1));
+ assert!(!(rev0 == rev1));
+ assert!(rev0 != rev1);
+
+
+ }
+
+ // TODO: add tests for revisions comparisons
+
+}
+
+
+mod messages {
+ use super::*;
+
+ #[test]
+ fn test_add_message() {
+ let mailbox = MailBox::new();
+ let db = notmuch::Database::create(&mailbox.path()).unwrap();
+
+ let (msgid, filename) = mailbox.deliver(None, None, None, None, vec![], true, None, false, false, false).unwrap();
+ let msg = db.index_file(&filename, None).unwrap();
+
+ assert!(msg.filename() == filename);
+ assert!(msg.id() == msgid);
+
+ }
+
+ #[test]
+ fn test_remove_message() {
+ let mailbox = MailBox::new();
+ let db = notmuch::Database::create(&mailbox.path()).unwrap();
+
+ let (msgid, filename) = mailbox.deliver(None, None, None, None, vec![], true, None, false, false, false).unwrap();
+ let msg = db.index_file(&filename, None).unwrap();
+ assert!(db.find_message(&msgid).unwrap().is_some());
+
+ db.remove_message(&filename).unwrap();
+ assert!(db.find_message(&msgid).unwrap().is_none());
+ }
+
+ #[test]
+ fn test_find_message() {
+ let mailbox = MailBox::new();
+ let db = notmuch::Database::create(&mailbox.path()).unwrap();
+
+ let (msgid, filename) = mailbox.deliver(None, None, None, None, vec![], true, None, false, false, false).unwrap();
+ let msg0 = db.index_file(&filename, None).unwrap();
+
+ let msg1 = db.find_message(&msgid).unwrap().unwrap();
+ assert!(msg0.id() == msgid);
+ assert!(msg0.id() == msg1.id());
+
+ assert!(msg0.filename() == filename);
+ assert!(msg0.filename() == msg1.filename());
+ }
+
+ #[test]
+ fn test_find_message_notfound() {
+ let mailbox = MailBox::new();
+ let db = notmuch::Database::create(&mailbox.path()).unwrap();
+
+ assert!(db.find_message(&"foo").unwrap().is_none());
+ }
+
+}
+
+mod tags {
+ use super::*;
+
+ #[test]
+ fn test_none() {
+ let mailbox = MailBox::new();
+ let db = notmuch::Database::create(&mailbox.path()).unwrap();
+
+ let tags = db.all_tags().unwrap();
+
+ assert!(tags.count() == 0);
+ }
+
+ #[test]
+ fn test_some() {
+ let mailbox = MailBox::new();
+ let db = notmuch::Database::create(&mailbox.path()).unwrap();
+ let (_, filename) = mailbox.deliver(None, None, None, None, vec![], true, None, false, false, false).unwrap();
+ let msg = db.index_file(&filename, None).unwrap();
+
+ msg.add_tag(&"hello").unwrap();
+ let tags: Vec<String> = db.all_tags().unwrap().collect();
+
+ assert!(tags.len() == 1);
+ assert!(tags.iter().any(|x| x == "hello"));
+ }
+
+ #[test]
+ fn test_iters() {
+ let mailbox = MailBox::new();
+ let db = notmuch::Database::create(&mailbox.path()).unwrap();
+
+ let t1: Vec<String> = db.all_tags().unwrap().collect();
+ let t2: Vec<String> = db.all_tags().unwrap().collect();
+ assert!(t1 == t2);
+ }
+
+}
+
+struct PopulatedDatabase {
+ // Return a read-write Database.
+ // The database will have 3 messages, 2 threads.
+
+ pub mailbox: MailBox,
+ pub database: notmuch::Database,
+}
+
+impl PopulatedDatabase {
+ pub fn new() -> Self{
+ let mailbox = MailBox::new();
+
+ let (msgid, _) = mailbox.deliver(None, Some("foo".to_string()), None, None, vec![], true, None, false, false, false).unwrap();
+ mailbox.deliver(None, Some("bar".to_string()), None, None, vec![], true, None, false, false, false).unwrap();
+ mailbox.deliver(None, Some("baz".to_string()), None, None, vec![("In-Reply-To".to_string(), format!("<{}>", msgid))], true, None, false, false, false).unwrap();
+
+ let cmd = NotmuchCommand::new(&mailbox.path());
+ cmd.run(vec!["new"]).unwrap();
+
+ let database = notmuch::Database::open(&mailbox.path(), notmuch::DatabaseMode::ReadWrite).unwrap();
+
+ Self {
+ mailbox,
+ database
+ }
+ }
+}
+
+mod query {
+ use super::*;
+
+ #[test]
+ fn test_count_messages() {
+ let db = PopulatedDatabase::new();
+
+ let query = db.database.create_query("*").unwrap();
+ assert!(query.count_messages().unwrap() == 3);
+ }
+
+ #[test]
+ fn test_message_no_results() {
+ let db = PopulatedDatabase::new();
+
+ let query = db.database.create_query("not_a_matching_query").unwrap();
+ let mut messages = query.search_messages().unwrap();
+ let msg = messages.next();
+ assert!(msg.is_none());
+ }
+
+ #[test]
+ fn test_message_match() {
+ let db = PopulatedDatabase::new();
+
+ let query = db.database.create_query("*").unwrap();
+ let mut messages = query.search_messages().unwrap();
+ let msg = messages.next();
+ assert!(msg.is_some());
+ }
+
+ #[test]
+ fn test_count_threads() {
+ let db = PopulatedDatabase::new();
+
+ let query = db.database.create_query("*").unwrap();
+ assert!(query.count_threads().unwrap() == 2);
+ }
+
+ #[test]
+ fn test_threads_no_results() {
+ let db = PopulatedDatabase::new();
+
+ let query = db.database.create_query("not_a_matching_query").unwrap();
+ let mut threads = query.search_threads().unwrap();
+ let thrd = threads.next();
+ assert!(thrd.is_none());
+ }
+
+ #[test]
+ fn test_threads_match() {
+ let db = PopulatedDatabase::new();
+
+ let query = db.database.create_query("*").unwrap();
+ let mut threads = query.search_threads().unwrap();
+ let thrd = threads.next();
+ assert!(thrd.is_some());
+ }
+}
+