<commit_before>// ignore-windows: No libc on Windows
// compile-flags: -Zmiri-disable-isolation

#![feature(rustc_private)]
#![allow(unused)] // necessary on macos due to conditional compilation

use std::path::PathBuf;

extern crate libc;

fn tmp() -> PathBuf {
    std::env::var("MIRI_TEMP").map(PathBuf::from).unwrap_or_else(|_| std::env::temp_dir())
}

#[cfg(not(target_os = "macos"))]
fn test_posix_fadvise() {
    use std::convert::TryInto;
    use std::fs::{remove_file, File};
    use std::io::Write;
    use std::os::unix::io::AsRawFd;

    let path = tmp().join("miri_test_libc.txt");
    // Cleanup before test
    remove_file(&path).ok();

    // Set up an open file
    let mut file = File::create(&path).unwrap();
    let bytes = b"Hello, World!\n";
    file.write(bytes).unwrap();

    // Test calling posix_fadvise on a file.
    let result = unsafe {
        libc::posix_fadvise(
            file.as_raw_fd(),
            0,
            bytes.len().try_into().unwrap(),
            libc::POSIX_FADV_DONTNEED,
        )
    };
    drop(file);
    remove_file(&path).unwrap();
    assert_eq!(result, 0);
}

fn test_mutex_libc_init_recursive() {
    unsafe {
        let mut attr: libc::pthread_mutexattr_t = std::mem::zeroed();
        assert_eq!(libc::pthread_mutexattr_init(&mut attr as *mut _), 0);
        assert_eq!(libc::pthread_mutexattr_settype(&mut attr as *mut _, libc::PTHREAD_MUTEX_RECURSIVE), 0);
        let mut mutex: libc::pthread_mutex_t = std::mem::zeroed();
        assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mut attr as *mut _), 0);
        assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), libc::EPERM);
        assert_eq!(libc::pthread_mutex_destroy(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutexattr_destroy(&mut attr as *mut _), 0);
    }
}

fn test_mutex_libc_init_normal() {
    unsafe {
        let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed();
        assert_eq!(libc::pthread_mutexattr_settype(&mut mutexattr as *mut _, 0x12345678), libc::EINVAL);
        assert_eq!(libc::pthread_mutexattr_settype(&mut mutexattr as *mut _, libc::PTHREAD_MUTEX_NORMAL), 0);
        let mut mutex: libc::pthread_mutex_t = std::mem::zeroed();
        assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0);
        assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), libc::EBUSY);
        assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_destroy(&mut mutex as *mut _), 0);
    }
}

fn test_mutex_libc_init_errorcheck() {
    unsafe {
        let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed();
        assert_eq!(libc::pthread_mutexattr_settype(&mut mutexattr as *mut _, libc::PTHREAD_MUTEX_ERRORCHECK), 0);
        let mut mutex: libc::pthread_mutex_t = std::mem::zeroed();
        assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0);
        assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), libc::EBUSY);
        assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), libc::EDEADLK);
        assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), libc::EPERM);
        assert_eq!(libc::pthread_mutex_destroy(&mut mutex as *mut _), 0);
    }
}

// Only linux provides PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
// libc for macOS just has the default PTHREAD_MUTEX_INITIALIZER.
#[cfg(target_os = "linux")]
fn test_mutex_libc_static_initializer_recursive() {
    let mutex = std::cell::UnsafeCell::new(libc::PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP);
    unsafe {
        assert_eq!(libc::pthread_mutex_lock(mutex.get()), 0);
        assert_eq!(libc::pthread_mutex_trylock(mutex.get()), 0);
        assert_eq!(libc::pthread_mutex_unlock(mutex.get()), 0);
        assert_eq!(libc::pthread_mutex_unlock(mutex.get()), 0);
        assert_eq!(libc::pthread_mutex_trylock(mutex.get()), 0);
        assert_eq!(libc::pthread_mutex_lock(mutex.get()), 0);
        assert_eq!(libc::pthread_mutex_unlock(mutex.get()), 0);
        assert_eq!(libc::pthread_mutex_unlock(mutex.get()), 0);
        assert_eq!(libc::pthread_mutex_unlock(mutex.get()), libc::EPERM);
        assert_eq!(libc::pthread_mutex_destroy(mutex.get()), 0);
    }
}

// Testing the behavior of std::sync::RwLock does not fully exercise the pthread rwlock shims, we
// need to go a layer deeper and test the behavior of the libc functions, because
// std::sys::unix::rwlock::RWLock itself keeps track of write_locked and num_readers.
fn test_rwlock_libc_static_initializer() {
    let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);
    unsafe {
        assert_eq!(libc::pthread_rwlock_rdlock(rw.get()), 0);
        assert_eq!(libc::pthread_rwlock_rdlock(rw.get()), 0);
        assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0);
        assert_eq!(libc::pthread_rwlock_tryrdlock(rw.get()), 0);
        assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0);
        assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), libc::EBUSY);
        assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0);

        assert_eq!(libc::pthread_rwlock_wrlock(rw.get()), 0);
        assert_eq!(libc::pthread_rwlock_tryrdlock(rw.get()), libc::EBUSY);
        assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), libc::EBUSY);
        assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0);

        assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), 0);
        assert_eq!(libc::pthread_rwlock_tryrdlock(rw.get()), libc::EBUSY);
        assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), libc::EBUSY);
        assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0);

        assert_eq!(libc::pthread_rwlock_destroy(rw.get()), 0);
    }
}

fn main() {
    #[cfg(not(target_os = "macos"))]
    test_posix_fadvise();

    test_mutex_libc_init_recursive();
    test_mutex_libc_init_normal();
    test_mutex_libc_init_errorcheck();
    test_rwlock_libc_static_initializer();

    #[cfg(target_os = "linux")]
    test_mutex_libc_static_initializer_recursive();
}
<commit_msg>Clean up conditional compilation<commit_after>// ignore-windows: No libc on Windows
// compile-flags: -Zmiri-disable-isolation

#![feature(rustc_private)]
#![allow(unused)] // necessary on macos due to conditional compilation

use std::path::PathBuf;

extern crate libc;

fn tmp() -> PathBuf {
    std::env::var("MIRI_TEMP").map(PathBuf::from).unwrap_or_else(|_| std::env::temp_dir())
}

#[cfg(target_os = "linux")]
fn test_posix_fadvise() {
    use std::convert::TryInto;
    use std::fs::{remove_file, File};
    use std::io::Write;
    use std::os::unix::io::AsRawFd;

    let path = tmp().join("miri_test_libc.txt");
    // Cleanup before test
    remove_file(&path).ok();

    // Set up an open file
    let mut file = File::create(&path).unwrap();
    let bytes = b"Hello, World!\n";
    file.write(bytes).unwrap();

    // Test calling posix_fadvise on a file.
    let result = unsafe {
        libc::posix_fadvise(
            file.as_raw_fd(),
            0,
            bytes.len().try_into().unwrap(),
            libc::POSIX_FADV_DONTNEED,
        )
    };
    drop(file);
    remove_file(&path).unwrap();
    assert_eq!(result, 0);
}

fn test_mutex_libc_init_recursive() {
    unsafe {
        let mut attr: libc::pthread_mutexattr_t = std::mem::zeroed();
        assert_eq!(libc::pthread_mutexattr_init(&mut attr as *mut _), 0);
        assert_eq!(libc::pthread_mutexattr_settype(&mut attr as *mut _, libc::PTHREAD_MUTEX_RECURSIVE), 0);
        let mut mutex: libc::pthread_mutex_t = std::mem::zeroed();
        assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mut attr as *mut _), 0);
        assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), libc::EPERM);
        assert_eq!(libc::pthread_mutex_destroy(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutexattr_destroy(&mut attr as *mut _), 0);
    }
}

fn test_mutex_libc_init_normal() {
    unsafe {
        let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed();
        assert_eq!(libc::pthread_mutexattr_settype(&mut mutexattr as *mut _, 0x12345678), libc::EINVAL);
        assert_eq!(libc::pthread_mutexattr_settype(&mut mutexattr as *mut _, libc::PTHREAD_MUTEX_NORMAL), 0);
        let mut mutex: libc::pthread_mutex_t = std::mem::zeroed();
        assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0);
        assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), libc::EBUSY);
        assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_destroy(&mut mutex as *mut _), 0);
    }
}

fn test_mutex_libc_init_errorcheck() {
    unsafe {
        let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed();
        assert_eq!(libc::pthread_mutexattr_settype(&mut mutexattr as *mut _, libc::PTHREAD_MUTEX_ERRORCHECK), 0);
        let mut mutex: libc::pthread_mutex_t = std::mem::zeroed();
        assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0);
        assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), libc::EBUSY);
        assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), libc::EDEADLK);
        assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
        assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), libc::EPERM);
        assert_eq!(libc::pthread_mutex_destroy(&mut mutex as *mut _), 0);
    }
}

// Only linux provides PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
// libc for macOS just has the default PTHREAD_MUTEX_INITIALIZER.
#[cfg(target_os = "linux")]
fn test_mutex_libc_static_initializer_recursive() {
    let mutex = std::cell::UnsafeCell::new(libc::PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP);
    unsafe {
        assert_eq!(libc::pthread_mutex_lock(mutex.get()), 0);
        assert_eq!(libc::pthread_mutex_trylock(mutex.get()), 0);
        assert_eq!(libc::pthread_mutex_unlock(mutex.get()), 0);
        assert_eq!(libc::pthread_mutex_unlock(mutex.get()), 0);
        assert_eq!(libc::pthread_mutex_trylock(mutex.get()), 0);
        assert_eq!(libc::pthread_mutex_lock(mutex.get()), 0);
        assert_eq!(libc::pthread_mutex_unlock(mutex.get()), 0);
        assert_eq!(libc::pthread_mutex_unlock(mutex.get()), 0);
        assert_eq!(libc::pthread_mutex_unlock(mutex.get()), libc::EPERM);
        assert_eq!(libc::pthread_mutex_destroy(mutex.get()), 0);
    }
}

// Testing the behavior of std::sync::RwLock does not fully exercise the pthread rwlock shims, we
// need to go a layer deeper and test the behavior of the libc functions, because
// std::sys::unix::rwlock::RWLock itself keeps track of write_locked and num_readers.
fn test_rwlock_libc_static_initializer() {
    let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);
    unsafe {
        assert_eq!(libc::pthread_rwlock_rdlock(rw.get()), 0);
        assert_eq!(libc::pthread_rwlock_rdlock(rw.get()), 0);
        assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0);
        assert_eq!(libc::pthread_rwlock_tryrdlock(rw.get()), 0);
        assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0);
        assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), libc::EBUSY);
        assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0);

        assert_eq!(libc::pthread_rwlock_wrlock(rw.get()), 0);
        assert_eq!(libc::pthread_rwlock_tryrdlock(rw.get()), libc::EBUSY);
        assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), libc::EBUSY);
        assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0);

        assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), 0);
        assert_eq!(libc::pthread_rwlock_tryrdlock(rw.get()), libc::EBUSY);
        assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), libc::EBUSY);
        assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0);

        assert_eq!(libc::pthread_rwlock_destroy(rw.get()), 0);
    }
}

fn main() {
    #[cfg(target_os = "linux")]
    test_posix_fadvise();

    test_mutex_libc_init_recursive();
    test_mutex_libc_init_normal();
    test_mutex_libc_init_errorcheck();
    test_rwlock_libc_static_initializer();

    #[cfg(target_os = "linux")]
    test_mutex_libc_static_initializer_recursive();
}
<|endoftext|>


<commit_before>use conduit::{Handler, Method};

use cargo_registry::Model;
use cargo_registry::krate::EncodableCrate;
use cargo_registry::user::{User, NewUser, EncodableUser};
use cargo_registry::version::EncodableVersion;

#[derive(RustcDecodable)]
struct AuthResponse {
    url: String,
    state: String,
}
#[derive(RustcDecodable)]
pub struct UserShowResponse {
    pub user: EncodableUser,
}

