Commit 635cdc06 authored by Nick Mathewson's avatar Nick Mathewson 🌉
Browse files

Write a few misc unit tests

parent cae16665
......@@ -447,7 +447,7 @@ impl CircMgr {
// TODO: This is way too long, AND it should be an option.
let timeout = Duration::new(10, 0);
let mut error = RetryError::new();
let mut error = RetryError::while_doing("build a circuit");
for _ in 0..n_tries {
let result = tor_rtcompat::timer::timeout(
......
......@@ -828,3 +828,36 @@ pub(super) const FALLBACKS: &[FallbackDir] = &[
ipv6_orport: Some("[2001:6b0:7:125::243]:9001"),
},
];
#[cfg(test)]
mod test {
use super::*;
use crate::fallback::FallbackDir as Converted;
use hex_literal::hex;
use tor_linkspec::ChanTarget;
#[test]
fn convert() {
let d = FallbackDir {
rsa_identity: "FFA72BD683BC2FCF988356E6BEC1E490F313FB07",
ed_identity: "1XEqrNow2olJOwX09N2/ixwregJsPECgqGAOvNMfL5U",
ipv4_orport: "193.11.164.243:9001",
ipv6_orport: Some("[2001:6b0:7:125::243]:9001"),
};
let fd: Converted = (&d).into();
assert_eq!(
fd.rsa_identity(),
&RSAIdentity::from_bytes(&hex!("FFA72BD683BC2FCF988356E6BEC1E490F313FB07")).unwrap()
);
assert_eq!(
fd.ed_identity(),
&Ed25519Identity::from_bytes(&hex!(
"d5712aacda30da89493b05f4f4ddbf8b1c2b7a026c3c40a0a8600ebcd31f2f95"
))
.unwrap()
);
assert_eq!(fd.addrs().len(), 2);
assert_eq!(fd.addrs()[0], "193.11.164.243:9001".parse().unwrap());
assert_eq!(fd.addrs()[1], "[2001:6b0:7:125::243]:9001".parse().unwrap());
}
}
......@@ -19,7 +19,7 @@ use std::fmt::{Display, Error as FmtError, Formatter};
#[derive(Debug, Default)]
pub struct RetryError {
/// The operation we were trying to do.
doing: Option<&'static str>,
doing: &'static str,
/// The errors that we encountered when doing the operation.
// TODO: It might be nice to have this crate not depend on anyhow.
// When I first tried to do that, though, I ran into a big pile of
......@@ -31,17 +31,6 @@ pub struct RetryError {
impl std::error::Error for RetryError {}
impl RetryError {
/// Create a new RetryError, with no failed attempts.
///
/// This RetryError should not be used as-is, since when no
/// [`Error`]s have been pushed into it, it doesn't represent an
/// actual failure.
pub fn new() -> Self {
RetryError {
doing: None,
errors: Vec::new(),
}
}
/// Crate a new RetryError, with no failed attempts,
///
/// The provided `doing` argument is a short string that describes
......@@ -49,11 +38,12 @@ impl RetryError {
/// will be used to format the final error message; it should be a
/// phrase that can go after "while trying to".
///
/// As with [`RetryError::new()`], the result of this method
/// shouln't be used before any errors are pushed into it.
/// This RetryError should not be used as-is, since when no
/// [`Error`]s have been pushed into it, it doesn't represent an
/// actual failure.
pub fn while_doing(doing: &'static str) -> Self {
RetryError {
doing: Some(doing),
doing,
errors: Vec::new(),
}
}
......@@ -78,20 +68,18 @@ impl RetryError {
impl Display for RetryError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self.errors.len() {
0 => writeln!(
0 => write!(
f,
"Programming error: Somebody made a RetryError without any errors!"
"Programming error: somebody made a RetryError without any errors!"
),
1 => self.errors[0].fmt(f),
n => {
match self.doing {
Some(doing) => writeln!(
f,
"Tried to {} {} times, but all attempts failed.",
doing, n
),
None => writeln!(f, "Tried {} times, but all attempts failed.", n),
}?;
writeln!(
f,
"Tried to {} {} times, but all attempts failed.",
self.doing, n
)?;
for (idx, e) in self.sources().enumerate() {
write!(f, "Attempt {}:\n{}\n", idx + 1, e)?;
}
......@@ -106,7 +94,7 @@ mod test {
use super::*;
#[test]
fn bad_bools() {
fn bad_parse1() {
let mut err = RetryError::while_doing("convert some things");
if let Err(e) = "maybe".parse::<bool>() {
err.push(e);
......@@ -131,4 +119,24 @@ invalid IP address syntax
"
);
}
#[test]
fn no_problems() {
let empty = RetryError::while_doing("immanentize the eschaton");
let disp = format!("{}", empty);
assert_eq!(
disp,
"Programming error: somebody made a RetryError without any errors!"
);
}
#[test]
fn one_problem() {
let mut err = RetryError::while_doing("connect to torproject.org");
if let Err(e) = "teh_g1b50n".parse::<std::net::IpAddr>() {
err.push(e);
}
let disp = format!("{}", err);
assert_eq!(disp, "invalid IP address syntax");
}
}
......@@ -37,18 +37,47 @@ pub mod timer {
/// Pause until the wall-clock is at `when` or later, trying to
/// recover from clock jumps.
pub async fn sleep_until_wallclock(when: SystemTime) {
/// We never sleep more than this much, in case our system clock jumps.
const MAX_SLEEP: Duration = Duration::from_secs(600);
loop {
let now = SystemTime::now();
if now >= when {
return;
}
let remainder = when
.duration_since(now)
.unwrap_or_else(|_| Duration::from_secs(0));
let delay = std::cmp::min(MAX_SLEEP, remainder);
let delay = calc_next_delay(now, when);
crate::task::sleep(delay).await;
}
}
/// Return the amount of time we should wait next, when running
/// sleep_until_wallclock().
///
/// (This is a separate function for testing.)
fn calc_next_delay(now: SystemTime, when: SystemTime) -> Duration {
/// We never sleep more than this much, in case our system clock jumps
const MAX_SLEEP: Duration = Duration::from_secs(600);
let remainder = when
.duration_since(now)
.unwrap_or_else(|_| Duration::from_secs(0));
std::cmp::min(MAX_SLEEP, remainder)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn sleep_delay() {
use calc_next_delay as calc;
let minute = Duration::from_secs(60);
let second = Duration::from_secs(1);
let start = SystemTime::now();
let target = start + 30 * minute;
assert_eq!(calc(start, target), minute * 10);
assert_eq!(calc(target + minute, target), minute * 0);
assert_eq!(calc(target, target), minute * 0);
assert_eq!(calc(target - second, target), second);
assert_eq!(calc(target - minute * 9, target), minute * 9);
assert_eq!(calc(target - minute * 11, target), minute * 10);
}
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment