<commit_before>use util::codec::bytes;
use storage::{Key, RefKey, Value, KvPair, Mutation};
use storage::Engine;
use storage::mvcc::MvccTxn;
use super::shard_mutex::ShardMutex;
use super::{Error, Result};

pub struct TxnStore {
    engine: Box<Engine>,
    shard_mutex: ShardMutex,
}

const SHARD_MUTEX_SIZE: usize = 256;

impl TxnStore {
    pub fn new(engine: Box<Engine>) -> TxnStore {
        TxnStore {
            engine: engine,
            shard_mutex: ShardMutex::new(SHARD_MUTEX_SIZE),
        }
    }

    pub fn get(&self, key: RefKey, start_ts: u64) -> Result<Option<Value>> {
        let _guard = self.shard_mutex.lock(&[key]);
        let txn = MvccTxn::new(self.engine.as_ref(), start_ts);
        Ok(try!(txn.get(key)))
    }

    #[allow(dead_code)]
    pub fn batch_get(&self, keys: &[RefKey], start_ts: u64) -> Vec<Result<Option<Value>>> {
        let txn = MvccTxn::new(self.engine.as_ref(), start_ts);
        let mut results = Vec::<_>::with_capacity(keys.len());
        for k in keys {
            let _guard = self.shard_mutex.lock(keys);
            results.push(txn.get(k).map_err(Error::from));
        }
        results
    }

    pub fn scan(&self, key: RefKey, limit: usize, start_ts: u64) -> Result<Vec<Result<KvPair>>> {
        let mut results = vec![];
        let mut key = key.to_vec();
        let txn = MvccTxn::new(self.engine.as_ref(), start_ts);
        while results.len() < limit {
            let mut next_key = match try!(self.engine.seek(&bytes::encode_bytes(&key))) {
                Some((key, _)) => {
                    let (key, _) = try!(bytes::decode_bytes(&key));
                    key
                }
                None => break,
            };
            let _guard = self.shard_mutex.lock(&next_key);
            match txn.get(&next_key) {
                Ok(Some(value)) => results.push(Ok((next_key.clone(), value))),
                Ok(None) => {}
                Err(e) => results.push(Err(Error::from(e))),
            };
            next_key.push(b'\0');
            key = next_key;
        }
        Ok(results)
    }

    pub fn prewrite(&self, writes: Vec<Mutation>, primary: Key, start_ts: u64) -> Result<Vec<Key>> {
        let locked_keys: Vec<Key> = writes.iter().map(|x| x.key().to_owned()).collect();

        let _guard = self.shard_mutex.lock(&locked_keys);
        let mut txn = MvccTxn::new(self.engine.as_ref(), start_ts);
        for w in writes {
            try!(txn.prewrite(w, &primary));
        }
        try!(txn.submit());
        // TODO(disksing): rollback if error occurs
        Ok(locked_keys)
    }

    pub fn commit(&self, keys: Vec<Key>, start_ts: u64, commit_ts: u64) -> Result<()> {
        let _guard = self.shard_mutex.lock(&keys);
        let mut txn = MvccTxn::new(self.engine.as_ref(), start_ts);
        for k in keys {
            try!(txn.commit(&k, commit_ts));
        }
        try!(txn.submit());
        Ok(())
    }
}
<commit_msg>storage/txn: add tests<commit_after>use util::codec::bytes;
use storage::{Key, RefKey, Value, KvPair, Mutation};
use storage::Engine;
use storage::mvcc::MvccTxn;
use super::shard_mutex::ShardMutex;
use super::{Error, Result};

pub struct TxnStore {
    engine: Box<Engine>,
    shard_mutex: ShardMutex,
}

const SHARD_MUTEX_SIZE: usize = 256;

impl TxnStore {
    pub fn new(engine: Box<Engine>) -> TxnStore {
        TxnStore {
            engine: engine,
            shard_mutex: ShardMutex::new(SHARD_MUTEX_SIZE),
        }
    }

    pub fn get(&self, key: RefKey, start_ts: u64) -> Result<Option<Value>> {
        let _guard = self.shard_mutex.lock(&[key]);
        let txn = MvccTxn::new(self.engine.as_ref(), start_ts);
        Ok(try!(txn.get(key)))
    }

    #[allow(dead_code)]
    pub fn batch_get(&self, keys: &[RefKey], start_ts: u64) -> Vec<Result<Option<Value>>> {
        let txn = MvccTxn::new(self.engine.as_ref(), start_ts);
        let mut results = Vec::<_>::with_capacity(keys.len());
        for k in keys {
            let _guard = self.shard_mutex.lock(keys);
            results.push(txn.get(k).map_err(Error::from));
        }
        results
    }

    pub fn scan(&self, key: RefKey, limit: usize, start_ts: u64) -> Result<Vec<Result<KvPair>>> {
        let mut results = vec![];
        let mut key = key.to_vec();
        let txn = MvccTxn::new(self.engine.as_ref(), start_ts);
        while results.len() < limit {
            let mut next_key = match try!(self.engine.seek(&bytes::encode_bytes(&key))) {
                Some((key, _)) => {
                    let (key, _) = try!(bytes::decode_bytes(&key));
                    key
                }
                None => break,
            };
            let _guard = self.shard_mutex.lock(&next_key);
            match txn.get(&next_key) {
                Ok(Some(value)) => results.push(Ok((next_key.clone(), value))),
                Ok(None) => {}
                Err(e) => results.push(Err(Error::from(e))),
            };
            next_key.push(b'\0');
            key = next_key;
        }
        Ok(results)
    }

    pub fn prewrite(&self, writes: Vec<Mutation>, primary: Key, start_ts: u64) -> Result<Vec<Key>> {
        let locked_keys: Vec<Key> = writes.iter().map(|x| x.key().to_owned()).collect();

        let _guard = self.shard_mutex.lock(&locked_keys);
        let mut txn = MvccTxn::new(self.engine.as_ref(), start_ts);
        for w in writes {
            try!(txn.prewrite(w, &primary));
        }
        try!(txn.submit());
        Ok(locked_keys)
    }

    pub fn commit(&self, keys: Vec<Key>, start_ts: u64, commit_ts: u64) -> Result<()> {
        let _guard = self.shard_mutex.lock(&keys);
        let mut txn = MvccTxn::new(self.engine.as_ref(), start_ts);
        for k in keys {
            try!(txn.commit(&k, commit_ts));
        }
        try!(txn.submit());
        Ok(())
    }

