<commit_before>extern crate iron;
extern crate staticfile;
extern crate redis;
extern crate rustc_serialize;
extern crate hyper;
extern crate url;
extern crate time;
extern crate tempdir;
extern crate zip;

#[macro_use]
extern crate router;

// for logging
#[macro_use]
extern crate log;
extern crate env_logger;

use std::fs::create_dir_all;
use std::fs::File;
use std::path::Path;
use std::io::{Read, Cursor, Result as ioResult, Write};
use std::fs;
use std::u8;
use std::env;
use std::thread;
use std::sync::mpsc;
use std::process::Command;
use tempdir::TempDir;
use time::now_utc;
use zip::*;

use rustc_serialize::json::Json;

use iron::modifiers::Redirect;
use iron::prelude::*;
use iron::status;
use iron::Url as iUrl;

use url::Url;

use hyper::Client;
use hyper::header;

use staticfile::Static;
use router::Router;

use redis::{Commands, PipelineCommands};

struct ClippyResult {
    failed: bool,
    warnings: u32,
    errors: u32
}

fn update_for_github(user: &str, repo: &str, sha: &str) -> Result<ClippyResult, &'static str> {
    let redis: redis::Connection = setup_redis();
    let github_url = format!("https://github.com/{0}/{1}/archive/{2}.zip",
                             user,
                             repo,
                             sha);
    let log_key =  format!("log/github/{0}/{1}:{2}",
                             user,
                             repo,
                             sha);

    // starting a log file
    if let Ok(existing) = redis::transaction(&redis, &[log_key.clone()], |pipe| {
        match redis.exists(log_key.clone()) {
            Ok(Some(false)) => {
                pipe.cmd("RPUSH")
                        .arg(log_key.clone())
                        .arg(format!("{0} started processing github/{1}/{2}:{3}",
                                            time::now_utc().rfc3339(),
                                            user,
                                            repo,
                                            sha))
                        .ignore()
                        .execute(&redis);
                return Ok(Some(false));
                },
            _ => Ok(Some(true))
        }}) {
        if existing {
            // we have been alerted, the key already existed
            // so someone else is writing a log file. We should stop now.
            return Err("Already running!");
        }
    }

    log_redis(&redis, &log_key, "Creating Temp Directory...");

    if let Ok(tmp_dir) = TempDir::new(&format!("github_{0}_{1}_{2}",
                                                user,
                                                repo,
                                                sha)) {
        log_redis(&redis, &log_key, &format!("Fetching {}", &github_url));

        if let Some(zip_body) = fetch(&Client::new(), &github_url) {
            log_redis(&redis, &log_key, "Extracting archive");
            if let Ok(mut archive) = zip::ZipArchive::new(std::io::Cursor::new(zip_body)) {
                for i in 0..archive.len()
                {
                    let mut zip_file = archive.by_index(i).unwrap();
                    let full_path = tmp_dir.path().join(zip_file.name());
                    let extracted_path = full_path.as_path();
                    let mut writer = File::create(extracted_path).unwrap();
                    let mut buffer: Vec<u8> = vec![];

                    fs::create_dir_all(extracted_path.parent().unwrap()).unwrap();
                    zip_file.read_to_end(&mut buffer).unwrap();
                    writer.write(&buffer).unwrap();
                }

                log_redis(&redis, &log_key, "Running clippy.");

                let output = Command::new("cargo").args(&["rustc", "--", "-Zunstable-options", "-Zextra-plugins=clippy", "-Zno-trans", "-lclippy", "--error-format=json"])
                                      .current_dir(&tmp_dir.path())
                                      .output()
                                      .unwrap_or_else(|e| {
                                          log_redis(&redis, &log_key,
                                                    &format!("Clippy execution failed: {}", &e));
                                          panic!("Failed to execute clippy: {}", e);
                                      });
                Err("Not Yet Implemented")
            } else {
                log_redis(&redis, &log_key, "Extracting archive failed.");
                Err("Extracting archive failed.")
            }
        } else {
            log_redis(&redis, &log_key, "Fetching from github failed!");
            Err("Fetching from github failed!")
        }
    } else {
        log_redis(&redis, &log_key, "Creating Temp Directory failed");
        Err("Creating Temp Directory failed")
    }
}

fn main() {
    // setup logger
    env_logger::init().unwrap();

    let router = router!(
        get "/github/:user/:repo/:branch/badge.svg" => github_handler,
        get "/github/:user/:repo/badge.svg" => github_handler,
        get "/" => Static::new(Path::new("static"))
    );

    warn!("Server running at 8080");
    Iron::new(router).http("0.0.0.0:8080").unwrap();

    fn github_handler(req: &mut Request) -> IronResult<Response> {

        let ref router = req.extensions.get::<Router>().unwrap();
        let redis: redis::Connection = setup_redis();

        let user = router.find("user").unwrap();
        let repo = router.find("repo").unwrap();
        let branch = router.find("branch").unwrap_or("master");
        let ext = router.find("ext").unwrap_or("svg");
        let key = format!("badge/github/{}/{}:{}", user, repo, branch);

        // Create a client.

        if let Some(url) = get_redis_redir(&redis, &key) {
            return redir(&url, &req.url);
        }

        let hyper_client: Client = Client::new();

        let github_url = format!("https://api.github.com/repos/{0}/{1}/git/refs/heads/{2}",
                                 user,
                                 repo,
                                 branch);
        if let Some(body) = fetch(&hyper_client, &github_url) {
            if let Ok(json) = Json::from_str(&body) {
                if let Some(sha) = json.find_path(&["object", "sha"]) {
                    let sha_key = format!("badge/github/{}/{}:{} ", user, repo, sha);

                    if let Some(url) = get_redis_redir(&redis, &sha_key) {
                        set_redis_cache(&redis, &key, &url.clone().serialize());
                        return redir(&url, &req.url);
                    }

                    let linting_url = format!("https://img.shields.io/badge/clippy-linting-blue.\
                                               {}?",
                                              &ext);

                    set_redis_cache(&redis, &sha_key, &linting_url);
                    set_redis_cache(&redis, &key, &linting_url);

                    let user = user.to_owned();
                    let repo =repo.to_owned();
                    let sha = sha.to_string().to_owned();
                    thread::spawn(move || {
                        update_for_github(&user, &repo, &sha);
                    });

                    // let resp = format!("There shall be content here for {}", key);
                    return redir(&Url::parse(&linting_url).unwrap(), &req.url);
                } else {
                    warn!("{}: SHA not found in JSON: {}", &github_url, &json);
                    return Ok(Response::with((status::NotFound,
                                              format!("Couldn't find on github {}", &github_url))));
                }
            } else {
                warn!("{}: Couldn't parse Githubs JSON response: {}",
                      &github_url,
                      &body);
                return Ok(Response::with((status::InternalServerError,
                                          "Couldn't parse Githubs JSON response")));
            }
        } else {
            return Ok(Response::with((status::NotFound,
                                      format!("Couldn't find on github {}", &github_url))));
        }

        Ok(Response::with(status::InternalServerError))
    }
}

fn log_redis(redis: &redis::Connection, key: &str, value: &str) {
    redis::pipe()
        .cmd("RPUSH")
            .arg(key.clone())
            .arg(format!("{0} {1}",
                         time::now_utc().rfc3339(),
                         value))
            .ignore()
        .execute(redis);
}

fn set_redis_cache(redis: &redis::Connection, key: &str, value: &str) {
    redis::pipe()
        .cmd("SET").arg(key.clone()).arg(value).ignore()
        .cmd("EXPIRE").arg(key.clone()).arg(5 * 60).ignore() // we expire in 5min
        .execute(redis);
}

fn redir(url: &Url, source_url: &iUrl) -> IronResult<Response> {
    match iUrl::from_generic_url(url.clone()) {
        Ok(mut redir_url) => {
            if let Some(ref query) = source_url.query {
                redir_url.query = Some(query.clone());
            }
            Ok(Response::with((status::TemporaryRedirect, Redirect(redir_url))))
        }
        Err(err) => Ok(Response::with((status::InternalServerError, err))),
    }
}

fn get_redis_redir(redis: &redis::Connection, key: &str) -> Option<Url> {
    let result: Option<String> = get_redis_value(redis, key);
    if result.is_some() {
        if let Ok(url) = Url::parse(&result.unwrap()) {
            return Some(url);
        }
    }
    return None;
}

fn get_redis_value(redis: &redis::Connection, key: &str) -> Option<String> {

    if let Ok(Some(cached_result)) = redis.get(key) {
        let cached_value: Option<String> = cached_result;
        if cached_value.is_some() {
            return cached_value;
        }
    }

    return None;
}

fn fetch(client: &Client, url: &str) -> Option<String> {
    let res = client.get(url)
                    .header(header::UserAgent("Clippy".to_owned()))
                    .header(header::Connection::close());
    if let Ok(mut res) = res.send() {
        let mut body = String::new();
        if res.read_to_string(&mut body).is_ok() {
            return Some(body);
        }
    }

    return None;
}

fn setup_redis() -> redis::Connection {
    let url = redis::parse_redis_url(&env::var("REDIS_URL")
                                          .unwrap_or("redis://redis/".to_owned()))
                  .unwrap();
    redis::Client::open(url)
        .unwrap()
        .get_connection()
        .unwrap()
}
<commit_msg>big refactoring<commit_after>extern crate iron;
extern crate staticfile;
extern crate redis;
extern crate rustc_serialize;
extern crate hyper;
extern crate url;
extern crate time;
extern crate tempdir;
extern crate zip;

#[macro_use]
extern crate router;

// for logging
#[macro_use]
extern crate log;
extern crate env_logger;

use std::fs::create_dir_all;
use std::fs::File;
use std::path::Path;
use std::io::{Read, Cursor, Result as ioResult, Write};
use std::fs;
use std::u8;
use std::vec::Vec;
use std::env;
use std::thread;
use std::sync::mpsc;
use std::process::Command;
use tempdir::TempDir;
use time::now_utc;
use zip::*;

use rustc_serialize::json::Json;

use iron::modifiers::Redirect;
use iron::prelude::*;
use iron::status;
use iron::Url as iUrl;

use url::Url;

use hyper::Client;
use hyper::header;

use staticfile::Static;
use router::Router;

use redis::{Commands, PipelineCommands, Value};

struct ClippyResult {
    failed: bool,
    warnings: u32,
    errors: u32
}

static LINTING_BADGE_URL: &'static str = "https://img.shields.io/badge/clippy-linting-blue.";

