<commit_before>use std::cmp;
use std::io::Read;
use std::io::Write;
use std::io::Result;

// A stream that is seeded with incoming data which can be consumed and
// records data written to it (like a socket).
//
// This allows us to unit test a transport without using sockets. :)
pub struct TestStream {
    pub incoming: Vec<u8>,
    pub incoming_cursor: usize,

    pub repeating_mode: bool, // Replay the same bytes over and over
    pub incoming_rep: Vec<u8>,
    pub incoming_rep_cursor: usize,

    pub outgoing: Vec<u8>,
}

impl TestStream {
    pub fn new(incoming: Vec<u8>) -> TestStream {
        TestStream {
            incoming: incoming,
            incoming_cursor: 0,

            repeating_mode: false,
            incoming_rep: vec![],
            incoming_rep_cursor: 0,

            // Should be a good fit for most test responses
            outgoing: Vec::with_capacity(200),
        }
    }

    pub fn set_incoming_rep(&mut self, buffer: Vec<u8>) -> &mut Self {
        self.incoming_rep_cursor = 0;
        self.incoming_rep = buffer;
        self.repeating_mode = true;
        self
    }

    fn read_repeating(&mut self, buf: &mut [u8]) -> Result<usize> {
        let read_len = buf.len();

        let mut read_cnt: usize = 0;
        let mut buf_cur: usize = 0;

        loop {
            if read_cnt >= read_len {
                break;
            }

            // We're going to read until the end of the buffer, then loop
            let left_to_read = cmp::min(read_len - read_cnt,
                                        self.incoming_rep.len() -
                                        self.incoming_rep_cursor);

            for i in 0..left_to_read {
                buf[buf_cur + i] = self.incoming_rep[self.incoming_rep_cursor +
                                                     i];
            }

            read_cnt += left_to_read;
            buf_cur += left_to_read;
            self.incoming_rep_cursor += left_to_read;

            // Have we reached the end? Reset the cursor
            if self.incoming_rep_cursor == self.incoming_rep.len() {
                self.incoming_rep_cursor = 0;
            }
        }

        Ok(read_cnt)
    }

    fn read_linear(&mut self, buf: &mut [u8]) -> Result<usize> {
        // We can only read either as much as we have in incoming, or as big as
        // the output buffer is.
        let read_len = cmp::min(buf.len(),
                                self.incoming.len() - self.incoming_cursor);

        for i in 0..read_len {
            buf[i] = self.incoming[self.incoming_cursor + i];
        }
        self.incoming_cursor += read_len;

        Ok(read_len)
    }
}

impl Read for TestStream {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        if self.repeating_mode {
            return self.read_repeating(buf);
        } else {
            return self.read_linear(buf);
        }
    }
}

impl Write for TestStream {
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        self.outgoing.extend(buf.iter().cloned());

        Ok(buf.len())
    }

    fn flush(&mut self) -> Result<()> {
        Ok(())
    }
}

#[test]
fn test_stream_read_whole() {
    let mut ts = TestStream::new(vec![1, 2, 3]);

    let mut buf = [0; 4];
    let read_cnt = ts.read(&mut buf).unwrap();
    assert_eq!(buf, [1, 2, 3, 0]);
    assert_eq!(read_cnt, 3);
    assert_eq!(ts.incoming, [1, 2, 3]);
    assert_eq!(ts.incoming_cursor, 3);
}

#[test]
fn test_stream_read_incremental() {
    let mut ts = TestStream::new(vec![1, 2, 3]);

    // Read once
    let mut buf = [0; 2];
    let read_cnt = ts.read(&mut buf).unwrap();
    assert_eq!(read_cnt, 2);
    assert_eq!(buf, [1, 2]);
    assert_eq!(ts.incoming, [1, 2, 3]);
    assert_eq!(ts.incoming_cursor, 2);

    // Read once more
    let mut buf = [0; 2];
    let read_cnt = ts.read(&mut buf).unwrap();
    assert_eq!(read_cnt, 1);
    assert_eq!(buf, [3, 0]);
    assert_eq!(ts.incoming, [1, 2, 3]);
    assert_eq!(ts.incoming_cursor, 3);
}

#[test]
fn test_stream_read_repeating() {
    let mut ts = TestStream::new(vec![]);
    ts.set_incoming_rep(vec![1, 2, 3]);

    // Read once
    let mut buf = [0; 2];
    let read_cnt = ts.read(&mut buf).unwrap();
    assert_eq!(read_cnt, 2);
    assert_eq!(buf, [1, 2]);
    assert_eq!(ts.incoming_rep, [1, 2, 3]);
    assert_eq!(ts.incoming_rep_cursor, 2);

    // Read once more
    let mut buf = [0; 5];
    let read_cnt = ts.read(&mut buf).unwrap();
    assert_eq!(read_cnt, 5);
    assert_eq!(buf, [3, 1, 2, 3, 1]);
    assert_eq!(ts.incoming_rep, [1, 2, 3]);
    assert_eq!(ts.incoming_rep_cursor, 1);
}

#[test]
fn test_stream_write() {
    let mut ts = TestStream::new(vec![]);

    // Write once
    let buf = [1, 2];
    let write_cnt = ts.write(&buf).unwrap();
    assert_eq!(write_cnt, 2);
    assert_eq!(ts.outgoing, [1, 2]);

    // Write once more
    let buf = [3, 4, 5];
    let write_cnt = ts.write(&buf).unwrap();
    assert_eq!(write_cnt, 3);
    assert_eq!(ts.outgoing, [1, 2, 3, 4, 5]);
}
<commit_msg>simplify<commit_after>use std::cmp;
use std::io::Read;
use std::io::Write;
use std::io::Result;

// A stream that is seeded with incoming data which can be consumed and
// records data written to it (like a socket).
//
// This allows us to unit test a transport without using sockets. :)
pub struct TestStream {
    pub incoming: Vec<u8>,
    pub incoming_cursor: usize,

    pub repeating_mode: bool, // Replay the same bytes over and over
    pub incoming_rep: Vec<u8>,
    pub incoming_rep_cursor: usize,

    pub outgoing: Vec<u8>,
}

impl TestStream {
    pub fn new(incoming: Vec<u8>) -> TestStream {
        TestStream {
            incoming: incoming,
            incoming_cursor: 0,

            repeating_mode: false,
            incoming_rep: vec![],
            incoming_rep_cursor: 0,

            // Should be a good fit for most test responses
            outgoing: Vec::with_capacity(200),
        }
    }

    pub fn set_incoming_rep(&mut self, buffer: Vec<u8>) -> &mut Self {
        self.incoming_rep_cursor = 0;
        self.incoming_rep = buffer;
        self.repeating_mode = true;
        self
    }

    fn read_repeating(&mut self, buf: &mut [u8]) -> Result<usize> {
        let read_len = buf.len();

        let mut read_cnt: usize = 0;
        let mut buf_cur: usize = 0;

        while read_cnt < read_len {
            // We're going to read until the end of the buffer, then loop
            let left_to_read = cmp::min(read_len - read_cnt,
                                        self.incoming_rep.len() -
                                        self.incoming_rep_cursor);

            for i in 0..left_to_read {
                buf[buf_cur + i] = self.incoming_rep[self.incoming_rep_cursor +
                                                     i];
            }

            read_cnt += left_to_read;
            buf_cur += left_to_read;
            self.incoming_rep_cursor += left_to_read;

            // Have we reached the end? Reset the cursor
            if self.incoming_rep_cursor == self.incoming_rep.len() {
                self.incoming_rep_cursor = 0;
            }
        }

        Ok(read_cnt)
    }

    fn read_linear(&mut self, buf: &mut [u8]) -> Result<usize> {
        // We can only read either as much as we have in incoming, or as big as
        // the output buffer is.
        let read_len = cmp::min(buf.len(),
                                self.incoming.len() - self.incoming_cursor);

        for i in 0..read_len {
            buf[i] = self.incoming[self.incoming_cursor + i];
        }
        self.incoming_cursor += read_len;

        Ok(read_len)
    }
}

impl Read for TestStream {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        if self.repeating_mode {
            return self.read_repeating(buf);
        } else {
            return self.read_linear(buf);
        }
    }
}

impl Write for TestStream {
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        self.outgoing.extend(buf.iter().cloned());

        Ok(buf.len())
    }

    fn flush(&mut self) -> Result<()> {
        Ok(())
    }
}

#[test]
fn test_stream_read_whole() {
    let mut ts = TestStream::new(vec![1, 2, 3]);

    let mut buf = [0; 4];
    let read_cnt = ts.read(&mut buf).unwrap();
    assert_eq!(buf, [1, 2, 3, 0]);
    assert_eq!(read_cnt, 3);
    assert_eq!(ts.incoming, [1, 2, 3]);
    assert_eq!(ts.incoming_cursor, 3);
}

#[test]
fn test_stream_read_incremental() {
    let mut ts = TestStream::new(vec![1, 2, 3]);

    // Read once
    let mut buf = [0; 2];
    let read_cnt = ts.read(&mut buf).unwrap();
    assert_eq!(read_cnt, 2);
    assert_eq!(buf, [1, 2]);
    assert_eq!(ts.incoming, [1, 2, 3]);
    assert_eq!(ts.incoming_cursor, 2);

    // Read once more
    let mut buf = [0; 2];
    let read_cnt = ts.read(&mut buf).unwrap();
    assert_eq!(read_cnt, 1);
    assert_eq!(buf, [3, 0]);
    assert_eq!(ts.incoming, [1, 2, 3]);
    assert_eq!(ts.incoming_cursor, 3);
}

#[test]
fn test_stream_read_repeating() {
    let mut ts = TestStream::new(vec![]);
    ts.set_incoming_rep(vec![1, 2, 3]);

    // Read once
    let mut buf = [0; 2];
    let read_cnt = ts.read(&mut buf).unwrap();
    assert_eq!(read_cnt, 2);
    assert_eq!(buf, [1, 2]);
    assert_eq!(ts.incoming_rep, [1, 2, 3]);
    assert_eq!(ts.incoming_rep_cursor, 2);

    // Read once more
    let mut buf = [0; 5];
    let read_cnt = ts.read(&mut buf).unwrap();
    assert_eq!(read_cnt, 5);
    assert_eq!(buf, [3, 1, 2, 3, 1]);
    assert_eq!(ts.incoming_rep, [1, 2, 3]);
    assert_eq!(ts.incoming_rep_cursor, 1);
}

#[test]
fn test_stream_write() {
    let mut ts = TestStream::new(vec![]);

    // Write once
    let buf = [1, 2];
    let write_cnt = ts.write(&buf).unwrap();
    assert_eq!(write_cnt, 2);
    assert_eq!(ts.outgoing, [1, 2]);

    // Write once more
    let buf = [3, 4, 5];
    let write_cnt = ts.write(&buf).unwrap();
    assert_eq!(write_cnt, 3);
    assert_eq!(ts.outgoing, [1, 2, 3, 4, 5]);
}
<|endoftext|>


<commit_before>use self::ImproperCtype::*;
use crate::syntax::atom::Atom::{self, *};
use crate::syntax::{Type, Types};
use proc_macro2::Ident;