#[test]
fn auth_gives_a_token() {
    let (_b, app, middle) = ::app();
    let mut req = ::req(app, Method::Get, "/authorize_url");
    let mut response = ok_resp!(middle.call(&mut req));
    let json: AuthResponse = ::json(&mut response);
    assert!(json.url.contains(&json.state));
}

#[test]
fn access_token_needs_data() {
    let (_b, app, middle) = ::app();
    let mut req = ::req(app, Method::Get, "/authorize");
    let mut response = ok_resp!(middle.call(&mut req));
    let json: ::Bad = ::json(&mut response);
    assert!(json.errors[0].detail.contains("invalid state"));
}

#[test]
fn user_insert() {
    let (_b, app, _middle) = ::app();
    let conn = t!(app.database.get());
    let tx = t!(conn.transaction());

    let user = t!(User::find_or_insert(&tx, 1, "foo", None, None, None, "bar"));
    assert_eq!(t!(User::find(&tx, user.id)), user);

    assert_eq!(
        t!(User::find_or_insert(&tx, 1, "foo", None, None, None, "bar")),
        user
    );
    let user2 = t!(User::find_or_insert(&tx, 1, "foo", None, None, None, "baz"));
    assert!(user != user2);
    assert_eq!(user.id, user2.id);
    assert_eq!(user2.gh_access_token, "baz");

    let user3 = t!(User::find_or_insert(&tx, 1, "bar", None, None, None, "baz"));
    assert!(user != user3);
    assert_eq!(user.id, user3.id);
    assert_eq!(user3.gh_login, "bar");
}

#[test]
fn me() {
    let (_b, app, middle) = ::app();
    let mut req = ::req(app, Method::Get, "/me");
    let response = t_resp!(middle.call(&mut req));
    assert_eq!(response.status.0, 403);

    let user = ::mock_user(&mut req, ::user("foo"));

    let mut response = ok_resp!(middle.call(&mut req));
    let json: UserShowResponse = ::json(&mut response);

    assert_eq!(json.user.email, user.email);
}

#[test]
fn show() {
    let (_b, app, middle) = ::app();
    {
        let conn = t!(app.diesel_database.get());

        t!(NewUser::new(1, "foo", Some("foo@bar.com"), None, None, "bar").create_or_update(&conn));
        t!(NewUser::new(2, "bar", Some("bar@baz.com"), None, None, "bar").create_or_update(&conn));
    }

    let mut req = ::req(app.clone(), Method::Get, "/api/v1/users/foo");
    let mut response = ok_resp!(middle.call(&mut req));
    let json: UserShowResponse = ::json(&mut response);
    assert_eq!(Some("foo@bar.com".into()), json.user.email);
    assert_eq!("foo", json.user.login);

    let mut response = ok_resp!(middle.call(req.with_path("/api/v1/users/bar")));
    let json: UserShowResponse = ::json(&mut response);
    assert_eq!(Some("bar@baz.com".into()), json.user.email);
    assert_eq!("bar", json.user.login);
    assert_eq!(Some("https://github.com/bar".into()), json.user.url);
}

#[test]
fn crates_by_user_id() {
    let (_b, app, middle) = ::app();
    let u;
    {
        let conn = app.diesel_database.get().unwrap();
        u = ::new_user("foo").create_or_update(&conn).unwrap();
        ::CrateBuilder::new("foo_my_packages", u.id).expect_build(&conn);
    }

    let mut req = ::req(app, Method::Get, "/api/v1/crates");
    req.with_query(&format!("user_id={}", u.id));
    let mut response = ok_resp!(middle.call(&mut req));

    #[derive(RustcDecodable)]
    struct Response {
        crates: Vec<EncodableCrate>,
    }
    let response: Response = ::json(&mut response);
    assert_eq!(response.crates.len(), 1);
}

#[test]
fn following() {
    #[derive(RustcDecodable)]
    struct R {
        versions: Vec<EncodableVersion>,
        meta: Meta,
    }
    #[derive(RustcDecodable)]
    struct Meta {
        more: bool,
    }

    let (_b, app, middle) = ::app();
    let mut req = ::req(app.clone(), Method::Get, "/");
    {
        let conn = app.diesel_database.get().unwrap();
        let user = ::new_user("foo").create_or_update(&conn).unwrap();
        ::sign_in_as(&mut req, &user);

        ::CrateBuilder::new("foo_fighters", user.id)
            .version("1.0.0")
            .expect_build(&conn);

        ::CrateBuilder::new("bar_fighters", user.id)
            .version("1.0.0")
            .expect_build(&conn);
    }

    let mut response = ok_resp!(middle.call(
        req.with_path("/me/updates").with_method(Method::Get),
    ));
    let r = ::json::<R>(&mut response);
    assert_eq!(r.versions.len(), 0);
    assert_eq!(r.meta.more, false);

    ok_resp!(
        middle.call(
            req.with_path("/api/v1/crates/foo_fighters/follow")
                .with_method(Method::Put),
        )
    );
    ok_resp!(
        middle.call(
            req.with_path("/api/v1/crates/bar_fighters/follow")
                .with_method(Method::Put),
        )
    );

    let mut response = ok_resp!(middle.call(
        req.with_path("/me/updates").with_method(Method::Get),
    ));
    let r = ::json::<R>(&mut response);
    assert_eq!(r.versions.len(), 2);
    assert_eq!(r.meta.more, false);

    let mut response = ok_resp!(
        middle.call(
            req.with_path("/me/updates")
                .with_method(Method::Get)
                .with_query("per_page=1"),
        )
    );
    let r = ::json::<R>(&mut response);
    assert_eq!(r.versions.len(), 1);
    assert_eq!(r.meta.more, true);

    ok_resp!(
        middle.call(
            req.with_path("/api/v1/crates/bar_fighters/follow")
                .with_method(Method::Delete),
        )
    );
    let mut response = ok_resp!(
        middle.call(
            req.with_path("/me/updates")
                .with_method(Method::Get)
                .with_query("page=2&per_page=1"),
        )
    );
    let r = ::json::<R>(&mut response);
    assert_eq!(r.versions.len(), 0);
    assert_eq!(r.meta.more, false);

    bad_resp!(middle.call(req.with_query("page=0")));
}
<commit_msg>Re-add test that updating user's github login doesn't invalidate api tokens<commit_after>use std::sync::atomic::Ordering;

use conduit::{Handler, Method};

use cargo_registry::Model;
use cargo_registry::token::ApiToken;
use cargo_registry::krate::EncodableCrate;
use cargo_registry::user::{User, NewUser, EncodableUser};
use cargo_registry::version::EncodableVersion;

#[derive(RustcDecodable)]
struct AuthResponse {
    url: String,
    state: String,
}
#[derive(RustcDecodable)]
pub struct UserShowResponse {
    pub user: EncodableUser,
}

#[test]
fn auth_gives_a_token() {
    let (_b, app, middle) = ::app();
    let mut req = ::req(app, Method::Get, "/authorize_url");
    let mut response = ok_resp!(middle.call(&mut req));
    let json: AuthResponse = ::json(&mut response);
    assert!(json.url.contains(&json.state));
}

#[test]
fn access_token_needs_data() {
    let (_b, app, middle) = ::app();
    let mut req = ::req(app, Method::Get, "/authorize");
    let mut response = ok_resp!(middle.call(&mut req));
    let json: ::Bad = ::json(&mut response);
    assert!(json.errors[0].detail.contains("invalid state"));
}

#[test]
fn user_insert() {
    let (_b, app, _middle) = ::app();
    let conn = t!(app.database.get());
    let tx = t!(conn.transaction());

    let user = t!(User::find_or_insert(&tx, 1, "foo", None, None, None, "bar"));
    assert_eq!(t!(User::find(&tx, user.id)), user);

    assert_eq!(
        t!(User::find_or_insert(&tx, 1, "foo", None, None, None, "bar")),
        user
    );
    let user2 = t!(User::find_or_insert(&tx, 1, "foo", None, None, None, "baz"));
    assert!(user != user2);
    assert_eq!(user.id, user2.id);
    assert_eq!(user2.gh_access_token, "baz");

    let user3 = t!(User::find_or_insert(&tx, 1, "bar", None, None, None, "baz"));
    assert!(user != user3);
    assert_eq!(user.id, user3.id);
    assert_eq!(user3.gh_login, "bar");
}

#[test]
fn me() {
    let (_b, app, middle) = ::app();
    let mut req = ::req(app, Method::Get, "/me");
    let response = t_resp!(middle.call(&mut req));
    assert_eq!(response.status.0, 403);

    let user = ::mock_user(&mut req, ::user("foo"));

    let mut response = ok_resp!(middle.call(&mut req));
    let json: UserShowResponse = ::json(&mut response);

    assert_eq!(json.user.email, user.email);
}

#[test]
fn show() {
    let (_b, app, middle) = ::app();
    {
        let conn = t!(app.diesel_database.get());

        t!(NewUser::new(1, "foo", Some("foo@bar.com"), None, None, "bar").create_or_update(&conn));
        t!(NewUser::new(2, "bar", Some("bar@baz.com"), None, None, "bar").create_or_update(&conn));
    }

    let mut req = ::req(app.clone(), Method::Get, "/api/v1/users/foo");
    let mut response = ok_resp!(middle.call(&mut req));
    let json: UserShowResponse = ::json(&mut response);
    assert_eq!(Some("foo@bar.com".into()), json.user.email);
    assert_eq!("foo", json.user.login);

    let mut response = ok_resp!(middle.call(req.with_path("/api/v1/users/bar")));
    let json: UserShowResponse = ::json(&mut response);
    assert_eq!(Some("bar@baz.com".into()), json.user.email);
    assert_eq!("bar", json.user.login);
    assert_eq!(Some("https://github.com/bar".into()), json.user.url);
}

#[test]
fn crates_by_user_id() {
    let (_b, app, middle) = ::app();
    let u;
    {
        let conn = app.diesel_database.get().unwrap();
        u = ::new_user("foo").create_or_update(&conn).unwrap();
        ::CrateBuilder::new("foo_my_packages", u.id).expect_build(&conn);
    }

    let mut req = ::req(app, Method::Get, "/api/v1/crates");
    req.with_query(&format!("user_id={}", u.id));
    let mut response = ok_resp!(middle.call(&mut req));

    #[derive(RustcDecodable)]
    struct Response {
        crates: Vec<EncodableCrate>,
    }
    let response: Response = ::json(&mut response);
    assert_eq!(response.crates.len(), 1);
}

#[test]
fn following() {
    #[derive(RustcDecodable)]
    struct R {
        versions: Vec<EncodableVersion>,
        meta: Meta,
    }
    #[derive(RustcDecodable)]
    struct Meta {
        more: bool,
    }

    let (_b, app, middle) = ::app();
    let mut req = ::req(app.clone(), Method::Get, "/");
    {
        let conn = app.diesel_database.get().unwrap();
        let user = ::new_user("foo").create_or_update(&conn).unwrap();
        ::sign_in_as(&mut req, &user);

        ::CrateBuilder::new("foo_fighters", user.id)
            .version("1.0.0")
            .expect_build(&conn);

        ::CrateBuilder::new("bar_fighters", user.id)
            .version("1.0.0")
            .expect_build(&conn);
    }

    let mut response = ok_resp!(middle.call(
        req.with_path("/me/updates").with_method(Method::Get),
    ));
    let r = ::json::<R>(&mut response);
    assert_eq!(r.versions.len(), 0);
    assert_eq!(r.meta.more, false);

    ok_resp!(
        middle.call(
            req.with_path("/api/v1/crates/foo_fighters/follow")
                .with_method(Method::Put),
        )
    );
    ok_resp!(
        middle.call(
            req.with_path("/api/v1/crates/bar_fighters/follow")
                .with_method(Method::Put),
        )
    );

    let mut response = ok_resp!(middle.call(
        req.with_path("/me/updates").with_method(Method::Get),
    ));
    let r = ::json::<R>(&mut response);
    assert_eq!(r.versions.len(), 2);
    assert_eq!(r.meta.more, false);

    let mut response = ok_resp!(
        middle.call(
            req.with_path("/me/updates")
                .with_method(Method::Get)
                .with_query("per_page=1"),
        )
    );
    let r = ::json::<R>(&mut response);
    assert_eq!(r.versions.len(), 1);
    assert_eq!(r.meta.more, true);

    ok_resp!(
        middle.call(
            req.with_path("/api/v1/crates/bar_fighters/follow")
                .with_method(Method::Delete),
        )
    );
    let mut response = ok_resp!(
        middle.call(
            req.with_path("/me/updates")
                .with_method(Method::Get)
                .with_query("page=2&per_page=1"),
        )
    );
    let r = ::json::<R>(&mut response);
    assert_eq!(r.versions.len(), 0);
    assert_eq!(r.meta.more, false);

    bad_resp!(middle.call(req.with_query("page=0")));
}