fn update_for_github(user: &str, repo: &str, sha: &str) -> Result<ClippyResult, &'static str> {
    let redis: redis::Connection = setup_redis();
    let github_url = format!("https://github.com/{0}/{1}/archive/{2}.zip",
                             user,
                             repo,
                             sha);
    let base_key = format!("github/{0}/{1}:{2}",
                             user,
                             repo,
                             sha);
    let log_key =  format!("log/{}", &base_key);
    let badge_key =  format!("badge/{}", &base_key);
    // let result_key =  format!("result/{}", &base_key);

    // starting a log file
    if let Ok(existing) = redis::transaction(&redis, &[log_key.clone(), badge_key.clone()], |pipe| {
        match redis.exists(badge_key.clone()) {
            Ok(Some(false)) => {
                pipe.cmd("RPUSH")
                        .arg(log_key.clone())
                        .arg(format!("{0} started processing github/{1}/{2}:{3}",
                                            time::now_utc().rfc3339(),
                                            user,
                                            repo,
                                            sha))
                        .ignore()
                    .cmd("SET")
                        .arg(badge_key.clone())
                        .arg(LINTING_BADGE_URL)
                        .ignore()
                    .cmd("EXPIRE")
                        .arg(badge_key.clone())
                        .arg(300)
                        .ignore()
                    .execute(&redis);
                return Ok(Some(false));
                },
            _ => Ok(Some(true))
        }}) {
        if existing {
            // we have been alerted, the key already existed
            // so someone else is writing a log file. We should stop now.
            return Err("Already running!");
        }
    }

    log_redis(&redis, &log_key, "Creating Temp Directory...");

    if let Ok(tmp_dir) = TempDir::new(&format!("github_{0}_{1}_{2}",
                                                user,
                                                repo,
                                                sha)) {
        log_redis(&redis, &log_key, &format!("Fetching {}", &github_url));

        if let Some(zip_body) = fetch(&Client::new(), &github_url) {
            log_redis(&redis, &log_key, "Extracting archive");
            if let Ok(mut archive) = zip::ZipArchive::new(std::io::Cursor::new(zip_body)) {
                for i in 0..archive.len()
                {
                    let mut zip_file = archive.by_index(i).unwrap();
                    let full_path = tmp_dir.path().join(zip_file.name());
                    let extracted_path = full_path.as_path();
                    let mut writer = File::create(extracted_path).unwrap();
                    let mut buffer: Vec<u8> = vec![];

                    fs::create_dir_all(extracted_path.parent().unwrap()).unwrap();
                    zip_file.read_to_end(&mut buffer).unwrap();
                    writer.write(&buffer).unwrap();
                }

                log_redis(&redis, &log_key, "Running clippy.");

                let output = Command::new("cargo").args(&["rustc", "--", "-Zunstable-options", "-Zextra-plugins=clippy", "-Zno-trans", "-lclippy", "--error-format=json"])
                                      .current_dir(&tmp_dir.path())
                                      .output()
                                      .unwrap_or_else(|e| {
                                          log_redis(&redis, &log_key,
                                                    &format!("Clippy execution failed: {}", &e));
                                          panic!("Failed to execute clippy: {}", e);
                                      });
                Err("Not Yet Implemented")
            } else {
                log_redis(&redis, &log_key, "Extracting archive failed.");
                Err("Extracting archive failed.")
            }
        } else {
            log_redis(&redis, &log_key, "Fetching from github failed!");
            Err("Fetching from github failed!")
        }
    } else {
        log_redis(&redis, &log_key, "Creating Temp Directory failed");
        Err("Creating Temp Directory failed")
    }
}

fn trigger_update(user: &str, repo: &str, sha: &str){

    let user = user.to_owned();
    let repo = repo.to_owned();
    let sha = sha.to_owned();
    thread::spawn(move || {
        update_for_github(&user, &repo, &sha);
    });

}

fn main() {
    // setup logger
    env_logger::init().unwrap();

    let router = router!(
        get "/github/sha/:user/:repo/:sha/:method" => github_handler,
        get "/github/:user/:repo/:branch/:method" => github_finder,
        get "/github/:user/:repo/:method" => github_finder,
        get "/" => Static::new(Path::new("static"))
    );

    warn!("Server running at 8080");
    Iron::new(router).http("0.0.0.0:8080").unwrap();

    fn github_finder(req: &mut Request) -> IronResult<Response> {

        let ref router = req.extensions.get::<Router>().unwrap();
        let redis: redis::Connection = setup_redis();
        let hyper_client: Client = Client::new();

        let user = router.find("user").unwrap();
        let repo = router.find("repo").unwrap();
        let branch = router.find("branch").unwrap_or("master");
        let method = router.find("method").unwrap_or("badge.svg");

        let redis_key = format!("cached-sha/github/{0}/{1}:{2}", user, repo, branch);
        let mut target_url = req.url.clone().into_generic_url().to_owned();

        match redis.get(redis_key.to_owned()){
            // we have a cached value, redirect directly
            Ok(Value::Data(sha)) =>{
                {
                    let mut path = target_url.path_mut().unwrap();
                    path.clear();
                    path.extend_from_slice(&["github".to_owned(), "sha".to_owned(), user.to_owned(), repo.to_owned(), String::from_utf8(sha).unwrap().to_owned(), method.to_owned()]);
                }
                return redir(&target_url, &req.url);
            },
            _ => {
                let github_url = format!("https://api.github.com/repos/{0}/{1}/git/refs/heads/{2}",
                                         user,
                                         repo,
                                         branch);
                if let Some(body) = fetch(&hyper_client, &github_url) {
                    if let Ok(json) = Json::from_str(&body) {
                        if let Some(&Json::String(ref sha)) = json.find_path(&["object", "sha"]) {
                            {
                                let mut path = target_url.path_mut().unwrap();
                                path.clear();
                                path.extend_from_slice(&["github".to_owned(), "sha".to_owned(), user.to_owned(), repo.to_owned(), sha.to_string().to_owned(), method.to_owned()]);
                            }
                            set_redis_cache(&redis, &redis_key, &target_url.serialize());
                            return redir(&target_url, &req.url);

                        } else {
                            warn!("{}: SHA not found in JSON: {}", &github_url, &json);
                            return Ok(Response::with((status::NotFound,
                                                      format!("Couldn't find on Github {}", &github_url))));
                        }
                    } else {
                        warn!("{}: Couldn't parse Githubs JSON response: {}",
                              &github_url,
                              &body);
                        return Ok(Response::with((status::InternalServerError,
                                                  "Couldn't parse Githubs JSON response")));
                    }
                } else {
                    return Ok(Response::with((status::NotFound,
                                              format!("Couldn't find on Github {}", &github_url))));
                }
                Ok(Response::with(status::InternalServerError))

            }
        }
    }

    fn github_handler(req: &mut Request) -> IronResult<Response> {

        let ref router = req.extensions.get::<Router>().unwrap();
        let redis: redis::Connection = setup_redis();

        let user = router.find("user").unwrap();
        let repo = router.find("repo").unwrap();
        let sha = router.find("sha").unwrap();
        let filename: Vec<&str> = router.find("method")
                                        .unwrap_or("badge.svg")
                                        .rsplitn(1, '.')
                                        .collect();
        let (method, ext) = match filename.len() {
            2 => (filename[0], filename[1]),
            _ => (filename[0], "")
        };

        let redis_key = format!("{0}/github/{1}/{2}:{3}", method, user, repo, sha);

        match method {
            "badge" => {
                // if this is a badge, then we might have a cached version
                match redis.get(redis_key.to_owned()){
                    Ok(Some(Value::Data(base_url))) => {
                        let base_url = String::from_utf8(base_url).unwrap().to_owned();
                        let target_badge = match req.url.clone().query {
                            Some(query) => format!("{}.{}?{}", base_url, ext, query),
                            _ => format!("{}.{}", base_url, ext)
                        };
                        Ok(Response::with((status::TemporaryRedirect,
                                           Redirect(iUrl::parse(&target_badge).unwrap()))))
                    },
                    _ => {
                        trigger_update(&user, &repo, &sha);
                        let target_badge = format!("{}.{}", LINTING_BADGE_URL, ext);
                        return redir(&Url::parse(&target_badge).unwrap(), &req.url);
                    }
                }
            },
            // "logs" => {
            //     return Ok(Response::with((status::InternalServerError,
            //                        "Not Yet Implemented")));
            //
            // }
            // "status" => {
            //     return Ok(Response::with((status::InternalServerError,
            //                        "Not Yet Implemented")));
            //
            // }
            _ => {
                return Ok(Response::with((status::InternalServerError,
                                   "Not Yet Implemented")));
            }
        }
    }
}

fn log_redis(redis: &redis::Connection, key: &str, value: &str) {
    redis::pipe()
        .cmd("RPUSH")
            .arg(key.clone())
            .arg(format!("{0} {1}",
                         time::now_utc().rfc3339(),
                         value))
            .ignore()
        .execute(redis);
}

fn set_redis_cache(redis: &redis::Connection, key: &str, value: &str) {
    redis::pipe()
        .cmd("SET").arg(key.clone()).arg(value).ignore()
        .cmd("EXPIRE").arg(key.clone()).arg(5 * 60).ignore() // we expire in 5min
        .execute(redis);
}

fn redir(url: &Url, source_url: &iUrl) -> IronResult<Response> {
    match iUrl::from_generic_url(url.clone()) {
        Ok(mut redir_url) => {
            if let Some(ref query) = source_url.query {
                redir_url.query = Some(query.clone());
            }
            Ok(Response::with((status::TemporaryRedirect, Redirect(redir_url))))
        }
        Err(err) => Ok(Response::with((status::InternalServerError, err))),
    }
}

fn fetch(client: &Client, url: &str) -> Option<String> {
    let res = client.get(url)
                    .header(header::UserAgent("Clippy".to_owned()))
                    .header(header::Connection::close());
    if let Ok(mut res) = res.send() {
        let mut body = String::new();
        if res.read_to_string(&mut body).is_ok() {
            return Some(body);
        }
    }

    return None;
}

fn setup_redis() -> redis::Connection {
    let url = redis::parse_redis_url(&env::var("REDIS_URL")
                                          .unwrap_or("redis://redis/".to_owned()))
                  .unwrap();
    redis::Client::open(url)
        .unwrap()
        .get_connection()
        .unwrap()
}
<|endoftext|>


<commit_before>// xfail-stage0

use std;
import std::ivec;