pub enum ImproperCtype<'a> {
    Definite(bool),
    Depends(&'a Ident),
}

impl<'a> Types<'a> {
    // yes, no, maybe
    pub fn determine_improper_ctype(&self, ty: &Type) -> ImproperCtype<'a> {
        match ty {
            Type::Ident(ident) => {
                let ident = &ident.rust;
                if let Some(atom) = Atom::from(ident) {
                    Definite(atom == RustString)
                } else if let Some(strct) = self.structs.get(ident) {
                    Depends(&strct.name.rust) // iterate to fixed-point
                } else {
                    Definite(self.rust.contains(ident))
                }
            }
            Type::RustBox(_)
            | Type::RustVec(_)
            | Type::Str(_)
            | Type::Fn(_)
            | Type::Void(_)
            | Type::SliceRef(_) => Definite(true),
            Type::UniquePtr(_) | Type::SharedPtr(_) | Type::WeakPtr(_) | Type::CxxVector(_) => {
                Definite(false)
            }
            Type::Ref(ty) => self.determine_improper_ctype(&ty.inner),
            Type::Ptr(ty) => self.determine_improper_ctype(&ty.inner),
            Type::Array(ty) => self.determine_improper_ctype(&ty.inner),
        }
    }
}
<commit_msg>Add a void ptr cast for extern type aliases containing Rust-native types<commit_after>use self::ImproperCtype::*;
use crate::syntax::atom::Atom::{self, *};
use crate::syntax::{Type, Types};
use proc_macro2::Ident;

pub enum ImproperCtype<'a> {
    Definite(bool),
    Depends(&'a Ident),
}

impl<'a> Types<'a> {
    // yes, no, maybe
    pub fn determine_improper_ctype(&self, ty: &Type) -> ImproperCtype<'a> {
        match ty {
            Type::Ident(ident) => {
                let ident = &ident.rust;
                if let Some(atom) = Atom::from(ident) {
                    Definite(atom == RustString)
                } else if let Some(strct) = self.structs.get(ident) {
                    Depends(&strct.name.rust) // iterate to fixed-point
                } else {
                    Definite(self.rust.contains(ident) || self.aliases.contains_key(ident))
                }
            }
            Type::RustBox(_)
            | Type::RustVec(_)
            | Type::Str(_)
            | Type::Fn(_)
            | Type::Void(_)
            | Type::SliceRef(_) => Definite(true),
            Type::UniquePtr(_) | Type::SharedPtr(_) | Type::WeakPtr(_) | Type::CxxVector(_) => {
                Definite(false)
            }
            Type::Ref(ty) => self.determine_improper_ctype(&ty.inner),
            Type::Ptr(ty) => self.determine_improper_ctype(&ty.inner),
            Type::Array(ty) => self.determine_improper_ctype(&ty.inner),
        }
    }
}
<|endoftext|>


<commit_before>//! Tidy check to enforce rules about platform-specific code in std.
//!
//! This is intended to maintain existing standards of code
//! organization in hopes that the standard library will continue to
//! be refactored to isolate platform-specific bits, making porting
//! easier; where "standard library" roughly means "all the
//! dependencies of the std and test crates".
//!
//! This generally means placing restrictions on where `cfg(unix)`,
//! `cfg(windows)`, `cfg(target_os)` and `cfg(target_env)` may appear,
//! the basic objective being to isolate platform-specific code to the
//! platform-specific `std::sys` modules, and to the allocation,
//! unwinding, and libc crates.
//!
//! Following are the basic rules, though there are currently
//! exceptions:
//!
//! - core may not have platform-specific code.
//! - libpanic_abort may have platform-specific code.
//! - libpanic_unwind may have platform-specific code.
//! - libunwind may have platform-specific code.
//! - other crates in the std facade may not.
//! - std may have platform-specific code in the following places:
//!   - `sys/unix/`
//!   - `sys/windows/`
//!   - `os/`
//!
//! `std/sys_common` should _not_ contain platform-specific code.
//! Finally, because std contains tests with platform-specific
//! `ignore` attributes, once the parser encounters `mod tests`,
//! platform-specific cfgs are allowed. Not sure yet how to deal with
//! this in the long term.

use std::iter::Iterator;
use std::path::Path;

// Paths that may contain platform-specific code.
const EXCEPTION_PATHS: &[&str] = &[
    // std crates
    "src/libpanic_abort",
    "src/libpanic_unwind",
    "src/libunwind",
    // black_box implementation is LLVM-version specific and it uses
    // target_os to tell targets with different LLVM-versions apart
    // (e.g. `wasm32-unknown-emscripten` vs `wasm32-unknown-unknown`):
    "src/libcore/hint.rs",
    "src/libstd/sys/", // Platform-specific code for std lives here.
    // This has the trailing slash so that sys_common is not excepted.
    "src/libstd/os", // Platform-specific public interfaces
    "src/rtstartup", // Not sure what to do about this. magic stuff for mingw
    // temporary exceptions
    "src/libstd/lib.rs",
    "src/libstd/path.rs",
    "src/libstd/f32.rs",
    "src/libstd/f64.rs",
    // Integration test for platform-specific run-time feature detection:
    "src/libstd/tests/run-time-detect.rs",
    "src/libstd/net/test.rs",
    "src/libstd/sys_common/mod.rs",
    "src/libstd/sys_common/net.rs",
    "src/libstd/sys_common/backtrace.rs",
    // panic_unwind shims
    "src/libstd/panicking.rs",
    "src/libterm", // Not sure how to make this crate portable, but test crate needs it.
    "src/libtest", // Probably should defer to unstable `std::sys` APIs.
    "src/libstd/sync/mpsc", // some tests are only run on non-emscripten
    // std testing crates, okay for now at least
    "src/libcore/tests",
    "src/liballoc/tests/lib.rs",
    // The `VaList` implementation must have platform specific code.
    // The Windows implementation of a `va_list` is always a character
    // pointer regardless of the target architecture. As a result,
    // we must use `#[cfg(windows)]` to conditionally compile the
    // correct `VaList` structure for windows.
    "src/libcore/ffi.rs",
    // non-std crates
    "src/test",
    "src/tools",
    "src/librustc",
    "src/librustdoc",
    "src/librustc_ast",
    "src/bootstrap",
];

pub fn check(path: &Path, bad: &mut bool) {
    // Sanity check that the complex parsing here works.
    let mut saw_target_arch = false;
    let mut saw_cfg_bang = false;
    super::walk(path, &mut super::filter_dirs, &mut |entry, contents| {
        let file = entry.path();
        let filestr = file.to_string_lossy().replace("\\", "/");
        if !filestr.ends_with(".rs") {
            return;
        }

        let is_exception_path = EXCEPTION_PATHS.iter().any(|s| filestr.contains(&**s));
        if is_exception_path {
            return;
        }

        check_cfgs(contents, &file, bad, &mut saw_target_arch, &mut saw_cfg_bang);
    });

    assert!(saw_target_arch);
    assert!(saw_cfg_bang);
}

fn check_cfgs(
    contents: &str,
    file: &Path,
    bad: &mut bool,
    saw_target_arch: &mut bool,
    saw_cfg_bang: &mut bool,
) {
    // For now it's ok to have platform-specific code after 'mod tests'.
    let mod_tests_idx = find_test_mod(contents);
    let contents = &contents[..mod_tests_idx];
    // Pull out all `cfg(...)` and `cfg!(...)` strings.
    let cfgs = parse_cfgs(contents);

    let mut line_numbers: Option<Vec<usize>> = None;
    let mut err = |idx: usize, cfg: &str| {
        if line_numbers.is_none() {
            line_numbers = Some(contents.match_indices('\n').map(|(i, _)| i).collect());
        }
        let line_numbers = line_numbers.as_ref().expect("");
        let line = match line_numbers.binary_search(&idx) {
            Ok(_) => unreachable!(),
            Err(i) => i + 1,
        };
        tidy_error!(bad, "{}:{}: platform-specific cfg: {}", file.display(), line, cfg);
    };

    for (idx, cfg) in cfgs {
        // Sanity check that the parsing here works.
        if !*saw_target_arch && cfg.contains("target_arch") {
            *saw_target_arch = true
        }
        if !*saw_cfg_bang && cfg.contains("cfg!") {
            *saw_cfg_bang = true
        }

        let contains_platform_specific_cfg = cfg.contains("target_os")
            || cfg.contains("target_env")
            || cfg.contains("target_vendor")
            || cfg.contains("unix")
            || cfg.contains("windows");

        if !contains_platform_specific_cfg {
            continue;
        }

        let preceeded_by_doc_comment = {
            let pre_contents = &contents[..idx];
            let pre_newline = pre_contents.rfind('\n');
            let pre_doc_comment = pre_contents.rfind("///");
            match (pre_newline, pre_doc_comment) {
                (Some(n), Some(c)) => n < c,
                (None, Some(_)) => true,
                (_, None) => false,
            }
        };

        if preceeded_by_doc_comment {
            continue;
        }

        err(idx, cfg);
    }
}

fn find_test_mod(contents: &str) -> usize {
    if let Some(mod_tests_idx) = contents.find("mod tests") {
        // Also capture a previous line indicating that "mod tests" is cfg'd out.
        let prev_newline_idx = contents[..mod_tests_idx].rfind('\n').unwrap_or(mod_tests_idx);
        let prev_newline_idx = contents[..prev_newline_idx].rfind('\n');
        if let Some(nl) = prev_newline_idx {
            let prev_line = &contents[nl + 1..mod_tests_idx];
            if prev_line.contains("cfg(all(test, not(target_os")
                || prev_line.contains("cfg(all(test, not(any(target_os")
            {
                nl
            } else {
                mod_tests_idx
            }
        } else {
            mod_tests_idx
        }
    } else {
        contents.len()
    }
}