#[test]
fn updating_existing_user_doesnt_change_api_token() {
    let (_b, app, _middle) = ::app();
    let conn = t!(app.diesel_database.get());

    let gh_user_id = ::NEXT_ID.fetch_add(1, Ordering::SeqCst) as i32;

    let original_user = t!(NewUser::new(gh_user_id, "foo", None, None, None, "foo_token").create_or_update(&conn));
    let token = t!(ApiToken::insert(&conn, original_user.id, "foo"));

    t!(NewUser::new(gh_user_id, "bar", None, None, None, "bar_token").create_or_update(&conn));
    let user = t!(User::find_by_api_token(&conn, &token.token));

    assert_eq!("bar", user.gh_login);
    assert_eq!("bar_token", user.gh_access_token);
}
<|endoftext|>


<commit_before>//! A simple adaptive threadpool that returns a oneshot future.

use std::sync::Arc;

use crate::{OneShot, Result};

#[cfg(all(
    not(miri),
    any(
        windows,
        target_os = "linux",
        target_os = "macos",
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
    )
))]
mod queue {
    use std::{
        collections::VecDeque,
        sync::Once,
        time::{Duration, Instant},
    };

    use parking_lot::{Condvar, Mutex};

    use crate::{debug_delay, Lazy, OneShot, Result};

    pub(super) static BLOCKING_QUEUE: Lazy<Queue, fn() -> Queue> =
        Lazy::new(Default::default);
    pub(super) static IO_QUEUE: Lazy<Queue, fn() -> Queue> =
        Lazy::new(Default::default);
    pub(super) static SNAPSHOT_QUEUE: Lazy<Queue, fn() -> Queue> =
        Lazy::new(Default::default);

    type Work = Box<dyn FnOnce() + Send + 'static>;

    pub(super) fn spawn_to<F, R>(
        work: F,
        queue: &'static Queue,
    ) -> Result<OneShot<R>>
    where
        F: FnOnce() -> R + Send + 'static,
        R: Send + 'static + Sized,
    {
        static START_THREADS: Once = Once::new();

        START_THREADS.call_once(|| {
            std::thread::Builder::new()
                .name("sled-io-thread".into())
                .spawn(|| IO_QUEUE.perform_work(true, false))
                .expect("failed to spawn critical IO thread");

            std::thread::Builder::new()
                .name("sled-blocking-thread".into())
                .spawn(|| BLOCKING_QUEUE.perform_work(true, false))
                .expect("failed to spawn critical blocking thread");

            std::thread::Builder::new()
                .name("sled-snapshot-thread".into())
                .spawn(|| SNAPSHOT_QUEUE.perform_work(false, false))
                .expect("failed to spawn critical snapshot thread");
        });

        let (promise_filler, promise) = OneShot::pair();
        let task = move || {
            promise_filler.fill((work)());
        };

        queue.send(Box::new(task));

        Ok(promise)
    }

    #[derive(Default)]
    pub(super) struct Queue {
        cv: Condvar,
        mu: Mutex<VecDeque<Work>>,
    }

    #[allow(unsafe_code)]
    unsafe impl Send for Queue {}

    impl Queue {
        fn recv_timeout(&self, duration: Duration) -> Option<(Work, usize)> {
            let mut queue = self.mu.lock();

            let cutoff = Instant::now() + duration;

            while queue.is_empty() {
                let res = self.cv.wait_until(&mut queue, cutoff);
                if res.timed_out() {
                    break;
                }
            }

            queue.pop_front().map(|w| (w, queue.len()))
        }

        fn send(&self, work: Work) -> usize {
            let mut queue = self.mu.lock();
            queue.push_back(work);

            let len = queue.len();

            // having held the mutex makes this linearized
            // with the notify below.
            drop(queue);

            self.cv.notify_all();

            len
        }

        fn perform_work(&'static self, elastic: bool, temporary: bool) {
            let wait_limit = Duration::from_millis(100);

            let mut last_employed = Instant::now();
            while !temporary || last_employed.elapsed() < Duration::from_secs(5)
            {
                let guard = crate::pin();
                guard.flush();
                drop(guard);
                debug_delay();
                let task_opt = self.recv_timeout(wait_limit);

                if let Some((task, outstanding_work)) = task_opt {
                    // spin up some help if we're falling behind
                    if elastic && outstanding_work > 5 {
                        let spawn_res = std::thread::Builder::new()
                            .name("sled-temporary-thread".into())
                            .spawn(move || self.perform_work(false, true));
                        if let Err(e) = spawn_res {
                            log::error!(
                                "failed to spin-up temporary work thread: {:?}",
                                e
                            );
                        }
                    }

                    // execute the work sent to this thread
                    (task)();

                    last_employed = Instant::now();
                }
            }
        }
    }
}

/// Spawn a function on the threadpool.
pub fn spawn<F, R>(work: F) -> Result<OneShot<R>>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static + Sized,
{
    spawn_to(work, &queue::BLOCKING_QUEUE)
}

#[cfg(any(
    miri,
    not(any(
        windows,
        target_os = "linux",
        target_os = "macos",
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
    ))
))]
mod queue {
    /// This is the polyfill that just executes things synchronously.
    use crate::{OneShot, Result};

    pub(super) fn spawn_to<F, R>(work: F, _: &()) -> Result<OneShot<R>>
    where
        F: FnOnce() -> R + Send + 'static,
        R: Send + 'static,
    {
        // Polyfill for platforms other than those we explicitly trust to
        // perform threaded work on. Just execute a task without involving threads.
        let (promise_filler, promise) = OneShot::pair();
        promise_filler.fill((work)());
        Ok(promise)
    }

    pub(super) const IO_QUEUE: () = ();
    pub(super) const BLOCKING_QUEUE: () = ();
    pub(super) const SNAPSHOT_QUEUE: () = ();
}

use queue::spawn_to;

pub fn truncate(
    config: crate::RunningConfig,
    at: u64,
) -> Result<OneShot<Result<()>>> {
    spawn_to(
        move || {
            log::debug!("truncating file to length {}", at);
            config
                .file
                .set_len(at)
                .and_then(|_| config.file.sync_all())
                .map_err(|e| e.into())
        },
        &queue::IO_QUEUE,
    )
}

pub fn take_fuzzy_snapshot(
    pc: crate::pagecache::PageCache,
) -> Result<OneShot<()>> {
    spawn_to(
        move || {
            if let Err(e) = pc.take_fuzzy_snapshot() {
                log::error!("failed to write snapshot: {:?}", e);
            }
        },
        &queue::SNAPSHOT_QUEUE,
    )
}

pub(crate) fn write_to_log(
    iobuf: Arc<crate::pagecache::iobuf::IoBuf>,
    iobufs: Arc<crate::pagecache::iobuf::IoBufs>,
) -> Result<OneShot<()>> {
    spawn_to(
        move || {
            let lsn = iobuf.lsn;
            if let Err(e) = iobufs.write_to_log(iobuf) {
                log::error!(
                    "hit error while writing iobuf with lsn {}: {:?}",
                    lsn,
                    e
                );

                // store error before notifying so that waiting threads will see
                // it
                iobufs.config.set_global_error(e);

                let intervals = iobufs.intervals.lock();

                // having held the mutex makes this linearized
                // with the notify below.
                drop(intervals);

                let _notified = iobufs.interval_updated.notify_all();
            }
        },
        &queue::IO_QUEUE,
    )
}
<commit_msg>Add a specific truncation thread to make dependency loops between the SA and blocking IO impossible<commit_after>//! A simple adaptive threadpool that returns a oneshot future.

use std::sync::Arc;

use crate::{OneShot, Result};

#[cfg(all(
    not(miri),
    any(
        windows,
        target_os = "linux",
        target_os = "macos",
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
    )
))]
mod queue {
    use std::{
        collections::VecDeque,
        sync::Once,
        time::{Duration, Instant},
    };

    use parking_lot::{Condvar, Mutex};

    use crate::{debug_delay, Lazy, OneShot, Result};

    pub(super) static BLOCKING_QUEUE: Lazy<Queue, fn() -> Queue> =
        Lazy::new(Default::default);
    pub(super) static IO_QUEUE: Lazy<Queue, fn() -> Queue> =
        Lazy::new(Default::default);
    pub(super) static SNAPSHOT_QUEUE: Lazy<Queue, fn() -> Queue> =
        Lazy::new(Default::default);
    pub(super) static TRUNCATE_QUEUE: Lazy<Queue, fn() -> Queue> =
        Lazy::new(Default::default);

    type Work = Box<dyn FnOnce() + Send + 'static>;

    pub(super) fn spawn_to<F, R>(
        work: F,
        queue: &'static Queue,
    ) -> Result<OneShot<R>>
    where
        F: FnOnce() -> R + Send + 'static,
        R: Send + 'static + Sized,
    {
        static START_THREADS: Once = Once::new();

        START_THREADS.call_once(|| {
            std::thread::Builder::new()
                .name("sled-io-thread".into())
                .spawn(|| IO_QUEUE.perform_work(true, false))
                .expect("failed to spawn critical IO thread");

            std::thread::Builder::new()
                .name("sled-blocking-thread".into())
                .spawn(|| BLOCKING_QUEUE.perform_work(true, false))
                .expect("failed to spawn critical blocking thread");

            std::thread::Builder::new()
                .name("sled-snapshot-thread".into())
                .spawn(|| SNAPSHOT_QUEUE.perform_work(false, false))
                .expect("failed to spawn critical snapshot thread");

            std::thread::Builder::new()
                .name("sled-truncate-thread".into())
                .spawn(|| TRUNCATE_QUEUE.perform_work(false, false))
                .expect("failed to spawn critical truncation thread");
        });

        let (promise_filler, promise) = OneShot::pair();
        let task = move || {
            promise_filler.fill((work)());
        };

        queue.send(Box::new(task));

        Ok(promise)
    }

    #[derive(Default)]
    pub(super) struct Queue {
        cv: Condvar,
        mu: Mutex<VecDeque<Work>>,
    }

    #[allow(unsafe_code)]
    unsafe impl Send for Queue {}

    impl Queue {
        fn recv_timeout(&self, duration: Duration) -> Option<(Work, usize)> {
            let mut queue = self.mu.lock();

            let cutoff = Instant::now() + duration;

            while queue.is_empty() {
                let res = self.cv.wait_until(&mut queue, cutoff);
                if res.timed_out() {
                    break;
                }
            }

            queue.pop_front().map(|w| (w, queue.len()))
        }

        fn send(&self, work: Work) -> usize {
            let mut queue = self.mu.lock();
            queue.push_back(work);

            let len = queue.len();

            // having held the mutex makes this linearized
            // with the notify below.
            drop(queue);

            self.cv.notify_all();

            len
        }

        fn perform_work(&'static self, elastic: bool, temporary: bool) {
            let wait_limit = Duration::from_millis(100);

            let mut unemployed_loops = 0;
            while !temporary || unemployed_loops < 50 {
                // take on a bit of GC labor
                let guard = crate::pin();
                guard.flush();
                drop(guard);

                debug_delay();
                let task_opt = self.recv_timeout(wait_limit);

                if let Some((task, outstanding_work)) = task_opt {
                    // spin up some help if we're falling behind
                    if elastic && outstanding_work > 5 {
                        let spawn_res = std::thread::Builder::new()
                            .name("sled-temporary-thread".into())
                            .spawn(move || self.perform_work(false, true));
                        if let Err(e) = spawn_res {
                            log::error!(
                                "failed to spin-up temporary work thread: {:?}",
                                e
                            );
                        }
                    }

                    // execute the work sent to this thread
                    (task)();

                    unemployed_loops = 0;
                } else {
                    unemployed_loops += 1;
                }
            }
        }
    }
}