    #[allow(dead_code)]
    pub fn rollback(&self, keys: Vec<Key>, start_ts: u64) -> Result<()> {
        let _guard = self.shard_mutex.lock(&keys);
        let mut txn = MvccTxn::new(self.engine.as_ref(), start_ts);
        for k in keys {
            try!(txn.rollback(&k));
        }
        try!(txn.submit());
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use storage::{Mutation, Key, KvPair};
    use storage::engine::{self, Dsn};

    trait TxnStoreAssert {
        fn get_none(&self, key: &[u8], ts: u64);
        fn get_err(&self, key: &[u8], ts: u64);
        fn get_ok(&self, key: &[u8], ts: u64, expect: &[u8]);
        fn put_ok(&self, key: &[u8], value: &[u8], start_ts: u64, commit_ts: u64);
        fn delete_ok(&self, key: &[u8], start_ts: u64, commit_ts: u64);
        fn scan_ok(&self,
                   start_key: &[u8],
                   limit: usize,
                   ts: u64,
                   expect: Vec<Option<(&'static [u8], &'static [u8])>>);
        fn prewrite_ok(&self, mutations: Vec<Mutation>, start_ts: u64);
        fn prewrite_err(&self, mutations: Vec<Mutation>, start_ts: u64);
        fn commit_ok(&self, keys: Vec<&[u8]>, start_ts: u64, commit_ts: u64);
        fn commit_err(&self, keys: Vec<&[u8]>, start_ts: u64, commit_ts: u64);
        fn rollback_ok(&self, keys: Vec<&[u8]>, start_ts: u64);
        fn rollback_err(&self, keys: Vec<&[u8]>, start_ts: u64);
    }

    impl TxnStoreAssert for TxnStore {
        fn get_none(&self, key: &[u8], ts: u64) {
            assert_eq!(self.get(key, ts).unwrap(), None);
        }
        fn get_err(&self, key: &[u8], ts: u64) {
            assert!(self.get(key, ts).is_err());
        }
        fn get_ok(&self, key: &[u8], ts: u64, expect: &[u8]) {
            assert_eq!(self.get(key, ts).unwrap().unwrap(), expect);
        }
        fn put_ok(&self, key: &[u8], value: &[u8], start_ts: u64, commit_ts: u64) {
            self.prewrite(vec![Mutation::Put((key.to_vec(), value.to_vec()))],
                          key.to_vec(),
                          start_ts)
                .unwrap();
            self.commit(vec![key.to_vec()], start_ts, commit_ts).unwrap();
        }
        fn delete_ok(&self, key: &[u8], start_ts: u64, commit_ts: u64) {
            self.prewrite(vec![Mutation::Delete(key.to_vec())], key.to_vec(), start_ts).unwrap();
            self.commit(vec![key.to_vec()], start_ts, commit_ts).unwrap();
        }
        fn scan_ok(&self,
                   start_key: &[u8],
                   limit: usize,
                   ts: u64,
                   expect: Vec<Option<(&'static [u8], &'static [u8])>>) {

            let result = self.scan(start_key, limit, ts).unwrap();
            let result: Vec<Option<KvPair>> = result.into_iter()
                                                    .map(Result::ok)
                                                    .collect();
            let expect: Vec<Option<KvPair>> = expect.into_iter()
                                                    .map(|x| {
                                                        x.map(|(k, v)| (k.to_vec(), v.to_vec()))
                                                    })
                                                    .collect();
            assert_eq!(result, expect);
        }
        fn prewrite_ok(&self, mutations: Vec<Mutation>, start_ts: u64) {
            self.prewrite(mutations, vec![], start_ts).unwrap();
        }
        fn prewrite_err(&self, mutations: Vec<Mutation>, start_ts: u64) {
            assert!(self.prewrite(mutations, vec![], start_ts).is_err());
        }
        fn commit_ok(&self, keys: Vec<&[u8]>, start_ts: u64, commit_ts: u64) {
            let keys: Vec<Key> = keys.iter().map(|x| x.to_vec()).collect();
            self.commit(keys, start_ts, commit_ts).unwrap();
        }
        fn commit_err(&self, keys: Vec<&[u8]>, start_ts: u64, commit_ts: u64) {
            let keys: Vec<Key> = keys.iter().map(|x| x.to_vec()).collect();
            assert!(self.commit(keys, start_ts, commit_ts).is_err());
        }
        fn rollback_ok(&self, keys: Vec<&[u8]>, start_ts: u64) {
            let keys: Vec<Key> = keys.iter().map(|x| x.to_vec()).collect();
            self.rollback(keys, start_ts).unwrap();
        }
        fn rollback_err(&self, keys: Vec<&[u8]>, start_ts: u64) {
            let keys: Vec<Key> = keys.iter().map(|x| x.to_vec()).collect();
            assert!(self.rollback(keys, start_ts).is_err());
        }
    }

    #[test]
    fn test_txn_store_get() {
        let engine = engine::new_engine(Dsn::Memory).unwrap();
        let store = TxnStore::new(engine);

        // not exist
        store.get_none(b"x", 10);
        // after put
        store.put_ok(b"x", b"x", 5, 10);
        store.get_none(b"x", 9);
        store.get_ok(b"x", 10, b"x");
        store.get_ok(b"x", 11, b"x");
    }

    #[test]
    fn test_txn_store_delete() {
        let engine = engine::new_engine(Dsn::Memory).unwrap();
        let store = TxnStore::new(engine);

        store.put_ok(b"x", b"x5-10", 5, 10);
        store.delete_ok(b"x", 15, 20);
        store.get_none(b"x", 5);
        store.get_none(b"x", 9);
        store.get_ok(b"x", 10, b"x5-10");
        store.get_ok(b"x", 19, b"x5-10");
        store.get_none(b"x", 20);
        store.get_none(b"x", 21);
    }

    #[test]
    fn test_txn_store_scan() {
        let engine = engine::new_engine(Dsn::Memory).unwrap();
        let store = TxnStore::new(engine);

        // ver10: A(10) - B(_) - C(10) - D(_) - E(10)
        store.put_ok(b"A", b"A10", 5, 10);
        store.put_ok(b"C", b"C10", 5, 10);
        store.put_ok(b"E", b"E10", 5, 10);

        let check_v10 = || {
            store.scan_ok(b"", 0, 10, vec![]);
            store.scan_ok(b"", 1, 10, vec![Some((b"A", b"A10"))]);
            store.scan_ok(b"", 2, 10, vec![Some((b"A", b"A10")), Some((b"C", b"C10"))]);
            store.scan_ok(b"",
                          3,
                          10,
                          vec![Some((b"A", b"A10")), Some((b"C", b"C10")), Some((b"E", b"E10"))]);
            store.scan_ok(b"",
                          4,
                          10,
                          vec![Some((b"A", b"A10")), Some((b"C", b"C10")), Some((b"E", b"E10"))]);
            store.scan_ok(b"A",
                          3,
                          10,
                          vec![Some((b"A", b"A10")), Some((b"C", b"C10")), Some((b"E", b"E10"))]);
            store.scan_ok(b"A\x00",
                          3,
                          10,
                          vec![Some((b"C", b"C10")), Some((b"E", b"E10"))]);

            store.scan_ok(b"C",
                          4,
                          10,
                          vec![Some((b"C", b"C10")), Some((b"E", b"E10"))]);
            store.scan_ok(b"F", 1, 10, vec![]);

        };
        check_v10();

        // ver20: A(10) - B(20) - C(10) - D(20) - E(10)
        store.put_ok(b"B", b"B20", 15, 20);
        store.put_ok(b"D", b"D20", 15, 20);

        let check_v20 = || {
            store.scan_ok(b"",
                          5,
                          20,
                          vec![Some((b"A", b"A10")),
                               Some((b"B", b"B20")),
                               Some((b"C", b"C10")),
                               Some((b"D", b"D20")),
                               Some((b"E", b"E10"))]);
            store.scan_ok(b"C",
                          5,
                          20,
                          vec![Some((b"C", b"C10")), Some((b"D", b"D20")), Some((b"E", b"E10"))]);
            store.scan_ok(b"D\x00", 1, 20, vec![Some((b"E", b"E10"))]);
        };
        check_v10();
        check_v20();

        // ver30: A(_) - B(20) - C(10) - D(_) - E(10)
        store.delete_ok(b"A", 25, 30);
        store.delete_ok(b"D", 25, 30);

        let check_v30 = || {
            store.scan_ok(b"",
                          5,
                          30,
                          vec![Some((b"B", b"B20")), Some((b"C", b"C10")), Some((b"E", b"E10"))]);
            store.scan_ok(b"A", 1, 30, vec![Some((b"B", b"B20"))]);
            store.scan_ok(b"C\x00", 5, 30, vec![Some((b"E", b"E10"))]);
        };
        check_v10();
        check_v20();
        check_v30();

        // ver40: A(_) - B(_) - C(40) - D(40) - E(10)
        store.delete_ok(b"B", 35, 40);
        store.put_ok(b"C", b"C40", 35, 40);
        store.put_ok(b"D", b"D40", 35, 40);

        let check_v40 = || {
            store.scan_ok(b"",
                          5,
                          40,
                          vec![Some((b"C", b"C40")), Some((b"D", b"D40")), Some((b"E", b"E10"))]);
            store.scan_ok(b"",
                          5,
                          100,
                          vec![Some((b"C", b"C40")), Some((b"D", b"D40")), Some((b"E", b"E10"))]);
        };
        check_v10();
        check_v20();
        check_v30();
        check_v40();
    }

    use std::sync::{Arc, Mutex};
    use std::thread;

    struct Oracle {
        ts: Mutex<u64>,
    }

    impl Oracle {
        fn new() -> Oracle {
            Oracle { ts: Mutex::new(0) }
        }

        fn get_ts(&self) -> u64 {
            let mut ts = self.ts.lock().unwrap();
            *ts += 1;
            *ts
        }
    }

    const INC_MAX_RETRY: usize = 100;

    fn inc(store: &TxnStore, oracle: &Oracle, key: &[u8]) -> Result<i32, ()> {
        for _ in 0..INC_MAX_RETRY {
            let start_ts = oracle.get_ts();
            let number: i32 = match store.get(key, start_ts) {
                Ok(Some(x)) => String::from_utf8(x).unwrap().parse().unwrap(),
                Ok(None) => 0,
                Err(_) => continue,
            };
            let next = number + 1;
            if let Err(_) = store.prewrite(vec![Mutation::Put((key.to_vec(),
                                                               next.to_string().into_bytes()))],
                                           key.to_vec(),
                                           start_ts) {
                continue;
            }
            let commit_ts = oracle.get_ts();
            if let Err(_) = store.commit(vec![key.to_vec()], start_ts, commit_ts) {
                continue;
            }
            return Ok(number);
        }
        Err(())
    }

    #[test]
    fn test_isolation_inc() {
        const THREAD_NUM: usize = 4;
        const INC_PER_THREAD: usize = 100;

        let engine = engine::new_engine(Dsn::Memory).unwrap();
        let store = Arc::new(TxnStore::new(engine));
        let oracle = Arc::new(Oracle::new());
        let punch_card = Arc::new(Mutex::new(vec![false; THREAD_NUM * INC_PER_THREAD]));

        let mut threads = vec![];
        for _ in 0..THREAD_NUM {
            let (punch_card, store, oracle) = (punch_card.clone(), store.clone(), oracle.clone());
            threads.push(thread::spawn(move || {
                for _ in 0..INC_PER_THREAD {
                    let number = inc(&store, &oracle, b"key").unwrap() as usize;
                    let mut punch = punch_card.lock().unwrap();
                    assert_eq!(punch[number], false);
                    punch[number] = true;
                }
            }));
        }
        for t in threads {
            t.join().unwrap();
        }
        assert_eq!(inc(&store, &oracle, b"key").unwrap() as usize,
                   THREAD_NUM * INC_PER_THREAD);
    }

    fn format_key(x: usize) -> Key {
        format!("k{}", x).into_bytes()
    }

    fn inc_multi(store: &TxnStore, oracle: &Oracle, n: usize) -> bool {
        'retry: for _ in 0..INC_MAX_RETRY {
            let start_ts = oracle.get_ts();
            let keys: Vec<Key> = (0..n).map(format_key).collect();
            let mut mutations = vec![];
            for key in keys.iter().take(n) {
                let number = match store.get(&key, start_ts) {
                    Ok(Some(n)) => String::from_utf8(n).unwrap().parse().unwrap(),
                    Ok(None) => 0,
                    Err(_) => continue 'retry,
                };
                let next = number + 1;
                mutations.push(Mutation::Put((key.clone(), next.to_string().into_bytes())));
            }
            if let Err(_) = store.prewrite(mutations, format_key(0), start_ts) {
                continue;
            }
            let commit_ts = oracle.get_ts();
            if let Err(_) = store.commit(keys, start_ts, commit_ts) {
                continue;
            }
            return true;
        }
        false
    }