fn parse_cfgs<'a>(contents: &'a str) -> Vec<(usize, &'a str)> {
    let candidate_cfgs = contents.match_indices("cfg");
    let candidate_cfg_idxs = candidate_cfgs.map(|(i, _)| i);
    // This is puling out the indexes of all "cfg" strings
    // that appear to be tokens followed by a parenthesis.
    let cfgs = candidate_cfg_idxs.filter(|i| {
        let pre_idx = i.saturating_sub(*i);
        let succeeds_non_ident = !contents
            .as_bytes()
            .get(pre_idx)
            .cloned()
            .map(char::from)
            .map(char::is_alphanumeric)
            .unwrap_or(false);
        let contents_after = &contents[*i..];
        let first_paren = contents_after.find('(');
        let paren_idx = first_paren.map(|ip| i + ip);
        let preceeds_whitespace_and_paren = paren_idx
            .map(|ip| {
                let maybe_space = &contents[*i + "cfg".len()..ip];
                maybe_space.chars().all(|c| char::is_whitespace(c) || c == '!')
            })
            .unwrap_or(false);

        succeeds_non_ident && preceeds_whitespace_and_paren
    });

    cfgs.flat_map(|i| {
        let mut depth = 0;
        let contents_from = &contents[i..];
        for (j, byte) in contents_from.bytes().enumerate() {
            match byte {
                b'(' => {
                    depth += 1;
                }
                b')' => {
                    depth -= 1;
                    if depth == 0 {
                        return Some((i, &contents_from[..=j]));
                    }
                }
                _ => {}
            }
        }

        // if the parentheses are unbalanced just ignore this cfg -- it'll be caught when attempting
        // to run the compiler, and there's no real reason to lint it separately here
        None
    })
    .collect()
}
<commit_msg>add exception for liballoc benches to tidy<commit_after>//! Tidy check to enforce rules about platform-specific code in std.
//!
//! This is intended to maintain existing standards of code
//! organization in hopes that the standard library will continue to
//! be refactored to isolate platform-specific bits, making porting
//! easier; where "standard library" roughly means "all the
//! dependencies of the std and test crates".
//!
//! This generally means placing restrictions on where `cfg(unix)`,
//! `cfg(windows)`, `cfg(target_os)` and `cfg(target_env)` may appear,
//! the basic objective being to isolate platform-specific code to the
//! platform-specific `std::sys` modules, and to the allocation,
//! unwinding, and libc crates.
//!
//! Following are the basic rules, though there are currently
//! exceptions:
//!
//! - core may not have platform-specific code.
//! - libpanic_abort may have platform-specific code.
//! - libpanic_unwind may have platform-specific code.
//! - libunwind may have platform-specific code.
//! - other crates in the std facade may not.
//! - std may have platform-specific code in the following places:
//!   - `sys/unix/`
//!   - `sys/windows/`
//!   - `os/`
//!
//! `std/sys_common` should _not_ contain platform-specific code.
//! Finally, because std contains tests with platform-specific
//! `ignore` attributes, once the parser encounters `mod tests`,
//! platform-specific cfgs are allowed. Not sure yet how to deal with
//! this in the long term.

use std::iter::Iterator;
use std::path::Path;

// Paths that may contain platform-specific code.
const EXCEPTION_PATHS: &[&str] = &[
    // std crates
    "src/libpanic_abort",
    "src/libpanic_unwind",
    "src/libunwind",
    // black_box implementation is LLVM-version specific and it uses
    // target_os to tell targets with different LLVM-versions apart
    // (e.g. `wasm32-unknown-emscripten` vs `wasm32-unknown-unknown`):
    "src/libcore/hint.rs",
    "src/libstd/sys/", // Platform-specific code for std lives here.
    // This has the trailing slash so that sys_common is not excepted.
    "src/libstd/os", // Platform-specific public interfaces
    "src/rtstartup", // Not sure what to do about this. magic stuff for mingw
    // temporary exceptions
    "src/libstd/lib.rs",
    "src/libstd/path.rs",
    "src/libstd/f32.rs",
    "src/libstd/f64.rs",
    // Integration test for platform-specific run-time feature detection:
    "src/libstd/tests/run-time-detect.rs",
    "src/libstd/net/test.rs",
    "src/libstd/sys_common/mod.rs",
    "src/libstd/sys_common/net.rs",
    "src/libstd/sys_common/backtrace.rs",
    // panic_unwind shims
    "src/libstd/panicking.rs",
    "src/libterm", // Not sure how to make this crate portable, but test crate needs it.
    "src/libtest", // Probably should defer to unstable `std::sys` APIs.
    "src/libstd/sync/mpsc", // some tests are only run on non-emscripten
    // std testing crates, okay for now at least
    "src/libcore/tests",
    "src/liballoc/tests/lib.rs",
    "src/liballoc/benches/lib.rs",
    // The `VaList` implementation must have platform specific code.
    // The Windows implementation of a `va_list` is always a character
    // pointer regardless of the target architecture. As a result,
    // we must use `#[cfg(windows)]` to conditionally compile the
    // correct `VaList` structure for windows.
    "src/libcore/ffi.rs",
    // non-std crates
    "src/test",
    "src/tools",
    "src/librustc",
    "src/librustdoc",
    "src/librustc_ast",
    "src/bootstrap",
];

pub fn check(path: &Path, bad: &mut bool) {
    // Sanity check that the complex parsing here works.
    let mut saw_target_arch = false;
    let mut saw_cfg_bang = false;
    super::walk(path, &mut super::filter_dirs, &mut |entry, contents| {
        let file = entry.path();
        let filestr = file.to_string_lossy().replace("\\", "/");
        if !filestr.ends_with(".rs") {
            return;
        }

        let is_exception_path = EXCEPTION_PATHS.iter().any(|s| filestr.contains(&**s));
        if is_exception_path {
            return;
        }

        check_cfgs(contents, &file, bad, &mut saw_target_arch, &mut saw_cfg_bang);
    });

    assert!(saw_target_arch);
    assert!(saw_cfg_bang);
}

fn check_cfgs(
    contents: &str,
    file: &Path,
    bad: &mut bool,
    saw_target_arch: &mut bool,
    saw_cfg_bang: &mut bool,
) {
    // For now it's ok to have platform-specific code after 'mod tests'.
    let mod_tests_idx = find_test_mod(contents);
    let contents = &contents[..mod_tests_idx];
    // Pull out all `cfg(...)` and `cfg!(...)` strings.
    let cfgs = parse_cfgs(contents);

    let mut line_numbers: Option<Vec<usize>> = None;
    let mut err = |idx: usize, cfg: &str| {
        if line_numbers.is_none() {
            line_numbers = Some(contents.match_indices('\n').map(|(i, _)| i).collect());
        }
        let line_numbers = line_numbers.as_ref().expect("");
        let line = match line_numbers.binary_search(&idx) {
            Ok(_) => unreachable!(),
            Err(i) => i + 1,
        };
        tidy_error!(bad, "{}:{}: platform-specific cfg: {}", file.display(), line, cfg);
    };

    for (idx, cfg) in cfgs {
        // Sanity check that the parsing here works.
        if !*saw_target_arch && cfg.contains("target_arch") {
            *saw_target_arch = true
        }
        if !*saw_cfg_bang && cfg.contains("cfg!") {
            *saw_cfg_bang = true
        }

        let contains_platform_specific_cfg = cfg.contains("target_os")
            || cfg.contains("target_env")
            || cfg.contains("target_vendor")
            || cfg.contains("unix")
            || cfg.contains("windows");

        if !contains_platform_specific_cfg {
            continue;
        }

        let preceeded_by_doc_comment = {
            let pre_contents = &contents[..idx];
            let pre_newline = pre_contents.rfind('\n');
            let pre_doc_comment = pre_contents.rfind("///");
            match (pre_newline, pre_doc_comment) {
                (Some(n), Some(c)) => n < c,
                (None, Some(_)) => true,
                (_, None) => false,
            }
        };

        if preceeded_by_doc_comment {
            continue;
        }

        err(idx, cfg);
    }
}

fn find_test_mod(contents: &str) -> usize {
    if let Some(mod_tests_idx) = contents.find("mod tests") {
        // Also capture a previous line indicating that "mod tests" is cfg'd out.
        let prev_newline_idx = contents[..mod_tests_idx].rfind('\n').unwrap_or(mod_tests_idx);
        let prev_newline_idx = contents[..prev_newline_idx].rfind('\n');
        if let Some(nl) = prev_newline_idx {
            let prev_line = &contents[nl + 1..mod_tests_idx];
            if prev_line.contains("cfg(all(test, not(target_os")
                || prev_line.contains("cfg(all(test, not(any(target_os")
            {
                nl
            } else {
                mod_tests_idx
            }
        } else {
            mod_tests_idx
        }
    } else {
        contents.len()
    }
}

fn parse_cfgs<'a>(contents: &'a str) -> Vec<(usize, &'a str)> {
    let candidate_cfgs = contents.match_indices("cfg");
    let candidate_cfg_idxs = candidate_cfgs.map(|(i, _)| i);
    // This is puling out the indexes of all "cfg" strings
    // that appear to be tokens followed by a parenthesis.
    let cfgs = candidate_cfg_idxs.filter(|i| {
        let pre_idx = i.saturating_sub(*i);
        let succeeds_non_ident = !contents
            .as_bytes()
            .get(pre_idx)
            .cloned()
            .map(char::from)
            .map(char::is_alphanumeric)
            .unwrap_or(false);
        let contents_after = &contents[*i..];
        let first_paren = contents_after.find('(');
        let paren_idx = first_paren.map(|ip| i + ip);
        let preceeds_whitespace_and_paren = paren_idx
            .map(|ip| {
                let maybe_space = &contents[*i + "cfg".len()..ip];
                maybe_space.chars().all(|c| char::is_whitespace(c) || c == '!')
            })
            .unwrap_or(false);

        succeeds_non_ident && preceeds_whitespace_and_paren
    });

    cfgs.flat_map(|i| {
        let mut depth = 0;
        let contents_from = &contents[i..];
        for (j, byte) in contents_from.bytes().enumerate() {
            match byte {
                b'(' => {
                    depth += 1;
                }
                b')' => {
                    depth -= 1;
                    if depth == 0 {
                        return Some((i, &contents_from[..=j]));
                    }
                }
                _ => {}
            }
        }

        // if the parentheses are unbalanced just ignore this cfg -- it'll be caught when attempting
        // to run the compiler, and there's no real reason to lint it separately here
        None
    })
    .collect()
}
<|endoftext|>


<commit_before>extern crate env_logger;
#[macro_use]
extern crate pretty_assertions;

extern crate gluon_base as base;
extern crate gluon_parser as parser;

mod support;

use base::ast::{Expr, Pattern, PatternField, TypedIdent};
use base::pos::{self, BytePos, Span, Spanned};
use base::types::Type;

use parser::{Error, ParseErrors, TokenizeError};

use support::*;

// The expected tokens aren't very interesting since they may change fairly often
fn remove_expected(errors: ParseErrors) -> ParseErrors {
    let f = |mut err: Spanned<Error, _>| {
        match err.value {
            Error::UnexpectedToken(_, ref mut expected)
            | Error::UnexpectedEof(ref mut expected) => expected.clear(),
            _ => (),
        }
        err.span = Span::default();
        err
    };
    ParseErrors::from(errors.into_iter().map(f).collect::<Vec<_>>())
}

#[test]
fn empty_input() {
    let _ = ::env_logger::init();

    let result = parse("");
    assert!(result.is_err());
    let (expr, err) = result.unwrap_err();
    assert_eq!(expr, Some(error()));

    let error = Error::UnexpectedEof(vec![]);
    let span = pos::span(BytePos::from(0), BytePos::from(0));
    assert_eq!(
        remove_expected(err),
        ParseErrors::from(vec![pos::spanned(span, error)])
    );
}

#[test]
fn missing_match_expr() {
    let _ = ::env_logger::init();

    let expr = r#"
    match with
    | x -> x
    "#;
    let result = parse(expr);
    assert!(result.is_err());
    let (expr, err) = result.unwrap_err();
    assert_eq!(
        clear_span(expr.unwrap()),
        case(
            error(),
            vec![(Pattern::Ident(TypedIdent::new(intern("x"))), id("x"))],
        )
    );

    let error = Error::UnexpectedToken("With".into(), vec![]);
    let span = pos::span(BytePos::from(0), BytePos::from(0));
    assert_eq!(
        remove_expected(err),
        ParseErrors::from(vec![pos::spanned(span, error)])
    );
}