fn test_reserve_and_on_heap() {
    let int[] v = ~[ 1, 2 ];
    assert (!ivec::on_heap(v));
    ivec::reserve(v, 8u);
    assert (ivec::on_heap(v));
}

fn test_unsafe_ptrs() {
    // Test on-stack copy-from-buf.
    auto a = ~[ 1, 2, 3 ];
    auto ptr = ivec::to_ptr(a);
    auto b = ~[];
    ivec::unsafe::copy_from_buf(b, ptr, 3u);
    assert (ivec::len(b) == 3u);
    assert (b.(0) == 1);
    assert (b.(1) == 2);
    assert (b.(2) == 3);

    // Test on-heap copy-from-buf.
    auto c = ~[ 1, 2, 3, 4, 5 ];
    ptr = ivec::to_ptr(c);
    auto d = ~[];
    ivec::unsafe::copy_from_buf(d, ptr, 5u);
    assert (ivec::len(d) == 5u);
    assert (d.(0) == 1);
    assert (d.(1) == 2);
    assert (d.(2) == 3);
    assert (d.(3) == 4);
    assert (d.(4) == 5);
}

fn test_init_fn() {
    fn square(uint n) -> uint { ret n * n; }

    // Test on-stack init-fn.
    auto v = ivec::init_fn(square, 3u);
    assert (ivec::len(v) == 3u);
    assert (v.(0) == 0u);
    assert (v.(1) == 1u);
    assert (v.(2) == 4u);

    // Test on-heap init-fn.
    v = ivec::init_fn(square, 5u);
    assert (ivec::len(v) == 5u);
    assert (v.(0) == 0u);
    assert (v.(1) == 1u);
    assert (v.(2) == 4u);
    assert (v.(3) == 9u);
    assert (v.(4) == 16u);
}

fn main() {
    test_reserve_and_on_heap();
    test_unsafe_ptrs();
    test_init_fn();
}

<commit_msg>test: Add test cases for ivec::init_elt, ivec::last, and ivec::slice<commit_after>// xfail-stage0

use std;
import std::ivec;
import std::option::none;
import std::option::some;

fn test_reserve_and_on_heap() {
    let int[] v = ~[ 1, 2 ];
    assert (!ivec::on_heap(v));
    ivec::reserve(v, 8u);
    assert (ivec::on_heap(v));
}

fn test_unsafe_ptrs() {
    // Test on-stack copy-from-buf.
    auto a = ~[ 1, 2, 3 ];
    auto ptr = ivec::to_ptr(a);
    auto b = ~[];
    ivec::unsafe::copy_from_buf(b, ptr, 3u);
    assert (ivec::len(b) == 3u);
    assert (b.(0) == 1);
    assert (b.(1) == 2);
    assert (b.(2) == 3);

    // Test on-heap copy-from-buf.
    auto c = ~[ 1, 2, 3, 4, 5 ];
    ptr = ivec::to_ptr(c);
    auto d = ~[];
    ivec::unsafe::copy_from_buf(d, ptr, 5u);
    assert (ivec::len(d) == 5u);
    assert (d.(0) == 1);
    assert (d.(1) == 2);
    assert (d.(2) == 3);
    assert (d.(3) == 4);
    assert (d.(4) == 5);
}

fn test_init_fn() {
    fn square(uint n) -> uint { ret n * n; }

    // Test on-stack init_fn.
    auto v = ivec::init_fn(square, 3u);
    assert (ivec::len(v) == 3u);
    assert (v.(0) == 0u);
    assert (v.(1) == 1u);
    assert (v.(2) == 4u);

    // Test on-heap init_fn.
    v = ivec::init_fn(square, 5u);
    assert (ivec::len(v) == 5u);
    assert (v.(0) == 0u);
    assert (v.(1) == 1u);
    assert (v.(2) == 4u);
    assert (v.(3) == 9u);
    assert (v.(4) == 16u);
}

fn test_init_elt() {
    // Test on-stack init_elt.
    auto v = ivec::init_elt(10u, 2u);
    assert (ivec::len(v) == 2u);
    assert (v.(0) == 10u);
    assert (v.(1) == 10u);

    // Test on-heap init_elt.
    v = ivec::init_elt(20u, 6u);
    assert (v.(0) == 20u);
    assert (v.(1) == 20u);
    assert (v.(2) == 20u);
    assert (v.(3) == 20u);
    assert (v.(4) == 20u);
    assert (v.(5) == 20u);
}

fn test_last() {
    auto n = ivec::last(~[]);
    assert (n == none);
    n = ivec::last(~[ 1, 2, 3 ]);
    assert (n == some(3));
    n = ivec::last(~[ 1, 2, 3, 4, 5 ]);
    assert (n == some(5));
}

fn test_slice() {
    // Test on-stack -> on-stack slice.
    auto v = ivec::slice(~[ 1, 2, 3 ], 1u, 3u);
    assert (ivec::len(v) == 2u);
    assert (v.(0) == 2);
    assert (v.(1) == 3);

    // Test on-heap -> on-stack slice.
    v = ivec::slice(~[ 1, 2, 3, 4, 5 ], 0u, 3u);
    assert (ivec::len(v) == 3u);
    assert (v.(0) == 1);
    assert (v.(1) == 2);
    assert (v.(2) == 3);

    // Test on-heap -> on-heap slice.
    v = ivec::slice(~[ 1, 2, 3, 4, 5, 6 ], 1u, 6u);
    assert (ivec::len(v) == 5u);
    assert (v.(0) == 2);
    assert (v.(1) == 3);
    assert (v.(2) == 4);
    assert (v.(3) == 5);
    assert (v.(4) == 6);
}

fn main() {
    test_reserve_and_on_heap();
    test_unsafe_ptrs();
    test_init_fn();
    test_init_elt();
    test_last();
    test_slice();
}

<|endoftext|>


<commit_before>use anyhow::{Context, Error};
use std::net::SocketAddr;
use std::sync::Arc;
use tokio::{
    io::{AsyncReadExt, AsyncWriteExt},
    net::{
        tcp::{OwnedReadHalf, OwnedWriteHalf},
        TcpListener, TcpStream,
    },
    runtime::Runtime,
    sync::broadcast::Sender,
};
use url::Url;

pub(crate) struct ChaosProxy {
    address: SocketAddr,
    backend_address: SocketAddr,

    runtime: Runtime,
    listener: TcpListener,

    break_networking_send: Sender<()>,
    restore_networking_send: Sender<()>,
}

impl ChaosProxy {
    pub(crate) fn new(backend_address: SocketAddr) -> Result<Arc<Self>, Error> {
        let runtime = Runtime::new().expect("failed to create Tokio runtime");
        let listener = runtime.block_on(TcpListener::bind("127.0.0.1:0"))?;

        let (break_networking_send, _) = tokio::sync::broadcast::channel(16);
        let (restore_networking_send, _) = tokio::sync::broadcast::channel(16);

        let instance = Arc::new(ChaosProxy {
            address: listener.local_addr()?,
            backend_address,

            listener,
            runtime,

            break_networking_send,
            restore_networking_send,
        });

        let instance_clone = instance.clone();
        instance.runtime.spawn(async move {
            if let Err(err) = instance_clone.server_loop().await {
                eprintln!("ChaosProxy server error: {err}");
            }
        });

        Ok(instance)
    }

    pub(crate) fn proxy_database_url(url: &str) -> Result<(Arc<Self>, String), Error> {
        let mut db_url = Url::parse(url).context("failed to parse database url")?;
        let backend_addr = db_url
            .socket_addrs(|| Some(5432))
            .context("could not resolve database url")?
            .get(0)
            .copied()
            .ok_or_else(|| anyhow::anyhow!("the database url does not point to any IP"))?;

        let instance = ChaosProxy::new(backend_addr).unwrap();
        db_url.set_ip_host(instance.address.ip()).unwrap();
        db_url.set_port(Some(instance.address.port())).unwrap();
        Ok((instance, db_url.into()))
    }

    pub(crate) fn break_networking(&self) {
        self.break_networking_send
            .send(())
            .expect("failed to send the break_networking message");
    }

    pub(crate) fn restore_networking(&self) {
        self.restore_networking_send
            .send(())
            .expect("failed to send the restore_networking message");
    }

    async fn server_loop(self: Arc<Self>) -> Result<(), Error> {
        let mut break_networking_recv = self.break_networking_send.subscribe();
        let mut restore_networking_recv = self.restore_networking_send.subscribe();

        loop {
            let (client_read, client_write) = tokio::select! {
                accepted = self.listener.accept() => accepted?.0.into_split(),

                // When networking is broken stop accepting connections until restore_networking()
                _ = break_networking_recv.recv() => {
                    let _ = restore_networking_recv.recv().await;
                    continue;
                },
            };
            let (backend_read, backend_write) = TcpStream::connect(&self.backend_address)
                .await?
                .into_split();

            let self_clone = self.clone();
            self.runtime.spawn(async move {
                if let Err(err) = self_clone.proxy_data(client_read, backend_write).await {
                    eprintln!("ChaosProxy connection error: {err}");
                }
            });

            let self_clone = self.clone();
            tokio::spawn(async move {
                if let Err(err) = self_clone.proxy_data(backend_read, client_write).await {
                    eprintln!("ChaosProxy connection error: {err}");
                }
            });
        }
    }

    async fn proxy_data(
        &self,
        mut from: OwnedReadHalf,
        mut to: OwnedWriteHalf,
    ) -> Result<(), Error> {
        let mut break_connections_recv = self.break_networking_send.subscribe();
        let mut buf = [0; 1024];

        loop {
            tokio::select! {
                len = from.read(&mut buf) => {
                    let len = len?;
                    if len == 0 {
                        // EOF, the socket was closed
                        return Ok(());
                    }
                    to.write(&buf[0..len]).await?;
                }
                _ = break_connections_recv.recv() => {
                    to.shutdown().await?;
                    return Ok(());
                }
            }
        }
    }
}
<commit_msg>Auto merge of #4673 - rust-lang:clippy/unused-io-amount, r=Turbo87<commit_after>use anyhow::{Context, Error};
use std::net::SocketAddr;
use std::sync::Arc;
use tokio::{
    io::{AsyncReadExt, AsyncWriteExt},
    net::{
        tcp::{OwnedReadHalf, OwnedWriteHalf},
        TcpListener, TcpStream,
    },
    runtime::Runtime,
    sync::broadcast::Sender,
};
use url::Url;

pub(crate) struct ChaosProxy {
    address: SocketAddr,
    backend_address: SocketAddr,

    runtime: Runtime,
    listener: TcpListener,