    #[test]
    fn test_isolation_multi_inc() {
        const THREAD_NUM: usize = 4;
        const KEY_NUM: usize = 4;
        const INC_PER_THREAD: usize = 100;

        let engine = engine::new_engine(Dsn::Memory).unwrap();
        let store = Arc::new(TxnStore::new(engine));
        let oracle = Arc::new(Oracle::new());

        let mut threads = vec![];
        for _ in 0..THREAD_NUM {
            let (store, oracle) = (store.clone(), oracle.clone());
            threads.push(thread::spawn(move || {
                for _ in 0..INC_PER_THREAD {
                    assert!(inc_multi(&store, &oracle, KEY_NUM));
                }
            }));
        }
        for t in threads {
            t.join().unwrap();
        }
        for n in 0..KEY_NUM {
            assert_eq!(inc(&store, &oracle, &format_key(n)).unwrap() as usize,
                       THREAD_NUM * INC_PER_THREAD);
        }
    }

    use test::Bencher;
    use tempdir::TempDir;

    #[bench]
    fn bench_txn_store_memory_inc(b: &mut Bencher) {
        let engine = engine::new_engine(Dsn::Memory).unwrap();
        let store = TxnStore::new(engine);
        let oracle = Oracle::new();

        b.iter(|| {
            inc(&store, &oracle, b"key").unwrap();
        });
    }

    #[bench]
    fn bench_txn_store_memory_inc_x100(b: &mut Bencher) {
        let engine = engine::new_engine(Dsn::Memory).unwrap();
        let store = TxnStore::new(engine);
        let oracle = Oracle::new();

        b.iter(|| {
            inc_multi(&store, &oracle, 100);
        });
    }

    #[bench]
    fn bench_txn_store_rocksdb_inc(b: &mut Bencher) {
        let dir = TempDir::new("rocksdb_test").unwrap();
        let engine = engine::new_engine(Dsn::RocksDBPath(dir.path().to_str().unwrap())).unwrap();
        let store = TxnStore::new(engine);
        let oracle = Oracle::new();

        b.iter(|| {
            inc(&store, &oracle, b"key").unwrap();
        });
    }

    #[bench]
    fn bench_txn_store_rocksdb_inc_x100(b: &mut Bencher) {
        let dir = TempDir::new("rocksdb_test").unwrap();
        let engine = engine::new_engine(Dsn::RocksDBPath(dir.path().to_str().unwrap())).unwrap();
        let store = TxnStore::new(engine);
        let oracle = Oracle::new();

        b.iter(|| {
            inc_multi(&store, &oracle, 100);
        });
    }
}
<|endoftext|>


<commit_before>// Copyright 2012 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.

//! ncurses-compatible database discovery
//!
//! Does not support hashed database, only filesystem!

use std::env;
use std::fs;
use std::path::PathBuf;

/// Return path to database entry for `term`
pub fn get_dbpath_for_term(term: &str) -> Option<PathBuf> {
    let mut dirs_to_search = Vec::new();
    let first_char = match term.chars().next() {
        Some(c) => c,
        None => return None,
    };

    // Find search directory
    match env::var_os("TERMINFO") {
        Some(dir) => dirs_to_search.push(PathBuf::from(dir)),
        None => {
            if let Some(mut homedir) = env::home_dir() {
                // ncurses compatibility;
                homedir.push(".terminfo");
                dirs_to_search.push(homedir)
            }
            match env::var("TERMINFO_DIRS") {
                Ok(dirs) => {
                    for i in dirs.split(':') {
                        if i == "" {
                            dirs_to_search.push(PathBuf::from("/usr/share/terminfo"));
                        } else {
                            dirs_to_search.push(PathBuf::from(i));
                        }
                    }
                }
                // Found nothing in TERMINFO_DIRS, use the default paths:
                // According to  /etc/terminfo/README, after looking at
                // ~/.terminfo, ncurses will search /etc/terminfo, then
                // /lib/terminfo, and eventually /usr/share/terminfo.
                Err(..) => {
                    dirs_to_search.push(PathBuf::from("/etc/terminfo"));
                    dirs_to_search.push(PathBuf::from("/lib/terminfo"));
                    dirs_to_search.push(PathBuf::from("/usr/share/terminfo"));
                }
            }
        }
    };

    // Look for the terminal in all of the search directories
    for mut p in dirs_to_search {
        if fs::metadata(&p).is_ok() {
            p.push(&first_char.to_string());
            p.push(&term);
            if fs::metadata(&p).is_ok() {
                return Some(p);
            }
            p.pop();
            p.pop();

            // on some installations the dir is named after the hex of the char
            // (e.g. OS X)
            p.push(&format!("{:x}", first_char as usize));
            p.push(term);
            if fs::metadata(&p).is_ok() {
                return Some(p);
            }
        }
    }
    None
}
<commit_msg>Fix terminfo database search path<commit_after>// Copyright 2012 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.

//! ncurses-compatible database discovery
//!
//! Does not support hashed database, only filesystem!

use std::env;
use std::fs;
use std::path::PathBuf;