#[test]
fn wrong_indent_expression() {
    let _ = ::env_logger::init();

    let result = parse(
        r#"
let y =
    let x = 1
    x
   2
y
"#,
    );
    let error = Error::UnexpectedToken("IntLiteral".into(), vec![]);
    let span = pos::span(BytePos::from(0), BytePos::from(0));
    let errors = ParseErrors::from(vec![pos::spanned(span, error)]);

    assert_eq!(remove_expected(result.unwrap_err().1), errors);
}

#[test]
fn unclosed_string() {
    let _ = ::env_logger::init();

    let result = parse(
        r#"
"abc
"#,
    );
    assert!(result.is_err());
}

#[test]
fn tokenizer_error_is_returned() {
    let _ = ::env_logger::init();

    let result = parse(
        r#"
12345678901234567890 test
"#,
    );

    let error = Error::Token(TokenizeError::NonParseableInt);
    let span = pos::span(BytePos::from(0), BytePos::from(0));
    let errors = ParseErrors::from(vec![pos::spanned(span, error)]);

    assert_eq!(remove_expected(result.unwrap_err().1), errors);
}

#[test]
fn tokenizer_error_at_eof_is_returned() {
    let _ = ::env_logger::init();

    let result = parse(
        r#"
12345678901234567890
"#,
    );

    let error = Error::Token(TokenizeError::NonParseableInt);
    let span = pos::span(BytePos::from(0), BytePos::from(0));
    let errors = ParseErrors::from(vec![pos::spanned(span, error)]);

    assert_eq!(remove_expected(result.unwrap_err().1), errors);
}

#[test]
fn no_infinite_loop_from_default_block() {
    let _ = ::env_logger::init();

    let result = parse(
        r#"
let x = 1

    x,
    y = 1
}
"#,
    );
    assert!(result.is_err());
}

#[test]
fn missing_pattern() {
    let _ = ::env_logger::init();

    let expr = r#"
    match 1 with
    | -> x
    "#;
    let result = parse(expr);
    assert!(result.is_err());
    let (expr, err) = result.unwrap_err();
    assert_eq!(
        clear_span(expr.unwrap()),
        case(int(1), vec![(Pattern::Error, id("x"))])
    );

    let error = Error::UnexpectedToken("RArrow".into(), vec![]);
    let span = pos::span(BytePos::from(0), BytePos::from(0));
    assert_eq!(
        remove_expected(err),
        ParseErrors::from(vec![pos::spanned(span, error)])
    );
}

#[test]
fn incomplete_alternative() {
    let _ = ::env_logger::init();

    let expr = r#"
    match 1 with
    | //
    "#;
    let result = parse(expr);
    assert!(result.is_err());
    let (expr, err) = result.unwrap_err();
    assert_eq!(
        clear_span(expr.unwrap()),
        case(int(1), vec![(Pattern::Error, error())])
    );

    let error = Error::UnexpectedToken("CloseBlock".into(), vec![]);
    let span = pos::span(BytePos::from(0), BytePos::from(0));
    assert_eq!(
        remove_expected(err),
        ParseErrors::from(vec![pos::spanned(span, error)])
    );
}

#[test]
fn incomplete_alternative_before_complete_alternative() {
    let _ = ::env_logger::init();

    let expr = r#"
    match 1 with
    | //
    | x -> x
    "#;
    let result = parse(expr);
    assert!(result.is_err());
    let (expr, err) = result.unwrap_err();
    assert_eq!(
        clear_span(expr.unwrap()),
        case(
            int(1),
            vec![
                (Pattern::Error, error()),
                (Pattern::Ident(TypedIdent::new(intern("x"))), id("x")),
            ],
        )
    );

    let error = Error::UnexpectedToken("Pipe".into(), vec![]);
    let span = pos::span(BytePos::from(0), BytePos::from(0));
    assert_eq!(
        remove_expected(err),
        ParseErrors::from(vec![pos::spanned(span, error)])
    );
}

#[test]
fn incomplete_alternative_with_partial_pattern() {
    let _ = ::env_logger::init();

    let expr = r#"
    match 1 with
    | { x = }
    "#;
    let result = parse(expr);
    assert!(result.is_err());
    let (expr, err) = result.unwrap_err();
    assert_eq!(
        clear_span(expr.unwrap()),
        case(
            int(1),
            vec![
                (
                    Pattern::Record {
                        typ: Type::hole(),
                        types: vec![],
                        fields: vec![
                            PatternField {
                                name: no_loc(intern("x")),
                                value: Some(no_loc(Pattern::Error)),
                            },
                        ],
                    },
                    error(),
                ),
            ],
        )
    );

    let errors = vec![
        no_loc(Error::UnexpectedToken("RBrace".into(), vec![])),
        no_loc(Error::UnexpectedToken("CloseBlock".into(), vec![])),
    ];
    assert_eq!(remove_expected(err), ParseErrors::from(errors));
}

#[test]
fn incomplete_let_binding() {
    let _ = ::env_logger::init();

    let expr = r#"
    let test =
    1
    "#;
    let result = parse(expr);
    assert!(result.is_err());
    let (expr, err) = result.unwrap_err();
    assert_eq!(
        clear_span(expr.unwrap()),
        let_("test", no_loc(Expr::Error(None)), int(1),)
    );

    let errors = vec![no_loc(Error::UnexpectedToken("CloseBlock".into(), vec![]))];
    assert_eq!(remove_expected(err), ParseErrors::from(errors));
}

#[test]
fn incomplete_let_binding_2() {
    let _ = ::env_logger::init();

    let expr = r#"
    let test = io
    "#;
    let result = parse(expr);
    assert!(result.is_err());
    let (expr, err) = result.unwrap_err();
    assert_eq!(
        clear_span(expr.unwrap()),
        let_("test", id("io"), no_loc(Expr::Error(None)))
    );

    let errors = vec![no_loc(Error::UnexpectedToken("CloseBlock".into(), vec![]))];
    assert_eq!(remove_expected(err), ParseErrors::from(errors));
}

#[test]
fn unterminated_char_literal() {
    let _ = ::env_logger::init();

    let expr = r#"
    'a
    "#;
    let result = parse(expr);
    assert!(result.is_err());
    let (_expr, err) = result.unwrap_err();

    let error = Error::Token(TokenizeError::UnterminatedCharLiteral);
    let span = pos::span(BytePos::from(5), BytePos::from(5));
    assert_eq!(err, ParseErrors::from(vec![pos::spanned(span, error)]));
}
<commit_msg>Fix small behaviour change due to LALRPOP update<commit_after>extern crate env_logger;
#[macro_use]
extern crate pretty_assertions;

extern crate gluon_base as base;
extern crate gluon_parser as parser;

mod support;

use base::ast::{Expr, Pattern, PatternField, TypedIdent};
use base::pos::{self, BytePos, Span, Spanned};
use base::types::Type;

use parser::{Error, ParseErrors, TokenizeError};

use support::*;

// The expected tokens aren't very interesting since they may change fairly often
fn remove_expected(errors: ParseErrors) -> ParseErrors {
    let f = |mut err: Spanned<Error, _>| {
        match err.value {
            Error::UnexpectedToken(_, ref mut expected)
            | Error::UnexpectedEof(ref mut expected) => expected.clear(),
            _ => (),
        }
        err.span = Span::default();
        err
    };
    ParseErrors::from(errors.into_iter().map(f).collect::<Vec<_>>())
}

#[test]
fn empty_input() {
    let _ = ::env_logger::init();

    let result = parse("");
    assert!(result.is_err());
    let (expr, err) = result.unwrap_err();
    assert_eq!(expr, Some(error()));

    let error = Error::UnexpectedEof(vec![]);
    let span = pos::span(BytePos::from(0), BytePos::from(0));
    assert_eq!(
        remove_expected(err),
        ParseErrors::from(vec![pos::spanned(span, error)])
    );
}

#[test]
fn missing_match_expr() {
    let _ = ::env_logger::init();

    let expr = r#"
    match with
    | x -> x
    "#;
    let result = parse(expr);
    assert!(result.is_err());
    let (expr, err) = result.unwrap_err();
    assert_eq!(
        clear_span(expr.unwrap()),
        case(
            error(),
            vec![(Pattern::Ident(TypedIdent::new(intern("x"))), id("x"))],
        )
    );

    let error = Error::UnexpectedToken("With".into(), vec![]);
    let span = pos::span(BytePos::from(0), BytePos::from(0));
    assert_eq!(
        remove_expected(err),
        ParseErrors::from(vec![pos::spanned(span, error)])
    );
}

#[test]
fn wrong_indent_expression() {
    let _ = ::env_logger::init();

    let result = parse(
        r#"
let y =
    let x = 1
    x
   2
y
"#,
    );
    let error = Error::UnexpectedToken("IntLiteral".into(), vec![]);
    let span = pos::span(BytePos::from(0), BytePos::from(0));
    let errors = ParseErrors::from(vec![pos::spanned(span, error)]);

    assert_eq!(remove_expected(result.unwrap_err().1), errors);
}

#[test]
fn unclosed_string() {
    let _ = ::env_logger::init();

    let result = parse(
        r#"
"abc
"#,
    );
    assert!(result.is_err());
}

#[test]
fn tokenizer_error_is_returned() {
    let _ = ::env_logger::init();

    let result = parse(
        r#"
12345678901234567890 test
"#,
    );

    let error = Error::Token(TokenizeError::NonParseableInt);
    let span = pos::span(BytePos::from(0), BytePos::from(0));
    let errors = ParseErrors::from(vec![pos::spanned(span, error)]);

    assert_eq!(remove_expected(result.unwrap_err().1), errors);
}

#[test]
fn tokenizer_error_at_eof_is_returned() {
    let _ = ::env_logger::init();

    let result = parse(
        r#"
12345678901234567890
"#,
    );

    let error = Error::Token(TokenizeError::NonParseableInt);
    let span = pos::span(BytePos::from(0), BytePos::from(0));
    let errors = ParseErrors::from(vec![pos::spanned(span, error)]);

    assert_eq!(remove_expected(result.unwrap_err().1), errors);
}

#[test]
fn no_infinite_loop_from_default_block() {
    let _ = ::env_logger::init();

    let result = parse(
        r#"
let x = 1

    x,
    y = 1
}
"#,
    );
    assert!(result.is_err());
}

#[test]
fn missing_pattern() {
    let _ = ::env_logger::init();

    let expr = r#"
    match 1 with
    | -> x
    "#;
    let result = parse(expr);
    assert!(result.is_err());
    let (expr, err) = result.unwrap_err();
    assert_eq!(
        clear_span(expr.unwrap()),
        case(int(1), vec![(Pattern::Error, id("x"))])
    );

    let error = Error::UnexpectedToken("RArrow".into(), vec![]);
    let span = pos::span(BytePos::from(0), BytePos::from(0));
    assert_eq!(
        remove_expected(err),
        ParseErrors::from(vec![pos::spanned(span, error)])
    );
}

