Compare commits

...

1 Commits

Author SHA1 Message Date
Floris Bruynooghe
5e21399813 Create an integration test in rust
This is an attempt at a first integration test in rust, using two
accounts which send real email to each other.
2019-11-03 20:54:32 +01:00
5 changed files with 487 additions and 1 deletions

View File

@@ -505,6 +505,8 @@ pub fn perform_smtp_jobs(context: &Context) {
}
}
pub fn perform_smtp_fetch(_context: &Context) {}
pub fn perform_smtp_idle(context: &Context) {
info!(context, "SMTP-idle started...",);
{

View File

@@ -190,7 +190,13 @@ impl Kml {
}
}
// location streaming
/// Starts streaming locations to a chat.
///
/// # Parameters
///
/// * `context` - The [Context].
/// * `chat_id` - The ID of the chat to send locations to.
/// * `seconds` - The duration for which to stream the location.
pub fn send_locations_to_chat(context: &Context, chat_id: u32, seconds: i64) {
let now = time();
if !(seconds < 0 || chat_id <= DC_CHAT_ID_LAST_SPECIAL) {

View File

@@ -0,0 +1 @@
xcLYBF297/EBCADEgI6RhCnbfNnKqlPzHNAmCVJ65KniO0reqaR94CFxcMRcO3k5XZDAo0u1KbqobehYm39ggtoqtmattjgG/AbR+Ex90CjbT1PbIi4Efkt91deMzt4G/S/POtqnIt8nbPSohMRkTOBKI0y7DSGneZTx+uj/GuE+aWbM2ubENsW283onnUKqDUOtxqQoBT3vWocsS6iViZCjqhqqQPX4OVK0gaQDLFCm63NC5VnazSvJkmDFFhG2bN8ncZ3wVibai7r3scF+I+hCnR4ll+/Q02WdpCSW0V26DXUQiCadTri6jDAi3JAGCnsCUiXR9780yAms+idWjPAXv2l9PfvSYkITABEBAAEACACopQS20szxuOdaTnCaN+JUoq+NFW7P4L9S9hlcht1s9LExz0EtAKZZDkzNgLDYGOvOEDZz6BnBiqX49GiFZgucbROI7vwBrMV1TpJb/OBhcQP7rxdSvD0qB4Lc6srGlXYsozXCN1BPkJgr+QsnJuuz+fm556Hk5KT7r+tZ/wAVEMNLIz8ta06MS7DGMQCh8kSRXldQznSAV2etG7OmSeTka/DISA9y1lW4MWAlvaa435sXqB1zQJPXQiRG6WVhZ9MwX/nZEOGutdeA8O4D4wU520UipJNtSXbm+CQeVTzse7+ZDvpwkOLSXfp0BDOO+Hv1/bcByzH2JwhVQSZfzRXxBADO6w8xeVlxjVHkXS76sRvVLdmHy7Jo3b9DojaUgrQvB1uOlmHZ4WwiHi2xRkaJoY9AAa1Ndf4g9y2BvKpHmUxfVvRjXRQuuLLkjjU2RudxDsdDw4rVytjQafDFgyqeYUBlx0XN79HG4ATBbkI+A0hlIaP4Ja/RuIQGGI+DA+LtjQQA8xz+KuTKmKmEUb+PPQMww/ucVcC4bV66Rqz/GUqjdvmsPH5UCW7NdmrSnqJ/7n2WOK4OuZ9Wx3lmCeZ4r3FlbGT7sauojKeCsnJTbXZdMJ+4FHUI6dF0EjOpsHfGrKkWuGmPJoMA/N6Lx4wMyO/UjyZ/HYrq4h+B40TjNzKq9h8D/AxMUgg5n57fUZj31sQE4RGODGEtTFGFtTuC2Ih0Goc1DdG9vsx2nDtM7HI2P4RjHKolhTmbCqnbmoi+frOZoHskWpRzESQVQJAmCz3h4gDPcLsvK7K5KJ17XXR1GA1mWgEpP4H1s4D/Ke4ucwFFztIQrTIF93ptqqipZCbJlqH8Q33NETxib2JAZXhhbXBsZS5jb20+wsCJBBABCAAzAhkBBQJdvfAxAhsDBAsJCAcGFQgJCgsCAxYCARYhBMCyk5nNgMGX89zC8GVj9ZoUEAYDAAoJEGVj9ZoUEAYDWBoIAJhfY396iAauf0X+WBBTjEEJR9svmbxIIaGah3GSlYpAVXK44mLjKeJIdDzxFJ7nhfs+wEkSpU0NMnQsifdtZd+bjEN1hQxJq3WEoeyPEEp/KFOw312zt1ucysDao59oji4LLkZhKPjBz7v8e/DaWKty45Cv2t2/3+g5IWHChmyzbd0hhGpuQ6osR4iNV9xatLYWncJMUgow1YLgtAV2XBu/5B0bZA9oqHw9JX0oMWmBjHvU2ngsreUbQTcA10S2ExzfFjE9WeArTv7suVQLmcteBxLqjbFZ3UqpLraJmNejdRp0SE+OJAtiZKhq2PYPm6Wl+i2VLdScjkiqnsLjx9XHwtgEXb3v8QEIAMbfa0AldcdhCUX1ma7eZ2bA7zYLI2RbKNvBePk4Dnig/MVJBe2PUVBbo78TCxnExLZl7P7+faelKxWR5IVyy70NLglYlpL22Q/Ul3GBuUjhqCpolpkvBfqjrFa8L1Oo7g8vkrkFLPK9Ul1MVlAQ6mt/oWWObWWiO1FfHw4NiHMvGnnkRQSEg2qbmNzXvi4YIt1RhBCg1hdUJP9l1k8avRFpO9rcF3x3MM7ZuzED7zR7j0qExyguu76zYRsI/q3COy5Gw+kwl0hpN2nQSUqS0g6xZ7zxbB09ygXUS4IkZHN2tCBMgEzQh8axIQ+3ogaDC7RDESTi24+T7JXIjFY9FaEAEQEAAQAH/jWqUpHDyg2cdNkpFmim4XZL+AE4bjuFkfgDNHbkFpucrbk7JFtfwkyR/hTwuZ0hiQfDZ3nECPp1SrQOY4FTYgFJDjQ9cJyF+jsYXimmHO663htbj9AUbWOeSUI6k/babisw5kIBUIjMZ+5/TAddGTUbAt2Z2pGDfshNh97N7hVOlXe8N/4lEgTL4IMMc3Ub31t4XIzO1I5weonu7Hhj96arwe+Zo/O6BmO3+LuTuDSlh5kFmjVnN2AqwdHq3OGWhGoDv568tUsdnHTv9ps2OLa8JJqE/5/5gowdxR+K7ufjnMTIAtMKBWWC781urXUjN0kLBWjIHfXQa3FHV85TacEEAPXdSWe9Ua+P9mNkdbrxMPz6xeZ451WMnQ3Qx9is/7Ij7wCZtpgXl5Wq6bGlJhc7lNjNOCl/RR282pWNIVG4Z6h84hIZhiAUWrjiEK4hxxCe907CAp8MF2YANOX10EZAW5IQ4VhMAqtvqAZlkpo6z4UTbe3lyIG/RV4gAMYaMRUZBADPEjVz4F0FemnXFzzu9ssHnJMdFz+n+9sE6HKO63Jm6RXg0hE5UAdtMt8FoUipbZeoiqDcNWQ+JQU/7YyruLIMd86pLT7an7ojENrP70XJGDlvHVUcsoV5FtvBl/dSv5keYLOy7Jk4qER6cad06j/Yf4jNNOtEAAND9XUwrZNNyQP/eBmSmVA0RdqA6yw3WQvOejC+bEuUBxUjiGtFobG2Ch1E0qj+RJRtsg/kWN014+eWZbQHwOQ2LkIfFOlhZjXCE+QlAel/bm9rDw5kVWM/cm4sOqjxxWnebaxCNaAV1i/wuVO02Wg4stylUSGuIjZDtVX4gvO266TLQi6d1mreNg47C8LAdgQYAQgAIAUCXb3wMQIbDBYhBMCyk5nNgMGX89zC8GVj9ZoUEAYDAAoJEGVj9ZoUEAYD41wH/jIQgva+k3vmGtfYDR5tB/IdEpc6MjGJxo2NwOkBKYJfaigyK3dmZ1DY8ZfkYMfQ9s5d4cW3Lel4t7nRH5Vh5FiaIWlDuxfGVTMLNpOzlXswgHlwckrfJucVWk3/hLT/xStsSjC+SwKSC6+ejmHIqkSqbTztwVCABg63otzREV4NspEsSrO0+SUD+n2mpFFeo4ULjPXEtlJzrmoJNdByDBEODiMFUyw0voMXN13ZqFv46HVtmembBxc8tJXtHX8rvC2ODiyygI3y3HENJPYR+CBGY/v8K8sg35i7PidUEsK/V3NJRTU0WkI+NS+4b80xE5KxizQMTDNPiSuTOlb7gO4=

View File

@@ -0,0 +1 @@
xsBNBF297/EBCADEgI6RhCnbfNnKqlPzHNAmCVJ65KniO0reqaR94CFxcMRcO3k5XZDAo0u1KbqobehYm39ggtoqtmattjgG/AbR+Ex90CjbT1PbIi4Efkt91deMzt4G/S/POtqnIt8nbPSohMRkTOBKI0y7DSGneZTx+uj/GuE+aWbM2ubENsW283onnUKqDUOtxqQoBT3vWocsS6iViZCjqhqqQPX4OVK0gaQDLFCm63NC5VnazSvJkmDFFhG2bN8ncZ3wVibai7r3scF+I+hCnR4ll+/Q02WdpCSW0V26DXUQiCadTri6jDAi3JAGCnsCUiXR9780yAms+idWjPAXv2l9PfvSYkITABEBAAHNETxib2JAZXhhbXBsZS5jb20+wsCJBBABCAAzAhkBBQJdvfAxAhsDBAsJCAcGFQgJCgsCAxYCARYhBMCyk5nNgMGX89zC8GVj9ZoUEAYDAAoJEGVj9ZoUEAYDWBoIAJhfY396iAauf0X+WBBTjEEJR9svmbxIIaGah3GSlYpAVXK44mLjKeJIdDzxFJ7nhfs+wEkSpU0NMnQsifdtZd+bjEN1hQxJq3WEoeyPEEp/KFOw312zt1ucysDao59oji4LLkZhKPjBz7v8e/DaWKty45Cv2t2/3+g5IWHChmyzbd0hhGpuQ6osR4iNV9xatLYWncJMUgow1YLgtAV2XBu/5B0bZA9oqHw9JX0oMWmBjHvU2ngsreUbQTcA10S2ExzfFjE9WeArTv7suVQLmcteBxLqjbFZ3UqpLraJmNejdRp0SE+OJAtiZKhq2PYPm6Wl+i2VLdScjkiqnsLjx9XOwE0EXb3v8QEIAMbfa0AldcdhCUX1ma7eZ2bA7zYLI2RbKNvBePk4Dnig/MVJBe2PUVBbo78TCxnExLZl7P7+faelKxWR5IVyy70NLglYlpL22Q/Ul3GBuUjhqCpolpkvBfqjrFa8L1Oo7g8vkrkFLPK9Ul1MVlAQ6mt/oWWObWWiO1FfHw4NiHMvGnnkRQSEg2qbmNzXvi4YIt1RhBCg1hdUJP9l1k8avRFpO9rcF3x3MM7ZuzED7zR7j0qExyguu76zYRsI/q3COy5Gw+kwl0hpN2nQSUqS0g6xZ7zxbB09ygXUS4IkZHN2tCBMgEzQh8axIQ+3ogaDC7RDESTi24+T7JXIjFY9FaEAEQEAAcLAdgQYAQgAIAUCXb3wMQIbDBYhBMCyk5nNgMGX89zC8GVj9ZoUEAYDAAoJEGVj9ZoUEAYD41wH/jIQgva+k3vmGtfYDR5tB/IdEpc6MjGJxo2NwOkBKYJfaigyK3dmZ1DY8ZfkYMfQ9s5d4cW3Lel4t7nRH5Vh5FiaIWlDuxfGVTMLNpOzlXswgHlwckrfJucVWk3/hLT/xStsSjC+SwKSC6+ejmHIqkSqbTztwVCABg63otzREV4NspEsSrO0+SUD+n2mpFFeo4ULjPXEtlJzrmoJNdByDBEODiMFUyw0voMXN13ZqFv46HVtmembBxc8tJXtHX8rvC2ODiyygI3y3HENJPYR+CBGY/v8K8sg35i7PidUEsK/V3NJRTU0WkI+NS+4b80xE5KxizQMTDNPiSuTOlb7gO4=

476
tests/location.rs Normal file
View File

@@ -0,0 +1,476 @@
//! Integration tests for location streaming.
use std::collections::{HashMap, VecDeque};
use std::mem::discriminant;
use std::path::Path;
use std::sync::{atomic, Arc, Condvar, Mutex};
use std::thread;
use itertools::Itertools;
use libc::uintptr_t;
use serde::Deserialize;
use tempfile;
use deltachat::chat;
use deltachat::config::Config;
use deltachat::contact::Contact;
use deltachat::context::Context;
use deltachat::job;
use deltachat::location;
use deltachat::Event;
/// Credentials for a test account.
///
/// This is populated by the JSON returned from the account provider's
/// API.
#[derive(Debug, Deserialize)]
struct AccountCredentials {
email: String,
password: String,
}
impl AccountCredentials {
/// Creates a new online account.
///
/// Invoke the API of the account provider to create a new
/// temporary account.
fn new(provider_url: &str) -> AccountCredentials {
let (post_url, token) = provider_url.splitn(2, '#').next_tuple().unwrap();
let mut data: HashMap<&str, u64> = HashMap::new();
data.insert("token_create_user", token.parse().unwrap());
let client = reqwest::Client::new();
let mut response = client.post(post_url).json(&data).send().unwrap();
assert!(
response.status().is_success(),
format!("Failed to create new tmpuser: {}", response.status())
);
response.json().unwrap()
}
}
#[derive(Debug)]
struct EventsItem {
acc_name: String,
when: std::time::Duration,
event: Event,
}
#[derive(Debug)]
struct EventsQueue {
name: String,
events: Mutex<VecDeque<EventsItem>>,
cond: Condvar,
}
impl EventsQueue {
fn new(name: &str) -> EventsQueue {
EventsQueue {
name: name.to_string(),
events: Mutex::new(VecDeque::new()),
cond: Condvar::new(),
}
}
fn push(&self, evt: EventsItem) {
let mut queue = self.events.lock().unwrap();
queue.push_back(evt);
self.cond.notify_all();
}
fn wait_for(&self, event: Event, data: bool) -> Result<(), ()> {
println!(
"==> [{}] Waiting for: {:?} match-data={}",
self.name, event, data
);
let mut queue = self.events.lock().unwrap();
let start_time = std::time::Instant::now();
loop {
while let Some(item) = queue.pop_front() {
let hit = match data {
true => event == item.event,
false => discriminant(&event) == discriminant(&item.event),
};
self.log_event(&item);
if hit {
println!(
"<== [{}] Found {:?} match-data={} in {:?}",
self.name,
event,
data,
start_time.elapsed()
);
return Ok(());
}
}
if start_time.elapsed().as_secs() > 25 {
println!(
"=!= [{}] Timed out waiting for {:?} match-data={}",
self.name, event, data
);
return Err(());
}
queue = self.cond.wait(queue).unwrap();
}
}
fn clear(&self) {
let mut queue = self.events.lock().unwrap();
while let Some(item) = queue.pop_front() {
self.log_event(&item);
}
}
fn log_event(&self, item: &EventsItem) {
match &item.event {
Event::Info(msg) => println!("I [{} {:?}]: {}", item.acc_name, item.when, msg),
Event::Warning(msg) => println!("W [{} {:?}]: {}", item.acc_name, item.when, msg),
Event::Error(msg) => println!("E [{} {:?}]: {}", item.acc_name, item.when, msg),
_ => println!("Evt [{} {:?}]: {:?}", item.acc_name, item.when, item.event),
}
}
fn clear_log_events(&self) {
let mut queue = self.events.lock().unwrap();
for item in queue.iter() {
self.log_event(item)
}
queue.retain(|item| match item.event {
Event::Info(_) | Event::Warning(_) | Event::Error(_) => false,
_ => true,
});
}
}
/// A Configured DeltaChat account.
#[derive(Debug)]
struct Account {
name: String,
creds: AccountCredentials,
ctx: Arc<Context>,
events: Arc<EventsQueue>,
running: Arc<atomic::AtomicBool>,
imap_handle: Option<thread::JoinHandle<()>>,
mvbox_handle: Option<thread::JoinHandle<()>>,
sentbox_handle: Option<thread::JoinHandle<()>>,
smtp_handle: Option<thread::JoinHandle<()>>,
}
impl Account {
fn new(name: &str, dir: &Path, keys: KeyPair, start: std::time::Instant) -> Account {
// Create events queue and callback.
let events = Arc::new(EventsQueue::new(name));
let events_cb = Arc::clone(&events);
let name_cb = name.to_string();
let cb = move |_ctx: &Context, evt: Event| -> uintptr_t {
events_cb.push(EventsItem {
acc_name: name_cb.clone(),
when: start.elapsed(),
event: evt,
});
0
};
// Create and configure the context.
let dbfile = dir.join(format!("{}.db", name));
let creds = AccountCredentials::new(&Account::liveconfig_url());
println!("Account credentials for {}: {:#?}", name, creds);
let ctx = Arc::new(Context::new(Box::new(cb), "TestClient".into(), dbfile).unwrap());
ctx.set_config(Config::Addr, Some(&creds.email)).unwrap();
ctx.set_config(Config::MailPw, Some(&creds.password))
.unwrap();
keys.save_as_self(&ctx);
deltachat::configure::configure(&ctx);
// Start the threads.
let running = Arc::new(atomic::AtomicBool::new(true));
let imap_handle = Self::start_imap(name, Arc::clone(&ctx), Arc::clone(&running));
let mvbox_handle = Self::start_mvbox(name, Arc::clone(&ctx), Arc::clone(&running));
let sentbox_handle = Self::start_sentbox(name, Arc::clone(&ctx), Arc::clone(&running));
let smtp_handle = Self::start_smtp(name, Arc::clone(&ctx), Arc::clone(&running));
events.clear_log_events();
Account {
name: name.to_string(),
creds,
ctx,
events,
running,
imap_handle: Some(imap_handle),
mvbox_handle: Some(mvbox_handle),
sentbox_handle: Some(sentbox_handle),
smtp_handle: Some(smtp_handle),
}
}
/// Find the liveconfig URL.
///
/// Prefers the `DCC_TMPACCOUNT_PROVIDER`, will also use the
/// `DCC_PY_LIVECONFIG` environment variable and finally fall back
/// to finding a file named `liveconfig` and starting with
/// `#:provider:https://`.
fn liveconfig_url() -> String {
if let Some(url) = std::env::var("DCC_TMPACCOUNT_PROVIDER").ok() {
return url;
}
if let Some(url) = std::env::var("DCC_PY_LIVECONFIG").ok() {
return url;
}
let mut dir = Some(Path::new(".").canonicalize().unwrap());
loop {
let cfg_fname = match dir {
Some(path) => {
dir = path.parent().map(|p| p.to_path_buf());
path.join("liveconfig")
}
None => break,
};
if cfg_fname.is_file() {
let raw_data = std::fs::read(&cfg_fname).unwrap();
let data = String::from_utf8(raw_data).unwrap();
for line in data.lines() {
if line.starts_with("#:provider:https://") {
let (_, url) = line.split_at(11);
return url.to_string();
}
}
panic!("No provider URL in {}", cfg_fname.display());
}
}
panic!("Found no liveconfig");
}
fn start_imap(
name: &str,
ctx: Arc<Context>,
running: Arc<atomic::AtomicBool>,
) -> thread::JoinHandle<()> {
thread::Builder::new()
.name(format!("{}-imap", name))
.spawn(move || {
while running.load(atomic::Ordering::Relaxed) {
job::perform_imap_jobs(&ctx);
job::perform_imap_fetch(&ctx);
if !running.load(atomic::Ordering::Relaxed) {
break;
}
job::perform_imap_idle(&ctx);
}
})
.unwrap()
}
fn start_mvbox(
name: &str,
ctx: Arc<Context>,
running: Arc<atomic::AtomicBool>,
) -> thread::JoinHandle<()> {
thread::Builder::new()
.name(format!("{}-mvbox", name))
.spawn(move || {
while running.load(atomic::Ordering::Relaxed) {
job::perform_mvbox_jobs(&ctx);
job::perform_mvbox_fetch(&ctx);
if !running.load(atomic::Ordering::Relaxed) {
break;
}
job::perform_mvbox_idle(&ctx);
}
})
.unwrap()
}
fn start_sentbox(
name: &str,
ctx: Arc<Context>,
running: Arc<atomic::AtomicBool>,
) -> thread::JoinHandle<()> {
thread::Builder::new()
.name(format!("{}-sentbox", name))
.spawn(move || {
while running.load(atomic::Ordering::Relaxed) {
job::perform_sentbox_jobs(&ctx);
job::perform_sentbox_fetch(&ctx);
if !running.load(atomic::Ordering::Relaxed) {
break;
}
job::perform_sentbox_idle(&ctx);
}
})
.unwrap()
}
fn start_smtp(
name: &str,
ctx: Arc<Context>,
running: Arc<atomic::AtomicBool>,
) -> thread::JoinHandle<()> {
thread::Builder::new()
.name(format!("{}-smtp", name))
.spawn(move || {
while running.load(atomic::Ordering::Relaxed) {
job::perform_smtp_jobs(&ctx);
job::perform_smtp_fetch(&ctx);
if !running.load(atomic::Ordering::Relaxed) {
break;
}
job::perform_smtp_idle(&ctx);
}
})
.unwrap()
}
/// Goes through the events queue and prints all log events.
///
/// Each processed event is removed from the queue.
fn process_log_events(&self) {}
}
impl Drop for Account {
fn drop(&mut self) {
println!("Terminating Account {}", self.name);
self.running.store(false, atomic::Ordering::Relaxed);
job::interrupt_imap_idle(&self.ctx);
job::interrupt_mvbox_idle(&self.ctx);
self.imap_handle.take().unwrap().join().unwrap();
self.mvbox_handle.take().unwrap().join().unwrap();
self.events.clear();
println!("Account {} Terminated", self.name);
}
}
/// Helper struct to handle account key pairs.
struct KeyPair {
public: deltachat::key::Key,
private: deltachat::key::Key,
}
impl KeyPair {
/// Create a new [KeyPair].
///
/// # Example
///
/// ```
/// let alice_keys = KeyPair::new(
/// include_str!("../test-data/key/public.asc"),
/// include_str!("../test-data/key/private.asc"),
/// );
/// ```
fn new(public_data: &str, private_data: &str) -> KeyPair {
let public =
deltachat::key::Key::from_base64(public_data, deltachat::constants::KeyType::Public)
.unwrap();
let private =
deltachat::key::Key::from_base64(private_data, deltachat::constants::KeyType::Private)
.unwrap();
KeyPair { public, private }
}
/// Saves a key into the context as the default key of the self address.
///
/// [Config::Addr] must already be set.
fn save_as_self(&self, ctx: &Context) {
let addr = ctx.get_config(Config::Addr).unwrap();
let ok = deltachat::key::dc_key_save_self_keypair(
&ctx,
&self.public,
&self.private,
&addr,
true,
&ctx.sql,
);
assert_eq!(ok, true);
}
}
#[test]
fn test_location_streaming() {
// Create accounts
let start = std::time::Instant::now();
let tmpdir = tempfile::tempdir().unwrap();
let alice_keys = KeyPair::new(
include_str!("../test-data/key/public.asc"),
include_str!("../test-data/key/private.asc"),
);
let alice = Account::new("alice", tmpdir.path(), alice_keys, start);
let bob_keys = KeyPair::new(
include_str!("../test-data/key/public2.asc"),
include_str!("../test-data/key/private2.asc"),
);
let bob = Account::new("bob", tmpdir.path(), bob_keys, start);
alice
.events
.wait_for(Event::ConfigureProgress(1000), true)
.unwrap();
bob.events
.wait_for(Event::ConfigureProgress(1000), true)
.unwrap();
// Create contacts and chats.
let contact_bob = Contact::create(&alice.ctx, "Bob", &bob.creds.email).unwrap();
let contact_alice = Contact::create(&bob.ctx, "Alice", &bob.creds.email).unwrap();
let alice_to_bob = deltachat::chat::create_by_contact_id(&alice.ctx, contact_bob).unwrap();
let bob_to_alice = deltachat::chat::create_by_contact_id(&bob.ctx, contact_alice).unwrap();
alice.events.clear();
bob.events.clear();
println!("### Starting location streaming from Alice to Bob");
assert!(!location::is_sending_locations_to_chat(
&alice.ctx,
alice_to_bob
));
assert!(!location::is_sending_locations_to_chat(
&bob.ctx,
bob_to_alice
));
location::send_locations_to_chat(&alice.ctx, alice_to_bob, 100);
assert!(location::is_sending_locations_to_chat(
&alice.ctx,
alice_to_bob
));
alice
.events
.wait_for(Event::SmtpMessageSent(Default::default()), false)
.unwrap();
assert_eq!(location::set(&alice.ctx, 1.0, 1.0, 1.0), true);
alice
.events
.wait_for(Event::LocationChanged(Default::default()), false)
.unwrap();
assert_eq!(location::set(&alice.ctx, 1.1, 1.1, 1.0), true);
chat::send_text_msg(&alice.ctx, alice_to_bob, "ping".to_string()).unwrap();
alice
.events
.wait_for(Event::SmtpMessageSent(Default::default()), false)
.unwrap();
println!("### Looking for location messages received by Bob");
// First message is the "enabled-location-streaming" command.
bob.events
.wait_for(
Event::MsgsChanged {
chat_id: Default::default(),
msg_id: Default::default(),
},
false,
)
.unwrap();
// Core emits location changed before the incoming message. Sadly
// the the ordering requirement is brittle.
bob.events
.wait_for(Event::LocationChanged(Default::default()), false)
.unwrap();
// Next message is the "ping" one which should contain a location.
bob.events
.wait_for(
Event::MsgsChanged {
chat_id: Default::default(),
msg_id: Default::default(),
},
false,
)
.unwrap();
let positions = location::get_range(&bob.ctx, bob_to_alice, contact_alice, 0, 0);
println!("pos len: {}", positions.len());
println!("{:#?}", positions);
assert!(false, "THE END");
}