/// Return path to database entry for `term`
pub fn get_dbpath_for_term(term: &str) -> Option<PathBuf> {
    let mut dirs_to_search = Vec::new();
    let first_char = match term.chars().next() {
        Some(c) => c,
        None => return None,
    };

    // Find search directory
    if let Some(dir) = env::var_os("TERMINFO") {
        dirs_to_search.push(PathBuf::from(dir));
    }

    if let Ok(dirs) = match env::var("TERMINFO_DIRS") {
        for i in dirs.split(':') {
            if i == "" {
                dirs_to_search.push(PathBuf::from("/usr/share/terminfo"));
            } else {
                dirs_to_search.push(PathBuf::from(i));
            }
        }
    } else {
        // Found nothing in TERMINFO_DIRS, use the default paths:
        // According to  /etc/terminfo/README, after looking at
        // ~/.terminfo, ncurses will search /etc/terminfo, then
        // /lib/terminfo, and eventually /usr/share/terminfo.
        // On Haiku the database can be found at /boot/system/data/terminfo
        if let Some(mut homedir) = env::home_dir() {
            homedir.push(".terminfo");
            dirs_to_search.push(homedir)
        }

        dirs_to_search.push(PathBuf::from("/etc/terminfo"));
        dirs_to_search.push(PathBuf::from("/lib/terminfo"));
        dirs_to_search.push(PathBuf::from("/usr/share/terminfo"));
        dirs_to_search.push(PathBuf::from("/boot/system/data/terminfo"));
    }

    // Look for the terminal in all of the search directories
    for mut p in dirs_to_search {
        if fs::metadata(&p).is_ok() {
            p.push(&first_char.to_string());
            p.push(&term);
            if fs::metadata(&p).is_ok() {
                return Some(p);
            }
            p.pop();
            p.pop();

            // on some installations the dir is named after the hex of the char
            // (e.g. OS X)
            p.push(&format!("{:x}", first_char as usize));
            p.push(term);
            if fs::metadata(&p).is_ok() {
                return Some(p);
            }
        }
    }
    None
}
<|endoftext|>


<commit_before>use clap::{error::ErrorKind, ArgAction, Command};

fn common() -> Command {
    Command::new("foo")
}

fn with_version() -> Command {
    common().version("3.0")
}

fn with_long_version() -> Command {
    common().long_version("3.0 (abcdefg)")
}

fn with_subcommand() -> Command {
    with_version().subcommand(Command::new("bar").subcommand(Command::new("baz")))
}

#[test]
fn no_version_flag_short() {
    let res = common().try_get_matches_from("foo -V".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), clap::error::ErrorKind::UnknownArgument);
}

#[test]
fn no_version_flag_long() {
    let res = common().try_get_matches_from("foo --version".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), clap::error::ErrorKind::UnknownArgument);
}

#[test]
fn version_flag_from_version_short() {
    let res = with_version().try_get_matches_from("foo -V".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::DisplayVersion);
    assert_eq!(err.to_string(), "foo 3.0\n");
}

#[test]
fn version_flag_from_version_long() {
    let res = with_version().try_get_matches_from("foo --version".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::DisplayVersion);
    assert_eq!(err.to_string(), "foo 3.0\n");
}

#[test]
fn version_flag_from_long_version_short() {
    let res = with_long_version().try_get_matches_from("foo -V".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::DisplayVersion);
    assert_eq!(err.to_string(), "foo 3.0 (abcdefg)\n");
}

#[test]
fn version_flag_from_long_version_long() {
    let res = with_long_version().try_get_matches_from("foo --version".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::DisplayVersion);
    assert_eq!(err.to_string(), "foo 3.0 (abcdefg)\n");
}

#[test]
#[cfg(debug_assertions)]
#[should_panic = "Command foo: Long option names must be unique for each argument, but '--version' is in use by both 'ver' and 'version' (call `cmd.disable_version_flag(true)` to remove the auto-generated `--version`)"]
fn override_version_long_with_user_flag() {
    with_version()
        .arg(
            clap::Arg::new("ver")
                .long("version")
                .action(ArgAction::SetTrue),
        )
        .debug_assert();
}

#[test]
#[cfg(debug_assertions)]
#[should_panic = "Command foo: Short option names must be unique for each argument, but '-V' is in use by both 'ver' and 'version' (call `cmd.disable_version_flag(true)` to remove the auto-generated `--version`)"]
fn override_version_short_with_user_flag() {
    with_version()
        .arg(clap::Arg::new("ver").short('V').action(ArgAction::SetTrue))
        .debug_assert();
}

#[test]
fn no_propagation_by_default_long() {
    // Version Flag should not be propagated to subcommands
    let res = with_subcommand().try_get_matches_from("foo bar --version".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::UnknownArgument);
}

#[test]
fn no_propagation_by_default_short() {
    let res = with_subcommand().try_get_matches_from("foo bar -V".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::UnknownArgument);
}

#[test]
fn propagate_version_long() {
    let res = with_subcommand()
        .propagate_version(true)
        .try_get_matches_from("foo bar --version".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::DisplayVersion);
}

#[test]
fn propagate_version_short() {
    let res = with_subcommand()
        .propagate_version(true)
        .try_get_matches_from("foo bar -V".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::DisplayVersion);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "`ArgAction::Version` used without providing Command::version or Command::long_version"]
fn version_required() {
    let _res = common()
        .arg(clap::arg!(--version).action(ArgAction::Version))
        .try_get_matches_from("foo -z".split(' '));
}

#[test]
fn mut_arg_version_no_auto_version() {
    let res = common()
        .mut_arg("version", |v| v.short('z').action(ArgAction::SetTrue))
        .try_get_matches_from("foo -z".split(' '));

    assert!(res.is_ok(), "{}", res.unwrap_err());
    assert_eq!(res.unwrap().get_one::<bool>("version").copied(), Some(true));
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "No version information via Command::version or Command::long_version to propagate"]
fn propagate_version_no_version_info() {
    let _res = common()
        .propagate_version(true)
        .subcommand(Command::new("bar"))
        .try_get_matches_from("foo".split(' '));
}
<commit_msg>test(help): Verify how version interacts<commit_after>use clap::{error::ErrorKind, ArgAction, Command};

use crate::utils;

fn common() -> Command {
    Command::new("foo")
}

fn with_version() -> Command {
    common().version("3.0")
}

fn with_long_version() -> Command {
    common().long_version("3.0 (abcdefg)")
}

fn with_both() -> Command {
    common().version("3.0").long_version("3.0 (abcdefg)")
}

fn with_subcommand() -> Command {
    with_version().subcommand(Command::new("bar").subcommand(Command::new("baz")))
}

#[test]
fn version_short_flag_no_version() {
    let res = common().try_get_matches_from("foo -V".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), clap::error::ErrorKind::UnknownArgument);
}

#[test]
fn version_long_flag_no_version() {
    let res = common().try_get_matches_from("foo --version".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), clap::error::ErrorKind::UnknownArgument);
}

#[test]
fn version_short_flag_with_version() {
    let res = with_version().try_get_matches_from("foo -V".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::DisplayVersion);
    assert_eq!(err.to_string(), "foo 3.0\n");
}

#[test]
fn version_long_flag_with_version() {
    let res = with_version().try_get_matches_from("foo --version".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::DisplayVersion);
    assert_eq!(err.to_string(), "foo 3.0\n");
}

#[test]
fn version_short_flag_with_long_version() {
    let res = with_long_version().try_get_matches_from("foo -V".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::DisplayVersion);
    assert_eq!(err.to_string(), "foo 3.0 (abcdefg)\n");
}

#[test]
fn version_long_flag_with_long_version() {
    let res = with_long_version().try_get_matches_from("foo --version".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::DisplayVersion);
    assert_eq!(err.to_string(), "foo 3.0 (abcdefg)\n");
}

#[test]
fn version_short_flag_with_both() {
    let res = with_both().try_get_matches_from("foo -V".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::DisplayVersion);
    assert_eq!(err.to_string(), "foo 3.0\n");
}

#[test]
fn version_long_flag_with_both() {
    let res = with_both().try_get_matches_from("foo --version".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::DisplayVersion);
    assert_eq!(err.to_string(), "foo 3.0 (abcdefg)\n");
}

#[test]
fn help_short_flag_no_version() {
    static EXPECTED: &str = "\
foo

Usage:
    foo

Options:
    -h, --help    Print help information
";
    let cmd = common();
    utils::assert_output(cmd, "foo -h", EXPECTED, false);
}

#[test]
fn help_long_flag_no_version() {
    static EXPECTED: &str = "\
foo

Usage:
    foo

Options:
    -h, --help    Print help information
";
    let cmd = common();
    utils::assert_output(cmd, "foo --help", EXPECTED, false);
}

#[test]
fn help_short_flag_with_version() {
    static EXPECTED: &str = "\
foo 3.0

Usage:
    foo

Options:
    -h, --help       Print help information
    -V, --version    Print version information
";
    let cmd = with_version();
    utils::assert_output(cmd, "foo -h", EXPECTED, false);
}