    break_networking_send: Sender<()>,
    restore_networking_send: Sender<()>,
}

impl ChaosProxy {
    pub(crate) fn new(backend_address: SocketAddr) -> Result<Arc<Self>, Error> {
        let runtime = Runtime::new().expect("failed to create Tokio runtime");
        let listener = runtime.block_on(TcpListener::bind("127.0.0.1:0"))?;

        let (break_networking_send, _) = tokio::sync::broadcast::channel(16);
        let (restore_networking_send, _) = tokio::sync::broadcast::channel(16);

        let instance = Arc::new(ChaosProxy {
            address: listener.local_addr()?,
            backend_address,

            listener,
            runtime,

            break_networking_send,
            restore_networking_send,
        });

        let instance_clone = instance.clone();
        instance.runtime.spawn(async move {
            if let Err(err) = instance_clone.server_loop().await {
                eprintln!("ChaosProxy server error: {err}");
            }
        });

        Ok(instance)
    }

    pub(crate) fn proxy_database_url(url: &str) -> Result<(Arc<Self>, String), Error> {
        let mut db_url = Url::parse(url).context("failed to parse database url")?;
        let backend_addr = db_url
            .socket_addrs(|| Some(5432))
            .context("could not resolve database url")?
            .get(0)
            .copied()
            .ok_or_else(|| anyhow::anyhow!("the database url does not point to any IP"))?;

        let instance = ChaosProxy::new(backend_addr).unwrap();
        db_url.set_ip_host(instance.address.ip()).unwrap();
        db_url.set_port(Some(instance.address.port())).unwrap();
        Ok((instance, db_url.into()))
    }

    pub(crate) fn break_networking(&self) {
        self.break_networking_send
            .send(())
            .expect("failed to send the break_networking message");
    }

    pub(crate) fn restore_networking(&self) {
        self.restore_networking_send
            .send(())
            .expect("failed to send the restore_networking message");
    }

    async fn server_loop(self: Arc<Self>) -> Result<(), Error> {
        let mut break_networking_recv = self.break_networking_send.subscribe();
        let mut restore_networking_recv = self.restore_networking_send.subscribe();

        loop {
            let (client_read, client_write) = tokio::select! {
                accepted = self.listener.accept() => accepted?.0.into_split(),

                // When networking is broken stop accepting connections until restore_networking()
                _ = break_networking_recv.recv() => {
                    let _ = restore_networking_recv.recv().await;
                    continue;
                },
            };
            let (backend_read, backend_write) = TcpStream::connect(&self.backend_address)
                .await?
                .into_split();

            let self_clone = self.clone();
            self.runtime.spawn(async move {
                if let Err(err) = self_clone.proxy_data(client_read, backend_write).await {
                    eprintln!("ChaosProxy connection error: {err}");
                }
            });

            let self_clone = self.clone();
            tokio::spawn(async move {
                if let Err(err) = self_clone.proxy_data(backend_read, client_write).await {
                    eprintln!("ChaosProxy connection error: {err}");
                }
            });
        }
    }

    async fn proxy_data(
        &self,
        mut from: OwnedReadHalf,
        mut to: OwnedWriteHalf,
    ) -> Result<(), Error> {
        let mut break_connections_recv = self.break_networking_send.subscribe();
        let mut buf = [0; 1024];

        loop {
            tokio::select! {
                len = from.read(&mut buf) => {
                    let len = len?;
                    if len == 0 {
                        // EOF, the socket was closed
                        return Ok(());
                    }
                    to.write_all(&buf[0..len]).await?;
                }
                _ = break_connections_recv.recv() => {
                    to.shutdown().await?;
                    return Ok(());
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// The following definitions are correct for aarch64 and x86_64,
// but may be wrong for mips64

pub type c_long = i64;
pub type c_ulong = u64;
pub type mode_t = u32;
pub type off64_t = i64;
pub type socklen_t = u32;

s! {
    pub struct sigset_t {
        __val: [::c_ulong; 1],
    }

    pub struct sigaction {
        pub sa_flags: ::c_int,
        pub sa_sigaction: ::sighandler_t,
        pub sa_mask: ::sigset_t,
        pub sa_restorer: ::Option<extern fn()>,
    }

    pub struct rlimit64 {
        pub rlim_cur: ::c_ulonglong,
        pub rlim_max: ::c_ulonglong,
    }

    pub struct pthread_attr_t {
        pub flags: u32,
        pub stack_base: *mut ::c_void,
        pub stack_size: ::size_t,
        pub guard_size: ::size_t,
        pub sched_policy: i32,
        pub sched_priority: i32,
        __reserved: [::c_char; 16],
    }

    pub struct passwd {
        pub pw_name: *mut ::c_char,
        pub pw_passwd: *mut ::c_char,
        pub pw_uid: ::uid_t,
        pub pw_gid: ::gid_t,
        pub pw_gecos: *mut ::c_char,
        pub pw_dir: *mut ::c_char,
        pub pw_shell: *mut ::c_char,
    }

    pub struct statfs {
        pub f_type: u64,
        pub f_bsize: u64,
        pub f_blocks: u64,
        pub f_bfree: u64,
        pub f_bavail: u64,
        pub f_files: u64,
        pub f_ffree: u64,
        pub f_fsid: ::__fsid_t,
        pub f_namelen: u64,
        pub f_frsize: u64,
        pub f_flags: u64,
        pub f_spare: [u64; 4],
    }

    pub struct sysinfo {
        pub uptime: ::c_long,
        pub loads: [::c_ulong; 3],
        pub totalram: ::c_ulong,
        pub freeram: ::c_ulong,
        pub sharedram: ::c_ulong,
        pub bufferram: ::c_ulong,
        pub totalswap: ::c_ulong,
        pub freeswap: ::c_ulong,
        pub procs: ::c_ushort,
        pub pad: ::c_ushort,
        pub totalhigh: ::c_ulong,
        pub freehigh: ::c_ulong,
        pub mem_unit: ::c_uint,
        pub _f: [::c_char; 0],
    }

    pub struct statfs64 {
        pub f_type: u64,
        pub f_bsize: u64,
        pub f_blocks: u64,
        pub f_bfree: u64,
        pub f_bavail: u64,
        pub f_files: u64,
        pub f_ffree: u64,
        pub f_fsid: ::__fsid_t,
        pub f_namelen: u64,
        pub f_frsize: u64,
        pub f_flags: u64,
        pub f_spare: [u64; 4],
    }

    pub struct statvfs64 {
        pub f_bsize: ::c_ulong,
        pub f_frsize: ::c_ulong,
        pub f_blocks: u64,
        pub f_bfree: u64,
        pub f_bavail: u64,
        pub f_files: u64,
        pub f_ffree: u64,
        pub f_favail: u64,
        pub f_fsid: ::c_ulong,
        pub f_flag: ::c_ulong,
        pub f_namemax: ::c_ulong,
        __f_spare: [::c_int; 6],
    }

    pub struct pthread_barrier_t {
        __private: [i64; 4],
    }

    pub struct pthread_spinlock_t {
        __private: i64,
    }
}

s_no_extra_traits! {
    pub struct pthread_mutex_t {
        value: ::c_int,
        __reserved: [::c_char; 36],
    }

    pub struct pthread_cond_t {
        value: ::c_int,
        __reserved: [::c_char; 44],
    }

    pub struct pthread_rwlock_t {
        numLocks: ::c_int,
        writerThreadId: ::c_int,
        pendingReaders: ::c_int,
        pendingWriters: ::c_int,
        attr: i32,
        __reserved: [::c_char; 36],
    }

    pub struct sigset64_t {
        __bits: [::c_ulong; 1]
    }
}

cfg_if! {
    if #[cfg(feature = "extra_traits")] {
        impl PartialEq for pthread_mutex_t {
            fn eq(&self, other: &pthread_mutex_t) -> bool {
                self.value == other.value
                    && self
                    .__reserved
                    .iter()
                    .zip(other.__reserved.iter())
                    .all(|(a,b)| a == b)
            }
        }

        impl Eq for pthread_mutex_t {}

        impl ::fmt::Debug for pthread_mutex_t {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                f.debug_struct("pthread_mutex_t")
                    .field("value", &self.value)
                    // FIXME: .field("__reserved", &self.__reserved)
                    .finish()
            }
        }

        impl ::hash::Hash for pthread_mutex_t {
            fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
                self.value.hash(state);
                self.__reserved.hash(state);
            }
        }

        impl PartialEq for pthread_cond_t {
            fn eq(&self, other: &pthread_cond_t) -> bool {
                self.value == other.value
                    && self
                    .__reserved
                    .iter()
                    .zip(other.__reserved.iter())
                    .all(|(a,b)| a == b)
            }
        }

        impl Eq for pthread_cond_t {}

        impl ::fmt::Debug for pthread_cond_t {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                f.debug_struct("pthread_cond_t")
                    .field("value", &self.value)
                    // FIXME: .field("__reserved", &self.__reserved)
                    .finish()
            }
        }

        impl ::hash::Hash for pthread_cond_t {
            fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
                self.value.hash(state);
                self.__reserved.hash(state);
            }
        }

        impl PartialEq for pthread_rwlock_t {
            fn eq(&self, other: &pthread_rwlock_t) -> bool {
                self.numLocks == other.numLocks
                    && self.writerThreadId == other.writerThreadId
                    && self.pendingReaders == other.pendingReaders
                    && self.pendingWriters == other.pendingWriters
                    && self.attr == other.attr
                    && self
                    .__reserved
                    .iter()
                    .zip(other.__reserved.iter())
                    .all(|(a,b)| a == b)
            }
        }

        impl Eq for pthread_rwlock_t {}

        impl ::fmt::Debug for pthread_rwlock_t {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                f.debug_struct("pthread_rwlock_t")
                    .field("numLocks", &self.numLocks)
                    .field("writerThreadId", &self.writerThreadId)
                    .field("pendingReaders", &self.pendingReaders)
                    .field("pendingWriters", &self.pendingWriters)
                    .field("attr", &self.attr)
                    // FIXME: .field("__reserved", &self.__reserved)
                    .finish()
            }
        }

        impl ::hash::Hash for pthread_rwlock_t {
            fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
                self.numLocks.hash(state);
                self.writerThreadId.hash(state);
                self.pendingReaders.hash(state);
                self.pendingWriters.hash(state);
                self.attr.hash(state);
                self.__reserved.hash(state);
            }
        }

        impl ::fmt::Debug for sigset64_t {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                f.debug_struct("sigset64_t")
                    .field("__bits", &self.__bits)
                    .finish()
            }
        }
    }
}