/// Spawn a function on the threadpool.
pub fn spawn<F, R>(work: F) -> Result<OneShot<R>>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static + Sized,
{
    spawn_to(work, &queue::BLOCKING_QUEUE)
}

#[cfg(any(
    miri,
    not(any(
        windows,
        target_os = "linux",
        target_os = "macos",
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
    ))
))]
mod queue {
    /// This is the polyfill that just executes things synchronously.
    use crate::{OneShot, Result};

    pub(super) fn spawn_to<F, R>(work: F, _: &()) -> Result<OneShot<R>>
    where
        F: FnOnce() -> R + Send + 'static,
        R: Send + 'static,
    {
        // Polyfill for platforms other than those we explicitly trust to
        // perform threaded work on. Just execute a task without involving threads.
        let (promise_filler, promise) = OneShot::pair();
        promise_filler.fill((work)());
        Ok(promise)
    }

    pub(super) const IO_QUEUE: () = ();
    pub(super) const BLOCKING_QUEUE: () = ();
    pub(super) const SNAPSHOT_QUEUE: () = ();
    pub(super) const TRUNCATE_QUEUE: () = ();
}

use queue::spawn_to;

pub fn truncate(
    config: crate::RunningConfig,
    at: u64,
) -> Result<OneShot<Result<()>>> {
    spawn_to(
        move || {
            log::debug!("truncating file to length {}", at);
            config
                .file
                .set_len(at)
                .and_then(|_| config.file.sync_all())
                .map_err(|e| e.into())
        },
        &queue::TRUNCATE_QUEUE,
    )
}

pub fn take_fuzzy_snapshot(
    pc: crate::pagecache::PageCache,
) -> Result<OneShot<()>> {
    spawn_to(
        move || {
            if let Err(e) = pc.take_fuzzy_snapshot() {
                log::error!("failed to write snapshot: {:?}", e);
            }
        },
        &queue::SNAPSHOT_QUEUE,
    )
}

pub(crate) fn write_to_log(
    iobuf: Arc<crate::pagecache::iobuf::IoBuf>,
    iobufs: Arc<crate::pagecache::iobuf::IoBufs>,
) -> Result<OneShot<()>> {
    spawn_to(
        move || {
            let lsn = iobuf.lsn;
            if let Err(e) = iobufs.write_to_log(iobuf) {
                log::error!(
                    "hit error while writing iobuf with lsn {}: {:?}",
                    lsn,
                    e
                );

                // store error before notifying so that waiting threads will see
                // it
                iobufs.config.set_global_error(e);

                let intervals = iobufs.intervals.lock();

                // having held the mutex makes this linearized
                // with the notify below.
                drop(intervals);

                let _notified = iobufs.interval_updated.notify_all();
            }
        },
        &queue::IO_QUEUE,
    )
}
<|endoftext|>


<commit_before>use std::io::File;
use std::io::fs::walk_dir;
use std::io::fs::copy;
use std::io::fs::rename;
use std::io::fs::stat;
use std::io::fs::change_file_times;
use std::io::TempDir;
use std::io::fs::PathExtensions;
use std::io::Command;

#[test]
fn test_renames_via_single_process() {
    test_renames("");
}

#[test]
fn test_renames_via_external_process() {
    test_renames("--use-external-process");
}

fn test_renames<T: ToCStr>(argument: T) {

    // Create a temporary directory and write some
    // files into it
    let original_directory = TempDir::new("").unwrap();
    write_file(original_directory.path(), "file1".to_string(), "000".to_string());
    write_file(original_directory.path(), "file2".to_string(), "1111".to_string());
    write_file(original_directory.path(), "file3".to_string(), "22222".to_string());

    // Copy all those files into a new directory
    let new_directory = TempDir::new("").unwrap();
    copy_directory_contents(original_directory.path(), new_directory.path());

    // Rename some of the files of the original directory
    rename_keeping_modified_time(
        &original_directory.path().join("file1"),
        &original_directory.path().join("file7"));
    rename_keeping_modified_time(
        &original_directory.path().join("file2"),
        &original_directory.path().join("file11"));

    // Run mvsync
    let mut process = match Command::new("./target/mvsync")
        .arg(original_directory.path().as_str().unwrap())
        .arg(new_directory.path().as_str().unwrap())
        .arg(argument)
        .spawn()
        {
            Ok(process) => process,
            Err(e) => fail!("failed to execute process: {}", e),
        };

    // Set a timeout of 4 seconds
    process.set_timeout(Some(4_000));

    match process.wait()
    {
        Ok(status) =>
        {
            println!("{}", process.stdout.as_mut().unwrap().read_to_string().unwrap());
            println!("{}", process.stderr.as_mut().unwrap().read_to_string().unwrap());
        },
        Err(e) =>
        {
            process.signal_kill();
            println!("{}", process.stdout.as_mut().unwrap().read_to_string().unwrap());
            println!("{}", process.stderr.as_mut().unwrap().read_to_string().unwrap());
            fail!("failed to execute process: {}", e)
        }
    }

    // Get the output
    //let output = match process.wait_with_output()
    //{
    //    Ok(output) => output,
    //    Err(e) => {
    //        fail!("process timemout: {}", e);
    //    }
    //};

    //println!("status: {}", output.status);
    //println!("stdout: {}", String::from_utf8_lossy(output.output.as_slice()));
    //println!("stderr: {}", String::from_utf8_lossy(output.error.as_slice()));

    // Verify that the new directory contents have been renamed
    // accordingly
    assert!(new_directory.path().join("file1").exists() == false);
    assert!(new_directory.path().join("file2").exists() == false);
    assert!(new_directory.path().join("file7").exists() == true);
    assert!(new_directory.path().join("file11").exists() == true);
}

fn write_file(directory: &Path, file_name: String, contents: String) {

    let full_path = directory.join(file_name);
    let mut file = File::create(&full_path);
    file.write(contents.as_bytes()).unwrap();

}

fn copy_directory_contents(source: &Path, destination: &Path) {

   for item in walk_dir(source).unwrap() {
       let relative_path = item.path_relative_from(source).unwrap();
       let destination_path = destination.join(relative_path);
       copy(&item, &destination_path).unwrap();
       let metadata = stat(&item).unwrap();
       change_file_times(&item, metadata.accessed, metadata.modified).unwrap();
       change_file_times(&destination_path, metadata.accessed, metadata.modified).unwrap();
   }

}

fn rename_keeping_modified_time(source: &Path, destination: &Path) {

    let metadata = stat(source).unwrap();
    rename(source, destination).unwrap();
    change_file_times(destination, metadata.accessed, metadata.modified).unwrap();
}
<commit_msg>Add argument parsing test<commit_after>use std::io::File;
use std::io::fs::walk_dir;
use std::io::fs::copy;
use std::io::fs::rename;
use std::io::fs::stat;
use std::io::fs::change_file_times;
use std::io::TempDir;
use std::io::fs::PathExtensions;
use std::io::Command;

#[test]
fn test_renames_via_single_process() {
    test_renames("");
}

#[test]
fn test_renames_via_external_process() {
    test_renames("--use-external-process");
}

#[test]
fn test_incorrect_option() {

    let mut process = Command::new("./target/mvsync")
        .arg("/source")
        .arg("/destination")
        .arg("--this-flag-does-not-exists")
        .spawn()
        .unwrap();

   assert!(process.wait().unwrap().success() == false);
}

fn test_renames<T: ToCStr>(argument: T) {

    // Create a temporary directory and write some
    // files into it
    let original_directory = TempDir::new("").unwrap();
    write_file(original_directory.path(), "file1".to_string(), "000".to_string());
    write_file(original_directory.path(), "file2".to_string(), "1111".to_string());
    write_file(original_directory.path(), "file3".to_string(), "22222".to_string());

    // Copy all those files into a new directory
    let new_directory = TempDir::new("").unwrap();
    copy_directory_contents(original_directory.path(), new_directory.path());

    // Rename some of the files of the original directory
    rename_keeping_modified_time(
        &original_directory.path().join("file1"),
        &original_directory.path().join("file7"));
    rename_keeping_modified_time(
        &original_directory.path().join("file2"),
        &original_directory.path().join("file11"));

    // Run mvsync
    let mut process = match Command::new("./target/mvsync")
        .arg(original_directory.path().as_str().unwrap())
        .arg(new_directory.path().as_str().unwrap())
        .arg(argument)
        .spawn()
        {
            Ok(process) => process,
            Err(e) => fail!("failed to execute process: {}", e),
        };

    // Set a timeout of 4 seconds
    process.set_timeout(Some(4_000));

    match process.wait()
    {
        Ok(status) =>
        {
            println!("{}", process.stdout.as_mut().unwrap().read_to_string().unwrap());
            println!("{}", process.stderr.as_mut().unwrap().read_to_string().unwrap());
        },
        Err(e) =>
        {
            process.signal_kill();
            println!("{}", process.stdout.as_mut().unwrap().read_to_string().unwrap());
            println!("{}", process.stderr.as_mut().unwrap().read_to_string().unwrap());
            fail!("failed to execute process: {}", e)
        }
    }

    // Get the output
    //let output = match process.wait_with_output()
    //{
    //    Ok(output) => output,
    //    Err(e) => {
    //        fail!("process timemout: {}", e);
    //    }
    //};

    //println!("status: {}", output.status);
    //println!("stdout: {}", String::from_utf8_lossy(output.output.as_slice()));
    //println!("stderr: {}", String::from_utf8_lossy(output.error.as_slice()));

    // Verify that the new directory contents have been renamed
    // accordingly
    assert!(new_directory.path().join("file1").exists() == false);
    assert!(new_directory.path().join("file2").exists() == false);
    assert!(new_directory.path().join("file7").exists() == true);
    assert!(new_directory.path().join("file11").exists() == true);
}

fn write_file(directory: &Path, file_name: String, contents: String) {

    let full_path = directory.join(file_name);
    let mut file = File::create(&full_path);
    file.write(contents.as_bytes()).unwrap();

}

fn copy_directory_contents(source: &Path, destination: &Path) {

   for item in walk_dir(source).unwrap() {
       let relative_path = item.path_relative_from(source).unwrap();
       let destination_path = destination.join(relative_path);
       copy(&item, &destination_path).unwrap();
       let metadata = stat(&item).unwrap();
       change_file_times(&item, metadata.accessed, metadata.modified).unwrap();
       change_file_times(&destination_path, metadata.accessed, metadata.modified).unwrap();
   }

}

fn rename_keeping_modified_time(source: &Path, destination: &Path) {

    let metadata = stat(source).unwrap();
    rename(source, destination).unwrap();
    change_file_times(destination, metadata.accessed, metadata.modified).unwrap();
}
<|endoftext|>


<commit_before>// Copyright 2015-2016 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use std;
use std::{thread, time};
use std::fs::File;
use std::net::SocketAddr;
use std::net::{IpAddr, Ipv4Addr};
#[cfg(not(target_os = "linux"))]
use std::net::Ipv6Addr;
use std::io::{Read, Write};
use std::sync::Arc;
use std::sync::atomic;

use futures::Stream;
use native_tls;
use native_tls::{Certificate, TlsAcceptor};
use tokio_core::reactor::Core;

use {TlsStream, TlsStreamBuilder};

// this fails on linux for some reason. It appears that a buffer somewhere is dirty
//  and subsequent reads of a mesage buffer reads the wrong length. It works for 2 iterations
//  but not 3?
// #[cfg(not(target_os = "linux"))]
#[test]
fn test_tls_client_stream_ipv4() {
    tls_client_stream_test(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), false)
}

// FIXME: mtls is disabled at the moment, it causes a hang on Linux, and is currently not supported on macOS
#[cfg(feature = "mtls")]
#[test]
#[cfg(not(target_os = "macos"))] // ignored until Travis-CI fixes IPv6
fn test_tls_client_stream_ipv4_mtls() {
    tls_client_stream_test(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), true)
}

#[test]
#[cfg(not(target_os = "linux"))] // ignored until Travis-CI fixes IPv6
fn test_tls_client_stream_ipv6() {
    tls_client_stream_test(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), false)
}