#[test]
fn help_long_flag_with_version() {
    static EXPECTED: &str = "\
foo 3.0

Usage:
    foo

Options:
    -h, --help       Print help information
    -V, --version    Print version information
";
    let cmd = with_version();
    utils::assert_output(cmd, "foo --help", EXPECTED, false);
}

#[test]
fn help_short_flag_with_long_version() {
    static EXPECTED: &str = "\
foo 3.0 (abcdefg)

Usage:
    foo

Options:
    -h, --help       Print help information
    -V, --version    Print version information
";
    let cmd = with_long_version();
    utils::assert_output(cmd, "foo -h", EXPECTED, false);
}

#[test]
fn help_long_flag_with_long_version() {
    static EXPECTED: &str = "\
foo 3.0 (abcdefg)

Usage:
    foo

Options:
    -h, --help       Print help information
    -V, --version    Print version information
";
    let cmd = with_long_version();
    utils::assert_output(cmd, "foo --help", EXPECTED, false);
}

#[test]
fn help_short_flag_with_both() {
    static EXPECTED: &str = "\
foo 3.0

Usage:
    foo

Options:
    -h, --help       Print help information
    -V, --version    Print version information
";
    let cmd = with_both();
    utils::assert_output(cmd, "foo -h", EXPECTED, false);
}

#[test]
fn help_long_flag_with_both() {
    static EXPECTED: &str = "\
foo 3.0

Usage:
    foo

Options:
    -h, --help       Print help information
    -V, --version    Print version information
";
    let cmd = with_both();
    utils::assert_output(cmd, "foo --help", EXPECTED, false);
}

#[test]
#[cfg(debug_assertions)]
#[should_panic = "Command foo: Long option names must be unique for each argument, but '--version' is in use by both 'ver' and 'version' (call `cmd.disable_version_flag(true)` to remove the auto-generated `--version`)"]
fn override_version_long_with_user_flag() {
    with_version()
        .arg(
            clap::Arg::new("ver")
                .long("version")
                .action(ArgAction::SetTrue),
        )
        .debug_assert();
}

#[test]
#[cfg(debug_assertions)]
#[should_panic = "Command foo: Short option names must be unique for each argument, but '-V' is in use by both 'ver' and 'version' (call `cmd.disable_version_flag(true)` to remove the auto-generated `--version`)"]
fn override_version_short_with_user_flag() {
    with_version()
        .arg(clap::Arg::new("ver").short('V').action(ArgAction::SetTrue))
        .debug_assert();
}

#[test]
fn no_propagation_by_default_long() {
    // Version Flag should not be propagated to subcommands
    let res = with_subcommand().try_get_matches_from("foo bar --version".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::UnknownArgument);
}

#[test]
fn no_propagation_by_default_short() {
    let res = with_subcommand().try_get_matches_from("foo bar -V".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::UnknownArgument);
}

#[test]
fn propagate_version_long() {
    let res = with_subcommand()
        .propagate_version(true)
        .try_get_matches_from("foo bar --version".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::DisplayVersion);
}

#[test]
fn propagate_version_short() {
    let res = with_subcommand()
        .propagate_version(true)
        .try_get_matches_from("foo bar -V".split(' '));

    assert!(res.is_err());
    let err = res.unwrap_err();
    assert_eq!(err.kind(), ErrorKind::DisplayVersion);
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "`ArgAction::Version` used without providing Command::version or Command::long_version"]
fn version_required() {
    let _res = common()
        .arg(clap::arg!(--version).action(ArgAction::Version))
        .try_get_matches_from("foo -z".split(' '));
}

#[test]
fn mut_arg_version_no_auto_version() {
    let res = common()
        .mut_arg("version", |v| v.short('z').action(ArgAction::SetTrue))
        .try_get_matches_from("foo -z".split(' '));

    assert!(res.is_ok(), "{}", res.unwrap_err());
    assert_eq!(res.unwrap().get_one::<bool>("version").copied(), Some(true));
}

#[cfg(debug_assertions)]
#[test]
#[should_panic = "No version information via Command::version or Command::long_version to propagate"]
fn propagate_version_no_version_info() {
    let _res = common()
        .propagate_version(true)
        .subcommand(Command::new("bar"))
        .try_get_matches_from("foo".split(' '));
}
<|endoftext|>


<commit_before>#![cfg(all(feature = "full", feature = "fold"))]

//! The tests in this module do the following:
//!
//! 1. Parse a given expression in both `syn` and `syntex`.
//! 2. Fold over the expression adding brackets around each subexpression (with
//!    some complications - see the `syn_brackets` and `syntex_brackets`
//!    methods).
//! 3. Serialize the `syn` expression back into a string, and re-parse it with
//!    `syntex`.
//! 4. Respan all of the expressions, replacing the spans with the default spans.
//! 5. Compare the expressions with one another, if they are not equal fail.

#[macro_use]
extern crate quote;
extern crate syn;
extern crate synom;
extern crate syntex_pos;
extern crate syntex_syntax;
extern crate walkdir;
extern crate proc_macro2;

use syntex_syntax::ast;
use syntex_syntax::ptr::P;

use common::{respan, parse};

#[allow(dead_code)]
#[macro_use]
mod common;

/// Test some pre-set expressions chosen by us.
#[test]
fn test_simple_precedence() {
    const EXPRS: &[&str] = &[
        "1 + 2 * 3 + 4",
        "1 + 2 * ( 3 + 4 )",
        "{ for i in r { } *some_ptr += 1; }",
        "{ loop { break 5; } }",
        "{ if true { () }.mthd() }",
    ];

    let mut failed = 0;

    for input in EXPRS {
        let expr = if let Some(expr) = parse::syn_expr(input) {
            expr
        } else {
            failed += 1;
            continue;
        };

        let pf = match test_expressions(vec![expr]) {
            (1, 0) => "passed",
            (0, 1) => {
                failed += 1;
                "failed"
            }
            _ => unreachable!(),
        };
        errorf!("=== {}: {}\n", input, pf);
    }

    if failed > 0 {
        panic!("Failed {} tests", failed);
    }
}

/// Test expressions from rustc, like in `test_round_trip`.
#[test]
fn test_rustc_precedence() {
    use walkdir::{WalkDir, WalkDirIterator};
    use std::fs::File;
    use std::io::Read;

    common::check_min_stack();
    common::clone_rust();
    let abort_after = common::abort_after();
    if abort_after == 0 {
        panic!("Skipping all precedence tests");
    }

    let mut passed = 0;
    let mut failed = 0;

    let walk = WalkDir::new("tests/rust").sort_by(|a, b| a.cmp(b));
    for entry in walk.into_iter().filter_entry(common::base_dir_filter) {
        let entry = entry.unwrap();

        let path = entry.path();
        if path.is_dir() {
            continue;
        }

        let mut file = File::open(path).unwrap();
        let mut content = String::new();
        file.read_to_string(&mut content).unwrap();

        let (l_passed, l_failed) = match syn::parse_file(&content) {
            Ok(file) => {
                let exprs = collect_exprs(file);
                test_expressions(exprs)
            }
            Err(msg) => {
                errorf!("syn failed to parse\n{:?}\n", msg);
                failed += 1;
                (0, 1)
            }
        };

        passed += l_passed;
        failed += l_failed;

        errorf!("=== {}: {} passed | {} failed\n", path.display(), l_passed, l_failed);

        if failed >= abort_after {
            errorf!("Aborting Immediately due to ABORT_AFTER_FAILURE\n");
            break;
        }
    }

    errorf!("\n===== Precedence Test Results =====\n");
    errorf!("{} passed | {} failed\n", passed, failed);

    if failed > 0 {
        panic!("{} failures", failed);
    }
}

fn test_expressions(exprs: Vec<syn::Expr>) -> (u32, u32) {
    let mut passed = 0;
    let mut failed = 0;

    for expr in exprs {
        let raw = quote!(#expr).to_string();

        let syntex_ast = if let Some(e) = syntex_parse_and_rewrite(&raw) {
            e
        } else {
            failed += 1;
            errorf!("\nFAIL - syntex failed to parse raw\n");
            continue;
        };

        let syn_expr = syn_brackets(expr);
        let syn_ast = if let Some(e) = parse::syntex_expr(&quote!(#syn_expr).to_string()) {
            e
        } else {
            failed += 1;
            errorf!("\nFAIL - syntex failed to parse bracketed\n");
            continue;
        };

        let syn_ast = respan::respan_expr(syn_ast);
        let syntex_ast = respan::respan_expr(syntex_ast);

        if syn_ast == syntex_ast {
            passed += 1;
        } else {
            failed += 1;
            errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, syntex_ast);
        }
    }

    (passed, failed)
}

fn syntex_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
    parse::syntex_expr(input).and_then(|e| syntex_brackets(e))
}