#[test]
fn incomplete_alternative() {
    let _ = ::env_logger::init();

    let expr = r#"
    match 1 with
    | //
    "#;
    let result = parse(expr);
    assert!(result.is_err());
    let (expr, err) = result.unwrap_err();
    assert_eq!(
        clear_span(expr.unwrap()),
        case(int(1), vec![(Pattern::Error, error())])
    );

    let error = Error::UnexpectedToken("CloseBlock".into(), vec![]);
    let span = pos::span(BytePos::from(0), BytePos::from(0));
    assert_eq!(
        remove_expected(err),
        ParseErrors::from(vec![pos::spanned(span, error)])
    );
}

#[test]
fn incomplete_alternative_before_complete_alternative() {
    let _ = ::env_logger::init();

    let expr = r#"
    match 1 with
    | //
    | x -> x
    "#;
    let result = parse(expr);
    assert!(result.is_err());
    let (expr, err) = result.unwrap_err();
    assert_eq!(
        clear_span(expr.unwrap()),
        case(
            int(1),
            vec![
                (Pattern::Error, error()),
                (Pattern::Ident(TypedIdent::new(intern("x"))), id("x")),
            ],
        )
    );

    let error = Error::UnexpectedToken("Pipe".into(), vec![]);
    let span = pos::span(BytePos::from(0), BytePos::from(0));
    assert_eq!(
        remove_expected(err),
        ParseErrors::from(vec![pos::spanned(span, error)])
    );
}

#[test]
fn incomplete_alternative_with_partial_pattern() {
    let _ = ::env_logger::init();

    let expr = r#"
    match 1 with
    | { x = }
    "#;
    let result = parse(expr);
    assert!(result.is_err());
    let (expr, err) = result.unwrap_err();
    assert_eq!(
        clear_span(expr.unwrap()),
        case(
            int(1),
            vec![
                (
                    Pattern::Record {
                        typ: Type::hole(),
                        types: vec![],
                        fields: vec![
                            PatternField {
                                name: no_loc(intern("x")),
                                value: Some(no_loc(Pattern::Error)),
                            },
                        ],
                    },
                    error(),
                ),
            ],
        )
    );

    let errors = vec![
        no_loc(Error::UnexpectedToken("RBrace".into(), vec![])),
        no_loc(Error::UnexpectedToken("CloseBlock".into(), vec![])),
    ];
    assert_eq!(remove_expected(err), ParseErrors::from(errors));
}

#[test]
fn incomplete_let_binding() {
    let _ = ::env_logger::init();

    let expr = r#"
    let test =
    1
    "#;
    let result = parse(expr);
    assert!(result.is_err());
    let (expr, err) = result.unwrap_err();
    assert_eq!(
        clear_span(expr.unwrap()),
        let_("test", no_loc(Expr::Error(None)), int(1),)
    );

    let errors = vec![no_loc(Error::UnexpectedToken("CloseBlock".into(), vec![]))];
    assert_eq!(remove_expected(err), ParseErrors::from(errors));
}

#[test]
fn incomplete_let_binding_2() {
    let _ = ::env_logger::init();

    let expr = r#"
    let test = io
    "#;
    let result = parse(expr);
    assert!(result.is_err());
    let (expr, err) = result.unwrap_err();
    assert_eq!(
        clear_span(expr.unwrap()),
        let_("test", id("io"), no_loc(Expr::Error(None)))
    );

    let errors = vec![
        no_loc(Error::UnexpectedToken("CloseBlock".into(), vec![])),
        no_loc(Error::UnexpectedToken("CloseBlock".into(), vec![])),
    ];
    assert_eq!(remove_expected(err), ParseErrors::from(errors));
}

#[test]
fn unterminated_char_literal() {
    let _ = ::env_logger::init();

    let expr = r#"
    'a
    "#;
    let result = parse(expr);
    assert!(result.is_err());
    let (_expr, err) = result.unwrap_err();

    let error = Error::Token(TokenizeError::UnterminatedCharLiteral);
    let span = pos::span(BytePos::from(5), BytePos::from(5));
    assert_eq!(err, ParseErrors::from(vec![pos::spanned(span, error)]));
}
<|endoftext|>


<commit_before>use std::result::Result;
use std::marker::PhantomData;
use std::error::Error;
use serde::*;
use serde_json::*;

use super::super::tool::*;
use super::super::environment::*;

///
/// Represents a tool made from a function
///
pub struct FnTool<TIn: Deserialize, TOut: Serialize, TErr: Serialize> {
    function: Box<Fn(TIn) -> Result<TOut, TErr> + Send + Sync>
}

///
/// Creates a Tool from a function that can produce an error but does not use an environment
///
pub fn make_tool<TIn: Deserialize, TOut: Serialize, TErr: Serialize, F: 'static+Send+Sync+Fn(TIn) -> Result<TOut, TErr>>(function: F) -> FnTool<TIn, TOut, TErr> {
    FnTool { function: Box::new(function) }
}

///
/// Creates a Tool from a function that cannot produce an error and doesn't use an environment
///
pub fn make_pure_tool<TIn: Deserialize, TOut: Serialize, F: 'static+Send+Sync+Fn(TIn) -> TOut>(function: F) -> FnTool<TIn, TOut, ()> {
    make_tool(move |input| { Ok(function(input)) })
}

impl<TIn, TOut, TErr> Tool for FnTool<TIn, TOut, TErr>
where TIn: Deserialize, TOut: Serialize, TErr: Serialize {
    fn invoke_json(&self, input: Value, _environment: &Environment) -> Result<Value, Value> {
        // Decode
        let input_decoded = from_value::<TIn>(input);

        // Chain into the tool itself
        match input_decoded {
            Ok(input_decoded) => {
                // Successfully decoded as the tool's input format
                let result = (self.function)(input_decoded);

                // Encode the error or the result
                // The encoding can go wrong, and we need to handle it slightly differently for the error or the success case
                match result {
                    Ok(res) => {
                        let encoded = to_value(res);
                        match encoded {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "JSON encode failed",
                                "description":  erm.description()
                            }])
                        }
                    },

                    Err(erm) => {
                        let encoded = to_value(erm);
                        match encoded {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "Error encode failed",
                                "description":  erm.description()
                            }])
                        }
                    }
                }
            },

            Err(input_error) => {
                // Input does not match the format expected by the tool
                Err(json![{
                    "error":        "JSON input decode failed",
                    "description":  input_error.description(),
                }])
            }
        }
    }
}

///
/// Represents a tool with Rust types
///
pub struct TypedTool<TIn: Serialize, TOut: Deserialize> {
    param1: PhantomData<TIn>,
    param2: PhantomData<TOut>,
    tool: Box<Tool>

    // tool could be &'a Tool and it would work as Box<Tool> implements Tool - except rust's deref behaviour
    // gets in the way: trying to pass a reference to a box actually passes a reference to the boxed thing, which
    // then doesn't work because the size is unknown
}

impl<TIn: Serialize, TOut: Deserialize> TypedTool<TIn, TOut> {
    ///
    /// Creates an object that can be used to invoke a tool with typed parameters instead of pure JSON
    ///
    pub fn from(tool: Box<Tool>) -> TypedTool<TIn, TOut> {
        TypedTool { param1: PhantomData, param2: PhantomData, tool: tool }
    }

    ///
    /// Invokes this tool
    ///
    pub fn invoke(&self, input: TIn, environment: &Environment) -> Result<TOut, Value> {
        let json_input = to_value(input);

        match json_input {
            Ok(json_input) => {
                let json_output = self.tool.invoke_json(json_input, environment);

                match json_output {
                    Ok(json_output) => {
                        let result = from_value::<TOut>(json_output);
                        match result {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "Result decode failed",
                                "description":  erm.description()
                            }])
                        }
                    },

                    Err(json_error) => {
                        Err(json_error)
                    }
                }
            },

            Err(erm) => {
                Err(json![{
                    "error":        "Input encode failed",
                    "description":  erm.description()
                }])
            }
        }
    }
}

///
/// Extension trait for environments that makes it possible to get a Rust typed tool immediately
///
pub trait TypedEnvironment {
    ///
    /// Retrieves a tool using a Rust interface by name
    ///
    fn get_typed_tool<'a, TIn: Serialize, TOut: Deserialize>(&self, name: &str) -> Result<TypedTool<TIn, TOut>, RetrieveToolError>;
}

impl<TEnv> TypedEnvironment for TEnv where TEnv: Environment {
    ///
    /// Retrieves a tool using a Rust interface by name
    ///
    fn get_typed_tool<'a, TIn: Serialize, TOut: Deserialize>(&self, name: &str) -> Result<TypedTool<TIn, TOut>, RetrieveToolError> {
        self.get_json_tool(name).map(|tool| TypedTool::from(tool))
    }
}

#[cfg(test)]
mod test {
    use super::super::super::*;
    use super::super::*;
    use std::result::Result;
    use serde_json::*;              // Rust says unused, but the json! macro will not work without this

    #[derive(Serialize, Deserialize)]
    struct TestIn {
        input: i32
    }

    #[derive(Serialize, Deserialize, PartialEq, Eq)]
    struct TestOut {
        output: i32
    }

    fn test_tool(x: TestIn) -> Result<TestOut, ()> {
        Ok(TestOut { output: x.input + 1 })
    }

    #[test]
    fn can_call_tool_via_json_interface() {
        let tool        = make_tool(test_tool);
        let environment = EmptyEnvironment::new();
        let result      = tool.invoke_json(json![{ "input": 4 }], &environment);

        assert!(result == Ok(json![{ "output": 5 }]));
    }

    #[test]
    fn can_call_tool_via_typed_interface() {
        let tool        = make_pure_tool(|x: i32| { x+1 });
        let environment = EmptyEnvironment::new();

        let typed_tool  = TypedTool::from(Box::new(tool));
        let result      = typed_tool.invoke(4, &environment);

        assert!(result == Ok(5));
    }
}
<commit_msg>Add a ‘dynamic’ tool function that can use its environment<commit_after>use std::result::Result;
use std::marker::PhantomData;
use std::error::Error;
use serde::*;
use serde_json::*;

use super::super::tool::*;
use super::super::environment::*;

///
/// Represents a tool made from a function
///
pub struct FnTool<TIn: Deserialize, TOut: Serialize, TErr: Serialize> {
    function: Box<Fn(TIn, &Environment) -> Result<TOut, TErr> + Send + Sync>
}

///
/// Creates a Tool from a function that can produce an error and uses an environment
///
pub fn make_dynamic_tool<TIn: Deserialize, TOut: Serialize, TErr: Serialize, F: 'static+Send+Sync+Fn(TIn, &Environment) -> Result<TOut, TErr>>(function: F) -> FnTool<TIn, TOut, TErr> {
    FnTool { function: Box::new(function) }
}

///
/// Creates a Tool from a function that can produce an error but does not use an environment
///
pub fn make_tool<TIn: Deserialize, TOut: Serialize, TErr: Serialize, F: 'static+Send+Sync+Fn(TIn) -> Result<TOut, TErr>>(function: F) -> FnTool<TIn, TOut, TErr> {
    make_dynamic_tool(move |input, _| function(input))
}

///
/// Creates a Tool from a function that cannot produce an error and doesn't use an environment
///
pub fn make_pure_tool<TIn: Deserialize, TOut: Serialize, F: 'static+Send+Sync+Fn(TIn) -> TOut>(function: F) -> FnTool<TIn, TOut, ()> {
    make_dynamic_tool(move |input, _| Ok(function(input)))
}