const TEST_BYTES: &'static [u8; 8] = b"DEADBEEF";
const TEST_BYTES_LEN: usize = 8;

fn read_file(path: &str) -> Vec<u8> {
    let mut bytes = vec![];

    let mut file = File::open(path).expect(&format!("failed to open file: {}", path));
    file.read_to_end(&mut bytes)
        .expect(&format!("failed to read file: {}", path));
    bytes
}

#[allow(unused_mut)]
fn tls_client_stream_test(server_addr: IpAddr, mtls: bool) {
    let succeeded = Arc::new(atomic::AtomicBool::new(false));
    let succeeded_clone = succeeded.clone();
    thread::Builder::new()
        .name("thread_killer".to_string())
        .spawn(move || {
            let succeeded = succeeded_clone.clone();
            for _ in 0..15 {
                thread::sleep(time::Duration::from_secs(1));
                if succeeded.load(atomic::Ordering::Relaxed) {
                    return;
                }
            }

            panic!("timeout");
        })
        .unwrap();

    let root_cert_der = read_file("../tests/ca.der");

    // Generate X509 certificate
    let subject_name = "ns.example.com";
    let server_pkcs12_der = read_file("../tests/cert.p12");

    // TODO: need a timeout on listen
    let server = std::net::TcpListener::bind(SocketAddr::new(server_addr, 0)).unwrap();
    let server_addr = server.local_addr().unwrap();

    let send_recv_times = 4;

    let server_handle = thread::Builder::new().name("test_tls_client_stream:server".to_string()).spawn(move || {
    let pkcs12 = native_tls::Pkcs12::from_der(&server_pkcs12_der, "mypass").expect("Pkcs12::from_der");
    let mut tls = TlsAcceptor::builder(pkcs12).expect("build with pkcs12 failed");

    // #[cfg(target_os = "linux")]
    // {
    //   let mut openssl_builder = tls.builder_mut();
    //   let mut openssl_ctx_builder = openssl_builder.builder_mut();

    //   let mut mode = openssl::ssl::SslVerifyMode::empty();

    //   // FIXME: mtls tests hang on Linux...
    //   if mtls {
    //     //   mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;

    //     // let mut store = X509StoreBuilder::new().unwrap();
    //     // let root_ca = X509::from_der(&root_cert_der_copy).unwrap();
    //     // store.add_cert(root_ca).unwrap();
    //     // openssl_ctx_builder.set_verify_cert_store(store.build()).unwrap();
    //   } else {
    //     mode.insert(SSL_VERIFY_NONE);
    //   }

    //   openssl_ctx_builder.set_verify(mode);
    // }

    // FIXME: add CA on macOS

    let tls = tls.build().expect("tls build failed");

    // server_barrier.wait();
    let (socket, _) = server.accept().expect("tcp accept failed");
    socket.set_read_timeout(Some(std::time::Duration::from_secs(5))).unwrap(); // should recieve something within 5 seconds...
    socket.set_write_timeout(Some(std::time::Duration::from_secs(5))).unwrap(); // should recieve something within 5 seconds...

    let mut socket = tls.accept(socket).expect("tls accept failed");

    for _ in 0..send_recv_times {
      // wait for some bytes...
      let mut len_bytes = [0_u8; 2];
      socket.read_exact(&mut len_bytes).expect("SERVER: receive failed");
      let length = (len_bytes[0] as u16) << 8 & 0xFF00 | len_bytes[1] as u16 & 0x00FF;
      assert_eq!(length as usize, TEST_BYTES_LEN);

      let mut buffer = [0_u8; TEST_BYTES_LEN];
      socket.read_exact(&mut buffer).unwrap();

      // println!("read bytes iter: {}", i);
      assert_eq!(&buffer, TEST_BYTES);

      // bounce them right back...
      socket.write_all(&len_bytes).expect("SERVER: send length failed");
      socket.write_all(&buffer).expect("SERVER: send buffer failed");
      // println!("wrote bytes iter: {}", i);
      std::thread::yield_now();
    }
  }).unwrap();

    // let the server go first
    std::thread::yield_now();

    // setup the client, which is going to run on the testing thread...
    let mut io_loop = Core::new().unwrap();

    // the tests should run within 5 seconds... right?
    // TODO: add timeout here, so that test never hangs...
    // let timeout = Timeout::new(Duration::from_secs(5), &io_loop.handle());

    let trust_chain = Certificate::from_der(&root_cert_der).unwrap();

    // barrier.wait();
    let mut builder = TlsStreamBuilder::new();
    builder.add_ca(trust_chain);

    // fix MTLS
    // if mtls {
    //     config_mtls(&root_pkey, &root_name, &root_cert, &mut builder);
    // }

    let (stream, sender) = builder.build(server_addr, subject_name.to_string(), io_loop.handle());

    // TODO: there is a race failure here... a race with the server thread most likely...
    let mut stream = io_loop
        .run(stream)
        .ok()
        .expect("run failed to get stream");

    for _ in 0..send_recv_times {
        // test once
        sender
            .send((TEST_BYTES.to_vec(), server_addr))
            .expect("send failed");
        let (buffer, stream_tmp) = io_loop
            .run(stream.into_future())
            .ok()
            .expect("future iteration run failed");
        stream = stream_tmp;
        let (buffer, _) = buffer.expect("no buffer received");
        assert_eq!(&buffer, TEST_BYTES);
    }

    succeeded.store(true, std::sync::atomic::Ordering::Relaxed);
    server_handle.join().expect("server thread failed");
}

// TODO: fix MTLS
// #[allow(unused_variables)]
// fn config_mtls(root_pkey: &PKey,
//                root_name: &X509Name,
//                root_cert: &X509,
//                builder: &mut TlsStreamBuilder) {
//     // signed by the same root cert
//     let client_name = "resolv.example.com";
//     let (_ /*client_pkey*/, _ /*client_cert*/, client_identity) =
//         cert(client_name, root_pkey, root_name, root_cert);
//     let client_identity =
//         native_tls::Pkcs12::from_der(&client_identity.to_der().unwrap(), "mypass").unwrap();

//     #[cfg(feature = "mtls")]
//     builder.identity(client_identity);
// }
<commit_msg>fix kcov paths for native-tls<commit_after>// Copyright 2015-2016 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use std;
use std::env;
use std::fs::File;
use std::net::SocketAddr;
use std::net::{IpAddr, Ipv4Addr};
#[cfg(not(target_os = "linux"))]
use std::net::Ipv6Addr;
use std::io::{Read, Write};
use std::sync::Arc;
use std::sync::atomic;
use std::{thread, time};

use futures::Stream;
use native_tls;
use native_tls::{Certificate, TlsAcceptor};
use tokio_core::reactor::Core;

use {TlsStream, TlsStreamBuilder};

// this fails on linux for some reason. It appears that a buffer somewhere is dirty
//  and subsequent reads of a mesage buffer reads the wrong length. It works for 2 iterations
//  but not 3?
// #[cfg(not(target_os = "linux"))]
#[test]
fn test_tls_client_stream_ipv4() {
    tls_client_stream_test(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), false)
}

// FIXME: mtls is disabled at the moment, it causes a hang on Linux, and is currently not supported on macOS
#[cfg(feature = "mtls")]
#[test]
#[cfg(not(target_os = "macos"))] // ignored until Travis-CI fixes IPv6
fn test_tls_client_stream_ipv4_mtls() {
    tls_client_stream_test(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), true)
}

#[test]
#[cfg(not(target_os = "linux"))] // ignored until Travis-CI fixes IPv6
fn test_tls_client_stream_ipv6() {
    tls_client_stream_test(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), false)
}

const TEST_BYTES: &'static [u8; 8] = b"DEADBEEF";
const TEST_BYTES_LEN: usize = 8;

fn read_file(path: &str) -> Vec<u8> {
    let mut bytes = vec![];

    let mut file = File::open(path).expect(&format!("failed to open file: {}", path));
    file.read_to_end(&mut bytes)
        .expect(&format!("failed to read file: {}", path));
    bytes
}

#[allow(unused_mut)]
fn tls_client_stream_test(server_addr: IpAddr, mtls: bool) {
    let succeeded = Arc::new(atomic::AtomicBool::new(false));
    let succeeded_clone = succeeded.clone();
    thread::Builder::new()
        .name("thread_killer".to_string())
        .spawn(move || {
            let succeeded = succeeded_clone.clone();
            for _ in 0..15 {
                thread::sleep(time::Duration::from_secs(1));
                if succeeded.load(atomic::Ordering::Relaxed) {
                    return;
                }
            }

            panic!("timeout");
        })
        .unwrap();

    let server_path = env::var("TDNS_SERVER_SRC_ROOT").unwrap_or("../server".to_owned());
    println!("using server src path: {}", server_path);

    let root_cert_der = read_file(&format!("{}/../tests/ca.der", server_path));

    // Generate X509 certificate
    let subject_name = "ns.example.com";
    let server_pkcs12_der = read_file("../tests/cert.p12");

    // TODO: need a timeout on listen
    let server = std::net::TcpListener::bind(SocketAddr::new(server_addr, 0)).unwrap();
    let server_addr = server.local_addr().unwrap();

    let send_recv_times = 4;

    let server_handle = thread::Builder::new().name("test_tls_client_stream:server".to_string()).spawn(move || {
    let pkcs12 = native_tls::Pkcs12::from_der(&server_pkcs12_der, "mypass").expect("Pkcs12::from_der");
    let mut tls = TlsAcceptor::builder(pkcs12).expect("build with pkcs12 failed");

    // #[cfg(target_os = "linux")]
    // {
    //   let mut openssl_builder = tls.builder_mut();
    //   let mut openssl_ctx_builder = openssl_builder.builder_mut();

    //   let mut mode = openssl::ssl::SslVerifyMode::empty();

    //   // FIXME: mtls tests hang on Linux...
    //   if mtls {
    //     //   mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;

    //     // let mut store = X509StoreBuilder::new().unwrap();
    //     // let root_ca = X509::from_der(&root_cert_der_copy).unwrap();
    //     // store.add_cert(root_ca).unwrap();
    //     // openssl_ctx_builder.set_verify_cert_store(store.build()).unwrap();
    //   } else {
    //     mode.insert(SSL_VERIFY_NONE);
    //   }

    //   openssl_ctx_builder.set_verify(mode);
    // }

    // FIXME: add CA on macOS

    let tls = tls.build().expect("tls build failed");

    // server_barrier.wait();
    let (socket, _) = server.accept().expect("tcp accept failed");
    socket.set_read_timeout(Some(std::time::Duration::from_secs(5))).unwrap(); // should recieve something within 5 seconds...
    socket.set_write_timeout(Some(std::time::Duration::from_secs(5))).unwrap(); // should recieve something within 5 seconds...

    let mut socket = tls.accept(socket).expect("tls accept failed");

    for _ in 0..send_recv_times {
      // wait for some bytes...
      let mut len_bytes = [0_u8; 2];
      socket.read_exact(&mut len_bytes).expect("SERVER: receive failed");
      let length = (len_bytes[0] as u16) << 8 & 0xFF00 | len_bytes[1] as u16 & 0x00FF;
      assert_eq!(length as usize, TEST_BYTES_LEN);

      let mut buffer = [0_u8; TEST_BYTES_LEN];
      socket.read_exact(&mut buffer).unwrap();

      // println!("read bytes iter: {}", i);
      assert_eq!(&buffer, TEST_BYTES);

      // bounce them right back...
      socket.write_all(&len_bytes).expect("SERVER: send length failed");
      socket.write_all(&buffer).expect("SERVER: send buffer failed");
      // println!("wrote bytes iter: {}", i);
      std::thread::yield_now();
    }
  }).unwrap();

    // let the server go first
    std::thread::yield_now();

    // setup the client, which is going to run on the testing thread...
    let mut io_loop = Core::new().unwrap();

    // the tests should run within 5 seconds... right?
    // TODO: add timeout here, so that test never hangs...
    // let timeout = Timeout::new(Duration::from_secs(5), &io_loop.handle());

    let trust_chain = Certificate::from_der(&root_cert_der).unwrap();

    // barrier.wait();
    let mut builder = TlsStreamBuilder::new();
    builder.add_ca(trust_chain);

    // fix MTLS
    // if mtls {
    //     config_mtls(&root_pkey, &root_name, &root_cert, &mut builder);
    // }

    let (stream, sender) = builder.build(server_addr, subject_name.to_string(), io_loop.handle());

    // TODO: there is a race failure here... a race with the server thread most likely...
    let mut stream = io_loop
        .run(stream)
        .ok()
        .expect("run failed to get stream");

    for _ in 0..send_recv_times {
        // test once
        sender
            .send((TEST_BYTES.to_vec(), server_addr))
            .expect("send failed");
        let (buffer, stream_tmp) = io_loop
            .run(stream.into_future())
            .ok()
            .expect("future iteration run failed");
        stream = stream_tmp;
        let (buffer, _) = buffer.expect("no buffer received");
        assert_eq!(&buffer, TEST_BYTES);
    }

    succeeded.store(true, std::sync::atomic::Ordering::Relaxed);
    server_handle.join().expect("server thread failed");
}