/// Wrap every expression which is not already wrapped in parens with parens, to
/// reveal the precidence of the parsed expressions, and produce a stringified form
/// of the resulting expression.
///
/// This method operates on syntex objects.
fn syntex_brackets(syntex_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
    use syntex_syntax::ast::{Expr, ExprKind, Mac, Stmt, StmtKind, Pat, Ty, Field};
    use syntex_syntax::fold::{self, Folder};
    use syntex_syntax::util::ThinVec;
    use syntex_syntax::util::small_vector::SmallVector;
    use syntex_syntax::ext::quote::rt::DUMMY_SP;
    use syntex_syntax::codemap;

    fn expr(node: ExprKind) -> P<Expr> {
        P(Expr {
            id: ast::DUMMY_NODE_ID,
            node: node,
            span: DUMMY_SP,
            attrs: ThinVec::new(),
        })
    }

    struct BracketsFolder {
        failed: bool,
    };
    impl Folder for BracketsFolder {
        fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
            e.map(|e| {
                Expr {
                    node: match e.node {
                        ExprKind::Paren(inner) => {
                            ExprKind::Paren(inner.map(|e| {
                                fold::noop_fold_expr(e, self)
                            }))
                        }
                        ExprKind::If(..) |
                        ExprKind::Block(..) |
                        ExprKind::IfLet(..) => {
                            return fold::noop_fold_expr(e, self);
                        }
                        node => {
                            ExprKind::Paren(expr(node).map(|e| {
                                fold::noop_fold_expr(e, self)
                            }))
                        }
                    },
                    ..e
                }
            })
        }

        fn fold_field(&mut self, f: Field) -> Field {
            Field {
                ident: codemap::respan(f.ident.span, self.fold_ident(f.ident.node)),
                expr: if f.is_shorthand {
                    f.expr.map(|e| fold::noop_fold_expr(e, self))
                } else {
                    self.fold_expr(f.expr)
                },
                span: self.new_span(f.span),
                is_shorthand: f.is_shorthand,
                attrs: fold::fold_thin_attrs(f.attrs, self),
            }
        }

        // We don't want to look at expressions that might appear in patterns or
        // types yet. We'll look into comparing those in the future. For now
        // focus on expressions appearing in other places.
        fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> {
            pat
        }

        fn fold_ty(&mut self, ty: P<Ty>) -> P<Ty> {
            ty
        }

        fn fold_stmt(&mut self, stmt: Stmt) -> SmallVector<Stmt> {
            let node = match stmt.node {
                // Don't wrap toplevel expressions in statements.
                StmtKind::Expr(e) => {
                    StmtKind::Expr(e.map(|e| fold::noop_fold_expr(e, self)))
                }
                StmtKind::Semi(e) => {
                    StmtKind::Semi(e.map(|e| fold::noop_fold_expr(e, self)))
                }
                s => s,
            };

            SmallVector::one(Stmt {
                node: node,
                ..stmt
            })
        }

        fn fold_mac(&mut self, mac: Mac) -> Mac {
            // By default when folding over macros, syntex panics. This is
            // because it's usually not what you want, you want to run after
            // macro expansion. We do want to do that (syn doesn't do macro
            // expansion), so we implement fold_mac to just return the macro
            // unchanged.
            mac
        }
    }

    let mut folder = BracketsFolder {
        failed: false,
    };
    let e = folder.fold_expr(syntex_expr);
    if folder.failed {
        None
    } else {
        Some(e)
    }
}

/// Wrap every expression which is not already wrapped in parens with parens, to
/// reveal the precidence of the parsed expressions, and produce a stringified form
/// of the resulting expression.
fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
    use syn::*;
    use syn::fold::*;

    fn paren(folder: &mut BracketsFolder, node: ExprKind) -> ExprKind {
        ExprKind::Paren(ExprParen {
            expr: Box::new(noop_fold_expr(folder, Expr {
                node: node,
                attrs: vec![],
            })),
            paren_token: tokens::Paren::default(),
        })
    }

    struct BracketsFolder;
    impl Folder for BracketsFolder {
        fn fold_expr(&mut self, expr: Expr) -> Expr {
            let kind = match expr.node {
                ExprKind::Group(_) => unreachable!(),
                ExprKind::Paren(p) => paren(self, p.expr.node),
                ExprKind::If(..) |
                ExprKind::Block(..) |
                ExprKind::IfLet(..) => {
                    return noop_fold_expr(self, expr);
                }
                node => paren(self, node),
            };

            Expr {
                node: kind,
                ..expr
            }
        }

        fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
            match stmt {
                // Don't wrap toplevel expressions in statements.
                Stmt::Expr(e) => {
                    Stmt::Expr(Box::new(noop_fold_expr(self, *e)))
                }
                Stmt::Semi(e, semi) => {
                    Stmt::Semi(Box::new(noop_fold_expr(self, *e)), semi)
                }
                s => s,
            }
        }

        // We don't want to look at expressions that might appear in patterns or
        // types yet. We'll look into comparing those in the future. For now
        // focus on expressions appearing in other places.
        fn fold_pat(&mut self, pat: Pat) -> Pat {
            pat
        }

        fn fold_ty(&mut self, ty: Ty) -> Ty {
            ty
        }
    }

    let mut folder = BracketsFolder;
    folder.fold_expr(syn_expr)
}

/// Walk through a crate collecting all expressions we can find in it.
fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
    use synom::delimited::Delimited;
    use syn::*;
    use syn::fold::*;

    struct CollectExprsFolder(Vec<Expr>);
    impl Folder for CollectExprsFolder {
        fn fold_expr(&mut self, expr: Expr) -> Expr {
            self.0.push(expr);

            Expr {
                node: ExprKind::Tup(ExprTup {
                    args: Delimited::new(),
                    paren_token: tokens::Paren::default(),
                    lone_comma: None
                }),
                attrs: vec![],
            }
        }
    }

    let mut folder = CollectExprsFolder(vec![]);
    folder.fold_file(file);
    folder.0
}
<commit_msg>More unused crates<commit_after>#![cfg(all(feature = "full", feature = "fold"))]

//! The tests in this module do the following:
//!
//! 1. Parse a given expression in both `syn` and `syntex`.
//! 2. Fold over the expression adding brackets around each subexpression (with
//!    some complications - see the `syn_brackets` and `syntex_brackets`
//!    methods).
//! 3. Serialize the `syn` expression back into a string, and re-parse it with
//!    `syntex`.
//! 4. Respan all of the expressions, replacing the spans with the default spans.
//! 5. Compare the expressions with one another, if they are not equal fail.

#[macro_use]
extern crate quote;
extern crate syn;
extern crate synom;
extern crate syntex_syntax;
extern crate walkdir;

use syntex_syntax::ast;
use syntex_syntax::ptr::P;

use common::{respan, parse};

#[allow(dead_code)]
#[macro_use]
mod common;

/// Test some pre-set expressions chosen by us.
#[test]
fn test_simple_precedence() {
    const EXPRS: &[&str] = &[
        "1 + 2 * 3 + 4",
        "1 + 2 * ( 3 + 4 )",
        "{ for i in r { } *some_ptr += 1; }",
        "{ loop { break 5; } }",
        "{ if true { () }.mthd() }",
    ];

    let mut failed = 0;

    for input in EXPRS {
        let expr = if let Some(expr) = parse::syn_expr(input) {
            expr
        } else {
            failed += 1;
            continue;
        };

        let pf = match test_expressions(vec![expr]) {
            (1, 0) => "passed",
            (0, 1) => {
                failed += 1;
                "failed"
            }
            _ => unreachable!(),
        };
        errorf!("=== {}: {}\n", input, pf);
    }

    if failed > 0 {
        panic!("Failed {} tests", failed);
    }
}