impl<TIn, TOut, TErr> Tool for FnTool<TIn, TOut, TErr>
where TIn: Deserialize, TOut: Serialize, TErr: Serialize {
    fn invoke_json(&self, input: Value, environment: &Environment) -> Result<Value, Value> {
        // Decode
        let input_decoded = from_value::<TIn>(input);

        // Chain into the tool itself
        match input_decoded {
            Ok(input_decoded) => {
                // Successfully decoded as the tool's input format
                let result = (self.function)(input_decoded, environment);

                // Encode the error or the result
                // The encoding can go wrong, and we need to handle it slightly differently for the error or the success case
                match result {
                    Ok(res) => {
                        let encoded = to_value(res);
                        match encoded {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "JSON encode failed",
                                "description":  erm.description()
                            }])
                        }
                    },

                    Err(erm) => {
                        let encoded = to_value(erm);
                        match encoded {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "Error encode failed",
                                "description":  erm.description()
                            }])
                        }
                    }
                }
            },

            Err(input_error) => {
                // Input does not match the format expected by the tool
                Err(json![{
                    "error":        "JSON input decode failed",
                    "description":  input_error.description(),
                }])
            }
        }
    }
}

///
/// Represents a tool with Rust types
///
pub struct TypedTool<TIn: Serialize, TOut: Deserialize> {
    param1: PhantomData<TIn>,
    param2: PhantomData<TOut>,
    tool: Box<Tool>

    // tool could be &'a Tool and it would work as Box<Tool> implements Tool - except rust's deref behaviour
    // gets in the way: trying to pass a reference to a box actually passes a reference to the boxed thing, which
    // then doesn't work because the size is unknown
}

impl<TIn: Serialize, TOut: Deserialize> TypedTool<TIn, TOut> {
    ///
    /// Creates an object that can be used to invoke a tool with typed parameters instead of pure JSON
    ///
    pub fn from(tool: Box<Tool>) -> TypedTool<TIn, TOut> {
        TypedTool { param1: PhantomData, param2: PhantomData, tool: tool }
    }

    ///
    /// Invokes this tool
    ///
    pub fn invoke(&self, input: TIn, environment: &Environment) -> Result<TOut, Value> {
        let json_input = to_value(input);

        match json_input {
            Ok(json_input) => {
                let json_output = self.tool.invoke_json(json_input, environment);

                match json_output {
                    Ok(json_output) => {
                        let result = from_value::<TOut>(json_output);
                        match result {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "Result decode failed",
                                "description":  erm.description()
                            }])
                        }
                    },

                    Err(json_error) => {
                        Err(json_error)
                    }
                }
            },

            Err(erm) => {
                Err(json![{
                    "error":        "Input encode failed",
                    "description":  erm.description()
                }])
            }
        }
    }
}

///
/// Extension trait for environments that makes it possible to get a Rust typed tool immediately
///
pub trait TypedEnvironment {
    ///
    /// Retrieves a tool using a Rust interface by name
    ///
    fn get_typed_tool<'a, TIn: Serialize, TOut: Deserialize>(&self, name: &str) -> Result<TypedTool<TIn, TOut>, RetrieveToolError>;
}

impl<TEnv> TypedEnvironment for TEnv where TEnv: Environment {
    ///
    /// Retrieves a tool using a Rust interface by name
    ///
    fn get_typed_tool<'a, TIn: Serialize, TOut: Deserialize>(&self, name: &str) -> Result<TypedTool<TIn, TOut>, RetrieveToolError> {
        self.get_json_tool(name).map(|tool| TypedTool::from(tool))
    }
}

#[cfg(test)]
mod test {
    use super::super::super::*;
    use super::super::*;
    use std::result::Result;
    use serde_json::*;              // Rust says unused, but the json! macro will not work without this

    #[derive(Serialize, Deserialize)]
    struct TestIn {
        input: i32
    }

    #[derive(Serialize, Deserialize, PartialEq, Eq)]
    struct TestOut {
        output: i32
    }

    fn test_tool(x: TestIn) -> Result<TestOut, ()> {
        Ok(TestOut { output: x.input + 1 })
    }

    #[test]
    fn can_call_tool_via_json_interface() {
        let tool        = make_tool(test_tool);
        let environment = EmptyEnvironment::new();
        let result      = tool.invoke_json(json![{ "input": 4 }], &environment);

        assert!(result == Ok(json![{ "output": 5 }]));
    }

    #[test]
    fn can_call_tool_via_typed_interface() {
        let tool        = make_pure_tool(|x: i32| { x+1 });
        let environment = EmptyEnvironment::new();

        let typed_tool  = TypedTool::from(Box::new(tool));
        let result      = typed_tool.invoke(4, &environment);

        assert!(result == Ok(5));
    }
}
<|endoftext|>


<commit_before>//! Runs enqueued background jobs
//!
//! This binary will loop until interrupted. It will run all jobs in the
//! background queue, sleeping for 1 second whenever the queue is empty. If we
//! are unable to spawn workers to run jobs (either because we couldn't connect
//! to the DB, an error occurred while loading, or we just never heard back from
//! the worker thread), we will rebuild the runner and try again up to 5 times.
//! After the 5th occurrence, we will panic.
//!
//! Usage:
//!      cargo run --bin background-worker

#![warn(clippy::all, rust_2018_idioms)]

use cargo_registry::config;
use cargo_registry::worker::cloudfront::CloudFront;
use cargo_registry::{background_jobs::*, db};
use cargo_registry_index::{Repository, RepositoryConfig};
use diesel::r2d2;
use reqwest::blocking::Client;
use std::sync::{Arc, Mutex};
use std::thread::sleep;
use std::time::Duration;