// These constants must be of the same type of sigaction.sa_flags
pub const SA_NOCLDSTOP: ::c_int = 0x00000001;
pub const SA_NOCLDWAIT: ::c_int = 0x00000002;
pub const SA_NODEFER: ::c_int = 0x40000000;
pub const SA_ONSTACK: ::c_int = 0x08000000;
pub const SA_RESETHAND: ::c_int = 0x80000000;
pub const SA_RESTART: ::c_int = 0x10000000;
pub const SA_SIGINFO: ::c_int = 0x00000004;

pub const RTLD_GLOBAL: ::c_int = 0x00100;
pub const RTLD_NOW: ::c_int = 2;
pub const RTLD_DEFAULT: *mut ::c_void = 0i64 as *mut ::c_void;

// From NDK's linux/auxvec.h
pub const AT_NULL: ::c_ulong = 0;
pub const AT_IGNORE: ::c_ulong = 1;
pub const AT_EXECFD: ::c_ulong = 2;
pub const AT_PHDR: ::c_ulong = 3;
pub const AT_PHENT: ::c_ulong = 4;
pub const AT_PHNUM: ::c_ulong = 5;
pub const AT_PAGESZ: ::c_ulong = 6;
pub const AT_BASE: ::c_ulong = 7;
pub const AT_FLAGS: ::c_ulong = 8;
pub const AT_ENTRY: ::c_ulong = 9;
pub const AT_NOTELF: ::c_ulong = 10;
pub const AT_UID: ::c_ulong = 11;
pub const AT_EUID: ::c_ulong = 12;
pub const AT_GID: ::c_ulong = 13;
pub const AT_EGID: ::c_ulong = 14;
pub const AT_PLATFORM: ::c_ulong = 15;
pub const AT_HWCAP: ::c_ulong = 16;
pub const AT_CLKTCK: ::c_ulong = 17;
pub const AT_SECURE: ::c_ulong = 23;
pub const AT_BASE_PLATFORM: ::c_ulong = 24;
pub const AT_RANDOM: ::c_ulong = 25;
pub const AT_HWCAP2: ::c_ulong = 26;
pub const AT_EXECFN: ::c_ulong = 31;

pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t {
    value: 0,
    __reserved: [0; 36],
};
pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t {
    value: 0,
    __reserved: [0; 44],
};
pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t {
    numLocks: 0,
    writerThreadId: 0,
    pendingReaders: 0,
    pendingWriters: 0,
    attr: 0,
    __reserved: [0; 36],
};
pub const PTHREAD_STACK_MIN: ::size_t = 4096 * 4;
pub const CPU_SETSIZE: ::size_t = 1024;
pub const __CPU_BITS: ::size_t = 64;

pub const UT_LINESIZE: usize = 32;
pub const UT_NAMESIZE: usize = 32;
pub const UT_HOSTSIZE: usize = 256;

f! {
    // Sadly, Android before 5.0 (API level 21), the accept4 syscall is not
    // exposed by the libc. As work-around, we implement it through `syscall`
    // directly. This workaround can be removed if the minimum version of
    // Android is bumped. When the workaround is removed, `accept4` can be
    // moved back to `linux_like/mod.rs`
    pub fn accept4(
        fd: ::c_int,
        addr: *mut ::sockaddr,
        len: *mut ::socklen_t,
        flg: ::c_int
    ) -> ::c_int {
        ::syscall(SYS_accept4, fd, addr, len, flg) as ::c_int
    }
}

extern "C" {
    pub fn getauxval(type_: ::c_ulong) -> ::c_ulong;
    pub fn __system_property_wait(
        pi: *const ::prop_info,
        __old_serial: u32,
        __new_serial_ptr: *mut u32,
        __relative_timeout: *const ::timespec,
    ) -> bool;
}

cfg_if! {
    if #[cfg(target_arch = "x86_64")] {
        mod x86_64;
        pub use self::x86_64::*;
    } else if #[cfg(target_arch = "aarch64")] {
        mod aarch64;
        pub use self::aarch64::*;
    } else {
        // Unknown target_arch
    }
}
<commit_msg>Add android x86_64 user struct<commit_after>// The following definitions are correct for aarch64 and x86_64,
// but may be wrong for mips64

pub type c_long = i64;
pub type c_ulong = u64;
pub type mode_t = u32;
pub type off64_t = i64;
pub type socklen_t = u32;

s! {
    pub struct sigset_t {
        __val: [::c_ulong; 1],
    }

    pub struct sigaction {
        pub sa_flags: ::c_int,
        pub sa_sigaction: ::sighandler_t,
        pub sa_mask: ::sigset_t,
        pub sa_restorer: ::Option<extern fn()>,
    }

    pub struct rlimit64 {
        pub rlim_cur: ::c_ulonglong,
        pub rlim_max: ::c_ulonglong,
    }

    pub struct pthread_attr_t {
        pub flags: u32,
        pub stack_base: *mut ::c_void,
        pub stack_size: ::size_t,
        pub guard_size: ::size_t,
        pub sched_policy: i32,
        pub sched_priority: i32,
        __reserved: [::c_char; 16],
    }

    pub struct passwd {
        pub pw_name: *mut ::c_char,
        pub pw_passwd: *mut ::c_char,
        pub pw_uid: ::uid_t,
        pub pw_gid: ::gid_t,
        pub pw_gecos: *mut ::c_char,
        pub pw_dir: *mut ::c_char,
        pub pw_shell: *mut ::c_char,
    }

    pub struct statfs {
        pub f_type: u64,
        pub f_bsize: u64,
        pub f_blocks: u64,
        pub f_bfree: u64,
        pub f_bavail: u64,
        pub f_files: u64,
        pub f_ffree: u64,
        pub f_fsid: ::__fsid_t,
        pub f_namelen: u64,
        pub f_frsize: u64,
        pub f_flags: u64,
        pub f_spare: [u64; 4],
    }

    pub struct sysinfo {
        pub uptime: ::c_long,
        pub loads: [::c_ulong; 3],
        pub totalram: ::c_ulong,
        pub freeram: ::c_ulong,
        pub sharedram: ::c_ulong,
        pub bufferram: ::c_ulong,
        pub totalswap: ::c_ulong,
        pub freeswap: ::c_ulong,
        pub procs: ::c_ushort,
        pub pad: ::c_ushort,
        pub totalhigh: ::c_ulong,
        pub freehigh: ::c_ulong,
        pub mem_unit: ::c_uint,
        pub _f: [::c_char; 0],
    }

    pub struct statfs64 {
        pub f_type: u64,
        pub f_bsize: u64,
        pub f_blocks: u64,
        pub f_bfree: u64,
        pub f_bavail: u64,
        pub f_files: u64,
        pub f_ffree: u64,
        pub f_fsid: ::__fsid_t,
        pub f_namelen: u64,
        pub f_frsize: u64,
        pub f_flags: u64,
        pub f_spare: [u64; 4],
    }

    pub struct statvfs64 {
        pub f_bsize: ::c_ulong,
        pub f_frsize: ::c_ulong,
        pub f_blocks: u64,
        pub f_bfree: u64,
        pub f_bavail: u64,
        pub f_files: u64,
        pub f_ffree: u64,
        pub f_favail: u64,
        pub f_fsid: ::c_ulong,
        pub f_flag: ::c_ulong,
        pub f_namemax: ::c_ulong,
        __f_spare: [::c_int; 6],
    }

    pub struct pthread_barrier_t {
        __private: [i64; 4],
    }

    pub struct pthread_spinlock_t {
        __private: i64,
    }

    pub struct user_regs_struct {
        pub r15: ::c_ulonglong,
        pub r14: ::c_ulonglong,
        pub r13: ::c_ulonglong,
        pub r12: ::c_ulonglong,
        pub rbp: ::c_ulonglong,
        pub rbx: ::c_ulonglong,
        pub r11: ::c_ulonglong,
        pub r10: ::c_ulonglong,
        pub r9: ::c_ulonglong,
        pub r8: ::c_ulonglong,
        pub rax: ::c_ulonglong,
        pub rcx: ::c_ulonglong,
        pub rdx: ::c_ulonglong,
        pub rsi: ::c_ulonglong,
        pub rdi: ::c_ulonglong,
        pub orig_rax: ::c_ulonglong,
        pub rip: ::c_ulonglong,
        pub cs: ::c_ulonglong,
        pub eflags: ::c_ulonglong,
        pub rsp: ::c_ulonglong,
        pub ss: ::c_ulonglong,
        pub fs_base: ::c_ulonglong,
        pub gs_base: ::c_ulonglong,
        pub ds: ::c_ulonglong,
        pub es: ::c_ulonglong,
        pub fs: ::c_ulonglong,
        pub gs: ::c_ulonglong,
    }

    pub struct user {
        pub regs: user_regs_struct,
        pub u_fpvalid: ::c_int,
        pub i387: user_fpregs_struct,
        pub u_tsize: ::c_ulonglong,
        pub u_dsize: ::c_ulonglong,
        pub u_ssize: ::c_ulonglong,
        pub start_code: ::c_ulonglong,
        pub start_stack: ::c_ulonglong,
        pub signal: ::c_longlong,
        __reserved: ::c_int,
        #[cfg(target_pointer_width = "32")]
        __pad1: u32,
        pub u_ar0: *mut user_regs_struct,
        #[cfg(target_pointer_width = "32")]
        __pad2: u32,
        pub u_fpstate: *mut user_fpregs_struct,
        pub magic: ::c_ulonglong,
        pub u_comm: [::c_char; 32],
        pub u_debugreg: [::c_ulonglong; 8],
        pub error_code: ::c_ulonglong,
        pub fault_address: ::c_ulonglong,
    }

}

s_no_extra_traits! {
    pub struct pthread_mutex_t {
        value: ::c_int,
        __reserved: [::c_char; 36],
    }

    pub struct pthread_cond_t {
        value: ::c_int,
        __reserved: [::c_char; 44],
    }

    pub struct pthread_rwlock_t {
        numLocks: ::c_int,
        writerThreadId: ::c_int,
        pendingReaders: ::c_int,
        pendingWriters: ::c_int,
        attr: i32,
        __reserved: [::c_char; 36],
    }

    pub struct sigset64_t {
        __bits: [::c_ulong; 1]
    }

    pub struct user_fpregs_struct {
        pub cwd: ::c_ushort,
        pub swd: ::c_ushort,
        pub ftw: ::c_ushort,
        pub fop: ::c_ushort,
        pub rip: ::c_ulonglong,
        pub rdp: ::c_ulonglong,
        pub mxcsr: ::c_uint,
        pub mxcr_mask: ::c_uint,
        pub st_space: [::c_uint; 32],
        pub xmm_space: [::c_uint; 64],
        padding: [::c_uint; 24],
    }
}