// TODO: fix MTLS
// #[allow(unused_variables)]
// fn config_mtls(root_pkey: &PKey,
//                root_name: &X509Name,
//                root_cert: &X509,
//                builder: &mut TlsStreamBuilder) {
//     // signed by the same root cert
//     let client_name = "resolv.example.com";
//     let (_ /*client_pkey*/, _ /*client_cert*/, client_identity) =
//         cert(client_name, root_pkey, root_name, root_cert);
//     let client_identity =
//         native_tls::Pkcs12::from_der(&client_identity.to_der().unwrap(), "mypass").unwrap();

//     #[cfg(feature = "mtls")]
//     builder.identity(client_identity);
// }
<|endoftext|>


<commit_before>use cxx_gen::{generate_header_and_cc, Opt};

const CPP_EXAMPLE: &str = r#"
    #[cxx::bridge]
    mod ffi {
        extern "C" {
            pub fn do_cpp_thing(foo: &str);
        }
    }
"#;

#[test]
fn test_cpp() {
    let opt = Opt::default();
    let source = CPP_EXAMPLE.parse().unwrap();
    let output = generate_header_and_cc(source, &opt).unwrap();
    let output = std::str::from_utf8(&output.implementation).unwrap();
    // To avoid continual breakage we won't test every byte.
    // Let's look for the major features.
    assert!(output.contains("void cxxbridge04$do_cpp_thing(::rust::Str::Repr foo)"));
}

#[test]
fn test_annotation() {
    let mut opt = Opt::default();
    opt.cxx_impl_annotations = Some("ANNOTATION".to_owned());
    let source = CPP_EXAMPLE.parse().unwrap();
    let output = generate_header_and_cc(source, &opt).unwrap();
    let output = std::str::from_utf8(&output.implementation).unwrap();
    assert!(output.contains("ANNOTATION void cxxbridge04$do_cpp_thing(::rust::Str::Repr foo)"));
}
<commit_msg>Touch up cxx_gen test<commit_after>use cxx_gen::{generate_header_and_cc, Opt};
use std::str;

const BRIDGE0: &str = r#"
    #[cxx::bridge]
    mod ffi {
        extern "C" {
            pub fn do_cpp_thing(foo: &str);
        }
    }
"#;

#[test]
fn test_extern_c_function() {
    let opt = Opt::default();
    let source = BRIDGE0.parse().unwrap();
    let generated = generate_header_and_cc(source, &opt).unwrap();
    let output = str::from_utf8(&generated.implementation).unwrap();
    // To avoid continual breakage we won't test every byte.
    // Let's look for the major features.
    assert!(output.contains("void cxxbridge04$do_cpp_thing(::rust::Str::Repr foo)"));
}

#[test]
fn test_impl_annotation() {
    let mut opt = Opt::default();
    opt.cxx_impl_annotations = Some("ANNOTATION".to_owned());
    let source = BRIDGE0.parse().unwrap();
    let generated = generate_header_and_cc(source, &opt).unwrap();
    let output = str::from_utf8(&generated.implementation).unwrap();
    assert!(output.contains("ANNOTATION void cxxbridge04$do_cpp_thing(::rust::Str::Repr foo)"));
}
<|endoftext|>


<commit_before>pub use std::libc::*;
pub use std::libc::types::os::arch::extra::*;

pub type ATOM = WORD;
pub type UINT = c_uint;
pub type LONG = c_long;

// 32-bit specific
pub type UINT_PTR = c_uint;
pub type LONG_PTR = c_long;
pub type ULONG_PTR = c_ulong;

pub type HANDLE = PVOID;

pub type HBRUSH = HANDLE;
pub type HCURSOR = HICON;
pub type HMENU = HANDLE;
pub type HICON = HANDLE;
pub type HINSTANCE = HANDLE;
pub type HWND = HANDLE;
pub type HDC = HANDLE;

pub type WPARAM = UINT_PTR;
pub type LPARAM = LONG_PTR;

pub type PVOID = *c_void;

// extern fn(HWND, UINT, WPARAM, LPARAM) -> LRESULT
pub type WNDPROC = *u8;

pub struct SECURITY_ATTRIBUTES {
    nLength: DWORD,
    lpSecurityDescriptor: LPVOID,
    bInheritHandle: BOOL,
}

pub struct PROCESS_INFORMATION {
    hProcess: HANDLE,
    hTread: HANDLE,
    dwProcessId: DWORD,
    dwThreadId: DWORD,
}

pub struct STARTUPINFO {
    cb: DWORD,
    lpReserved: LPWSTR,
    lpDesktop: LPWSTR,
    lpTitle: LPWSTR,
    dwX: DWORD,
    dwY: DWORD,
    dwXSize: DWORD,
    dwYSize: DWORD,
    dwXCountChars: DWORD,
    dwYCountChars: DWORD,
    dwFillAttribute: DWORD,
    dwFlags: DWORD,
    wShowWindow: WORD,
    cbReserved2: WORD,
    lpReserved2: LPBYTE,
    hStdInput: HANDLE,
    hStdOutput: HANDLE,
    hStdError: HANDLE,
}

pub struct WNDCLASSEX {
    cbSize: UINT,
    style: UINT,
    lpfnWndProc: WNDPROC,
    cbClsExtra: c_int,
    cbWndExtra: c_int,
    hInstance: HINSTANCE,
    hIcon: HICON,
    hCursor: HCURSOR,
    hbrBackground: HBRUSH,
    lpszMenuName: LPCWSTR,
    lpszClassName: LPCWSTR,
    hIconSm: HICON,
}

pub struct CREATESTRUCT {
    lpCreateParams: LPVOID,
    hInstance: HINSTANCE,
    hMenu: HMENU,
    hwndParent: HWND,
    cy: c_int,
    cx: c_int,
    y: c_int,
    x: c_int,
    style: LONG,
    lpszName: LPCWSTR,
    lpszClass: LPCWSTR,
    dwExStyle: DWORD,
}

pub struct POINT {
    x: LONG,
    y: LONG,
}

pub struct MSG {
    hwnd: HWND,
    message: UINT,
    wParam: WPARAM,
    lParam: LPARAM,
    time: DWORD,
    pt: POINT,
}

pub struct RECT {
    left: LONG,
    top: LONG,
    right: LONG,
    bottom: LONG,
}

pub struct PAINTSTRUCT {
    hdc: HDC,
    fErase: BOOL,
    rcPaint: RECT,
    fRestore: BOOL,
    fIncUpdate: BOOL,
    rgbReserved: *[BYTE, ..32],
}

pub mod kernel32 {
    use ll::*;
    extern "stdcall" {
        unsafe fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;

        unsafe fn CreateProcessW(
            lpApplicationName: LPCWSTR, lpCommandLine: LPWSTR,
            lpProcessAttributes: *SECURITY_ATTRIBUTES,
            lpThreadAttributes: *SECURITY_ATTRIBUTES,
            bInheritHandles: BOOL,
            dwCreationFlags: DWORD,
            lpEnvironment: LPVOID,
            lpCurrentDirectory: LPCWSTR,
            lpStartupInfo: LPSTARTUPINFO,
            lpProcessInformation: LPPROCESS_INFORMATION
        ) -> BOOL;
    }
}

pub mod user32 {
    use ll::*;
    extern "stdcall" {
        unsafe fn CreateWindowExW(extrastyle: DWORD, classname: LPCWSTR,
                windowname: LPCWSTR, style: DWORD,
                x: c_int, y: c_int, width: c_int, height: c_int,
                parent: HWND, menu: HMENU, instance: HINSTANCE, param: LPVOID
        ) -> HWND;

        unsafe fn ShowWindow(hwnd: HWND, nCmdShow: c_int) -> BOOL;

        unsafe fn UpdateWindow(hwnd: HWND) -> BOOL;

        unsafe fn BeginPaint(hwnd: HWND, lpPaint: *PAINTSTRUCT) -> HDC;

        unsafe fn EndPaint(hwnd: HWND, lpPaint: *PAINTSTRUCT) -> BOOL;

        unsafe fn MessageBoxW(
                hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT
        ) -> c_int;

        unsafe fn RegisterClassExW(lpwcx: *WNDCLASSEX) -> ATOM;

        unsafe fn DefWindowProcW(
                hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM
        ) -> LRESULT;

        unsafe fn GetMessageW(
                lpMsg: *MSG, hWnd: HWND,
                wMsgFilterMin: UINT, wMsgFilterMAx: UINT
        ) -> BOOL;

        unsafe fn PeekMessageW(
                lpMsg: *MSG, hWnd: HWND,
                wMsgFilterMin: UINT, wMsgFilterMAx: UINT, wRemoveMsg: UINT
        ) -> BOOL;

        unsafe fn PostMessageW(
                hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM
        ) -> BOOL;

        unsafe fn PostQuitMessage(nExitCode: c_int);

        unsafe fn TranslateMessage(lpMsg: *MSG) -> BOOL;

        unsafe fn DispatchMessageW(lpMsg: *MSG) -> LRESULT;

        // 32-bit only
        unsafe fn GetClassLongW(hwnd: HWND, nIndex: c_int) -> DWORD;

        // 32-bit only
        unsafe fn SetClassLongW(
                hwnd: HWND, nIndex: c_int, dwNewLong: LONG
        ) -> DWORD;
    }
}

pub mod gdi32 {
    use ll::*;
    #[link_args = "-lgdi32"]
    extern "stdcall" {
        unsafe fn TextOutW(
                hdc: HDC, nXStart: c_int, nYStart: c_int,
                lpString: LPWSTR, cchString: c_int
        ) -> BOOL;
    }
}
<commit_msg>ll: memo on WNDPROC<commit_after>pub use std::libc::*;
pub use std::libc::types::os::arch::extra::*;

pub type ATOM = WORD;
pub type UINT = c_uint;
pub type LONG = c_long;

// 32-bit specific
pub type UINT_PTR = c_uint;
pub type LONG_PTR = c_long;
pub type ULONG_PTR = c_ulong;

pub type HANDLE = PVOID;

pub type HBRUSH = HANDLE;
pub type HCURSOR = HICON;
pub type HMENU = HANDLE;
pub type HICON = HANDLE;
pub type HINSTANCE = HANDLE;
pub type HWND = HANDLE;
pub type HDC = HANDLE;

pub type WPARAM = UINT_PTR;
pub type LPARAM = LONG_PTR;

pub type PVOID = *c_void;

// extern "stdcall" fn(HWND, UINT, WPARAM, LPARAM) -> LRESULT
pub type WNDPROC = *u8;

pub struct SECURITY_ATTRIBUTES {
    nLength: DWORD,
    lpSecurityDescriptor: LPVOID,
    bInheritHandle: BOOL,
}

pub struct PROCESS_INFORMATION {
    hProcess: HANDLE,
    hTread: HANDLE,
    dwProcessId: DWORD,
    dwThreadId: DWORD,
}