fn main() {
    println!("Booting runner");

    let config = config::Server::default();
    let uploader = config.base.uploader();

    if config.db.are_all_read_only() {
        loop {
            println!(
                "Cannot run background jobs with a read-only pool. Please scale background_worker \
                to 0 processes until the leader database is available."
            );
            sleep(Duration::from_secs(60));
        }
    }

    let db_url = db::connection_url(&config.db, &config.db.primary.url);

    let job_start_timeout = dotenv::var("BACKGROUND_JOB_TIMEOUT")
        .unwrap_or_else(|_| "30".into())
        .parse()
        .expect("Invalid value for `BACKGROUND_JOB_TIMEOUT`");

    println!("Cloning index");

    let repository_config = RepositoryConfig::from_environment();
    let repository = Arc::new(Mutex::new(
        Repository::open(&repository_config).expect("Failed to clone index"),
    ));
    println!("Index cloned");

    let cloudfront = CloudFront::from_environment();

    let build_runner = || {
        let client = Client::builder()
            .timeout(Duration::from_secs(45))
            .build()
            .expect("Couldn't build client");
        let environment = Environment::new_shared(
            repository.clone(),
            uploader.clone(),
            client,
            cloudfront.clone(),
        );
        let db_config = r2d2::Pool::builder().min_idle(Some(0));
        swirl::Runner::builder(environment)
            .connection_pool_builder(&db_url, db_config)
            .job_start_timeout(Duration::from_secs(job_start_timeout))
            .build()
    };
    let mut runner = build_runner();

    println!("Runner booted, running jobs");

    let mut failure_count = 0;

    loop {
        if let Err(e) = runner.run_all_pending_jobs() {
            failure_count += 1;
            if failure_count < 5 {
                eprintln!(
                    "Error running jobs (n = {}) -- retrying: {:?}",
                    failure_count, e,
                );
                runner = build_runner();
            } else {
                panic!("Failed to begin running jobs 5 times. Restarting the process");
            }
        }
        sleep(Duration::from_secs(1));
    }
}
<commit_msg>background-worker: Initialize `tracing` logger (#5487)<commit_after>//! Runs enqueued background jobs
//!
//! This binary will loop until interrupted. It will run all jobs in the
//! background queue, sleeping for 1 second whenever the queue is empty. If we
//! are unable to spawn workers to run jobs (either because we couldn't connect
//! to the DB, an error occurred while loading, or we just never heard back from
//! the worker thread), we will rebuild the runner and try again up to 5 times.
//! After the 5th occurrence, we will panic.
//!
//! Usage:
//!      cargo run --bin background-worker

#![warn(clippy::all, rust_2018_idioms)]

use cargo_registry::config;
use cargo_registry::worker::cloudfront::CloudFront;
use cargo_registry::{background_jobs::*, db};
use cargo_registry_index::{Repository, RepositoryConfig};
use diesel::r2d2;
use reqwest::blocking::Client;
use std::env;
use std::sync::{Arc, Mutex};
use std::thread::sleep;
use std::time::Duration;
use tracing_subscriber::{filter, prelude::*};

fn main() {
    println!("Booting runner");

    // Initialize logging

    let log_filter = env::var("RUST_LOG")
        .unwrap_or_default()
        .parse::<filter::Targets>()
        .expect("Invalid RUST_LOG value");

    tracing_subscriber::registry()
        .with(tracing_subscriber::fmt::layer().with_filter(log_filter))
        .init();

    let config = config::Server::default();
    let uploader = config.base.uploader();

    if config.db.are_all_read_only() {
        loop {
            println!(
                "Cannot run background jobs with a read-only pool. Please scale background_worker \
                to 0 processes until the leader database is available."
            );
            sleep(Duration::from_secs(60));
        }
    }

    let db_url = db::connection_url(&config.db, &config.db.primary.url);

    let job_start_timeout = dotenv::var("BACKGROUND_JOB_TIMEOUT")
        .unwrap_or_else(|_| "30".into())
        .parse()
        .expect("Invalid value for `BACKGROUND_JOB_TIMEOUT`");

    println!("Cloning index");

    let repository_config = RepositoryConfig::from_environment();
    let repository = Arc::new(Mutex::new(
        Repository::open(&repository_config).expect("Failed to clone index"),
    ));
    println!("Index cloned");

    let cloudfront = CloudFront::from_environment();

    let build_runner = || {
        let client = Client::builder()
            .timeout(Duration::from_secs(45))
            .build()
            .expect("Couldn't build client");
        let environment = Environment::new_shared(
            repository.clone(),
            uploader.clone(),
            client,
            cloudfront.clone(),
        );
        let db_config = r2d2::Pool::builder().min_idle(Some(0));
        swirl::Runner::builder(environment)
            .connection_pool_builder(&db_url, db_config)
            .job_start_timeout(Duration::from_secs(job_start_timeout))
            .build()
    };
    let mut runner = build_runner();

    println!("Runner booted, running jobs");

    let mut failure_count = 0;

    loop {
        if let Err(e) = runner.run_all_pending_jobs() {
            failure_count += 1;
            if failure_count < 5 {
                eprintln!(
                    "Error running jobs (n = {}) -- retrying: {:?}",
                    failure_count, e,
                );
                runner = build_runner();
            } else {
                panic!("Failed to begin running jobs 5 times. Restarting the process");
            }
        }
        sleep(Duration::from_secs(1));
    }
}
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (words) RFILE

use crate::common::util::*;
use std::io::{Seek, SeekFrom, Write};

static FILE1: &str = "truncate_test_1";
static FILE2: &str = "truncate_test_2";

#[test]
fn test_increase_file_size() {
    let expected = 5 * 1024;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file = at.make_file(FILE1);
    ucmd.args(&["-s", "+5K", FILE1]).succeeds();

    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_increase_file_size_kb() {
    let expected = 5 * 1000;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file = at.make_file(FILE1);
    ucmd.args(&["-s", "+5KB", FILE1]).succeeds();

    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_reference() {
    let expected = 5 * 1000;
    let scene = TestScenario::new(util_name!());
    let at = &scene.fixtures;
    let mut file = at.make_file(FILE2);

    // manpage: "A FILE argument that does not exist is created."
    // TODO: 'truncate' does not create the file in this case,
    //        but should because '--no-create' wasn't specified.
    at.touch(FILE1); // TODO: remove this when 'no-create' is fixed
    scene.ucmd().arg("-s").arg("+5KB").arg(FILE1).succeeds();

    scene
        .ucmd()
        .arg("--reference")
        .arg(FILE1)
        .arg(FILE2)
        .succeeds();

    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_decrease_file_size() {
    let expected = 6;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file = at.make_file(FILE2);
    file.write_all(b"1234567890").unwrap();
    ucmd.args(&["--size=-4", FILE2]).succeeds();
    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_space_in_size() {
    let expected = 4;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file = at.make_file(FILE2);
    file.write_all(b"1234567890").unwrap();
    ucmd.args(&["--size", " 4", FILE2]).succeeds();
    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_failed() {
    new_ucmd!().fails();
}

#[test]
fn test_failed_2() {
    let (_at, mut ucmd) = at_and_ucmd!();
    ucmd.args(&[FILE1]).fails();
}

#[test]
fn test_failed_incorrect_arg() {
    let (_at, mut ucmd) = at_and_ucmd!();
    ucmd.args(&["-s", "+5A", FILE1]).fails();
}

#[test]
fn test_at_most_shrinks() {
    let expected = 4;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file = at.make_file(FILE2);
    file.write_all(b"1234567890").unwrap();
    ucmd.args(&["--size", "<4", FILE2]).succeeds();
    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_at_most_no_change() {
    let expected = 10;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file = at.make_file(FILE2);
    file.write_all(b"1234567890").unwrap();
    ucmd.args(&["--size", "<40", FILE2]).succeeds();
    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_at_least_grows() {
    let expected = 15;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file = at.make_file(FILE2);
    file.write_all(b"1234567890").unwrap();
    ucmd.args(&["--size", ">15", FILE2]).succeeds();
    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_at_least_no_change() {
    let expected = 10;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file = at.make_file(FILE2);
    file.write_all(b"1234567890").unwrap();
    ucmd.args(&["--size", ">4", FILE2]).succeeds();
    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_round_down() {
    let expected = 8;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file = at.make_file(FILE2);
    file.write_all(b"1234567890").unwrap();
    ucmd.args(&["--size", "/4", FILE2]).succeeds();
    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_round_up() {
    let expected = 12;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file = at.make_file(FILE2);
    file.write_all(b"1234567890").unwrap();
    ucmd.args(&["--size", "%4", FILE2]).succeeds();
    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_size_and_reference() {
    let expected = 15;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file1 = at.make_file(FILE1);
    let mut file2 = at.make_file(FILE2);
    file1.write_all(b"1234567890").unwrap();
    ucmd.args(&["--reference", FILE1, "--size", "+5", FILE2])
        .succeeds();
    file2.seek(SeekFrom::End(0)).unwrap();
    let actual = file2.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_error_filename_only() {
    // truncate: you must specify either '--size' or '--reference'
    new_ucmd!().args(&["file"]).fails().stderr_contains(
        "error: The following required arguments were not provided:
    --reference <RFILE>
    --size <SIZE>",
    );
}

#[test]
fn test_invalid_numbers() {
    new_ucmd!()
        .args(&["-s", "0X", "file"])
        .fails()
        .stderr_contains("Invalid number: '0X'");
    new_ucmd!()
        .args(&["-s", "0XB", "file"])
        .fails()
        .stderr_contains("Invalid number: '0XB'");
    new_ucmd!()
        .args(&["-s", "0B", "file"])
        .fails()
        .stderr_contains("Invalid number: '0B'");
}

#[test]
fn test_reference_file_not_found() {
    new_ucmd!()
        .args(&["-r", "a", "b"])
        .fails()
        .stderr_contains("cannot stat 'a': No such file or directory");
}

#[test]
fn test_reference_with_size_file_not_found() {
    new_ucmd!()
        .args(&["-r", "a", "-s", "+1", "b"])
        .fails()
        .stderr_contains("cannot stat 'a': No such file or directory");
}

#[test]
fn test_truncate_bytes_size() {
    // TODO: this should succeed without error, uncomment when '--no-create' is fixed
    // new_ucmd!()
    //     .args(&["--no-create", "--size", "K", "file"])
    //     .succeeds();
    new_ucmd!()
        .args(&["--size", "1024R", "file"])
        .fails()
        .code_is(1)
        .stderr_only("truncate: Invalid number: '1024R'");
    #[cfg(not(target_pointer_width = "128"))]
    new_ucmd!()
        .args(&["--size", "1Y", "file"])
        .fails()
        .code_is(1)
        .stderr_only("truncate: Invalid number: '1Y': Value too large for defined data type");
    #[cfg(target_pointer_width = "32")]
    {
        let sizes = ["1000G", "10T"];
        for size in &sizes {
            new_ucmd!()
                .args(&["--size", size, "file"])
                .fails()
                .code_is(1)
                .stderr_only(format!(
                    "truncate: Invalid number: '{}': Value too large for defined data type",
                    size
                ));
        }
    }
}

/// Test that truncating a non-existent file creates that file.
#[test]
fn test_new_file() {
    let (at, mut ucmd) = at_and_ucmd!();
    let filename = "new_file_that_does_not_exist_yet";
    ucmd.args(&["-s", "8", filename])
        .succeeds()
        .no_stdout()
        .no_stderr();
    assert!(at.file_exists(filename));
    assert_eq!(at.read_bytes(filename), vec![b'\0'; 8]);
}

/// Test for not creating a non-existent file.
#[test]
fn test_new_file_no_create() {
    let (at, mut ucmd) = at_and_ucmd!();
    let filename = "new_file_that_does_not_exist_yet";
    ucmd.args(&["-s", "8", "-c", filename])
        .succeeds()
        .no_stdout()
        .no_stderr();
    assert!(!at.file_exists(filename));
}

#[test]
fn test_division_by_zero_size_only() {
    new_ucmd!()
        .args(&["-s", "/0", "file"])
        .fails()
        .no_stdout()
        .stderr_contains("division by zero");
    new_ucmd!()
        .args(&["-s", "%0", "file"])
        .fails()
        .no_stdout()
        .stderr_contains("division by zero");
}

#[test]
fn test_division_by_zero_reference_and_size() {
    let (at, mut ucmd) = at_and_ucmd!();
    at.make_file(FILE1);
    ucmd.args(&["-r", FILE1, "-s", "/0", "file"])
        .fails()
        .no_stdout()
        .stderr_contains("division by zero");

    let (at, mut ucmd) = at_and_ucmd!();
    at.make_file(FILE1);
    ucmd.args(&["-r", FILE1, "-s", "%0", "file"])
        .fails()
        .no_stdout()
        .stderr_contains("division by zero");
}

#[test]
fn test_no_such_dir() {
    new_ucmd!()
        .args(&["-s", "0", "a/b"])
        .fails()
        .no_stdout()
        .stderr_contains("cannot open 'a/b' for writing: No such file or directory");
}

/// Test that truncate with a relative size less than 0 is not an error.
#[test]
fn test_underflow_relative_size() {
    let (at, mut ucmd) = at_and_ucmd!();
    ucmd.args(&["-s-1", FILE1])
        .succeeds()
        .no_stdout()
        .no_stderr();
    assert!(at.file_exists(FILE1));
    assert!(at.read_bytes(FILE1).is_empty());
}
<commit_msg>truncate: add test_invalid_option<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (words) RFILE

use crate::common::util::*;
use std::io::{Seek, SeekFrom, Write};

static FILE1: &str = "truncate_test_1";
static FILE2: &str = "truncate_test_2";

#[test]
fn test_increase_file_size() {
    let expected = 5 * 1024;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file = at.make_file(FILE1);
    ucmd.args(&["-s", "+5K", FILE1]).succeeds();

    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_increase_file_size_kb() {
    let expected = 5 * 1000;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file = at.make_file(FILE1);
    ucmd.args(&["-s", "+5KB", FILE1]).succeeds();

    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_reference() {
    let expected = 5 * 1000;
    let scene = TestScenario::new(util_name!());
    let at = &scene.fixtures;
    let mut file = at.make_file(FILE2);

    // manpage: "A FILE argument that does not exist is created."
    // TODO: 'truncate' does not create the file in this case,
    //        but should because '--no-create' wasn't specified.
    at.touch(FILE1); // TODO: remove this when 'no-create' is fixed
    scene.ucmd().arg("-s").arg("+5KB").arg(FILE1).succeeds();

    scene
        .ucmd()
        .arg("--reference")
        .arg(FILE1)
        .arg(FILE2)
        .succeeds();

    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_decrease_file_size() {
    let expected = 6;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file = at.make_file(FILE2);
    file.write_all(b"1234567890").unwrap();
    ucmd.args(&["--size=-4", FILE2]).succeeds();
    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_space_in_size() {
    let expected = 4;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file = at.make_file(FILE2);
    file.write_all(b"1234567890").unwrap();
    ucmd.args(&["--size", " 4", FILE2]).succeeds();
    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_failed() {
    new_ucmd!().fails();
}

#[test]
fn test_failed_2() {
    let (_at, mut ucmd) = at_and_ucmd!();
    ucmd.args(&[FILE1]).fails();
}

#[test]
fn test_failed_incorrect_arg() {
    let (_at, mut ucmd) = at_and_ucmd!();
    ucmd.args(&["-s", "+5A", FILE1]).fails();
}

#[test]
fn test_at_most_shrinks() {
    let expected = 4;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file = at.make_file(FILE2);
    file.write_all(b"1234567890").unwrap();
    ucmd.args(&["--size", "<4", FILE2]).succeeds();
    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_at_most_no_change() {
    let expected = 10;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file = at.make_file(FILE2);
    file.write_all(b"1234567890").unwrap();
    ucmd.args(&["--size", "<40", FILE2]).succeeds();
    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_at_least_grows() {
    let expected = 15;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file = at.make_file(FILE2);
    file.write_all(b"1234567890").unwrap();
    ucmd.args(&["--size", ">15", FILE2]).succeeds();
    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_at_least_no_change() {
    let expected = 10;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file = at.make_file(FILE2);
    file.write_all(b"1234567890").unwrap();
    ucmd.args(&["--size", ">4", FILE2]).succeeds();
    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_round_down() {
    let expected = 8;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file = at.make_file(FILE2);
    file.write_all(b"1234567890").unwrap();
    ucmd.args(&["--size", "/4", FILE2]).succeeds();
    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_round_up() {
    let expected = 12;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file = at.make_file(FILE2);
    file.write_all(b"1234567890").unwrap();
    ucmd.args(&["--size", "%4", FILE2]).succeeds();
    file.seek(SeekFrom::End(0)).unwrap();
    let actual = file.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_size_and_reference() {
    let expected = 15;
    let (at, mut ucmd) = at_and_ucmd!();
    let mut file1 = at.make_file(FILE1);
    let mut file2 = at.make_file(FILE2);
    file1.write_all(b"1234567890").unwrap();
    ucmd.args(&["--reference", FILE1, "--size", "+5", FILE2])
        .succeeds();
    file2.seek(SeekFrom::End(0)).unwrap();
    let actual = file2.seek(SeekFrom::Current(0)).unwrap();
    assert!(
        expected == actual,
        "expected '{}' got '{}'",
        expected,
        actual
    );
}

#[test]
fn test_error_filename_only() {
    // truncate: you must specify either '--size' or '--reference'
    new_ucmd!()
        .args(&["file"])
        .fails()
        .code_is(1)
        .stderr_contains(
            "error: The following required arguments were not provided:
    --reference <RFILE>
    --size <SIZE>",
        );
}

#[test]
fn test_invalid_option() {
    // truncate: cli parsing error returns 1
    new_ucmd!()
        .args(&["--this-arg-does-not-exist"])
        .fails()
        .code_is(1);
}

#[test]
fn test_invalid_numbers() {
    new_ucmd!()
        .args(&["-s", "0X", "file"])
        .fails()
        .stderr_contains("Invalid number: '0X'");
    new_ucmd!()
        .args(&["-s", "0XB", "file"])
        .fails()
        .stderr_contains("Invalid number: '0XB'");
    new_ucmd!()
        .args(&["-s", "0B", "file"])
        .fails()
        .stderr_contains("Invalid number: '0B'");
}

#[test]
fn test_reference_file_not_found() {
    new_ucmd!()
        .args(&["-r", "a", "b"])
        .fails()
        .stderr_contains("cannot stat 'a': No such file or directory");
}

#[test]
fn test_reference_with_size_file_not_found() {
    new_ucmd!()
        .args(&["-r", "a", "-s", "+1", "b"])
        .fails()
        .stderr_contains("cannot stat 'a': No such file or directory");
}

#[test]
fn test_truncate_bytes_size() {
    // TODO: this should succeed without error, uncomment when '--no-create' is fixed
    // new_ucmd!()
    //     .args(&["--no-create", "--size", "K", "file"])
    //     .succeeds();
    new_ucmd!()
        .args(&["--size", "1024R", "file"])
        .fails()
        .code_is(1)
        .stderr_only("truncate: Invalid number: '1024R'");
    #[cfg(not(target_pointer_width = "128"))]
    new_ucmd!()
        .args(&["--size", "1Y", "file"])
        .fails()
        .code_is(1)
        .stderr_only("truncate: Invalid number: '1Y': Value too large for defined data type");
    #[cfg(target_pointer_width = "32")]
    {
        let sizes = ["1000G", "10T"];
        for size in &sizes {
            new_ucmd!()
                .args(&["--size", size, "file"])
                .fails()
                .code_is(1)
                .stderr_only(format!(
                    "truncate: Invalid number: '{}': Value too large for defined data type",
                    size
                ));
        }
    }
}

/// Test that truncating a non-existent file creates that file.
#[test]
fn test_new_file() {
    let (at, mut ucmd) = at_and_ucmd!();
    let filename = "new_file_that_does_not_exist_yet";
    ucmd.args(&["-s", "8", filename])
        .succeeds()
        .no_stdout()
        .no_stderr();
    assert!(at.file_exists(filename));
    assert_eq!(at.read_bytes(filename), vec![b'\0'; 8]);
}

/// Test for not creating a non-existent file.
#[test]
fn test_new_file_no_create() {
    let (at, mut ucmd) = at_and_ucmd!();
    let filename = "new_file_that_does_not_exist_yet";
    ucmd.args(&["-s", "8", "-c", filename])
        .succeeds()
        .no_stdout()
        .no_stderr();
    assert!(!at.file_exists(filename));
}

#[test]
fn test_division_by_zero_size_only() {
    new_ucmd!()
        .args(&["-s", "/0", "file"])
        .fails()
        .no_stdout()
        .stderr_contains("division by zero");
    new_ucmd!()
        .args(&["-s", "%0", "file"])
        .fails()
        .no_stdout()
        .stderr_contains("division by zero");
}

#[test]
fn test_division_by_zero_reference_and_size() {
    let (at, mut ucmd) = at_and_ucmd!();
    at.make_file(FILE1);
    ucmd.args(&["-r", FILE1, "-s", "/0", "file"])
        .fails()
        .no_stdout()
        .stderr_contains("division by zero");

    let (at, mut ucmd) = at_and_ucmd!();
    at.make_file(FILE1);
    ucmd.args(&["-r", FILE1, "-s", "%0", "file"])
        .fails()
        .no_stdout()
        .stderr_contains("division by zero");
}

#[test]
fn test_no_such_dir() {
    new_ucmd!()
        .args(&["-s", "0", "a/b"])
        .fails()
        .no_stdout()
        .stderr_contains("cannot open 'a/b' for writing: No such file or directory");
}

/// Test that truncate with a relative size less than 0 is not an error.
#[test]
fn test_underflow_relative_size() {
    let (at, mut ucmd) = at_and_ucmd!();
    ucmd.args(&["-s-1", FILE1])
        .succeeds()
        .no_stdout()
        .no_stderr();
    assert!(at.file_exists(FILE1));
    assert!(at.read_bytes(FILE1).is_empty());
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.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.

//! Shim which is passed to Cargo as "rustdoc" when running the bootstrap.
//!
//! See comments in `src/bootstrap/rustc.rs` for more information.

#![deny(warnings)]

extern crate bootstrap;

use std::env;
use std::process::Command;
use std::path::PathBuf;

fn main() {
    let args = env::args_os().skip(1).collect::<Vec<_>>();
    let rustdoc = env::var_os("RUSTDOC_REAL").expect("RUSTDOC_REAL was not set");
    let libdir = env::var_os("RUSTC_LIBDIR").expect("RUSTC_LIBDIR was not set");
    let stage = env::var("RUSTC_STAGE").expect("RUSTC_STAGE was not set");
    let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set");

    let mut dylib_path = bootstrap::util::dylib_path();
    dylib_path.insert(0, PathBuf::from(libdir));

    let mut cmd = Command::new(rustdoc);
    cmd.args(&args)
        .arg("--cfg")
        .arg(format!("stage{}", stage))
        .arg("--cfg")
        .arg("dox")
        .arg("--sysroot")
        .arg(sysroot)
        .env(bootstrap::util::dylib_path_var(),
             env::join_paths(&dylib_path).unwrap());

    // Force all crates compiled by this compiler to (a) be unstable and (b)
    // allow the `rustc_private` feature to link to other unstable crates
    // also in the sysroot.
    if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() {
        cmd.arg("-Z").arg("force-unstable-if-unmarked");
    }
    if let Some(linker) = env::var_os("RUSTC_TARGET_LINKER") {
        cmd.arg("--linker").arg(linker).arg("-Z").arg("unstable-options");
    }

    // Bootstrap's Cargo-command builder sets this variable to the current Rust version; let's pick
    // it up so we can make rustdoc print this into the docs
    if let Some(version) = env::var_os("RUSTDOC_CRATE_VERSION") {
        // This "unstable-options" can be removed when `--crate-version` is stabilized
        cmd.arg("-Z").arg("unstable-options")
           .arg("--crate-version").arg(version);

        // While we can assume that `-Z unstable-options` is set, let's also force rustdoc to panic
        // if pulldown rendering differences are found
        cmd.arg("--deny-render-differences");
    }

    std::process::exit(match cmd.status() {
        Ok(s) => s.code().unwrap_or(1),
        Err(e) => panic!("\n\nfailed to run {:?}: {}\n\n", cmd, e),
    })
}
<commit_msg>Accept verbosity in rustdoc.<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.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.

//! Shim which is passed to Cargo as "rustdoc" when running the bootstrap.
//!
//! See comments in `src/bootstrap/rustc.rs` for more information.

#![deny(warnings)]

extern crate bootstrap;

use std::env;
use std::process::Command;
use std::path::PathBuf;

fn main() {
    let args = env::args_os().skip(1).collect::<Vec<_>>();
    let rustdoc = env::var_os("RUSTDOC_REAL").expect("RUSTDOC_REAL was not set");
    let libdir = env::var_os("RUSTC_LIBDIR").expect("RUSTC_LIBDIR was not set");
    let stage = env::var("RUSTC_STAGE").expect("RUSTC_STAGE was not set");
    let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set");

    use std::str::FromStr;

    let verbose = match env::var("RUSTC_VERBOSE") {
        Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"),
        Err(_) => 0,
    };

    let mut dylib_path = bootstrap::util::dylib_path();
    dylib_path.insert(0, PathBuf::from(libdir));

    let mut cmd = Command::new(rustdoc);
    cmd.args(&args)
        .arg("--cfg")
        .arg(format!("stage{}", stage))
        .arg("--cfg")
        .arg("dox")
        .arg("--sysroot")
        .arg(sysroot)
        .env(bootstrap::util::dylib_path_var(),
             env::join_paths(&dylib_path).unwrap());

    // Force all crates compiled by this compiler to (a) be unstable and (b)
    // allow the `rustc_private` feature to link to other unstable crates
    // also in the sysroot.
    if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() {
        cmd.arg("-Z").arg("force-unstable-if-unmarked");
    }
    if let Some(linker) = env::var_os("RUSTC_TARGET_LINKER") {
        cmd.arg("--linker").arg(linker).arg("-Z").arg("unstable-options");
    }

    // Bootstrap's Cargo-command builder sets this variable to the current Rust version; let's pick
    // it up so we can make rustdoc print this into the docs
    if let Some(version) = env::var_os("RUSTDOC_CRATE_VERSION") {
        // This "unstable-options" can be removed when `--crate-version` is stabilized
        cmd.arg("-Z").arg("unstable-options")
           .arg("--crate-version").arg(version);

        // While we can assume that `-Z unstable-options` is set, let's also force rustdoc to panic
        // if pulldown rendering differences are found
        cmd.arg("--deny-render-differences");
    }

    if verbose > 1 {
        eprintln!("rustdoc command: {:?}", cmd);
    }

    std::process::exit(match cmd.status() {
        Ok(s) => s.code().unwrap_or(1),
        Err(e) => panic!("\n\nfailed to run {:?}: {}\n\n", cmd, e),
    })
}
<|endoftext|>


<commit_before>use cocoa::base::id;

pub struct RenderPipelineState(id);

impl_from_into_raw!(RenderPipelineState, of protocol "MTLRenderPipelineState");
<commit_msg>Add stub for RenderPipelineDescriptor<commit_after>use cocoa::base::id;

pub struct RenderPipelineState(id);

impl_from_into_raw!(RenderPipelineState, of protocol "MTLRenderPipelineState");

pub struct RenderPipelineDescriptor(id);

impl_from_into_raw!(RenderPipelineState, of class "MTLRenderPipelineDescriptor");
<|endoftext|>