cfg_if! {
    if #[cfg(feature = "extra_traits")] {
        impl PartialEq for pthread_mutex_t {
            fn eq(&self, other: &pthread_mutex_t) -> bool {
                self.value == other.value
                    && self
                    .__reserved
                    .iter()
                    .zip(other.__reserved.iter())
                    .all(|(a,b)| a == b)
            }
        }

        impl Eq for pthread_mutex_t {}

        impl ::fmt::Debug for pthread_mutex_t {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                f.debug_struct("pthread_mutex_t")
                    .field("value", &self.value)
                    // FIXME: .field("__reserved", &self.__reserved)
                    .finish()
            }
        }

        impl ::hash::Hash for pthread_mutex_t {
            fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
                self.value.hash(state);
                self.__reserved.hash(state);
            }
        }

        impl PartialEq for pthread_cond_t {
            fn eq(&self, other: &pthread_cond_t) -> bool {
                self.value == other.value
                    && self
                    .__reserved
                    .iter()
                    .zip(other.__reserved.iter())
                    .all(|(a,b)| a == b)
            }
        }

        impl Eq for pthread_cond_t {}

        impl ::fmt::Debug for pthread_cond_t {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                f.debug_struct("pthread_cond_t")
                    .field("value", &self.value)
                    // FIXME: .field("__reserved", &self.__reserved)
                    .finish()
            }
        }

        impl ::hash::Hash for pthread_cond_t {
            fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
                self.value.hash(state);
                self.__reserved.hash(state);
            }
        }

        impl PartialEq for pthread_rwlock_t {
            fn eq(&self, other: &pthread_rwlock_t) -> bool {
                self.numLocks == other.numLocks
                    && self.writerThreadId == other.writerThreadId
                    && self.pendingReaders == other.pendingReaders
                    && self.pendingWriters == other.pendingWriters
                    && self.attr == other.attr
                    && self
                    .__reserved
                    .iter()
                    .zip(other.__reserved.iter())
                    .all(|(a,b)| a == b)
            }
        }

        impl Eq for pthread_rwlock_t {}

        impl ::fmt::Debug for pthread_rwlock_t {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                f.debug_struct("pthread_rwlock_t")
                    .field("numLocks", &self.numLocks)
                    .field("writerThreadId", &self.writerThreadId)
                    .field("pendingReaders", &self.pendingReaders)
                    .field("pendingWriters", &self.pendingWriters)
                    .field("attr", &self.attr)
                    // FIXME: .field("__reserved", &self.__reserved)
                    .finish()
            }
        }

        impl ::hash::Hash for pthread_rwlock_t {
            fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
                self.numLocks.hash(state);
                self.writerThreadId.hash(state);
                self.pendingReaders.hash(state);
                self.pendingWriters.hash(state);
                self.attr.hash(state);
                self.__reserved.hash(state);
            }
        }

        impl ::fmt::Debug for sigset64_t {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                f.debug_struct("sigset64_t")
                    .field("__bits", &self.__bits)
                    .finish()
            }
        }
    }
}

// These constants must be of the same type of sigaction.sa_flags
pub const SA_NOCLDSTOP: ::c_int = 0x00000001;
pub const SA_NOCLDWAIT: ::c_int = 0x00000002;
pub const SA_NODEFER: ::c_int = 0x40000000;
pub const SA_ONSTACK: ::c_int = 0x08000000;
pub const SA_RESETHAND: ::c_int = 0x80000000;
pub const SA_RESTART: ::c_int = 0x10000000;
pub const SA_SIGINFO: ::c_int = 0x00000004;

pub const RTLD_GLOBAL: ::c_int = 0x00100;
pub const RTLD_NOW: ::c_int = 2;
pub const RTLD_DEFAULT: *mut ::c_void = 0i64 as *mut ::c_void;

// From NDK's linux/auxvec.h
pub const AT_NULL: ::c_ulong = 0;
pub const AT_IGNORE: ::c_ulong = 1;
pub const AT_EXECFD: ::c_ulong = 2;
pub const AT_PHDR: ::c_ulong = 3;
pub const AT_PHENT: ::c_ulong = 4;
pub const AT_PHNUM: ::c_ulong = 5;
pub const AT_PAGESZ: ::c_ulong = 6;
pub const AT_BASE: ::c_ulong = 7;
pub const AT_FLAGS: ::c_ulong = 8;
pub const AT_ENTRY: ::c_ulong = 9;
pub const AT_NOTELF: ::c_ulong = 10;
pub const AT_UID: ::c_ulong = 11;
pub const AT_EUID: ::c_ulong = 12;
pub const AT_GID: ::c_ulong = 13;
pub const AT_EGID: ::c_ulong = 14;
pub const AT_PLATFORM: ::c_ulong = 15;
pub const AT_HWCAP: ::c_ulong = 16;
pub const AT_CLKTCK: ::c_ulong = 17;
pub const AT_SECURE: ::c_ulong = 23;
pub const AT_BASE_PLATFORM: ::c_ulong = 24;
pub const AT_RANDOM: ::c_ulong = 25;
pub const AT_HWCAP2: ::c_ulong = 26;
pub const AT_EXECFN: ::c_ulong = 31;

pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t {
    value: 0,
    __reserved: [0; 36],
};
pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t {
    value: 0,
    __reserved: [0; 44],
};
pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t {
    numLocks: 0,
    writerThreadId: 0,
    pendingReaders: 0,
    pendingWriters: 0,
    attr: 0,
    __reserved: [0; 36],
};
pub const PTHREAD_STACK_MIN: ::size_t = 4096 * 4;
pub const CPU_SETSIZE: ::size_t = 1024;
pub const __CPU_BITS: ::size_t = 64;

pub const UT_LINESIZE: usize = 32;
pub const UT_NAMESIZE: usize = 32;
pub const UT_HOSTSIZE: usize = 256;

f! {
    // Sadly, Android before 5.0 (API level 21), the accept4 syscall is not
    // exposed by the libc. As work-around, we implement it through `syscall`
    // directly. This workaround can be removed if the minimum version of
    // Android is bumped. When the workaround is removed, `accept4` can be
    // moved back to `linux_like/mod.rs`
    pub fn accept4(
        fd: ::c_int,
        addr: *mut ::sockaddr,
        len: *mut ::socklen_t,
        flg: ::c_int
    ) -> ::c_int {
        ::syscall(SYS_accept4, fd, addr, len, flg) as ::c_int
    }
}

extern "C" {
    pub fn getauxval(type_: ::c_ulong) -> ::c_ulong;
    pub fn __system_property_wait(
        pi: *const ::prop_info,
        __old_serial: u32,
        __new_serial_ptr: *mut u32,
        __relative_timeout: *const ::timespec,
    ) -> bool;
}

cfg_if! {
    if #[cfg(target_arch = "x86_64")] {
        mod x86_64;
        pub use self::x86_64::*;
    } else if #[cfg(target_arch = "aarch64")] {
        mod aarch64;
        pub use self::aarch64::*;
    } else {
        // Unknown target_arch
    }
}
<|endoftext|>


<commit_before>// See LICENSE file for copyright and license details.

use std::cell::RefCell;
use time::precise_time_ns;
use glfw;
use cgmath::{Vector2};
use core::types::{Size2, MInt};
use core::conf::Config;
use core::fs::FileSystem;
use visualizer::mgl;
use visualizer::types::{MatId, ColorId, Time, ScreenPos};
use visualizer::shader::Shader;
use visualizer::font_stash::FontStash;
use visualizer::context::Context;
use visualizer::state_visualizer::{
    StateVisualizer,
    StartGame,
    EndGame,
    QuitMenu,
};
use visualizer::game_state_visualizer::GameStateVisualizer;
use visualizer::menu_state_visualizer::MenuStateVisualizer;

type EventsReceiver = Receiver<(f64, glfw::WindowEvent)>;

pub struct Visualizer {
    visualizers: Vec<Box<StateVisualizer+'static>>,
    dtime: Time,
    last_time: Time,
    glfw: glfw::Glfw,
    events: EventsReceiver,
    context: Context,
    fs: FileSystem,
    should_close: bool,
}

fn create_win(glfw: &glfw::Glfw, win_size: Size2<MInt>)
    -> (glfw::Window, EventsReceiver)
{
    let w = win_size.w as u32;
    let h = win_size.h as u32;
    let title = "Marauder";
    let flags = glfw::Windowed;
    glfw.create_window(w, h, title, flags).unwrap()
}

impl Visualizer {
    pub fn new() -> Visualizer {
        let fs = FileSystem::new();
        let glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
        let config = Config::new(&fs.get(&Path::new("data/conf_visualizer.json")));
        let win_size = config.get::<Size2<MInt>>("screen_size");
        let (win, events) = create_win(&glfw, win_size);
        glfw.make_context_current(Some(&win));
        mgl::load_gl_funcs_with(|procname| glfw.get_proc_address(procname));
        mgl::init_opengl();
        mgl::set_viewport(win_size);
        win.set_all_polling(true);
        let font_size = config.get("font_size");
        let font_stash = FontStash::new(
            &fs.get(&Path::new("data/DroidSerif-Regular.ttf")), font_size);
        let shader = Shader::new(
            &fs.get(&Path::new("data/normal.vs.glsl")),
            &fs.get(&Path::new("data/normal.fs.glsl")),
        );
        let mvp_mat_id = MatId{id: shader.get_uniform("mvp_mat")};
        let basic_color_id = ColorId{id: shader.get_uniform("basic_color")};
        let context = Context {
            win: win,
            win_size: win_size,
            config: config,
            mouse_pos: ScreenPos{v: Vector2::zero()},
            font_stash: RefCell::new(font_stash),
            shader: shader,
            mvp_mat_id: mvp_mat_id,
            basic_color_id: basic_color_id,
        };
        let visualizer = box MenuStateVisualizer::new(&context);
        Visualizer {
            visualizers: vec![visualizer as Box<StateVisualizer>],
            dtime: Time{n: 0},
            last_time: Time{n: precise_time_ns()},
            glfw: glfw,
            events: events,
            context: context,
            fs: fs,
            should_close: false,
        }
    }