/// Test expressions from rustc, like in `test_round_trip`.
#[test]
fn test_rustc_precedence() {
    use walkdir::{WalkDir, WalkDirIterator};
    use std::fs::File;
    use std::io::Read;

    common::check_min_stack();
    common::clone_rust();
    let abort_after = common::abort_after();
    if abort_after == 0 {
        panic!("Skipping all precedence tests");
    }

    let mut passed = 0;
    let mut failed = 0;

    let walk = WalkDir::new("tests/rust").sort_by(|a, b| a.cmp(b));
    for entry in walk.into_iter().filter_entry(common::base_dir_filter) {
        let entry = entry.unwrap();

        let path = entry.path();
        if path.is_dir() {
            continue;
        }

        let mut file = File::open(path).unwrap();
        let mut content = String::new();
        file.read_to_string(&mut content).unwrap();

        let (l_passed, l_failed) = match syn::parse_file(&content) {
            Ok(file) => {
                let exprs = collect_exprs(file);
                test_expressions(exprs)
            }
            Err(msg) => {
                errorf!("syn failed to parse\n{:?}\n", msg);
                failed += 1;
                (0, 1)
            }
        };

        passed += l_passed;
        failed += l_failed;

        errorf!("=== {}: {} passed | {} failed\n", path.display(), l_passed, l_failed);

        if failed >= abort_after {
            errorf!("Aborting Immediately due to ABORT_AFTER_FAILURE\n");
            break;
        }
    }

    errorf!("\n===== Precedence Test Results =====\n");
    errorf!("{} passed | {} failed\n", passed, failed);

    if failed > 0 {
        panic!("{} failures", failed);
    }
}

fn test_expressions(exprs: Vec<syn::Expr>) -> (u32, u32) {
    let mut passed = 0;
    let mut failed = 0;

    for expr in exprs {
        let raw = quote!(#expr).to_string();

        let syntex_ast = if let Some(e) = syntex_parse_and_rewrite(&raw) {
            e
        } else {
            failed += 1;
            errorf!("\nFAIL - syntex failed to parse raw\n");
            continue;
        };

        let syn_expr = syn_brackets(expr);
        let syn_ast = if let Some(e) = parse::syntex_expr(&quote!(#syn_expr).to_string()) {
            e
        } else {
            failed += 1;
            errorf!("\nFAIL - syntex failed to parse bracketed\n");
            continue;
        };

        let syn_ast = respan::respan_expr(syn_ast);
        let syntex_ast = respan::respan_expr(syntex_ast);

        if syn_ast == syntex_ast {
            passed += 1;
        } else {
            failed += 1;
            errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, syntex_ast);
        }
    }

    (passed, failed)
}

fn syntex_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
    parse::syntex_expr(input).and_then(|e| syntex_brackets(e))
}

/// Wrap every expression which is not already wrapped in parens with parens, to
/// reveal the precidence of the parsed expressions, and produce a stringified form
/// of the resulting expression.
///
/// This method operates on syntex objects.
fn syntex_brackets(syntex_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
    use syntex_syntax::ast::{Expr, ExprKind, Mac, Stmt, StmtKind, Pat, Ty, Field};
    use syntex_syntax::fold::{self, Folder};
    use syntex_syntax::util::ThinVec;
    use syntex_syntax::util::small_vector::SmallVector;
    use syntex_syntax::ext::quote::rt::DUMMY_SP;
    use syntex_syntax::codemap;

    fn expr(node: ExprKind) -> P<Expr> {
        P(Expr {
            id: ast::DUMMY_NODE_ID,
            node: node,
            span: DUMMY_SP,
            attrs: ThinVec::new(),
        })
    }

    struct BracketsFolder {
        failed: bool,
    };
    impl Folder for BracketsFolder {
        fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
            e.map(|e| {
                Expr {
                    node: match e.node {
                        ExprKind::Paren(inner) => {
                            ExprKind::Paren(inner.map(|e| {
                                fold::noop_fold_expr(e, self)
                            }))
                        }
                        ExprKind::If(..) |
                        ExprKind::Block(..) |
                        ExprKind::IfLet(..) => {
                            return fold::noop_fold_expr(e, self);
                        }
                        node => {
                            ExprKind::Paren(expr(node).map(|e| {
                                fold::noop_fold_expr(e, self)
                            }))
                        }
                    },
                    ..e
                }
            })
        }

        fn fold_field(&mut self, f: Field) -> Field {
            Field {
                ident: codemap::respan(f.ident.span, self.fold_ident(f.ident.node)),
                expr: if f.is_shorthand {
                    f.expr.map(|e| fold::noop_fold_expr(e, self))
                } else {
                    self.fold_expr(f.expr)
                },
                span: self.new_span(f.span),
                is_shorthand: f.is_shorthand,
                attrs: fold::fold_thin_attrs(f.attrs, self),
            }
        }

        // We don't want to look at expressions that might appear in patterns or
        // types yet. We'll look into comparing those in the future. For now
        // focus on expressions appearing in other places.
        fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> {
            pat
        }

        fn fold_ty(&mut self, ty: P<Ty>) -> P<Ty> {
            ty
        }

        fn fold_stmt(&mut self, stmt: Stmt) -> SmallVector<Stmt> {
            let node = match stmt.node {
                // Don't wrap toplevel expressions in statements.
                StmtKind::Expr(e) => {
                    StmtKind::Expr(e.map(|e| fold::noop_fold_expr(e, self)))
                }
                StmtKind::Semi(e) => {
                    StmtKind::Semi(e.map(|e| fold::noop_fold_expr(e, self)))
                }
                s => s,
            };

            SmallVector::one(Stmt {
                node: node,
                ..stmt
            })
        }

        fn fold_mac(&mut self, mac: Mac) -> Mac {
            // By default when folding over macros, syntex panics. This is
            // because it's usually not what you want, you want to run after
            // macro expansion. We do want to do that (syn doesn't do macro
            // expansion), so we implement fold_mac to just return the macro
            // unchanged.
            mac
        }
    }

    let mut folder = BracketsFolder {
        failed: false,
    };
    let e = folder.fold_expr(syntex_expr);
    if folder.failed {
        None
    } else {
        Some(e)
    }
}

/// Wrap every expression which is not already wrapped in parens with parens, to
/// reveal the precidence of the parsed expressions, and produce a stringified form
/// of the resulting expression.
fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
    use syn::*;
    use syn::fold::*;

    fn paren(folder: &mut BracketsFolder, node: ExprKind) -> ExprKind {
        ExprKind::Paren(ExprParen {
            expr: Box::new(noop_fold_expr(folder, Expr {
                node: node,
                attrs: vec![],
            })),
            paren_token: tokens::Paren::default(),
        })
    }

    struct BracketsFolder;
    impl Folder for BracketsFolder {
        fn fold_expr(&mut self, expr: Expr) -> Expr {
            let kind = match expr.node {
                ExprKind::Group(_) => unreachable!(),
                ExprKind::Paren(p) => paren(self, p.expr.node),
                ExprKind::If(..) |
                ExprKind::Block(..) |
                ExprKind::IfLet(..) => {
                    return noop_fold_expr(self, expr);
                }
                node => paren(self, node),
            };

            Expr {
                node: kind,
                ..expr
            }
        }

        fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
            match stmt {
                // Don't wrap toplevel expressions in statements.
                Stmt::Expr(e) => {
                    Stmt::Expr(Box::new(noop_fold_expr(self, *e)))
                }
                Stmt::Semi(e, semi) => {
                    Stmt::Semi(Box::new(noop_fold_expr(self, *e)), semi)
                }
                s => s,
            }
        }

        // We don't want to look at expressions that might appear in patterns or
        // types yet. We'll look into comparing those in the future. For now
        // focus on expressions appearing in other places.
        fn fold_pat(&mut self, pat: Pat) -> Pat {
            pat
        }

        fn fold_ty(&mut self, ty: Ty) -> Ty {
            ty
        }
    }

    let mut folder = BracketsFolder;
    folder.fold_expr(syn_expr)
}

/// Walk through a crate collecting all expressions we can find in it.
fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
    use synom::delimited::Delimited;
    use syn::*;
    use syn::fold::*;

    struct CollectExprsFolder(Vec<Expr>);
    impl Folder for CollectExprsFolder {
        fn fold_expr(&mut self, expr: Expr) -> Expr {
            self.0.push(expr);

            Expr {
                node: ExprKind::Tup(ExprTup {
                    args: Delimited::new(),
                    paren_token: tokens::Paren::default(),
                    lone_comma: None
                }),
                attrs: vec![],
            }
        }
    }

    let mut folder = CollectExprsFolder(vec![]);
    folder.fold_file(file);
    folder.0
}
<|endoftext|>