pub struct STARTUPINFO {
    cb: DWORD,
    lpReserved: LPWSTR,
    lpDesktop: LPWSTR,
    lpTitle: LPWSTR,
    dwX: DWORD,
    dwY: DWORD,
    dwXSize: DWORD,
    dwYSize: DWORD,
    dwXCountChars: DWORD,
    dwYCountChars: DWORD,
    dwFillAttribute: DWORD,
    dwFlags: DWORD,
    wShowWindow: WORD,
    cbReserved2: WORD,
    lpReserved2: LPBYTE,
    hStdInput: HANDLE,
    hStdOutput: HANDLE,
    hStdError: HANDLE,
}

pub struct WNDCLASSEX {
    cbSize: UINT,
    style: UINT,
    lpfnWndProc: WNDPROC,
    cbClsExtra: c_int,
    cbWndExtra: c_int,
    hInstance: HINSTANCE,
    hIcon: HICON,
    hCursor: HCURSOR,
    hbrBackground: HBRUSH,
    lpszMenuName: LPCWSTR,
    lpszClassName: LPCWSTR,
    hIconSm: HICON,
}

pub struct CREATESTRUCT {
    lpCreateParams: LPVOID,
    hInstance: HINSTANCE,
    hMenu: HMENU,
    hwndParent: HWND,
    cy: c_int,
    cx: c_int,
    y: c_int,
    x: c_int,
    style: LONG,
    lpszName: LPCWSTR,
    lpszClass: LPCWSTR,
    dwExStyle: DWORD,
}

pub struct POINT {
    x: LONG,
    y: LONG,
}

pub struct MSG {
    hwnd: HWND,
    message: UINT,
    wParam: WPARAM,
    lParam: LPARAM,
    time: DWORD,
    pt: POINT,
}

pub struct RECT {
    left: LONG,
    top: LONG,
    right: LONG,
    bottom: LONG,
}

pub struct PAINTSTRUCT {
    hdc: HDC,
    fErase: BOOL,
    rcPaint: RECT,
    fRestore: BOOL,
    fIncUpdate: BOOL,
    rgbReserved: *[BYTE, ..32],
}

pub mod kernel32 {
    use ll::*;
    extern "stdcall" {
        unsafe fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;

        unsafe fn CreateProcessW(
            lpApplicationName: LPCWSTR, lpCommandLine: LPWSTR,
            lpProcessAttributes: *SECURITY_ATTRIBUTES,
            lpThreadAttributes: *SECURITY_ATTRIBUTES,
            bInheritHandles: BOOL,
            dwCreationFlags: DWORD,
            lpEnvironment: LPVOID,
            lpCurrentDirectory: LPCWSTR,
            lpStartupInfo: LPSTARTUPINFO,
            lpProcessInformation: LPPROCESS_INFORMATION
        ) -> BOOL;
    }
}

pub mod user32 {
    use ll::*;
    extern "stdcall" {
        unsafe fn CreateWindowExW(extrastyle: DWORD, classname: LPCWSTR,
                windowname: LPCWSTR, style: DWORD,
                x: c_int, y: c_int, width: c_int, height: c_int,
                parent: HWND, menu: HMENU, instance: HINSTANCE, param: LPVOID
        ) -> HWND;

        unsafe fn ShowWindow(hwnd: HWND, nCmdShow: c_int) -> BOOL;

        unsafe fn UpdateWindow(hwnd: HWND) -> BOOL;

        unsafe fn BeginPaint(hwnd: HWND, lpPaint: *PAINTSTRUCT) -> HDC;

        unsafe fn EndPaint(hwnd: HWND, lpPaint: *PAINTSTRUCT) -> BOOL;

        unsafe fn MessageBoxW(
                hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT
        ) -> c_int;

        unsafe fn RegisterClassExW(lpwcx: *WNDCLASSEX) -> ATOM;

        unsafe fn DefWindowProcW(
                hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM
        ) -> LRESULT;

        unsafe fn GetMessageW(
                lpMsg: *MSG, hWnd: HWND,
                wMsgFilterMin: UINT, wMsgFilterMAx: UINT
        ) -> BOOL;

        unsafe fn PeekMessageW(
                lpMsg: *MSG, hWnd: HWND,
                wMsgFilterMin: UINT, wMsgFilterMAx: UINT, wRemoveMsg: UINT
        ) -> BOOL;

        unsafe fn PostMessageW(
                hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM
        ) -> BOOL;

        unsafe fn PostQuitMessage(nExitCode: c_int);

        unsafe fn TranslateMessage(lpMsg: *MSG) -> BOOL;

        unsafe fn DispatchMessageW(lpMsg: *MSG) -> LRESULT;

        // 32-bit only
        unsafe fn GetClassLongW(hwnd: HWND, nIndex: c_int) -> DWORD;

        // 32-bit only
        unsafe fn SetClassLongW(
                hwnd: HWND, nIndex: c_int, dwNewLong: LONG
        ) -> DWORD;
    }
}

pub mod gdi32 {
    use ll::*;
    #[link_args = "-lgdi32"]
    extern "stdcall" {
        unsafe fn TextOutW(
                hdc: HDC, nXStart: c_int, nYStart: c_int,
                lpString: LPWSTR, cchString: c_int
        ) -> BOOL;
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use clap::arg_enum;
use config::config::NodeConfig;
use failure::prelude::*;
use logger::prelude::*;
use regex::Regex;
use std::{fs, net::IpAddr, path::PathBuf, str::FromStr};
use structopt::StructOpt;

arg_enum! {
    #[derive(Debug)]
    pub enum TransactionPattern {
        Ring,
        Pairwise,
    }
}

/// CLI options for creating Benchmarker, shared by binaries that use Benchmarker.
#[derive(Debug, StructOpt)]
pub struct BenchOpt {
    /// Validator address list separated by whitespace: `ip_address:port ip_address:port ...`.
    /// It is required unless (and hence conflict with) swarm_config_dir is present.
    #[structopt(
        short = "a",
        long = "validator_addresses",
        conflicts_with = "swarm_config_dir",
        required_unless = "swarm_config_dir"
    )]
    pub validator_addresses: Vec<String>,
    /// libra_swarm's config file directory, which holds libra_node's config .toml file(s).
    /// It is requried unless (and hence conflicts with) validator_addresses .
    #[structopt(
        short = "s",
        long = "swarm_config_dir",
        conflicts_with = "validator_addresses",
        required_unless = "validator_addresses"
    )]
    pub swarm_config_dir: Option<String>,
    /// Metrics server process's address.
    /// If this argument is not present, RuBen will not spawn metrics server.
    #[structopt(short = "m", long = "metrics_server_address")]
    pub metrics_server_address: Option<String>,
    /// Valid faucet key file path.
    #[structopt(short = "f", long = "faucet_key_file_path", required = true)]
    pub faucet_key_file_path: String,
    /// Number of AC clients.
    /// If not specified or equals 0, it will be set to #validators * #cpus. Why this value?
    /// We want to create/connect 1 AC client to each AC thread on each validator.
    #[structopt(short = "c", long = "num_clients", default_value = "0")]
    pub num_clients: usize,
    /// Randomly distribute the clients to start sending requests over the stagger_range_ms time.
    /// A value of 1 ms effectively means starting all clients at once.
    #[structopt(short = "g", long = "stagger_range_ms", default_value = "64")]
    pub stagger_range_ms: u16,
    /// Submit constant number of requests per second per client; otherwise flood requests.
    #[structopt(short = "k", long = "submit_rate")]
    pub submit_rate: Option<u64>,
}

/// CLI options for RuBen.
#[derive(Debug, StructOpt)]
#[structopt(
    name = "RuBen",
    author = "Libra",
    about = "RuBen (Ru)ns The Libra (Ben)chmarker For You."
)]
pub struct RubenOpt {
    // Options for creating Benchmarker.
    #[structopt(flatten)]
    pub bench_opt: BenchOpt,
    /// TODO: Discard this option. Debug interface address in the form of ip_address:port.
    #[structopt(short = "d", long = "debug_address")]
    pub debug_address: Option<String>,
    /// Number of accounts to create in Libra.
    #[structopt(short = "n", long = "num_accounts", default_value = "32")]
    pub num_accounts: u64,
    /// Number of repetition to attempt, in one epoch, to increase overall number of sent requests.
    #[structopt(short = "r", long = "num_rounds", default_value = "1")]
    pub num_rounds: u64,
    /// Number of epochs to measure the TXN throughput, each time with newly created Benchmarker.
    #[structopt(short = "e", long = "num_epochs", default_value = "10")]
    pub num_epochs: u64,
    /// Choices of how to generate TXNs.
    #[structopt(
        short = "t",
        long = "txn_pattern",
        raw(possible_values = "&TransactionPattern::variants()"),
        case_insensitive = true,
        default_value = "Ring"
    )]
    pub txn_pattern: TransactionPattern,
}

/// CLI options for linear search max throughput.
#[derive(Debug, StructOpt)]
#[structopt(
    name = "max_throughput",
    author = "Libra",
    about = "Linear Search Maximum Throughput."
)]
pub struct SearchOpt {
    // Options for creating Benchmarker.
    #[structopt(flatten)]
    pub bench_opt: BenchOpt,
    /// Upper bound value of submission rate for each client.
    #[structopt(short = "u", long = "upper_bound", default_value = "8000")]
    pub upper_bound: u64,
    /// Lower bound value of submission rate for each client.
    #[structopt(short = "l", long = "lower_bound", default_value = "10")]
    pub lower_bound: u64,
    /// Increase step of submission rate for each client.
    #[structopt(short = "i", long = "inc_step", default_value = "10")]
    pub inc_step: u64,
    /// Number of epochs to send TXNs at a fixed rate.
    #[structopt(short = "e", long = "num_epochs", default_value = "4")]
    pub num_epochs: u64,
    /// How many times to repeat the same linear search. Each time with new accounts/TXNs.
    #[structopt(short = "b", long = "num_searches", default_value = "10")]
    pub num_searches: u64,
}

/// Helper that checks if address is valid, and converts unspecified address to localhost.
/// If parsing as numeric network address fails, treat as valid domain name.
fn parse_socket_address(address: &str, port: u16) -> String {
    match IpAddr::from_str(address) {
        Ok(ip_address) => {
            if ip_address.is_unspecified() {
                format!("localhost:{}", port)
            } else {
                format!("{}:{}", address, port)
            }
        }
        Err(_) => format!("{}:{}", address, port),
    }
}

/// Scan *.node.config.toml files under config_dir_name, parse them as node config
/// and return libra_swarm's node addresses info as a vector.
pub fn parse_swarm_config_from_dir(config_dir_name: &str) -> Result<Vec<String>> {
    let mut validator_addresses: Vec<String> = Vec::new();
    let re = Regex::new(r"[[:alnum:]]{64}\.node\.config\.toml").expect("failed to build regex");
    let config_dir = PathBuf::from(config_dir_name);
    if config_dir.is_dir() {
        for entry in fs::read_dir(config_dir).expect("invalid config directory") {
            let path = entry.expect("invalid path under config directory").path();
            if path.is_file() {
                let filename = path
                    .file_name()
                    .expect("failed to convert filename to string")
                    .to_str()
                    .expect("failed to convert filename to string");
                if re.is_match(filename) {
                    debug!("Parsing node config file {:?}.", filename);
                    let config_string = fs::read_to_string(&path)
                        .unwrap_or_else(|_| panic!("failed to load config file {:?}", filename));
                    let config = NodeConfig::parse(&config_string).unwrap_or_else(|_| {
                        panic!("failed to parse NodeConfig from {:?}", filename)
                    });
                    let address = parse_socket_address(
                        &config.admission_control.address,
                        config.admission_control.admission_control_service_port,
                    );
                    validator_addresses.push(address);
                }
            }
        }
    }
    if validator_addresses.is_empty() {
        bail!(
            "unable to parse validator_addresses from {}",
            config_dir_name
        )
    }
    Ok(validator_addresses)
}

impl BenchOpt {
    /// Override validator_addresses if swarm_config_dir is provided.
    pub fn try_parse_validator_addresses(&mut self) {
        if let Some(swarm_config_dir) = &self.swarm_config_dir {
            let validator_addresses =
                parse_swarm_config_from_dir(swarm_config_dir).expect("invalid arguments");
            self.validator_addresses = validator_addresses;
        }
    }

    /// Ensure at least one client for one validator.
    pub fn parse_num_clients(&mut self) {
        if self.num_clients == 0 {
            self.num_clients = self.validator_addresses.len() * num_cpus::get();
            info!(
                "Set number of clients in Benchmarker to {}",
                self.num_clients
            );
        }
    }