    fn get_events(&mut self) -> Vec<glfw::WindowEvent> {
        self.glfw.poll_events();
        let mut events = Vec::new();
        for (_, event) in glfw::flush_messages(&self.events) {
            events.push(event);
        }
        events
    }

    pub fn is_running(&self) -> bool {
        !self.should_close
    }

    fn handle_cmd(&mut self) {
        let cmd = match self.visualizers.last() {
            Some(visualizer) => visualizer.get_command(),
            None => fail!("No state visualizer"),
        };
        match cmd {
            Some(StartGame) => {
                let visualizer = box GameStateVisualizer::new(
                    &self.fs, &self.context);
                self.visualizers.push(visualizer as Box<StateVisualizer>);
            }
            Some(EndGame) => {
                let _ = self.visualizers.pop();
            },
            Some(QuitMenu) => {
                self.should_close = true;
            },
            None => {},
        }
    }

    // TODO: simplify
    pub fn tick(&mut self) {
        {
            let events = self.get_events();
            let visualizer = match self.visualizers.mut_last() {
                Some(visualizer) => visualizer,
                None => fail!("No state visualizer"),
            };
            for event in events.iter() {
                visualizer.handle_event(&self.context, *event);
                self.context.handle_event(*event);
            }
            visualizer.logic(&self.context);
            visualizer.draw(&self.context, self.dtime);
        }
        self.handle_cmd();
        self.update_time();
    }

    pub fn update_time(&mut self) {
        let time = precise_time_ns();
        self.dtime.n = time - self.last_time.n;
        self.last_time.n = time;
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<commit_msg>Updated for latest glfw-rs<commit_after>// See LICENSE file for copyright and license details.

use std::cell::RefCell;
use time::precise_time_ns;
use glfw;
use cgmath::{Vector2};
use core::types::{Size2, MInt};
use core::conf::Config;
use core::fs::FileSystem;
use visualizer::mgl;
use visualizer::types::{MatId, ColorId, Time, ScreenPos};
use visualizer::shader::Shader;
use visualizer::font_stash::FontStash;
use visualizer::context::Context;
use visualizer::state_visualizer::{
    StateVisualizer,
    StartGame,
    EndGame,
    QuitMenu,
};
use visualizer::game_state_visualizer::GameStateVisualizer;
use visualizer::menu_state_visualizer::MenuStateVisualizer;

type EventsReceiver = Receiver<(f64, glfw::WindowEvent)>;

pub struct Visualizer {
    visualizers: Vec<Box<StateVisualizer+'static>>,
    dtime: Time,
    last_time: Time,
    glfw: glfw::Glfw,
    events: EventsReceiver,
    context: Context,
    fs: FileSystem,
    should_close: bool,
}

fn create_win(glfw: &glfw::Glfw, win_size: Size2<MInt>)
    -> (glfw::Window, EventsReceiver)
{
    let w = win_size.w as u32;
    let h = win_size.h as u32;
    let title = "Marauder";
    let flags = glfw::Windowed;
    glfw.create_window(w, h, title, flags).unwrap()
}

impl Visualizer {
    pub fn new() -> Visualizer {
        let fs = FileSystem::new();
        let glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
        let config = Config::new(&fs.get(&Path::new("data/conf_visualizer.json")));
        let win_size = config.get::<Size2<MInt>>("screen_size");
        let (win, events) = create_win(&glfw, win_size);
        glfw.make_context_current(Some(&win));
        mgl::load_gl_funcs_with(|procname| win.get_proc_address(procname));
        mgl::init_opengl();
        mgl::set_viewport(win_size);
        win.set_all_polling(true);
        let font_size = config.get("font_size");
        let font_stash = FontStash::new(
            &fs.get(&Path::new("data/DroidSerif-Regular.ttf")), font_size);
        let shader = Shader::new(
            &fs.get(&Path::new("data/normal.vs.glsl")),
            &fs.get(&Path::new("data/normal.fs.glsl")),
        );
        let mvp_mat_id = MatId{id: shader.get_uniform("mvp_mat")};
        let basic_color_id = ColorId{id: shader.get_uniform("basic_color")};
        let context = Context {
            win: win,
            win_size: win_size,
            config: config,
            mouse_pos: ScreenPos{v: Vector2::zero()},
            font_stash: RefCell::new(font_stash),
            shader: shader,
            mvp_mat_id: mvp_mat_id,
            basic_color_id: basic_color_id,
        };
        let visualizer = box MenuStateVisualizer::new(&context);
        Visualizer {
            visualizers: vec![visualizer as Box<StateVisualizer>],
            dtime: Time{n: 0},
            last_time: Time{n: precise_time_ns()},
            glfw: glfw,
            events: events,
            context: context,
            fs: fs,
            should_close: false,
        }
    }

    fn get_events(&mut self) -> Vec<glfw::WindowEvent> {
        self.glfw.poll_events();
        let mut events = Vec::new();
        for (_, event) in glfw::flush_messages(&self.events) {
            events.push(event);
        }
        events
    }

    pub fn is_running(&self) -> bool {
        !self.should_close
    }

    fn handle_cmd(&mut self) {
        let cmd = match self.visualizers.last() {
            Some(visualizer) => visualizer.get_command(),
            None => fail!("No state visualizer"),
        };
        match cmd {
            Some(StartGame) => {
                let visualizer = box GameStateVisualizer::new(
                    &self.fs, &self.context);
                self.visualizers.push(visualizer as Box<StateVisualizer>);
            }
            Some(EndGame) => {
                let _ = self.visualizers.pop();
            },
            Some(QuitMenu) => {
                self.should_close = true;
            },
            None => {},
        }
    }

    // TODO: simplify
    pub fn tick(&mut self) {
        {
            let events = self.get_events();
            let visualizer = match self.visualizers.mut_last() {
                Some(visualizer) => visualizer,
                None => fail!("No state visualizer"),
            };
            for event in events.iter() {
                visualizer.handle_event(&self.context, *event);
                self.context.handle_event(*event);
            }
            visualizer.logic(&self.context);
            visualizer.draw(&self.context, self.dtime);
        }
        self.handle_cmd();
        self.update_time();
    }

    pub fn update_time(&mut self) {
        let time = precise_time_ns();
        self.dtime.n = time - self.last_time.n;
        self.last_time.n = time;
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<|endoftext|>


<commit_before>#![cfg_attr(
    not(any(
        feature = "vulkan",
        feature = "dx12",
        feature = "metal",
        feature = "gl"
    )),
    allow(dead_code)
)]

extern crate gfx_hal as hal;
extern crate gfx_warden as warden;
extern crate ron;
#[macro_use]
extern crate serde;

#[cfg(feature = "env_logger")]
extern crate env_logger;
#[cfg(feature = "dx12")]
extern crate gfx_backend_dx12;
#[cfg(any(feature = "gl", feature = "gl-headless"))]
extern crate gfx_backend_gl;
#[cfg(feature = "metal")]
extern crate gfx_backend_metal;
#[cfg(feature = "vulkan")]
extern crate gfx_backend_vulkan;

use std::collections::HashMap;
use std::fs::File;
use std::path::PathBuf;

use ron::de;

#[derive(Debug, Deserialize)]
enum Expectation {
    Buffer(String, Vec<u8>),
    ImageRow(String, usize, Vec<u8>),
}

#[derive(Debug, Deserialize)]
struct Test {
    features: hal::Features,
    jobs: Vec<String>,
    expect: Expectation,
}

type Suite = HashMap<String, HashMap<String, Test>>;

struct TestGroup {
    name: String,
    scene: warden::raw::Scene,
    tests: HashMap<String, Test>,
}

#[derive(Debug)]
struct TestResults {
    pass: usize,
    skip: usize,
    fail: usize,
}

#[derive(Default)]
struct Disabilities {}

struct Harness {
    base_path: PathBuf,
    suite: Vec<TestGroup>,
}

impl Harness {
    fn new(suite_name: &str) -> Self {
        let base_path = PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/../../reftests",));
        println!("Parsing test suite '{}'...", suite_name);

        let suite_path = base_path.join(suite_name).with_extension("ron");
        let suite = File::open(suite_path)
            .map_err(de::Error::from)
            .and_then(de::from_reader::<_, Suite>)
            .expect("failed to open/parse the suite")
            .into_iter()
            .map(|(name, tests)| {
                let path = base_path.join("scenes").join(&name).with_extension("ron");
                let scene = File::open(path)
                    .map_err(de::Error::from)
                    .and_then(de::from_reader)
                    .expect("failed to open/parse the scene");
                TestGroup { name, scene, tests }
            })
            .collect();

        Harness { base_path, suite }
    }

    fn run<I: hal::Instance>(&self, instance: I, _disabilities: Disabilities) -> usize {
        use crate::hal::PhysicalDevice;

        let mut results = TestResults {
            pass: 0,
            skip: 0,
            fail: 0,
        };
        for tg in &self.suite {
            let mut adapters = instance.enumerate_adapters();
            let adapter = adapters.remove(0);
            let features = adapter.physical_device.features();
            let limits = adapter.physical_device.limits();
            //println!("\t{:?}", adapter.info);
            println!("\tScene '{}':", tg.name);

            #[cfg(not(feature = "glsl-to-spirv"))]
            {
                let all_spirv = tg.scene.resources.values().all(|res| match *res {
                    warden::raw::Resource::Shader(ref name) => name.ends_with(".spirv"),
                    _ => true,
                });
                if !all_spirv {
                    println!("\t\tskipped {} tests (GLSL shaders)", tg.tests.len());
                    results.skip += tg.tests.len();
                    continue;
                }
            }

            let mut scene = warden::gpu::Scene::<I::Backend, _>::new(
                adapter,
                &tg.scene,
                self.base_path.join("data"),
            )
            .unwrap();

            for (test_name, test) in &tg.tests {
                print!("\t\tTest '{}' ...", test_name);
                if !features.contains(test.features) {
                    println!(
                        "\tskipped (features missing: {:?})",
                        test.features - features
                    );
                    results.skip += 1;
                }
                let mut max_compute_work_groups = [0; 3];
                for job_name in &test.jobs {
                    if let warden::raw::Job::Compute { dispatch, .. } = tg.scene.jobs[job_name] {
                        for (max, count) in max_compute_work_groups.iter_mut().zip(dispatch.iter()) {
                            *max = (*max).max(*count);
                        }
                    }
                }
                if max_compute_work_groups[0] > limits.max_compute_work_group_size[0]
                    || max_compute_work_groups[1] > limits.max_compute_work_group_size[1]
                    || max_compute_work_groups[2] > limits.max_compute_work_group_size[2]
                {
                    println!("\tskipped (compute {:?})", max_compute_work_groups);
                    results.skip += 1;
                    continue;
                }

                scene.run(test.jobs.iter().map(|x| x.as_str()));

                print!("\tran: ");
                let (guard, row, data) = match test.expect {
                    Expectation::Buffer(ref buffer, ref data) => {
                        (scene.fetch_buffer(buffer), 0, data)
                    }
                    Expectation::ImageRow(ref image, row, ref data) => {
                        (scene.fetch_image(image), row, data)
                    }
                };

                if data.as_slice() == guard.row(row) {
                    println!("PASS");
                    results.pass += 1;
                } else {
                    println!("FAIL {:?}", guard.row(row));
                    results.fail += 1;
                }
            }
        }

        println!("\t{:?}", results);
        results.fail
    }
}

fn main() {
    use std::{env, process};

    #[cfg(feature = "env_logger")]
    env_logger::init();
    let mut num_failures = 0;

    let suite_name = match env::args().nth(1) {
        Some(name) => name,
        None => {
            println!("Call with the argument of the reftest suite name");
            return;
        }
    };

    let harness = Harness::new(&suite_name);
    #[cfg(feature = "vulkan")]
    {
        println!("Warding Vulkan:");
        let instance = gfx_backend_vulkan::Instance::create("warden", 1);
        num_failures += harness.run(instance, Disabilities::default());
    }
    #[cfg(feature = "dx12")]
    {
        println!("Warding DX12:");
        let instance = gfx_backend_dx12::Instance::create("warden", 1);
        num_failures += harness.run(instance, Disabilities::default());
    }
    #[cfg(feature = "metal")]
    {
        println!("Warding Metal:");
        let instance = gfx_backend_metal::Instance::create("warden", 1);
        num_failures += harness.run(
            instance,
            Disabilities {
                ..Disabilities::default()
            },
        );
    }
    #[cfg(feature = "gl")]
    {
        use gfx_backend_gl::glutin;
        println!("Warding GL:");
        let events_loop = glutin::EventsLoop::new();
        let window = glutin::WindowedContext::new_windowed(
            glutin::WindowBuilder::new(),
            glutin::ContextBuilder::new().with_gl_profile(glutin::GlProfile::Core),
            &events_loop,
        )
        .unwrap();
        let instance = gfx_backend_gl::Surface::from_window(window);
        num_failures += harness.run(instance, Disabilities::default());
    }
    #[cfg(feature = "gl-headless")]
    {
        use gfx_backend_gl::glutin;
        println!("Warding GL headless:");
        let events_loop = glutin::EventsLoop::new();
        let context =
            glutin::Context::new_headless(&events_loop, glutin::ContextBuilder::new(), glutin::dpi::PhysicalSize::new(0.0, 0.0)).unwrap();
        let instance = gfx_backend_gl::Headless(context);
        num_failures += harness.run(instance, Disabilities::default());
    }
    let _ = harness;
    num_failures += 0; // mark as mutated
    process::exit(num_failures as _);
}
<commit_msg>Fix Warden Reftests<commit_after>#![cfg_attr(
    not(any(
        feature = "vulkan",
        feature = "dx12",
        feature = "metal",
        feature = "gl"
    )),
    allow(dead_code)
)]