<commit_before>use std::fs;
use std::io::{self, Read};
use std::net::TcpListener;
use std::process::{Stdio, Child};
use std::thread;
use std::time::Duration;

use cargotest::support::project;

#[cfg(unix)]
fn enabled() -> bool {
    true
}

// On Windows support for these tests is only enabled through the usage of job
// objects. Support for nested job objects, however, was added in recent-ish
// versions of Windows, so this test may not always be able to succeed.
//
// As a result, we try to add ourselves to a job object here
// can succeed or not.
#[cfg(windows)]
fn enabled() -> bool {
    use winapi::um::{handleapi, jobapi, jobapi2, processthreadsapi};

    unsafe {
        // If we're not currently in a job, then we can definitely run these
        // tests.
        let me = processthreadsapi::GetCurrentProcess();
        let mut ret = 0;
        let r = jobapi::IsProcessInJob(me, 0 as *mut _, &mut ret);
        assert_ne!(r, 0);
        if ret == ::winapi::shared::minwindef::FALSE {
            return true
        }

        // If we are in a job, then we can run these tests if we can be added to
        // a nested job (as we're going to create a nested job no matter what as
        // part of these tests.
        //
        // If we can't be added to a nested job, then these tests will
        // definitely fail, and there's not much we can do about that.
        let job = jobapi2::CreateJobObjectW(0 as *mut _, 0 as *const _);
        assert!(!job.is_null());
        let r = jobapi2::AssignProcessToJobObject(job, me);
        handleapi::CloseHandle(job);
        r != 0
    }
}

#[test]
#[ignore]
fn ctrl_c_kills_everyone() {
    if !enabled() {
        return
    }

    let listener = TcpListener::bind("127.0.0.1:0").unwrap();
    let addr = listener.local_addr().unwrap();

    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []
            build = "build.rs"
        "#)
        .file("src/lib.rs", "")
        .file("build.rs", &format!(r#"
            use std::net::TcpStream;
            use std::io::Read;

            fn main() {{
                let mut socket = TcpStream::connect("{}").unwrap();
                let _ = socket.read(&mut [0; 10]);
                panic!("that read should never return");
            }}
        "#, addr))
        .build();

    let mut cargo = p.cargo("build").build_command();
    cargo.stdin(Stdio::piped())
         .stdout(Stdio::piped())
         .stderr(Stdio::piped())
         .env("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE", "1");
    let mut child = cargo.spawn().unwrap();

    let mut sock = listener.accept().unwrap().0;
    ctrl_c(&mut child);

    assert!(!child.wait().unwrap().success());
    match sock.read(&mut [0; 10]) {
        Ok(n) => assert_eq!(n, 0),
        Err(e) => assert_eq!(e.kind(), io::ErrorKind::ConnectionReset),
    }

    // Ok so what we just did was spawn cargo that spawned a build script, then
    // we killed cargo in hopes of it killing the build script as well. If all
    // went well the build script is now dead. On Windows, however, this is
    // enforced with job objects which means that it may actually be in the
    // *process* of being torn down at this point.
    //
    // Now on Windows we can't completely remove a file until all handles to it
    // have been closed. Including those that represent running processes. So if
    // we were to return here then there may still be an open reference to some
    // file in the build directory. What we want to actually do is wait for the
    // build script to *complete* exit. Take care of that by blowing away the
    // build directory here, and panicking if we eventually spin too long
    // without being able to.
    for i in 0..10 {
        match fs::remove_dir_all(&p.root().join("target")) {
            Ok(()) => return,
            Err(e) => println!("attempt {}: {}", i, e),
        }
        thread::sleep(Duration::from_millis(100));
    }

    panic!("couldn't remove build directory after a few tries, seems like \
            we won't be able to!");
}

#[cfg(unix)]
fn ctrl_c(child: &mut Child) {
    use libc;

    let r = unsafe { libc::kill(-(child.id() as i32), libc::SIGINT) };
    if r < 0 {
        panic!("failed to kill: {}", io::Error::last_os_error());
    }
}

#[cfg(windows)]
fn ctrl_c(child: &mut Child) {
    child.kill().unwrap();
}
<commit_msg>Unignore test which now passes<commit_after>use std::fs;
use std::io::{self, Read};
use std::net::TcpListener;
use std::process::{Stdio, Child};
use std::thread;
use std::time::Duration;

use cargotest::support::project;

#[cfg(unix)]
fn enabled() -> bool {
    true
}

// On Windows support for these tests is only enabled through the usage of job
// objects. Support for nested job objects, however, was added in recent-ish
// versions of Windows, so this test may not always be able to succeed.
//
// As a result, we try to add ourselves to a job object here
// can succeed or not.
#[cfg(windows)]
fn enabled() -> bool {
    use winapi::um::{handleapi, jobapi, jobapi2, processthreadsapi};

    unsafe {
        // If we're not currently in a job, then we can definitely run these
        // tests.
        let me = processthreadsapi::GetCurrentProcess();
        let mut ret = 0;
        let r = jobapi::IsProcessInJob(me, 0 as *mut _, &mut ret);
        assert_ne!(r, 0);
        if ret == ::winapi::shared::minwindef::FALSE {
            return true
        }

        // If we are in a job, then we can run these tests if we can be added to
        // a nested job (as we're going to create a nested job no matter what as
        // part of these tests.
        //
        // If we can't be added to a nested job, then these tests will
        // definitely fail, and there's not much we can do about that.
        let job = jobapi2::CreateJobObjectW(0 as *mut _, 0 as *const _);
        assert!(!job.is_null());
        let r = jobapi2::AssignProcessToJobObject(job, me);
        handleapi::CloseHandle(job);
        r != 0
    }
}

#[test]
fn ctrl_c_kills_everyone() {
    if !enabled() {
        return
    }

    let listener = TcpListener::bind("127.0.0.1:0").unwrap();
    let addr = listener.local_addr().unwrap();

    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []
            build = "build.rs"
        "#)
        .file("src/lib.rs", "")
        .file("build.rs", &format!(r#"
            use std::net::TcpStream;
            use std::io::Read;

            fn main() {{
                let mut socket = TcpStream::connect("{}").unwrap();
                let _ = socket.read(&mut [0; 10]);
                panic!("that read should never return");
            }}
        "#, addr))
        .build();

    let mut cargo = p.cargo("build").build_command();
    cargo.stdin(Stdio::piped())
         .stdout(Stdio::piped())
         .stderr(Stdio::piped())
         .env("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE", "1");
    let mut child = cargo.spawn().unwrap();

    let mut sock = listener.accept().unwrap().0;
    ctrl_c(&mut child);

    assert!(!child.wait().unwrap().success());
    match sock.read(&mut [0; 10]) {
        Ok(n) => assert_eq!(n, 0),
        Err(e) => assert_eq!(e.kind(), io::ErrorKind::ConnectionReset),
    }

    // Ok so what we just did was spawn cargo that spawned a build script, then
    // we killed cargo in hopes of it killing the build script as well. If all
    // went well the build script is now dead. On Windows, however, this is
    // enforced with job objects which means that it may actually be in the
    // *process* of being torn down at this point.
    //
    // Now on Windows we can't completely remove a file until all handles to it
    // have been closed. Including those that represent running processes. So if
    // we were to return here then there may still be an open reference to some
    // file in the build directory. What we want to actually do is wait for the
    // build script to *complete* exit. Take care of that by blowing away the
    // build directory here, and panicking if we eventually spin too long
    // without being able to.
    for i in 0..10 {
        match fs::remove_dir_all(&p.root().join("target")) {
            Ok(()) => return,
            Err(e) => println!("attempt {}: {}", i, e),
        }
        thread::sleep(Duration::from_millis(100));
    }

    panic!("couldn't remove build directory after a few tries, seems like \
            we won't be able to!");
}

#[cfg(unix)]
fn ctrl_c(child: &mut Child) {
    use libc;

    let r = unsafe { libc::kill(-(child.id() as i32), libc::SIGINT) };
    if r < 0 {
        panic!("failed to kill: {}", io::Error::last_os_error());
    }
}

#[cfg(windows)]
fn ctrl_c(child: &mut Child) {
    child.kill().unwrap();
}
<|endoftext|>