    /// Using either specified constant rate or flood.
    pub fn parse_submit_rate(&self) -> u64 {
        self.submit_rate.unwrap_or(std::u64::MAX)
    }
}

impl RubenOpt {
    pub fn new_from_args() -> Self {
        let mut args = RubenOpt::from_args();
        args.bench_opt.try_parse_validator_addresses();
        args.bench_opt.parse_num_clients();
        args
    }
}

impl SearchOpt {
    pub fn new_from_args() -> Self {
        let mut args = SearchOpt::from_args();
        args.bench_opt.try_parse_validator_addresses();
        args.bench_opt.parse_num_clients();
        assert!(args.lower_bound > 0);
        assert!(args.inc_step > 0);
        assert!(args.lower_bound < args.upper_bound);
        args
    }
}

#[cfg(test)]
mod tests {
    use crate::cli_opt::{parse_socket_address, parse_swarm_config_from_dir};

    #[test]
    fn test_parse_socket_address() {
        assert_eq!(
            parse_socket_address("216.10.234.56", 12345),
            "216.10.234.56:12345"
        );
        assert_eq!(parse_socket_address("0.0.0.0", 12345), "localhost:12345");
        assert_eq!(parse_socket_address("::", 12345), "localhost:12345");
        assert_eq!(
            parse_socket_address("face:booc::0", 12345),
            "face:booc::0:12345"
        );
        assert_eq!(
            parse_socket_address("2401:dbff:121f:a2f1:face:d:6c:0", 12345),
            "2401:dbff:121f:a2f1:face:d:6c:0:12345"
        );
        assert_eq!(parse_socket_address("localhost", 12345), "localhost:12345");
        assert_eq!(
            parse_socket_address("www.facebook.com", 12345),
            "www.facebook.com:12345"
        );
    }

    #[test]
    fn test_parse_swarm_config_from_invalid_dir() {
        // Directory doesn't exist at all.
        let non_exist_dir_name = String::from("NonExistDir");
        let mut result = parse_swarm_config_from_dir(&non_exist_dir_name);
        assert_eq!(result.is_err(), true);

        // Directory exists but config file does not.
        let path = std::env::current_dir().expect("unable to get current dir");
        if let Some(dir_without_config) = path.to_str() {
            result = parse_swarm_config_from_dir(&dir_without_config);
            assert_eq!(result.is_err(), true);
        }
    }
}
<commit_msg>[benchmark] Remove debug_address option.<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use clap::arg_enum;
use config::config::NodeConfig;
use failure::prelude::*;
use logger::prelude::*;
use regex::Regex;
use std::{fs, net::IpAddr, path::PathBuf, str::FromStr};
use structopt::StructOpt;

arg_enum! {
    #[derive(Debug)]
    pub enum TransactionPattern {
        Ring,
        Pairwise,
    }
}

/// CLI options for creating Benchmarker, shared by binaries that use Benchmarker.
#[derive(Debug, StructOpt)]
pub struct BenchOpt {
    /// Validator address list separated by whitespace: `ip_address:port ip_address:port ...`.
    /// It is required unless (and hence conflict with) swarm_config_dir is present.
    #[structopt(
        short = "a",
        long = "validator_addresses",
        conflicts_with = "swarm_config_dir",
        required_unless = "swarm_config_dir"
    )]
    pub validator_addresses: Vec<String>,
    /// libra_swarm's config file directory, which holds libra_node's config .toml file(s).
    /// It is requried unless (and hence conflicts with) validator_addresses .
    #[structopt(
        short = "s",
        long = "swarm_config_dir",
        conflicts_with = "validator_addresses",
        required_unless = "validator_addresses"
    )]
    pub swarm_config_dir: Option<String>,
    /// Metrics server process's address.
    /// If this argument is not present, RuBen will not spawn metrics server.
    #[structopt(short = "m", long = "metrics_server_address")]
    pub metrics_server_address: Option<String>,
    /// Valid faucet key file path.
    #[structopt(short = "f", long = "faucet_key_file_path", required = true)]
    pub faucet_key_file_path: String,
    /// Number of AC clients.
    /// If not specified or equals 0, it will be set to #validators * #cpus. Why this value?
    /// We want to create/connect 1 AC client to each AC thread on each validator.
    #[structopt(short = "c", long = "num_clients", default_value = "0")]
    pub num_clients: usize,
    /// Randomly distribute the clients to start sending requests over the stagger_range_ms time.
    /// A value of 1 ms effectively means starting all clients at once.
    #[structopt(short = "g", long = "stagger_range_ms", default_value = "64")]
    pub stagger_range_ms: u16,
    /// Submit constant number of requests per second per client; otherwise flood requests.
    #[structopt(short = "k", long = "submit_rate")]
    pub submit_rate: Option<u64>,
}

/// CLI options for RuBen.
#[derive(Debug, StructOpt)]
#[structopt(
    name = "RuBen",
    author = "Libra",
    about = "RuBen (Ru)ns The Libra (Ben)chmarker For You."
)]
pub struct RubenOpt {
    // Options for creating Benchmarker.
    #[structopt(flatten)]
    pub bench_opt: BenchOpt,
    /// Number of accounts to create in Libra.
    #[structopt(short = "n", long = "num_accounts", default_value = "32")]
    pub num_accounts: u64,
    /// Number of repetition to attempt, in one epoch, to increase overall number of sent requests.
    #[structopt(short = "r", long = "num_rounds", default_value = "1")]
    pub num_rounds: u64,
    /// Number of epochs to measure the TXN throughput, each time with newly created Benchmarker.
    #[structopt(short = "e", long = "num_epochs", default_value = "10")]
    pub num_epochs: u64,
    /// Choices of how to generate TXNs.
    #[structopt(
        short = "t",
        long = "txn_pattern",
        raw(possible_values = "&TransactionPattern::variants()"),
        case_insensitive = true,
        default_value = "Ring"
    )]
    pub txn_pattern: TransactionPattern,
}

/// CLI options for linear search max throughput.
#[derive(Debug, StructOpt)]
#[structopt(
    name = "max_throughput",
    author = "Libra",
    about = "Linear Search Maximum Throughput."
)]
pub struct SearchOpt {
    // Options for creating Benchmarker.
    #[structopt(flatten)]
    pub bench_opt: BenchOpt,
    /// Upper bound value of submission rate for each client.
    #[structopt(short = "u", long = "upper_bound", default_value = "8000")]
    pub upper_bound: u64,
    /// Lower bound value of submission rate for each client.
    #[structopt(short = "l", long = "lower_bound", default_value = "10")]
    pub lower_bound: u64,
    /// Increase step of submission rate for each client.
    #[structopt(short = "i", long = "inc_step", default_value = "10")]
    pub inc_step: u64,
    /// Number of epochs to send TXNs at a fixed rate.
    #[structopt(short = "e", long = "num_epochs", default_value = "4")]
    pub num_epochs: u64,
    /// How many times to repeat the same linear search. Each time with new accounts/TXNs.
    #[structopt(short = "b", long = "num_searches", default_value = "10")]
    pub num_searches: u64,
}

/// Helper that checks if address is valid, and converts unspecified address to localhost.
/// If parsing as numeric network address fails, treat as valid domain name.
fn parse_socket_address(address: &str, port: u16) -> String {
    match IpAddr::from_str(address) {
        Ok(ip_address) => {
            if ip_address.is_unspecified() {
                format!("localhost:{}", port)
            } else {
                format!("{}:{}", address, port)
            }
        }
        Err(_) => format!("{}:{}", address, port),
    }
}

/// Scan *.node.config.toml files under config_dir_name, parse them as node config
/// and return libra_swarm's node addresses info as a vector.
pub fn parse_swarm_config_from_dir(config_dir_name: &str) -> Result<Vec<String>> {
    let mut validator_addresses: Vec<String> = Vec::new();
    let re = Regex::new(r"[[:alnum:]]{64}\.node\.config\.toml").expect("failed to build regex");
    let config_dir = PathBuf::from(config_dir_name);
    if config_dir.is_dir() {
        for entry in fs::read_dir(config_dir).expect("invalid config directory") {
            let path = entry.expect("invalid path under config directory").path();
            if path.is_file() {
                let filename = path
                    .file_name()
                    .expect("failed to convert filename to string")
                    .to_str()
                    .expect("failed to convert filename to string");
                if re.is_match(filename) {
                    debug!("Parsing node config file {:?}.", filename);
                    let config_string = fs::read_to_string(&path)
                        .unwrap_or_else(|_| panic!("failed to load config file {:?}", filename));
                    let config = NodeConfig::parse(&config_string).unwrap_or_else(|_| {
                        panic!("failed to parse NodeConfig from {:?}", filename)
                    });
                    let address = parse_socket_address(
                        &config.admission_control.address,
                        config.admission_control.admission_control_service_port,
                    );
                    validator_addresses.push(address);
                }
            }
        }
    }
    if validator_addresses.is_empty() {
        bail!(
            "unable to parse validator_addresses from {}",
            config_dir_name
        )
    }
    Ok(validator_addresses)
}

impl BenchOpt {
    /// Override validator_addresses if swarm_config_dir is provided.
    pub fn try_parse_validator_addresses(&mut self) {
        if let Some(swarm_config_dir) = &self.swarm_config_dir {
            let validator_addresses =
                parse_swarm_config_from_dir(swarm_config_dir).expect("invalid arguments");
            self.validator_addresses = validator_addresses;
        }
    }

    /// Ensure at least one client for one validator.
    pub fn parse_num_clients(&mut self) {
        if self.num_clients == 0 {
            self.num_clients = self.validator_addresses.len() * num_cpus::get();
            info!(
                "Set number of clients in Benchmarker to {}",
                self.num_clients
            );
        }
    }

    /// Using either specified constant rate or flood.
    pub fn parse_submit_rate(&self) -> u64 {
        self.submit_rate.unwrap_or(std::u64::MAX)
    }
}

impl RubenOpt {
    pub fn new_from_args() -> Self {
        let mut args = RubenOpt::from_args();
        args.bench_opt.try_parse_validator_addresses();
        args.bench_opt.parse_num_clients();
        args
    }
}

impl SearchOpt {
    pub fn new_from_args() -> Self {
        let mut args = SearchOpt::from_args();
        args.bench_opt.try_parse_validator_addresses();
        args.bench_opt.parse_num_clients();
        assert!(args.lower_bound > 0);
        assert!(args.inc_step > 0);
        assert!(args.lower_bound < args.upper_bound);
        args
    }
}

#[cfg(test)]
mod tests {
    use crate::cli_opt::{parse_socket_address, parse_swarm_config_from_dir};

    #[test]
    fn test_parse_socket_address() {
        assert_eq!(
            parse_socket_address("216.10.234.56", 12345),
            "216.10.234.56:12345"
        );
        assert_eq!(parse_socket_address("0.0.0.0", 12345), "localhost:12345");
        assert_eq!(parse_socket_address("::", 12345), "localhost:12345");
        assert_eq!(
            parse_socket_address("face:booc::0", 12345),
            "face:booc::0:12345"
        );
        assert_eq!(
            parse_socket_address("2401:dbff:121f:a2f1:face:d:6c:0", 12345),
            "2401:dbff:121f:a2f1:face:d:6c:0:12345"
        );
        assert_eq!(parse_socket_address("localhost", 12345), "localhost:12345");
        assert_eq!(
            parse_socket_address("www.facebook.com", 12345),
            "www.facebook.com:12345"
        );
    }

    #[test]
    fn test_parse_swarm_config_from_invalid_dir() {
        // Directory doesn't exist at all.
        let non_exist_dir_name = String::from("NonExistDir");
        let mut result = parse_swarm_config_from_dir(&non_exist_dir_name);
        assert_eq!(result.is_err(), true);

        // Directory exists but config file does not.
        let path = std::env::current_dir().expect("unable to get current dir");
        if let Some(dir_without_config) = path.to_str() {
            result = parse_swarm_config_from_dir(&dir_without_config);
            assert_eq!(result.is_err(), true);
        }
    }
}
<|endoftext|>