extern crate gfx_hal as hal;
extern crate gfx_warden as warden;
extern crate ron;
#[macro_use]
extern crate serde;

#[cfg(feature = "env_logger")]
extern crate env_logger;
#[cfg(feature = "dx12")]
extern crate gfx_backend_dx12;
#[cfg(any(feature = "gl", feature = "gl-headless"))]
extern crate gfx_backend_gl;
#[cfg(feature = "metal")]
extern crate gfx_backend_metal;
#[cfg(feature = "vulkan")]
extern crate gfx_backend_vulkan;

use std::collections::HashMap;
use std::fs::File;
use std::path::PathBuf;

use ron::de;

#[derive(Debug, Deserialize)]
enum Expectation {
    Buffer(String, Vec<u8>),
    ImageRow(String, usize, Vec<u8>),
}

#[derive(Debug, Deserialize)]
struct Test {
    features: hal::Features,
    jobs: Vec<String>,
    expect: Expectation,
}

type Suite = HashMap<String, HashMap<String, Test>>;

struct TestGroup {
    name: String,
    scene: warden::raw::Scene,
    tests: HashMap<String, Test>,
}

#[derive(Debug)]
struct TestResults {
    pass: usize,
    skip: usize,
    fail: usize,
}

#[derive(Default)]
struct Disabilities {}

struct Harness {
    base_path: PathBuf,
    suite: Vec<TestGroup>,
}

impl Harness {
    fn new(suite_name: &str) -> Self {
        let base_path = PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/../../reftests",));
        println!("Parsing test suite '{}'...", suite_name);

        let suite_path = base_path.join(suite_name).with_extension("ron");
        let suite = File::open(suite_path)
            .map_err(de::Error::from)
            .and_then(de::from_reader::<_, Suite>)
            .expect("failed to open/parse the suite")
            .into_iter()
            .map(|(name, tests)| {
                let path = base_path.join("scenes").join(&name).with_extension("ron");
                let scene = File::open(path)
                    .map_err(de::Error::from)
                    .and_then(de::from_reader)
                    .expect("failed to open/parse the scene");
                TestGroup { name, scene, tests }
            })
            .collect();

        Harness { base_path, suite }
    }

    fn run<I: hal::Instance>(&self, instance: I, _disabilities: Disabilities) -> usize {
        use crate::hal::PhysicalDevice;

        let mut results = TestResults {
            pass: 0,
            skip: 0,
            fail: 0,
        };
        for tg in &self.suite {
            let mut adapters = instance.enumerate_adapters();
            let adapter = adapters.remove(0);
            let features = adapter.physical_device.features();
            let limits = adapter.physical_device.limits();
            //println!("\t{:?}", adapter.info);
            println!("\tScene '{}':", tg.name);

            #[cfg(not(feature = "glsl-to-spirv"))]
            {
                let all_spirv = tg.scene.resources.values().all(|res| match *res {
                    warden::raw::Resource::Shader(ref name) => name.ends_with(".spirv"),
                    _ => true,
                });
                if !all_spirv {
                    println!("\t\tskipped {} tests (GLSL shaders)", tg.tests.len());
                    results.skip += tg.tests.len();
                    continue;
                }
            }

            let mut scene = warden::gpu::Scene::<I::Backend, _>::new(
                adapter,
                &tg.scene,
                self.base_path.join("data"),
            )
            .unwrap();

            for (test_name, test) in &tg.tests {
                print!("\t\tTest '{}' ...", test_name);
                if !features.contains(test.features) {
                    println!(
                        "\tskipped (features missing: {:?})",
                        test.features - features
                    );
                    results.skip += 1;
                }
                let mut max_compute_work_groups = [0; 3];
                for job_name in &test.jobs {
                    if let warden::raw::Job::Compute { dispatch, .. } = tg.scene.jobs[job_name] {
                        for (max, count) in max_compute_work_groups.iter_mut().zip(dispatch.iter()) {
                            *max = (*max).max(*count);
                        }
                    }
                }
                if max_compute_work_groups[0] > limits.max_compute_work_group_size[0]
                    || max_compute_work_groups[1] > limits.max_compute_work_group_size[1]
                    || max_compute_work_groups[2] > limits.max_compute_work_group_size[2]
                {
                    println!("\tskipped (compute {:?})", max_compute_work_groups);
                    results.skip += 1;
                    continue;
                }

                scene.run(test.jobs.iter().map(|x| x.as_str()));

                print!("\tran: ");
                let (guard, row, data) = match test.expect {
                    Expectation::Buffer(ref buffer, ref data) => {
                        (scene.fetch_buffer(buffer), 0, data)
                    }
                    Expectation::ImageRow(ref image, row, ref data) => {
                        (scene.fetch_image(image), row, data)
                    }
                };

                if data.as_slice() == guard.row(row) {
                    println!("PASS");
                    results.pass += 1;
                } else {
                    println!("FAIL {:?}", guard.row(row));
                    results.fail += 1;
                }
            }
        }

        println!("\t{:?}", results);
        results.fail
    }
}

fn main() {
    use std::{env, process};

    #[cfg(feature = "env_logger")]
    env_logger::init();
    let mut num_failures = 0;

    let suite_name = match env::args().nth(1) {
        Some(name) => name,
        None => {
            println!("Call with the argument of the reftest suite name");
            return;
        }
    };

    let harness = Harness::new(&suite_name);
    #[cfg(feature = "vulkan")]
    {
        println!("Warding Vulkan:");
        let instance = gfx_backend_vulkan::Instance::create("warden", 1);
        num_failures += harness.run(instance, Disabilities::default());
    }
    #[cfg(feature = "dx12")]
    {
        println!("Warding DX12:");
        let instance = gfx_backend_dx12::Instance::create("warden", 1);
        num_failures += harness.run(instance, Disabilities::default());
    }
    #[cfg(feature = "metal")]
    {
        println!("Warding Metal:");
        let instance = gfx_backend_metal::Instance::create("warden", 1);
        num_failures += harness.run(
            instance,
            Disabilities {
                ..Disabilities::default()
            },
        );
    }
    #[cfg(feature = "gl")]
    {
        use gfx_backend_gl::glutin;
        println!("Warding GL:");
        let events_loop = glutin::EventsLoop::new();
        let window = glutin::ContextBuilder::new()
            .with_gl_profile(glutin::GlProfile::Core)
            .build_windowed(glutin::WindowBuilder::new(), &events_loop)
            .unwrap();
        let window = unsafe { window.make_current() }.expect("Unable to make window current");
        let instance = gfx_backend_gl::Surface::from_window(window);
        num_failures += harness.run(instance, Disabilities::default());
    }
    #[cfg(feature = "gl-headless")]
    {
        use gfx_backend_gl::glutin;
        println!("Warding GL headless:");
        let events_loop = glutin::EventsLoop::new();
        let context = glutin::ContextBuilder::new()
            .build_headless(&events_loop, glutin::dpi::PhysicalSize::new(0.0, 0.0))
            .unwrap();
        let context = unsafe { context.make_current() }.expect("Unable to make context current");
        let instance = gfx_backend_gl::Headless::from_context(context);
        num_failures += harness.run(instance, Disabilities::default());
    }
    let _ = harness;
    num_failures += 0; // mark as mutated
    process::exit(num_failures as _);
}
<|endoftext|>