<commit_before>use std::old_io::process::ProcessExit::*;
use std::os::unix::prelude::*;

use std::os;
use std::cmp::*;

use types::WashArgs::*;

use util::*;
use constants::*;
use types::*;
use env::*;

fn source_func(args:&WashArgs, env:&mut WashEnv) -> Result<WashArgs, String> {
    let name = match args {
        &Empty => return Err("No arguments given".to_string()),
        &Long(_) => return Err("Can only source flat names".to_string()),
        &Flat(ref v) => v.clone()
    };
    env.load_script(Path::new(name), &args.slice(1, -1))
}

fn cd_func(args:&WashArgs, _:&mut WashEnv) -> Result<WashArgs, String> {
    let newp = {
        if args.len() == 0 {
            expand_path(Path::new("~"))
        } else if &args.get_flat(0)[..min(args.get_flat(0).len(), 2)] == "./" {
            // this specifical case can't be put through expand_path
            Path::new(&args.get_flat(0))
        } else {
            expand_path(Path::new(&args.get_flat(0)))
        }
    };
    match os::change_dir(&newp) {
        Err(e) => return Err(e.desc.to_string()),
        Ok(_) => return Ok(Empty)
    }
}

fn outs_func(args:&WashArgs, env:&mut WashEnv) -> Result<WashArgs, String> {
    let mut argf = args.flatten();
    env.outs(argf.as_slice());
    if argf.pop() != Some(NL) {
        env.outc(NL);
    }
    return Ok(Empty);
}

fn job_args(args:&WashArgs, env:&mut WashEnv) -> Result<(Option<Fd>, Option<Fd>, Option<Fd>,
                                                         String, Vec<String>), String> {
    // turns arguments into file descriptor options, command name and args
    // utility function because job_func and run_func use this same code
    let (mut stdin, mut stdout, mut stderr) = (None, None, None);
    let mut argc = args.flatten_vec();
    let mut name;
    loop {
        // check for stop
        try!(env.func_stop());
        // fail if only file descriptors given
        if argc.is_empty() {
            return Err("Don't know what to do with file descriptors".to_string());
        }
        // pop out arguments from the front until no more file descriptors remain
        name = argc.remove(0);
        if !FD_REGEX.is_match(name.as_slice()) {
            // we've reached the end of file descriptors
            break;
        } else {
            let caps = FD_REGEX.captures(name.as_slice()).unwrap();
            match str_to_usize(caps.at(2).unwrap()) {
                None => return Err(format!("{} could not be turned into usize", caps.at(2).unwrap())),
                Some(fd) => match caps.at(1).unwrap() {
                    path if path.is_empty() || path == "in" =>
                        // default to stdin
                        stdin = Some(fd as Fd),
                    path if path == "out" =>
                        stdout = Some(fd as Fd),
                    path if path == "err" =>
                        stderr = Some(fd as Fd),
                    _ => return Err(format!("{} is not a valid standard output", caps.at(1).unwrap()))
                }
            }
        }
    }
    return Ok((stdin, stdout, stderr, name, argc));
}

pub fn job_func(args:&WashArgs, env:&mut WashEnv) -> Result<WashArgs, String> {
    let id;
    if args.is_empty() || args.len() < 1 {
        return Err("No arguments given".to_string());
    } else if args.is_flat() {
        // easy case, no arguments to the function
        id = try!(env.run_job(&args.flatten(), &vec![]));
    } else if !args.get(0).is_flat() {
        return Err("Can only run flat names".to_string());
    } else if !FD_REGEX.is_match(args.get(0).flatten().as_slice()) {
        // easy case, no file descriptors given
        let args_slice = args.slice(1, -1);
        id = try!(env.run_job(&args.get(0).flatten(), &args_slice.flatten_vec()));
    } else {
        // hard case, file descriptors given
        let (stdin, stdout, stderr, name, argc) = try!(job_args(args, env));
        id = try!(env.run_job_fd(stdin, stdout, stderr, &name, &argc));
    }
    return Ok(Flat(format!("{}", id)));
}

pub fn job_output_func(args:&WashArgs, env:&mut WashEnv) -> Result<WashArgs, String> {
    let arg = args.get(0);
    if !arg.is_flat() {
        return Err("Give a job number".to_string());
    }
    let id = match str_to_usize(arg.flatten().as_slice()) {
        None => return Err(format!("Couldn't turn {} into a job number", arg.flatten())),
        Some(num) => num
    };
    let out = try!(env.job_output(&id));
    if !out.status.success() {
        return Err(String::from_utf8_lossy(out.error.as_slice()).into_owned());
    } else {
        return Ok(Flat(String::from_utf8_lossy(out.output.as_slice()).into_owned()));
    }
}

pub fn directed_job_func(args:&WashArgs, env:&mut WashEnv) -> Result<WashArgs, String> {
    return job_output_func(&try!(job_func(args, env)), env);
}

pub fn run_func(args:&WashArgs, env:&mut WashEnv) -> Result<WashArgs, String> {
    let out;
    if args.is_empty() || args.len() < 1 {
        return Err("No arguments given".to_string());
    } else if args.is_flat() {
        // easy case, no arguments to the function
        out = try!(env.run_command(&args.flatten(), &vec![]));
    } else if !args.get(0).is_flat() {
        return Err("Can only run flat names".to_string());
    } else if !FD_REGEX.is_match(args.get(0).flatten().as_slice()) {
        // easy case, no file descriptors given
        let args_slice = args.slice(1, -1);
        out = try!(env.run_command(&args.get(0).flatten(), &args_slice.flatten_vec()));
    } else {
        // hard case, file descriptors given
        let (stdin, stdout, stderr, name, argc) = try!(job_args(args, env));
        out = try!(env.run_command_fd(stdin, stdout, stderr, &name, &argc));
    }
    return match out {
        ExitSignal(sig) => {
            return Ok(Long(vec![Flat("signal".to_string()),
                                Flat(format!("{}", sig))]));
        },
        ExitStatus(status) => {
            return Ok(Long(vec![Flat("status".to_string()),
                                Flat(format!("{}", status))]));
        }
    }
}

pub fn jobs_func(_:&WashArgs, env:&mut WashEnv) -> Result<WashArgs, String> {
    let jobs = env.get_jobs();
    if jobs.len() == 0 {
        return Err("No jobs".to_string());
    } else {
        return Ok(env.get_jobs());
    }
}

pub fn fg_func(_:&WashArgs, env:&mut WashEnv) -> Result<WashArgs, String> {
    let mut id = try!(env.front_job());
    while !env.has_job(&id) {
        id = try!(env.front_job());
    }
    let name = try!(env.get_job(&id)).command.clone();
    env.outf(format_args!("Returning to: {}\n", name));
    try!(env.restart_job(&id));
    let out = try!(env.wait_job(&id));
    try!(env.remove_if_done(&id));
    return describe_process_output(&match out {
        ExitSignal(sig) => Long(vec![Flat("signal".to_string()),
                                     Flat(format!("{}", sig))]),
        ExitStatus(status) => Long(vec![Flat("status".to_string()),
                                        Flat(format!("{}", status))])
    }, env)
}

pub fn get_func(args:&WashArgs, env:&mut WashEnv) -> Result<WashArgs, String> {
    if args.len() < 1 {
        return Err("No variable name given".to_string());
    }
    let name = match args.get(0) {
        ref v if !v.is_flat() => {
            return Err("Variable names can only be flat".to_string());
        },
        ref v if !EQ_VAR_REGEX.is_match(v.flatten().as_slice()) => {
            return Err("Varibale names cannot contain whitespace, quotes, or parentheses".to_string());
        }
        v => v.flatten()
    };
    if EQ_PATH_REGEX.is_match(name.as_slice()) {
        let caps = EQ_PATH_REGEX.captures(name.as_slice()).unwrap();
        let path = caps.at(1).unwrap().to_string();
        let name = caps.at(2).unwrap().to_string();
        if path.is_empty() {
            // use default path
            // this can be used to set a variable
            // with a name containing a colon
            return env.getv(&name);
        } else {
            return env.getvp(&name, &path);
        }
    } else {
        return env.getv(&name);
    }
}

pub fn setp_func(args:&WashArgs, env:&mut WashEnv) -> Result<WashArgs, String> {
    if args.len() < 1 {
        env.variables = String::new();
        return Ok(Flat(String::new()));
    } else {
        let path = match args.get(0) {
            ref v if !v.is_flat() => {
                return Err("Variable paths can only be flat".to_string());
            }
            v => v.flatten()
        };
        if path == "env".to_string() {
            return Err("Cannot set variable path to environment variables".to_string());
        } else if path == "pipe".to_string() {
            return Err("Cannot set variable path to job pipes".to_string());
        } else {
            env.variables = path.clone();
            return Ok(Flat(path));
        }
    }
}

pub fn describe_process_output(args:&WashArgs, _:&mut WashEnv) -> Result<WashArgs, String> {
    let argv = args.flatten_vec();
    if args.is_empty() {
        return Err("Command Failed".to_string());
    } else if argv.len() < 2 {
        return Err(format!("Command failed: {}", args.flatten()));
    } else if argv == vec!["signal", "19"] || // SIGSTOP
        argv == vec!["signal", "20"] { // SIGTSTP
            return Err(format!("Command stopped"));
    } else if argv != vec!["status", "0"] {
        return Err(format!("Command failed with {} {}", argv[0], argv[1]));
    } else {
        return Ok(Empty);
    }
}

fn builtins_func(_:&WashArgs, _:&mut WashEnv) -> Result<WashArgs, String> {
    return Ok(Long(vec![
        Flat("$".to_string()),
        Flat("builtins".to_string()),
        Flat("cd".to_string()),
        Flat("fg".to_string()),
        Flat("get".to_string()),
        Flat("jobs".to_string()),
        Flat("run".to_string()),
        Flat("setp".to_string()),
        Flat("source".to_string())]));
}

pub fn load_builtins(env:&mut WashEnv) -> Result<WashArgs, String> {
    // functions
    try!(env.insf("source", source_func));
    try!(env.insf("cd", cd_func));
    try!(env.insf("builtins", builtins_func));
    try!(env.insf("outs", outs_func));
    try!(env.insf("$", directed_job_func));
    try!(env.insf("run", run_func));
    try!(env.insf("get", get_func));
    try!(env.insf("setp", setp_func));
    try!(env.insf("jobs", jobs_func));
    try!(env.insf("job", job_func));
    try!(env.insf("fg", fg_func));

    // commands that aren't really meant to be called by users
    try!(env.insf("describe_process_output", describe_process_output));

    return Ok(Empty);
}
<commit_msg>Implement returning to specific jobs<commit_after>use std::old_io::process::ProcessExit::*;
use std::os::unix::prelude::*;

use std::os;
use std::cmp::*;

use types::WashArgs::*;

use util::*;
use constants::*;
use types::*;
use env::*;

fn source_func(args:&WashArgs, env:&mut WashEnv) -> Result<WashArgs, String> {
    let name = match args {
        &Empty => return Err("No arguments given".to_string()),
        &Long(_) => return Err("Can only source flat names".to_string()),
        &Flat(ref v) => v.clone()
    };
    env.load_script(Path::new(name), &args.slice(1, -1))
}

fn cd_func(args:&WashArgs, _:&mut WashEnv) -> Result<WashArgs, String> {
    let newp = {
        if args.len() == 0 {
            expand_path(Path::new("~"))
        } else if &args.get_flat(0)[..min(args.get_flat(0).len(), 2)] == "./" {
            // this specifical case can't be put through expand_path
            Path::new(&args.get_flat(0))
        } else {
            expand_path(Path::new(&args.get_flat(0)))
        }
    };
    match os::change_dir(&newp) {
        Err(e) => return Err(e.desc.to_string()),
        Ok(_) => return Ok(Empty)
    }
}

fn outs_func(args:&WashArgs, env:&mut WashEnv) -> Result<WashArgs, String> {
    let mut argf = args.flatten();
    env.outs(argf.as_slice());
    if argf.pop() != Some(NL) {
        env.outc(NL);
    }
    return Ok(Empty);
}

fn job_args(args:&WashArgs, env:&mut WashEnv) -> Result<(Option<Fd>, Option<Fd>, Option<Fd>,
                                                         String, Vec<String>), String> {
    // turns arguments into file descriptor options, command name and args
    // utility function because job_func and run_func use this same code
    let (mut stdin, mut stdout, mut stderr) = (None, None, None);
    let mut argc = args.flatten_vec();
    let mut name;
    loop {
        // check for stop
        try!(env.func_stop());
        // fail if only file descriptors given
        if argc.is_empty() {
            return Err("Don't know what to do with file descriptors".to_string());
        }
        // pop out arguments from the front until no more file descriptors remain
        name = argc.remove(0);
        if !FD_REGEX.is_match(name.as_slice()) {
            // we've reached the end of file descriptors
            break;
        } else {
            let caps = FD_REGEX.captures(name.as_slice()).unwrap();
            match str_to_usize(caps.at(2).unwrap()) {
                None => return Err(format!("{} could not be turned into usize", caps.at(2).unwrap())),
                Some(fd) => match caps.at(1).unwrap() {
                    path if path.is_empty() || path == "in" =>
                        // default to stdin
                        stdin = Some(fd as Fd),
                    path if path == "out" =>
                        stdout = Some(fd as Fd),
                    path if path == "err" =>
                        stderr = Some(fd as Fd),
                    _ => return Err(format!("{} is not a valid standard output", caps.at(1).unwrap()))
                }
            }
        }
    }
    return Ok((stdin, stdout, stderr, name, argc));
}

pub fn job_func(args:&WashArgs, env:&mut WashEnv) -> Result<WashArgs, String> {
    let id;
    if args.is_empty() || args.len() < 1 {
        return Err("No arguments given".to_string());
    } else if args.is_flat() {
        // easy case, no arguments to the function
        id = try!(env.run_job(&args.flatten(), &vec![]));
    } else if !args.get(0).is_flat() {
        return Err("Can only run flat names".to_string());
    } else if !FD_REGEX.is_match(args.get(0).flatten().as_slice()) {
        // easy case, no file descriptors given
        let args_slice = args.slice(1, -1);
        id = try!(env.run_job(&args.get(0).flatten(), &args_slice.flatten_vec()));
    } else {
        // hard case, file descriptors given
        let (stdin, stdout, stderr, name, argc) = try!(job_args(args, env));
        id = try!(env.run_job_fd(stdin, stdout, stderr, &name, &argc));
    }
    return Ok(Flat(format!("{}", id)));
}

pub fn job_output_func(args:&WashArgs, env:&mut WashEnv) -> Result<WashArgs, String> {
    let arg = args.get(0);
    if !arg.is_flat() {
        return Err("Give a job number".to_string());
    }
    let id = match str_to_usize(arg.flatten().as_slice()) {
        None => return Err(format!("Couldn't turn {} into a job number", arg.flatten())),
        Some(num) => num
    };
    let out = try!(env.job_output(&id));
    if !out.status.success() {
        return Err(String::from_utf8_lossy(out.error.as_slice()).into_owned());
    } else {
        return Ok(Flat(String::from_utf8_lossy(out.output.as_slice()).into_owned()));
    }
}

pub fn directed_job_func(args:&WashArgs, env:&mut WashEnv) -> Result<WashArgs, String> {
    return job_output_func(&try!(job_func(args, env)), env);
}

pub fn run_func(args:&WashArgs, env:&mut WashEnv) -> Result<WashArgs, String> {
    let out;
    if args.is_empty() || args.len() < 1 {
        return Err("No arguments given".to_string());
    } else if args.is_flat() {
        // easy case, no arguments to the function
        out = try!(env.run_command(&args.flatten(), &vec![]));
    } else if !args.get(0).is_flat() {
        return Err("Can only run flat names".to_string());
    } else if !FD_REGEX.is_match(args.get(0).flatten().as_slice()) {
        // easy case, no file descriptors given
        let args_slice = args.slice(1, -1);
        out = try!(env.run_command(&args.get(0).flatten(), &args_slice.flatten_vec()));
    } else {
        // hard case, file descriptors given
        let (stdin, stdout, stderr, name, argc) = try!(job_args(args, env));
        out = try!(env.run_command_fd(stdin, stdout, stderr, &name, &argc));
    }
    return match out {
        ExitSignal(sig) => {
            return Ok(Long(vec![Flat("signal".to_string()),
                                Flat(format!("{}", sig))]));
        },
        ExitStatus(status) => {
            return Ok(Long(vec![Flat("status".to_string()),
                                Flat(format!("{}", status))]));
        }
    }
}

pub fn jobs_func(_:&WashArgs, env:&mut WashEnv) -> Result<WashArgs, String> {
    let jobs = env.get_jobs();
    if jobs.len() == 0 {
        return Err("No jobs".to_string());
    } else {
        return Ok(env.get_jobs());
    }
}

pub fn fg_func(args:&WashArgs, env:&mut WashEnv) -> Result<WashArgs, String> {
    let mut id;
    if args.len() < 1 {
        id = try!(env.front_job());
        while !env.has_job(&id) {
            id = try!(env.front_job());
        }
    } else {
        id = match str_to_usize(args.get(0).flatten().as_slice()) {
            None => return Err(format!("Not given a job number")),
            Some(v) => v
        };
        if !env.has_job(&id) {
            return Err(format!("Job not found"));
        }
    }
    let name = try!(env.get_job(&id)).command.clone();
    env.outf(format_args!("Returning to: {}\n", name));
    try!(env.restart_job(&id));
    let out = try!(env.wait_job(&id));
    try!(env.remove_if_done(&id));
    return describe_process_output(&match out {
        ExitSignal(sig) => Long(vec![Flat("signal".to_string()),
                                     Flat(format!("{}", sig))]),
        ExitStatus(status) => Long(vec![Flat("status".to_string()),
                                        Flat(format!("{}", status))])
    }, env)
}

pub fn get_func(args:&WashArgs, env:&mut WashEnv) -> Result<WashArgs, String> {
    if args.len() < 1 {
        return Err("No variable name given".to_string());
    }
    let name = match args.get(0) {
        ref v if !v.is_flat() => {
            return Err("Variable names can only be flat".to_string());
        },
        ref v if !EQ_VAR_REGEX.is_match(v.flatten().as_slice()) => {
            return Err("Varibale names cannot contain whitespace, quotes, or parentheses".to_string());
        }
        v => v.flatten()
    };
    if EQ_PATH_REGEX.is_match(name.as_slice()) {
        let caps = EQ_PATH_REGEX.captures(name.as_slice()).unwrap();
        let path = caps.at(1).unwrap().to_string();
        let name = caps.at(2).unwrap().to_string();
        if path.is_empty() {
            // use default path
            // this can be used to set a variable
            // with a name containing a colon
            return env.getv(&name);
        } else {
            return env.getvp(&name, &path);
        }
    } else {
        return env.getv(&name);
    }
}

pub fn setp_func(args:&WashArgs, env:&mut WashEnv) -> Result<WashArgs, String> {
    if args.len() < 1 {
        env.variables = String::new();
        return Ok(Flat(String::new()));
    } else {
        let path = match args.get(0) {
            ref v if !v.is_flat() => {
                return Err("Variable paths can only be flat".to_string());
            }
            v => v.flatten()
        };
        if path == "env".to_string() {
            return Err("Cannot set variable path to environment variables".to_string());
        } else if path == "pipe".to_string() {
            return Err("Cannot set variable path to job pipes".to_string());
        } else {
            env.variables = path.clone();
            return Ok(Flat(path));
        }
    }
}

pub fn describe_process_output(args:&WashArgs, _:&mut WashEnv) -> Result<WashArgs, String> {
    let argv = args.flatten_vec();
    if args.is_empty() {
        return Err("Command Failed".to_string());
    } else if argv.len() < 2 {
        return Err(format!("Command failed: {}", args.flatten()));
    } else if argv == vec!["signal", "19"] || // SIGSTOP
        argv == vec!["signal", "20"] { // SIGTSTP
            return Err(format!("Command stopped"));
    } else if argv != vec!["status", "0"] {
        return Err(format!("Command failed with {} {}", argv[0], argv[1]));
    } else {
        return Ok(Empty);
    }
}

fn builtins_func(_:&WashArgs, _:&mut WashEnv) -> Result<WashArgs, String> {
    return Ok(Long(vec![
        Flat("$".to_string()),
        Flat("builtins".to_string()),
        Flat("cd".to_string()),
        Flat("fg".to_string()),
        Flat("get".to_string()),
        Flat("jobs".to_string()),
        Flat("run".to_string()),
        Flat("setp".to_string()),
        Flat("source".to_string())]));
}

pub fn load_builtins(env:&mut WashEnv) -> Result<WashArgs, String> {
    // functions
    try!(env.insf("source", source_func));
    try!(env.insf("cd", cd_func));
    try!(env.insf("builtins", builtins_func));
    try!(env.insf("outs", outs_func));
    try!(env.insf("$", directed_job_func));
    try!(env.insf("run", run_func));
    try!(env.insf("get", get_func));
    try!(env.insf("setp", setp_func));
    try!(env.insf("jobs", jobs_func));
    try!(env.insf("job", job_func));
    try!(env.insf("fg", fg_func));

    // commands that aren't really meant to be called by users
    try!(env.insf("describe_process_output", describe_process_output));

    return Ok(Empty);
}
<|endoftext|>


<commit_before>use std::iter::Peekable;
use std::num::ParseIntError;
use xml::reader::events::*;
use xml::reader::Events;
use hyper::client::response::*;
use std::collections::HashMap;

/// generic Error for XML parsing
#[derive(Debug)]
pub struct XmlParseError(pub String);

impl XmlParseError {
	pub fn new(msg: &str) -> XmlParseError {
		XmlParseError(msg.to_string())
	}
}

/// syntactic sugar for the XML event stack we pass around
pub type XmlStack<'a> = Peekable<Events<'a, Response>>;

pub trait Peek {
    fn peek(&mut self) -> Option<&XmlEvent>;
}

pub trait Next {
	fn next(&mut self) -> Option<XmlEvent>;
}

// Wraps the Hyper Response type
pub struct XmlResponseFromAws<'b> {
	xml_stack: Peekable<Events<'b, Response>> // refactor to use XmlStack type?
}

// I cannot explain how these lifetimes work to a child, therefore I need to understand them better:
impl <'b>XmlResponseFromAws<'b> {
	pub fn new<'c>(stack: Peekable<Events<'b, Response>>) -> XmlResponseFromAws {
		XmlResponseFromAws {
			xml_stack: stack,
		}
	}
}

// Need peek and next implemented.
impl <'b>Peek for XmlResponseFromAws<'b> {
	fn peek(&mut self) -> Option<&XmlEvent> {
		return self.xml_stack.peek();
	}
}

impl <'b> Next for XmlResponseFromAws<'b> {
	fn next(&mut self) -> Option<XmlEvent> {
		return self.xml_stack.next();
	}
}

// // TODO: move to tests/xmlutils.rs
// pub struct XmlResponseFromFile {
// 	file_location: String
// }
//
// impl Read for XmlResponseFromFile {
// 	#[inline]
//     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
// 		// Get a Result reader from our specified file
//         panic!("Not implemented.");
//     }
// }
// /move to tests/xmlutils.rs

impl From<ParseIntError> for XmlParseError{
        fn from(_e:ParseIntError) -> XmlParseError { XmlParseError::new("ParseIntError") }
}

/// parse Some(String) if the next tag has the right name, otherwise None
pub fn optional_string_field<T: Peek + Next>(field_name: &str, stack: &mut T) -> Result<Option<String>, XmlParseError> {
	if try!(peek_at_name(stack)) == field_name {
		let val = try!(string_field(field_name, stack));
		Ok(Some(val))
	} else {
		Ok(None)
	}
}

/// return a string field with the right name or throw a parse error
pub fn string_field<T: Peek + Next>(name: &str, stack: &mut T) -> Result<String, XmlParseError> {
	try!(start_element(name, stack));
	let value = try!(characters(stack));
	try!(end_element(name, stack));
	Ok(value)
}

/// return some XML Characters
pub fn characters<T: Peek + Next>(stack: &mut T) -> Result<String, XmlParseError> {
	if let Some(XmlEvent::Characters(data)) = stack.next() {
		Ok(data.to_string())
	} else {
		Err(XmlParseError::new("Expected characters"))
	}
}

/// get the name of the current element in the stack.  throw a parse error if it's not a StartElement
pub fn peek_at_name<T: Peek + Next>(stack: &mut T) -> Result<String, XmlParseError> {
	let current = stack.peek();
	if let Some(&XmlEvent::StartElement{ref name, ..}) = current {
		Ok(name.local_name.to_string())
	} else {
		Ok("".to_string())
	}
}

/// consume a StartElement with a specific name or throw an XmlParseError
pub fn start_element<T: Peek + Next>(element_name: &str, stack: &mut T)  -> Result<HashMap<String, String>, XmlParseError> {
	let next = stack.next();
	if let Some(XmlEvent::StartElement { name, attributes, .. }) = next {
		if name.local_name != element_name {
			Err(XmlParseError::new(&format!("Expected {} got {}", element_name, name.local_name)))
		} else {
			let mut attr_map = HashMap::new();
			for attr in attributes {
				attr_map.insert(attr.name.local_name, attr.value);
			}
			Ok(attr_map)
		}
	}else {

    //  	println!("{:#?}", next);
		Err(XmlParseError::new(&format!("Expected StartElement {}", element_name)))
	}
}

/// consume an EndElement with a specific name or throw an XmlParseError
pub fn end_element<T: Peek + Next>(element_name: &str, stack: &mut T)  -> Result<(), XmlParseError> {
	let next = stack.next();
	if let Some(XmlEvent::EndElement { name, .. }) = next {
		if name.local_name != element_name {
			Err(XmlParseError::new(&format!("Expected {} got {}", element_name, name.local_name)))
		} else {
			Ok(())
		}
	}else {
		Err(XmlParseError::new(&format!("Expected EndElement {} got {:?}", element_name, next)))
	}
}
<commit_msg>Checkpoint: needs refactoring around how long the parser livers for file sourced XML.<commit_after>use std::iter::Peekable;
use std::num::ParseIntError;
use xml::reader::events::*;
use xml::reader::Events;
use hyper::client::response::*;
use std::collections::HashMap;
use std::io::prelude::*;
use std::io::BufReader;
use std::fs::File;
use std::path::Path;
use std::fs;
use std::error::Error;
use xml::reader::EventReader;

/// generic Error for XML parsing
#[derive(Debug)]
pub struct XmlParseError(pub String);

impl XmlParseError {
	pub fn new(msg: &str) -> XmlParseError {
		XmlParseError(msg.to_string())
	}
}

/// syntactic sugar for the XML event stack we pass around
pub type XmlStack<'a> = Peekable<Events<'a, Response>>;

pub trait Peek {
    fn peek(&mut self) -> Option<&XmlEvent>;
}

pub trait Next {
	fn next(&mut self) -> Option<XmlEvent>;
}

// Wraps the Hyper Response type
pub struct XmlResponseFromAws<'b> {
	xml_stack: Peekable<Events<'b, Response>> // refactor to use XmlStack type?
}

// I cannot explain how these lifetimes work to a child, therefore I need to understand them better:
impl <'b>XmlResponseFromAws<'b> {
	pub fn new<'c>(stack: Peekable<Events<'b, Response>>) -> XmlResponseFromAws {
		XmlResponseFromAws {
			xml_stack: stack,
		}
	}
}

// Need peek and next implemented.
impl <'b>Peek for XmlResponseFromAws<'b> {
	fn peek(&mut self) -> Option<&XmlEvent> {
		return self.xml_stack.peek();
	}
}

impl <'b> Next for XmlResponseFromAws<'b> {
	fn next(&mut self) -> Option<XmlEvent> {
		return self.xml_stack.next();
	}
}

// TODO: move to tests/xmlutils.rs
pub struct XmlResponseFromFile<'a> {
	xml_stack: Peekable<Events<'a, BufReader<File>>>,
}

// I cannot explain how these lifetimes work to a child, therefore I need to understand them better:
impl <'b>XmlResponseFromFile<'b> {
	// TODO: refactor to have caller supply the xml_stack not just location.
	pub fn new<'c>(file_location: &str) -> XmlResponseFromFile {
		let file = File::open("file.xml").unwrap();
	    let file = BufReader::new(file);

	    let mut parser = EventReader::new(file);
		XmlResponseFromFile {
			xml_stack: parser.events().peekable(),
		}

	}
}

// Need peek and next implemented.
impl <'b> Peek for XmlResponseFromFile <'b> {
	fn peek(&mut self) -> Option<&XmlEvent> {
		return self.xml_stack.peek();
	}
}

impl <'b> Next for XmlResponseFromFile <'b> {
	fn next(&mut self) -> Option<XmlEvent> {
		return self.xml_stack.next();
	}
}

// /move to tests/xmlutils.rs

impl From<ParseIntError> for XmlParseError{
        fn from(_e:ParseIntError) -> XmlParseError { XmlParseError::new("ParseIntError") }
}

/// parse Some(String) if the next tag has the right name, otherwise None
pub fn optional_string_field<T: Peek + Next>(field_name: &str, stack: &mut T) -> Result<Option<String>, XmlParseError> {
	if try!(peek_at_name(stack)) == field_name {
		let val = try!(string_field(field_name, stack));
		Ok(Some(val))
	} else {
		Ok(None)
	}
}

/// return a string field with the right name or throw a parse error
pub fn string_field<T: Peek + Next>(name: &str, stack: &mut T) -> Result<String, XmlParseError> {
	try!(start_element(name, stack));
	let value = try!(characters(stack));
	try!(end_element(name, stack));
	Ok(value)
}

/// return some XML Characters
pub fn characters<T: Peek + Next>(stack: &mut T) -> Result<String, XmlParseError> {
	if let Some(XmlEvent::Characters(data)) = stack.next() {
		Ok(data.to_string())
	} else {
		Err(XmlParseError::new("Expected characters"))
	}
}

/// get the name of the current element in the stack.  throw a parse error if it's not a StartElement
pub fn peek_at_name<T: Peek + Next>(stack: &mut T) -> Result<String, XmlParseError> {
	let current = stack.peek();
	if let Some(&XmlEvent::StartElement{ref name, ..}) = current {
		Ok(name.local_name.to_string())
	} else {
		Ok("".to_string())
	}
}

/// consume a StartElement with a specific name or throw an XmlParseError
pub fn start_element<T: Peek + Next>(element_name: &str, stack: &mut T)  -> Result<HashMap<String, String>, XmlParseError> {
	let next = stack.next();
	if let Some(XmlEvent::StartElement { name, attributes, .. }) = next {
		if name.local_name != element_name {
			Err(XmlParseError::new(&format!("Expected {} got {}", element_name, name.local_name)))
		} else {
			let mut attr_map = HashMap::new();
			for attr in attributes {
				attr_map.insert(attr.name.local_name, attr.value);
			}
			Ok(attr_map)
		}
	}else {

    //  	println!("{:#?}", next);
		Err(XmlParseError::new(&format!("Expected StartElement {}", element_name)))
	}
}

/// consume an EndElement with a specific name or throw an XmlParseError
pub fn end_element<T: Peek + Next>(element_name: &str, stack: &mut T)  -> Result<(), XmlParseError> {
	let next = stack.next();
	if let Some(XmlEvent::EndElement { name, .. }) = next {
		if name.local_name != element_name {
			Err(XmlParseError::new(&format!("Expected {} got {}", element_name, name.local_name)))
		} else {
			Ok(())
		}
	}else {
		Err(XmlParseError::new(&format!("Expected EndElement {} got {:?}", element_name, next)))
	}
}
<|endoftext|>


<commit_before>//! Chat API implementation.
use std::io;
use std::str::{self, FromStr};
use std::sync::Arc;

use futures::{Future, BoxFuture};
use tokio_core::channel::Sender;
use tokio_core::reactor::Handle;
use minihttp::Request;
use minihttp::enums::{Status, Method};
use minihttp::client::{HttpClient, Response as ClientResponse};
use rustc_serialize::json::{self, Json};

use intern::SessionId;
use websocket::Base64;
use super::{Cid, ProcessorPool};
use super::{serialize_cid};
use super::router::MessageRouter;
use super::processor::{Action, ConnectionMessage};
use super::message::{Meta, Kwargs, Message, MessageError};

pub struct ChatAPI {
    // shared between connections
    client: HttpClient,         // gets cloned for each request;
    router: MessageRouter,      // singleton per handler endpoint;
    proc_pool: ProcessorPool,   // singleton per handler endpoint;
}

pub struct SessionAPI {
    api: ChatAPI,

    session_id: SessionId,
    auth_token: String,
    conn_id: Cid,   // connection id should not be used here;
    channel: Sender<ConnectionMessage>,
}

impl ChatAPI {
    pub fn new(http_client: HttpClient, router: MessageRouter,
        proc_pool: ProcessorPool)
        -> ChatAPI
    {
        ChatAPI {
            client: http_client,
            router: router,
            proc_pool: proc_pool,
        }
    }

    /// Issue Auth call to backend.
    ///
    /// Send Auth message to proper backend
    /// returninng Hello/Error message.
    pub fn authorize_connection(&self, req: &Request, conn_id: Cid,
        channel: Sender<ConnectionMessage>)
        -> BoxFuture<ClientResponse, io::Error>
        // TODO: convert ClientResponse to Json or ConnectionMessage;
        //  Error to Status?
    {
        let http_cookies = req.headers.iter()
            .filter(|&&(ref k, _)| k == "Cookie")
            .map(|&(_, ref v)| v.clone())
            .collect::<String>();
        let http_auth = req.headers.iter()
            .find(|&&(ref k, _)| k == "Authorization")
            .map(|&(_, ref v)| v.clone())
            .unwrap_or("".to_string());
        let mut meta = Meta::new();
        let mut data = Kwargs::new();

        meta.insert("connection_id".to_string(),
            Json::String(serialize_cid(&conn_id)));
        // TODO: parse cookie string to hashmap;
        data.insert("http_cookie".into(),
            Json::String(http_cookies));
        data.insert("http_authorization".into(),
            Json::String(http_auth));

        self.proc_pool.send(Action::NewConnection {
            conn_id: conn_id,
            channel: channel,
        });

        let payload = Message::Auth(data).encode_with(&meta);
        let mut req = self.client.clone();
        req.request(Method::Post,
            self.router.get_auth_url().as_str());
        req.add_header("Content-Type".into(), "application/json");
        req.add_length(payload.as_bytes().len() as u64);
        req.done_headers();
        req.write_body(payload.as_bytes());
        req.done()
    }

    fn post(&self, method: &str, auth: &str, payload: &[u8])
        -> BoxFuture<ClientResponse, io::Error>
    {
        let url = self.router.resolve(method);
        let mut req = self.client.clone();
        req.request(Method::Post, url.as_str());
        req.add_header("Content-Type".into(), "application/json");
        req.add_header("Authorization".into(), auth);
        req.add_length(payload.len() as u64);
        req.done_headers();
        req.write_body(payload);
        req.done()
        // Result value must be either parsed message or parsed error;
    }

    /// Make instance of Session API (api bound to cid/ssid/tx-channel)
    /// and associate this session with ws connection
    pub fn session_api(self, session_id: SessionId, conn_id: Cid,
        userinfo: Json, channel: Sender<ConnectionMessage>)
        -> SessionAPI
    {
        // XXX: symbol cant be encoded in simple way
        fn encode(s: &SessionId) -> String {
            let auth = format!("{{\"user_id\":{}}}",
                json::encode(&s[..].to_string()).unwrap());
            format!("Tangle {}", Base64(auth.as_bytes()))
        }

        self.proc_pool.send(Action::Associate {
            conn_id: conn_id,
            session_id: session_id.clone(),
            metadata: Arc::new(userinfo),
        });

        SessionAPI {
            api: self,
            auth_token: encode(&session_id),
            session_id: session_id,
            conn_id: conn_id,
            channel: channel,
        }
    }
}

// only difference from ChatAPI -> Bound to concrete SessionId
impl SessionAPI {
    /// Send disconnect to processor.
    pub fn disconnect(&self) {
        self.api.proc_pool.send(Action::Disconnect { conn_id: self.conn_id });
    }

    pub fn method_call(&self, mut meta: Meta, message: Message, handle: &Handle)
    {
        let tx = self.channel.clone();
        let payload = message.encode_with(&meta);
        let call = self.api.post(message.method(),
            self.auth_token.as_str(), payload.as_bytes());
        handle.spawn(call
            .map_err(|e| info!("Http Error: {:?}", e))
            .and_then(move |resp| {
                let result = parse_response(resp)
                    .map(|data| Message::Result(data))
                    .unwrap_or_else(|e| {
                        let e = Message::Error(e);
                        e.update_meta(&mut meta);
                        e
                    })
                    .encode_with(&meta);
                // XXX: use proper ConnectionMessage
                tx.send(ConnectionMessage::Raw(result))
                .map_err(|e| info!("Remote send error: {:?}", e))
            })
        );
    }
}

/// Parse backend response.
fn parse_response(response: ClientResponse) -> Result<Json, MessageError>
{
    // TODO: check content-type
    let payload = match response.body {
        Some(ref data) => {
            str::from_utf8(&data[..])
            .map_err(|e| MessageError::from(e))
            .and_then(
                |s| Json::from_str(s).map_err(|e| MessageError::from(e))
            )?
        }
        None => Json::Null,
    };
    match (response.status, payload) {
        (Status::Ok, payload) => Ok(payload),
        (s, Json::Null) => Err(MessageError::HttpError(s, None)),
        (s, payload) => Err(MessageError::HttpError(s, Some(payload))),
    }
}

/// Parse userinfo received on Auth call;
pub fn parse_userinfo(response: ClientResponse) -> Message {
    use super::message::ValidationError::*;
    use super::message::MessageError::*;
    match parse_response(response) {
        Ok(Json::Object(data)) => {
            let sess_id = match data.get("user_id".into()) {
                Some(&Json::String(ref s)) => {
                    SessionId::from_str(s.as_str()).unwrap()  // XXX
                }
                _ => return Message::Error(ValidationError(InvalidUserId)),
            };
            Message::Hello(sess_id, Json::Object(data))
        }
        Ok(_) => Message::Error(ValidationError(ObjectExpected)),
        Err(err) => Message::Error(err),
    }
}
<commit_msg>Fix serialization of tangle auth data<commit_after>//! Chat API implementation.
use std::io;
use std::str::{self, FromStr};
use std::sync::Arc;

use futures::{Future, BoxFuture};
use tokio_core::channel::Sender;
use tokio_core::reactor::Handle;
use minihttp::Request;
use minihttp::enums::{Status, Method};
use minihttp::client::{HttpClient, Response as ClientResponse};
use rustc_serialize::json::{self, Json};

use intern::SessionId;
use websocket::Base64;
use super::{Cid, ProcessorPool};
use super::{serialize_cid};
use super::router::MessageRouter;
use super::processor::{Action, ConnectionMessage};
use super::message::{Meta, Kwargs, Message, MessageError};

pub struct ChatAPI {
    // shared between connections
    client: HttpClient,         // gets cloned for each request;
    router: MessageRouter,      // singleton per handler endpoint;
    proc_pool: ProcessorPool,   // singleton per handler endpoint;
}

pub struct SessionAPI {
    api: ChatAPI,

    session_id: SessionId,
    auth_token: String,
    conn_id: Cid,   // connection id should not be used here;
    channel: Sender<ConnectionMessage>,
}

impl ChatAPI {
    pub fn new(http_client: HttpClient, router: MessageRouter,
        proc_pool: ProcessorPool)
        -> ChatAPI
    {
        ChatAPI {
            client: http_client,
            router: router,
            proc_pool: proc_pool,
        }
    }

    /// Issue Auth call to backend.
    ///
    /// Send Auth message to proper backend
    /// returninng Hello/Error message.
    pub fn authorize_connection(&self, req: &Request, conn_id: Cid,
        channel: Sender<ConnectionMessage>)
        -> BoxFuture<ClientResponse, io::Error>
        // TODO: convert ClientResponse to Json or ConnectionMessage;
        //  Error to Status?
    {
        let http_cookies = req.headers.iter()
            .filter(|&&(ref k, _)| k == "Cookie")
            .map(|&(_, ref v)| v.clone())
            .collect::<String>();
        let http_auth = req.headers.iter()
            .find(|&&(ref k, _)| k == "Authorization")
            .map(|&(_, ref v)| v.clone())
            .unwrap_or("".to_string());
        let mut meta = Meta::new();
        let mut data = Kwargs::new();

        meta.insert("connection_id".to_string(),
            Json::String(serialize_cid(&conn_id)));
        // TODO: parse cookie string to hashmap;
        data.insert("http_cookie".into(),
            Json::String(http_cookies));
        data.insert("http_authorization".into(),
            Json::String(http_auth));

        self.proc_pool.send(Action::NewConnection {
            conn_id: conn_id,
            channel: channel,
        });

        let payload = Message::Auth(data).encode_with(&meta);
        let mut req = self.client.clone();
        req.request(Method::Post,
            self.router.get_auth_url().as_str());
        req.add_header("Content-Type".into(), "application/json");
        req.add_length(payload.as_bytes().len() as u64);
        req.done_headers();
        req.write_body(payload.as_bytes());
        req.done()
    }

    fn post(&self, method: &str, auth: &str, payload: &[u8])
        -> BoxFuture<ClientResponse, io::Error>
    {
        let url = self.router.resolve(method);
        let mut req = self.client.clone();
        req.request(Method::Post, url.as_str());
        req.add_header("Content-Type".into(), "application/json");
        req.add_header("Authorization".into(), auth);
        req.add_length(payload.len() as u64);
        req.done_headers();
        req.write_body(payload);
        req.done()
        // Result value must be either parsed message or parsed error;
    }

    /// Make instance of Session API (api bound to cid/ssid/tx-channel)
    /// and associate this session with ws connection
    pub fn session_api(self, session_id: SessionId, conn_id: Cid,
        userinfo: Json, channel: Sender<ConnectionMessage>)
        -> SessionAPI
    {
        // XXX: symbol cant be encoded in simple way
        fn encode(s: &SessionId) -> String {
            #[derive(RustcEncodable)]
            struct Auth<'a> {
                user_id: &'a SessionId,
            }
            format!("Tangle {}", Base64(json::encode(&Auth {
                user_id: s,
            }).unwrap().as_bytes()))
        }

        self.proc_pool.send(Action::Associate {
            conn_id: conn_id,
            session_id: session_id.clone(),
            metadata: Arc::new(userinfo),
        });

        SessionAPI {
            api: self,
            auth_token: encode(&session_id),
            session_id: session_id,
            conn_id: conn_id,
            channel: channel,
        }
    }
}

// only difference from ChatAPI -> Bound to concrete SessionId
impl SessionAPI {
    /// Send disconnect to processor.
    pub fn disconnect(&self) {
        self.api.proc_pool.send(Action::Disconnect { conn_id: self.conn_id });
    }

    pub fn method_call(&self, mut meta: Meta, message: Message, handle: &Handle)
    {
        let tx = self.channel.clone();
        let payload = message.encode_with(&meta);
        let call = self.api.post(message.method(),
            self.auth_token.as_str(), payload.as_bytes());
        handle.spawn(call
            .map_err(|e| info!("Http Error: {:?}", e))
            .and_then(move |resp| {
                let result = parse_response(resp)
                    .map(|data| Message::Result(data))
                    .unwrap_or_else(|e| {
                        let e = Message::Error(e);
                        e.update_meta(&mut meta);
                        e
                    })
                    .encode_with(&meta);
                // XXX: use proper ConnectionMessage
                tx.send(ConnectionMessage::Raw(result))
                .map_err(|e| info!("Remote send error: {:?}", e))
            })
        );
    }
}

/// Parse backend response.
fn parse_response(response: ClientResponse) -> Result<Json, MessageError>
{
    // TODO: check content-type
    let payload = match response.body {
        Some(ref data) => {
            str::from_utf8(&data[..])
            .map_err(|e| MessageError::from(e))
            .and_then(
                |s| Json::from_str(s).map_err(|e| MessageError::from(e))
            )?
        }
        None => Json::Null,
    };
    match (response.status, payload) {
        (Status::Ok, payload) => Ok(payload),
        (s, Json::Null) => Err(MessageError::HttpError(s, None)),
        (s, payload) => Err(MessageError::HttpError(s, Some(payload))),
    }
}

/// Parse userinfo received on Auth call;
pub fn parse_userinfo(response: ClientResponse) -> Message {
    use super::message::ValidationError::*;
    use super::message::MessageError::*;
    match parse_response(response) {
        Ok(Json::Object(data)) => {
            let sess_id = match data.get("user_id".into()) {
                Some(&Json::String(ref s)) => {
                    SessionId::from_str(s.as_str()).unwrap()  // XXX
                }
                _ => return Message::Error(ValidationError(InvalidUserId)),
            };
            Message::Hello(sess_id, Json::Object(data))
        }
        Ok(_) => Message::Error(ValidationError(ObjectExpected)),
        Err(err) => Message::Error(err),
    }
}
<|endoftext|>


<commit_before>//
// Copyright 2019 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

use abitest_grpc::proto::{oak_abi_test_service_client::OakAbiTestServiceClient, AbiTestRequest};
use anyhow::Context;
use assert_matches::assert_matches;
use log::{debug, error, info};
use maplit::hashmap;
use oak_abi::proto::oak::application::ConfigMap;
use serial_test::serial;
use std::{collections::HashMap, sync::Arc};

// Constants for Node config names that should match those in the textproto
// config held in ../../../config.
const FRONTEND_MODULE_NAME: &str = "frontend_module";
const BACKEND_MODULE_NAME: &str = "backend_module";
const FRONTEND_ENTRYPOINT_NAME: &str = "frontend_oak_main";

const FRONTEND_MANIFEST: &str = "../../module_0/rust/Cargo.toml";
const BACKEND_MANIFEST: &str = "../../module_1/rust/Cargo.toml";

const FRONTEND_MODULE_WASM_FILE_NAME: &str = "abitest_0_frontend.wasm";
const BACKEND_MODULE_WASM_FILE_NAME: &str = "abitest_1_backend.wasm";

fn build_wasm() -> anyhow::Result<HashMap<String, Vec<u8>>> {
    Ok(hashmap! {
        FRONTEND_MODULE_NAME.to_owned() => oak_tests::compile_rust_wasm(FRONTEND_MANIFEST, FRONTEND_MODULE_WASM_FILE_NAME, oak_tests::Profile::Debug).context("could not compile frontend module")?,
        BACKEND_MODULE_NAME.to_owned() => oak_tests::compile_rust_wasm(BACKEND_MANIFEST, BACKEND_MODULE_WASM_FILE_NAME, oak_tests::Profile::Debug).context("could not compile backend module")?,
    })
}

async fn setup() -> (
    Arc<oak_runtime::Runtime>,
    OakAbiTestServiceClient<tonic::transport::Channel>,
) {
    let _ = env_logger::builder().is_test(true).try_init();

    let wasm_modules = build_wasm().expect("failed to build wasm modules");
    let config = oak_tests::runtime_config_wasm(
        wasm_modules,
        FRONTEND_MODULE_NAME,
        FRONTEND_ENTRYPOINT_NAME,
        ConfigMap::default(),
    );
    let runtime =
        oak_runtime::configure_and_run(config).expect("unable to configure runtime with test wasm");

    let (channel, interceptor) = oak_tests::channel_and_interceptor().await;
    let client = OakAbiTestServiceClient::with_interceptor(channel, interceptor);

    (runtime, client)
}

#[tokio::test(core_threads = 2)]
#[serial]
async fn test_abi() {
    let (runtime, mut client) = setup().await;
    {
        // Skip tests that require the existence of an external service.
        let mut req = AbiTestRequest::default();
        req.exclude = "(Storage|GrpcClient|Roughtime)".to_string();

        info!("Sending request: {:?}", req);
        let result = client.run_tests(req).await;
        assert_matches!(result, Ok(_));

        info!("Runtime graph at exit is:\n{}", runtime.graph());

        let mut disabled = 0;
        let mut success = true;
        let results = result.unwrap().into_inner().results;
        for result in results {
            if result.disabled {
                disabled += 1;
                continue;
            }
            info!(
                "[ {} ] {}",
                if result.success { " OK " } else { "FAIL" },
                result.name
            );
            if !result.success {
                error!("Failure details: {}", result.details);
                success = false;
            }
        }
        if disabled > 0 {
            info!("YOU HAVE {} DISABLED TESTS", disabled);
        }
        assert_eq!(true, success);
    } // ensure futures are all dropped
    drop(client);
    runtime.stop();
}

#[tokio::test(core_threads = 2)]
#[serial]
async fn test_leaks() {
    let (runtime, mut client) = setup().await;

    {
        let (before_nodes, before_channels) = runtime.object_counts();
        info!(
            "Counts before test: Nodes={}, Channels={}",
            before_nodes, before_channels
        );

        // Run tests that are supposed to leave Node/channel counts in a known state.
        let mut req = AbiTestRequest::default();
        req.predictable_counts = true;

        debug!("Sending request: {:?}", req);
        let result = client.run_tests(req).await;
        assert_matches!(result, Ok(_));
        let results = result.unwrap().into_inner().results;

        let (after_nodes, after_channels) = runtime.object_counts();
        info!(
            "Counts change from test: Nodes={} => {}, Channels={} => {}",
            before_nodes, after_nodes, before_channels, after_channels
        );

        // Calculate the expected change in Node and channel counts for
        // these tests.
        let mut want_nodes = before_nodes;
        let mut want_channels = before_channels;
        for result in &results {
            assert_eq!(result.predictable_counts, true);
            if result.disabled {
                continue;
            }
            want_nodes += result.node_change;
            want_channels += result.channel_change;
        }

        if after_nodes != want_nodes || after_channels != want_channels {
            // One of the batch of tests has triggered an unexpected object count.
            // Repeat the tests one by one to find out which.
            let mut details = Vec::new();
            for result in results {
                if result.disabled {
                    continue;
                }

                let (before_nodes, before_channels) = runtime.object_counts();
                let (want_nodes, want_channels) = (
                    before_nodes + result.node_change,
                    before_channels + result.channel_change,
                );

                let mut req = AbiTestRequest::default();
                req.include = format!("^{}$", result.name);
                debug!("Sending request: {:?}", req);
                let this_result = client.run_tests(req).await;
                assert_matches!(this_result, Ok(_));
                let (after_nodes, after_channels) = runtime.object_counts();

                if after_nodes != want_nodes || after_channels != want_channels {
                    details.push(format!(
                    "[ LEAK ] {} (got {}=>{} nodes, {}=>{} channels, want {}=>{} nodes, {}=>{} channels)",
                    result.name, before_nodes, after_nodes, before_channels, after_channels,
                    before_nodes, want_nodes, before_channels, want_channels,
                ));
                } else {
                    details.push(format!(
                        "[ OK ] {} ({}=>{} nodes, {}=>{} channels, as expected)",
                        result.name, before_nodes, after_nodes, before_channels, after_channels,
                    ));
                }
                panic!("Leak of Nodes or channels found");
            }
            for detail in details {
                info!("{}", detail);
            }
            assert_eq!(want_nodes, after_nodes);
            assert_eq!(want_channels, after_channels);
        }
    } // ensure futures are all dropped
    drop(client);
    runtime.stop();
}
<commit_msg>abitest: better object leak logging<commit_after>//
// Copyright 2019 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

use abitest_grpc::proto::{oak_abi_test_service_client::OakAbiTestServiceClient, AbiTestRequest};
use anyhow::Context;
use assert_matches::assert_matches;
use log::{debug, error, info};
use maplit::hashmap;
use oak_abi::proto::oak::application::ConfigMap;
use serial_test::serial;
use std::{collections::HashMap, sync::Arc};

// Constants for Node config names that should match those in the textproto
// config held in ../../../config.
const FRONTEND_MODULE_NAME: &str = "frontend_module";
const BACKEND_MODULE_NAME: &str = "backend_module";
const FRONTEND_ENTRYPOINT_NAME: &str = "frontend_oak_main";

const FRONTEND_MANIFEST: &str = "../../module_0/rust/Cargo.toml";
const BACKEND_MANIFEST: &str = "../../module_1/rust/Cargo.toml";

const FRONTEND_MODULE_WASM_FILE_NAME: &str = "abitest_0_frontend.wasm";
const BACKEND_MODULE_WASM_FILE_NAME: &str = "abitest_1_backend.wasm";

fn build_wasm() -> anyhow::Result<HashMap<String, Vec<u8>>> {
    Ok(hashmap! {
        FRONTEND_MODULE_NAME.to_owned() => oak_tests::compile_rust_wasm(FRONTEND_MANIFEST, FRONTEND_MODULE_WASM_FILE_NAME, oak_tests::Profile::Debug).context("could not compile frontend module")?,
        BACKEND_MODULE_NAME.to_owned() => oak_tests::compile_rust_wasm(BACKEND_MANIFEST, BACKEND_MODULE_WASM_FILE_NAME, oak_tests::Profile::Debug).context("could not compile backend module")?,
    })
}

async fn setup() -> (
    Arc<oak_runtime::Runtime>,
    OakAbiTestServiceClient<tonic::transport::Channel>,
) {
    let _ = env_logger::builder().is_test(true).try_init();

    let wasm_modules = build_wasm().expect("failed to build wasm modules");
    let config = oak_tests::runtime_config_wasm(
        wasm_modules,
        FRONTEND_MODULE_NAME,
        FRONTEND_ENTRYPOINT_NAME,
        ConfigMap::default(),
    );
    let runtime =
        oak_runtime::configure_and_run(config).expect("unable to configure runtime with test wasm");

    let (channel, interceptor) = oak_tests::channel_and_interceptor().await;
    let client = OakAbiTestServiceClient::with_interceptor(channel, interceptor);

    (runtime, client)
}

#[tokio::test(core_threads = 2)]
#[serial]
async fn test_abi() {
    let (runtime, mut client) = setup().await;
    {
        // Skip tests that require the existence of an external service.
        //  - storage and gRPC client test require details of a gRPC service to connect to
        //  - Roughtime tests connect to the internet.
        let mut req = AbiTestRequest::default();
        req.exclude = "(Storage|GrpcClient|Roughtime)".to_string();

        info!("Sending request: {:?}", req);
        let result = client.run_tests(req).await;
        assert_matches!(result, Ok(_));

        info!("Runtime graph at exit is:\n{}", runtime.graph());

        let mut disabled = 0;
        let mut success = true;
        let results = result.unwrap().into_inner().results;
        for result in results {
            if result.disabled {
                disabled += 1;
                continue;
            }
            info!(
                "[ {} ] {}",
                if result.success { " OK " } else { "FAIL" },
                result.name
            );
            if !result.success {
                error!("Failure details: {}", result.details);
                success = false;
            }
        }
        if disabled > 0 {
            info!("YOU HAVE {} DISABLED TESTS", disabled);
        }
        assert_eq!(true, success);
    } // ensure futures are all dropped
    drop(client);
    runtime.stop();
}

#[tokio::test(core_threads = 2)]
#[serial]
async fn test_leaks() {
    let (runtime, mut client) = setup().await;

    {
        let (before_nodes, before_channels) = runtime.object_counts();
        info!(
            "Counts before test: Nodes={}, Channels={}",
            before_nodes, before_channels
        );

        // Run tests that are supposed to leave Node/channel counts in a known state.
        // Skip tests that require the existence of an external service.
        //  - storage and gRPC client test require details of a gRPC service to connect to
        //  - Roughtime tests connect to the internet.
        let mut req = AbiTestRequest::default();
        req.exclude = "(Storage|GrpcClient|Roughtime)".to_string();
        req.predictable_counts = true;

        debug!("Sending request: {:?}", req);
        let result = client.run_tests(req).await;
        assert_matches!(result, Ok(_));
        let results = result.unwrap().into_inner().results;

        let (after_nodes, after_channels) = runtime.object_counts();
        info!(
            "Counts change from test: Nodes={} => {}, Channels={} => {}",
            before_nodes, after_nodes, before_channels, after_channels
        );

        // Calculate the expected change in Node and channel counts for
        // these tests.
        let mut want_nodes = before_nodes;
        let mut want_channels = before_channels;
        for result in &results {
            assert_eq!(result.predictable_counts, true);
            if result.disabled {
                continue;
            }
            want_nodes += result.node_change;
            want_channels += result.channel_change;
        }

        if after_nodes != want_nodes || after_channels != want_channels {
            error!(
                "Batch test showed unexpected count change: got: node_delta={} channel_delta={}, want node_delta={} channel_delta={}",
                (after_nodes-before_nodes), (after_channels-before_channels),
                (want_nodes-before_nodes), (want_channels-before_channels),
            );
            // One of the batch of tests has triggered an unexpected object count.
            // Repeat the tests one by one to find out which.
            let mut details = Vec::new();
            for result in results {
                if result.disabled {
                    continue;
                }

                let (before_nodes, before_channels) = runtime.object_counts();
                let (want_nodes, want_channels) = (
                    before_nodes + result.node_change,
                    before_channels + result.channel_change,
                );

                let mut req = AbiTestRequest::default();
                req.include = format!("^{}$", result.name);
                debug!("Sending request: {:?}", req);
                let this_result = client.run_tests(req).await;
                assert_matches!(this_result, Ok(_));
                let (after_nodes, after_channels) = runtime.object_counts();

                if after_nodes != want_nodes || after_channels != want_channels {
                    details.push(format!(
                    "[ LEAK ] {} (got {}=>{} nodes, {}=>{} channels, want {}=>{} nodes, {}=>{} channels)",
                    result.name, before_nodes, after_nodes, before_channels, after_channels,
                    before_nodes, want_nodes, before_channels, want_channels,
                ));
                } else {
                    details.push(format!(
                        "[ OK ] {} ({}=>{} nodes, {}=>{} channels, as expected)",
                        result.name, before_nodes, after_nodes, before_channels, after_channels,
                    ));
                }
            }
            for detail in details {
                info!("{}", detail);
            }
            panic!("Leak of Nodes or channels found");
        }
    } // ensure futures are all dropped
    drop(client);
    runtime.stop();
}
<|endoftext|>


<commit_before>use doubly_linked_list::*;

#[test]
fn is_generic() {
    struct Foo;
    LinkedList::<Foo>::new();
}

// ———————————————————————————————————————————————————————————
// Tests for Step 1: push / pop at front and back
// ———————————————————————————————————————————————————————————

#[test]
#[ignore]
fn basics_empty_list() {
    let list: LinkedList<i32> = LinkedList::new();
    assert_eq!(list.len(), 0);
    assert!(list.is_empty());
}

// push / pop at back ————————————————————————————————————————
#[test]
#[ignore]
fn basics_single_element_back() {
    let mut list: LinkedList<i32> = LinkedList::new();
    list.push_back(5);

    assert_eq!(list.len(), 1);
    assert!(!list.is_empty());

    assert_eq!(list.pop_back(), Some(5));

    assert!(list.is_empty());
}

#[test]
#[ignore]
fn basics_push_pop_at_back() {
    let mut list: LinkedList<i32> = LinkedList::new();
    for i in 0..10 {
        list.push_back(i);
        assert!(!list.is_empty());
    }
    assert_eq!(list.len(), 10);
    for i in (0..10).rev() {
        assert!(!list.is_empty());
        assert_eq!(i, list.pop_back().unwrap());
    }
    assert_eq!(list.len(), 0);
    assert!(list.is_empty());
}

// push / pop at front ———————————————————————————————————————
#[test]
#[ignore]
fn basics_single_element_front() {
    let mut list: LinkedList<i32> = LinkedList::new();
    list.push_front(5);

    assert_eq!(list.len(), 1);
    assert!(!list.is_empty());

    assert_eq!(list.pop_front(), Some(5));

    assert!(list.is_empty());
}

#[test]
#[ignore]
fn basics_push_pop_at_front() {
    let mut list: LinkedList<i32> = LinkedList::new();
    for i in 0..10 {
        list.push_front(i);
        assert!(!list.is_empty());
    }
    assert_eq!(list.len(), 10);
    for i in (0..10).rev() {
        assert!(!list.is_empty());
        assert_eq!(i, list.pop_front().unwrap());
    }
    assert_eq!(list.len(), 0);
    assert!(list.is_empty());
}

// push / pop at mixed sides —————————————————————————————————
#[test]
#[ignore]
fn basics_push_front_pop_back() {
    let mut list: LinkedList<i32> = LinkedList::new();
    for i in 0..10 {
        list.push_front(i);
        assert!(!list.is_empty());
    }
    for i in 0..10 {
        assert!(!list.is_empty());
        assert_eq!(i, list.pop_back().unwrap());
    }
    assert!(list.is_empty());
}

#[test]
#[ignore]
fn basics_push_back_pop_front() {
    let mut list: LinkedList<i32> = LinkedList::new();
    for i in 0..10 {
        list.push_back(i);
        assert!(!list.is_empty());
    }
    for i in 0..10 {
        assert!(!list.is_empty());
        assert_eq!(i, list.pop_front().unwrap());
    }
    assert!(list.is_empty());
}

// ———————————————————————————————————————————————————————————
// Tests for Step 2: iteration
// ———————————————————————————————————————————————————————————

#[test]
#[ignore]
fn iter() {
    let mut list: LinkedList<i32> = LinkedList::new();
    for num in 0..10 {
        list.push_back(num);
    }

    for (num, &entered_num) in (0..10).zip(list.iter()) {
        assert_eq!(num, entered_num);
    }
}

// ———————————————————————————————————————————————————————————
// Tests for Step 3: full cursor functionality
// ———————————————————————————————————————————————————————————

#[test]
#[ignore]
fn cursor_insert_before_on_empty_list() {
    // insert_after on empty list is already tested via push_back()
    let mut list = LinkedList::new();
    list.cursor_front().insert_before(0);
    assert_eq!(Some(0), list.pop_front());
}

#[test]
#[ignore]
fn cursor_insert_after_in_middle() {
    let mut list = (0..10).collect::<LinkedList<_>>();

    {
        let mut cursor = list.cursor_front();
        let didnt_run_into_end = cursor.seek_forward(4);
        assert!(didnt_run_into_end);

        for n in (0..10).rev() {
            cursor.insert_after(n);
        }
    }

    assert_eq!(list.len(), 20);

    let expected = (0..5).chain(0..10).chain(5..10);

    assert!(expected.eq(list.iter().cloned()));
}

#[test]
#[ignore]
fn cursor_insert_before_in_middle() {
    let mut list = (0..10).collect::<LinkedList<_>>();

    {
        let mut cursor = list.cursor_back();
        let didnt_run_into_end = cursor.seek_backward(4);
        assert!(didnt_run_into_end);

        for n in 0..10 {
            cursor.insert_before(n);
        }
    }

    assert_eq!(list.len(), 20);

    let expected = (0..5).chain(0..10).chain(5..10);

    assert!(expected.eq(list.iter().cloned()));
}

// "iterates" via next() and checks that it visits the right elements
#[test]
#[ignore]
fn cursor_next_and_peek() {
    let mut list = (0..10).collect::<LinkedList<_>>();
    let mut cursor = list.cursor_front();

    assert_eq!(cursor.peek_mut(), Some(&mut 0));

    for n in 1..10 {
        let next = cursor.next().cloned();
        assert_eq!(next, Some(n));
        assert_eq!(next, cursor.peek_mut().cloned());
    }
}

// "iterates" via prev() and checks that it visits the right elements
#[test]
#[ignore]
fn cursor_prev_and_peek() {
    let mut list = (0..10).collect::<LinkedList<_>>();
    let mut cursor = list.cursor_back();

    assert_eq!(cursor.peek_mut(), Some(&mut 9));

    for n in (0..9).rev() {
        let prev = cursor.prev().cloned();
        assert_eq!(prev, Some(n));
        assert_eq!(prev, cursor.peek_mut().cloned());
    }
}

// removes all elements starting from the middle
#[test]
#[ignore]
fn cursor_take() {
    let mut list = (0..10).collect::<LinkedList<_>>();
    let mut cursor = list.cursor_front();
    cursor.seek_forward(5);

    for expected in (5..10).chain((0..5).rev()) {
        assert_eq!(cursor.take(), Some(expected));
    }
}

// ———————————————————————————————————————————————————————————
// Tests for Step 4: clean-up via `Drop`
// ———————————————————————————————————————————————————————————

// The leak tests that are also for this step are separated into
// their own files so that nothing else interferes with the allocator
// whilst they run

// checks number of drops
// may pass for incorrect programs if double frees happen
// exactly as often as destructor leaks
#[test]
#[ignore]
fn drop_no_double_frees() {
    use std::cell::Cell;
    struct DropCounter<'a>(&'a Cell<usize>);

    impl<'a> Drop for DropCounter<'a> {
        fn drop(&mut self) {
            let num = self.0.get();
            self.0.set(num + 1);
        }
    }

    const N: usize = 15;

    let counter = Cell::new(0);
    let list = std::iter::repeat_with(|| DropCounter(&counter))
        .take(N)
        .collect::<LinkedList<_>>();

    assert_eq!(list.len(), N);
    drop(list);
    assert_eq!(counter.get(), N);
}

#[test]
#[ignore]
fn drop_large_list() {
    drop((0..2_000_000).collect::<LinkedList<i32>>());
}

// ———————————————————————————————————————————————————————————
// Tests for Step 5 (advanced): covariance and Send/Sync
// ———————————————————————————————————————————————————————————

// These are compile time tests. They won't compile unless your
// code passes.
// Additional tests for code that must *not* compile are in
// pre_implemented.rs for technical reasons.

#[cfg(feature = "advanced")]
#[test]
#[ignore]
fn advanced_linked_list_is_send_sync() {
    trait AssertSend: Send {}
    trait AssertSync: Sync {}

    impl<T: Send> AssertSend for LinkedList<T> {}
    impl<T: Sync> AssertSync for LinkedList<T> {}
}

#[cfg(feature = "advanced")]
#[allow(dead_code)]
#[test]
#[ignore]
fn advanced_is_covariant() {
    fn a<'a>(x: LinkedList<&'static str>) -> LinkedList<&'a str> {
        x
    }

    fn a_iter<'a>(i: Iter<'static, &'static str>) -> Iter<'a, &'a str> {
        i
    }
}
<commit_msg>doubly-linked-list: Test len in more places<commit_after>use doubly_linked_list::*;

#[test]
fn is_generic() {
    struct Foo;
    LinkedList::<Foo>::new();
}

// ———————————————————————————————————————————————————————————
// Tests for Step 1: push / pop at front and back
// ———————————————————————————————————————————————————————————

#[test]
#[ignore]
fn basics_empty_list() {
    let list: LinkedList<i32> = LinkedList::new();
    assert_eq!(list.len(), 0);
    assert!(list.is_empty());
}

// push / pop at back ————————————————————————————————————————
#[test]
#[ignore]
fn basics_single_element_back() {
    let mut list: LinkedList<i32> = LinkedList::new();
    list.push_back(5);

    assert_eq!(list.len(), 1);
    assert!(!list.is_empty());

    assert_eq!(list.pop_back(), Some(5));

    assert_eq!(list.len(), 0);
    assert!(list.is_empty());
}

#[test]
#[ignore]
fn basics_push_pop_at_back() {
    let mut list: LinkedList<i32> = LinkedList::new();
    for i in 0..10 {
        list.push_back(i);
        assert_eq!(list.len(), i as usize + 1);
        assert!(!list.is_empty());
    }
    for i in (0..10).rev() {
        assert_eq!(list.len(), i as usize + 1);
        assert!(!list.is_empty());
        assert_eq!(i, list.pop_back().unwrap());
    }
    assert_eq!(list.len(), 0);
    assert!(list.is_empty());
}

// push / pop at front ———————————————————————————————————————
#[test]
#[ignore]
fn basics_single_element_front() {
    let mut list: LinkedList<i32> = LinkedList::new();
    list.push_front(5);

    assert_eq!(list.len(), 1);
    assert!(!list.is_empty());

    assert_eq!(list.pop_front(), Some(5));

    assert_eq!(list.len(), 0);
    assert!(list.is_empty());
}

#[test]
#[ignore]
fn basics_push_pop_at_front() {
    let mut list: LinkedList<i32> = LinkedList::new();
    for i in 0..10 {
        list.push_front(i);
        assert_eq!(list.len(), i as usize + 1);
        assert!(!list.is_empty());
    }
    for i in (0..10).rev() {
        assert_eq!(list.len(), i as usize + 1);
        assert!(!list.is_empty());
        assert_eq!(i, list.pop_front().unwrap());
    }
    assert_eq!(list.len(), 0);
    assert!(list.is_empty());
}

// push / pop at mixed sides —————————————————————————————————
#[test]
#[ignore]
fn basics_push_front_pop_back() {
    let mut list: LinkedList<i32> = LinkedList::new();
    for i in 0..10 {
        list.push_front(i);
        assert_eq!(list.len(), i as usize + 1);
        assert!(!list.is_empty());
    }
    for i in 0..10 {
        assert_eq!(list.len(), 10 - i as usize);
        assert!(!list.is_empty());
        assert_eq!(i, list.pop_back().unwrap());
    }
    assert_eq!(list.len(), 0);
    assert!(list.is_empty());
}

#[test]
#[ignore]
fn basics_push_back_pop_front() {
    let mut list: LinkedList<i32> = LinkedList::new();
    for i in 0..10 {
        list.push_back(i);
        assert_eq!(list.len(), i as usize + 1);
        assert!(!list.is_empty());
    }
    for i in 0..10 {
        assert_eq!(list.len(), 10 - i as usize);
        assert!(!list.is_empty());
        assert_eq!(i, list.pop_front().unwrap());
    }
    assert_eq!(list.len(), 0);
    assert!(list.is_empty());
}

// ———————————————————————————————————————————————————————————
// Tests for Step 2: iteration
// ———————————————————————————————————————————————————————————

#[test]
#[ignore]
fn iter() {
    let mut list: LinkedList<i32> = LinkedList::new();
    for num in 0..10 {
        list.push_back(num);
    }

    for (num, &entered_num) in (0..10).zip(list.iter()) {
        assert_eq!(num, entered_num);
    }
}

// ———————————————————————————————————————————————————————————
// Tests for Step 3: full cursor functionality
// ———————————————————————————————————————————————————————————

#[test]
#[ignore]
fn cursor_insert_before_on_empty_list() {
    // insert_after on empty list is already tested via push_back()
    let mut list = LinkedList::new();
    list.cursor_front().insert_before(0);
    assert_eq!(Some(0), list.pop_front());
}

#[test]
#[ignore]
fn cursor_insert_after_in_middle() {
    let mut list = (0..10).collect::<LinkedList<_>>();

    {
        let mut cursor = list.cursor_front();
        let didnt_run_into_end = cursor.seek_forward(4);
        assert!(didnt_run_into_end);

        for n in (0..10).rev() {
            cursor.insert_after(n);
        }
    }

    assert_eq!(list.len(), 20);

    let expected = (0..5).chain(0..10).chain(5..10);

    assert!(expected.eq(list.iter().cloned()));
}

#[test]
#[ignore]
fn cursor_insert_before_in_middle() {
    let mut list = (0..10).collect::<LinkedList<_>>();

    {
        let mut cursor = list.cursor_back();
        let didnt_run_into_end = cursor.seek_backward(4);
        assert!(didnt_run_into_end);

        for n in 0..10 {
            cursor.insert_before(n);
        }
    }

    assert_eq!(list.len(), 20);

    let expected = (0..5).chain(0..10).chain(5..10);

    assert!(expected.eq(list.iter().cloned()));
}

// "iterates" via next() and checks that it visits the right elements
#[test]
#[ignore]
fn cursor_next_and_peek() {
    let mut list = (0..10).collect::<LinkedList<_>>();
    let mut cursor = list.cursor_front();

    assert_eq!(cursor.peek_mut(), Some(&mut 0));

    for n in 1..10 {
        let next = cursor.next().cloned();
        assert_eq!(next, Some(n));
        assert_eq!(next, cursor.peek_mut().cloned());
    }
}

// "iterates" via prev() and checks that it visits the right elements
#[test]
#[ignore]
fn cursor_prev_and_peek() {
    let mut list = (0..10).collect::<LinkedList<_>>();
    let mut cursor = list.cursor_back();

    assert_eq!(cursor.peek_mut(), Some(&mut 9));

    for n in (0..9).rev() {
        let prev = cursor.prev().cloned();
        assert_eq!(prev, Some(n));
        assert_eq!(prev, cursor.peek_mut().cloned());
    }
}

// removes all elements starting from the middle
#[test]
#[ignore]
fn cursor_take() {
    let mut list = (0..10).collect::<LinkedList<_>>();
    let mut cursor = list.cursor_front();
    cursor.seek_forward(5);

    for expected in (5..10).chain((0..5).rev()) {
        assert_eq!(cursor.take(), Some(expected));
    }
}

// ———————————————————————————————————————————————————————————
// Tests for Step 4: clean-up via `Drop`
// ———————————————————————————————————————————————————————————

// The leak tests that are also for this step are separated into
// their own files so that nothing else interferes with the allocator
// whilst they run

// checks number of drops
// may pass for incorrect programs if double frees happen
// exactly as often as destructor leaks
#[test]
#[ignore]
fn drop_no_double_frees() {
    use std::cell::Cell;
    struct DropCounter<'a>(&'a Cell<usize>);

    impl<'a> Drop for DropCounter<'a> {
        fn drop(&mut self) {
            let num = self.0.get();
            self.0.set(num + 1);
        }
    }

    const N: usize = 15;

    let counter = Cell::new(0);
    let list = std::iter::repeat_with(|| DropCounter(&counter))
        .take(N)
        .collect::<LinkedList<_>>();

    assert_eq!(list.len(), N);
    drop(list);
    assert_eq!(counter.get(), N);
}

#[test]
#[ignore]
fn drop_large_list() {
    drop((0..2_000_000).collect::<LinkedList<i32>>());
}

// ———————————————————————————————————————————————————————————
// Tests for Step 5 (advanced): covariance and Send/Sync
// ———————————————————————————————————————————————————————————

// These are compile time tests. They won't compile unless your
// code passes.
// Additional tests for code that must *not* compile are in
// pre_implemented.rs for technical reasons.

#[cfg(feature = "advanced")]
#[test]
#[ignore]
fn advanced_linked_list_is_send_sync() {
    trait AssertSend: Send {}
    trait AssertSync: Sync {}

    impl<T: Send> AssertSend for LinkedList<T> {}
    impl<T: Sync> AssertSync for LinkedList<T> {}
}

#[cfg(feature = "advanced")]
#[allow(dead_code)]
#[test]
#[ignore]
fn advanced_is_covariant() {
    fn a<'a>(x: LinkedList<&'static str>) -> LinkedList<&'a str> {
        x
    }

    fn a_iter<'a>(i: Iter<'static, &'static str>) -> Iter<'a, &'a str> {
        i
    }
}
<|endoftext|>


<commit_before>#![allow(unstable)]
#![deny(unused_mut)]
extern crate zookeeper;

use std::time::Duration;
use zookeeper::{Acl, CreateMode, Watcher, WatchedEvent, ZooKeeper};
use zookeeper::perms;

use std::io::{BufferedReader, Command, Process};

struct LoggingWatcher;
impl Watcher for LoggingWatcher {
    fn handle(&self, e: &WatchedEvent) {
        println!("{:?}", e)
    }
}

fn start_zk() -> Process {
    match Command::new("java")
            .arg("-jar")
            .arg("zk-test-cluster/target/main.jar")
            .spawn() {
        Ok(p) => p,
        Err(e) => panic!("failed to execute process: {}", e),
    }
}

fn get_connect_string(server: &mut Process) -> String {
    let reader = server.stdout.take().unwrap();
    let mut reader = BufferedReader::new(reader);

    let mut connect_string = reader.read_line().unwrap();
    connect_string.pop(); // remove '\n'
    connect_string
}

#[test]
fn simple_integration_test() {
    // Create a test cluster and obtain its connection string
    let mut server = start_zk();
    let connect_string = get_connect_string(&mut server);

    // Connect to the test cluster
    let client = ZooKeeper::connect(connect_string.as_slice(), Duration::seconds(5), LoggingWatcher).unwrap();

    // Do the tests
    let acl1 = vec![Acl{perms: perms::ALL, scheme: "world".to_string(), id: "anyone".to_string()}];
    let create = client.create("/test", vec![8,8], acl1, CreateMode::Ephemeral);

    assert_eq!(create.ok(), Some("/test".to_string()));

    let exists = client.exists("/test", true);

    assert!(exists.is_ok());

    let children = client.get_children("/", true);

    assert_eq!(children.ok(), Some(vec!["zookeeper".to_string(), "test".to_string()]));

    // After closing the client all operations return Err
    client.close();

    let exists = client.exists("/test", true);

    assert!(exists.is_err());

    // Close the server
    server.signal_exit().unwrap();
}
<commit_msg>comparing against sorted get_children result<commit_after>#![allow(unstable)]
#![deny(unused_mut)]
extern crate zookeeper;

use zookeeper::{Acl, CreateMode, Watcher, WatchedEvent, ZooKeeper};
use zookeeper::perms;

use std::io::{BufferedReader, Command, Process};
use std::slice::SliceExt;
use std::time::Duration;

struct LoggingWatcher;
impl Watcher for LoggingWatcher {
    fn handle(&self, e: &WatchedEvent) {
        println!("{:?}", e)
    }
}

fn start_zk() -> Process {
    match Command::new("java")
            .arg("-jar")
            .arg("zk-test-cluster/target/main.jar")
            .spawn() {
        Ok(p) => p,
        Err(e) => panic!("failed to execute process: {}", e),
    }
}

fn get_connect_string(server: &mut Process) -> String {
    let reader = server.stdout.take().unwrap();
    let mut reader = BufferedReader::new(reader);

    let mut connect_string = reader.read_line().unwrap();
    connect_string.pop(); // remove '\n'
    connect_string
}

#[test]
fn simple_integration_test() {

    // Create a test cluster and obtain its connection string
    let mut server = start_zk();
    let connect_string = get_connect_string(&mut server);

    // Connect to the test cluster
    let client = ZooKeeper::connect(connect_string.as_slice(), Duration::seconds(5), LoggingWatcher).unwrap();

    // Do the tests
    let acl1 = vec![Acl{perms: perms::ALL, scheme: "world".to_string(), id: "anyone".to_string()}];
    let create = client.create("/test", vec![8,8], acl1, CreateMode::Ephemeral);

    assert_eq!(create.ok(), Some("/test".to_string()));

    let exists = client.exists("/test", true);

    assert!(exists.is_ok());

    let children = client.get_children("/", true);

    assert!(children.is_ok());

    let mut sorted_children = children.unwrap();
    sorted_children.sort();

    assert_eq!(sorted_children, vec!["test".to_string(), "zookeeper".to_string()]);

    // After closing the client all operations return Err
    client.close();

    let exists = client.exists("/test", true);

    assert!(exists.is_err());

    // Close the server
    server.signal_exit().unwrap();
}
<|endoftext|>


<commit_before>// ignore-windows: File handling is not implemented yet
// compile-flags: -Zmiri-disable-isolation

use std::fs::{File, create_dir, read_dir, remove_dir, remove_dir_all, remove_file, rename};
use std::io::{Read, Write, ErrorKind, Result, Seek, SeekFrom};
use std::path::{PathBuf, Path};

fn main() {
    test_file();
    test_file_clone();
    test_seek();
    test_metadata();
    test_symlink();
    test_errors();
    test_rename();
    test_directory();
}

/// Prepare: compute filename and make sure the file does not exist.
fn prepare(filename: &str) -> PathBuf {
    let tmp = std::env::temp_dir();
    let path = tmp.join(filename);
    // Clean the paths for robustness.
    remove_file(&path).ok();
    path
}

/// Prepare directory: compute directory name and make sure it does not exist.
fn prepare_dir(dirname: &str) -> PathBuf {
    let tmp = std::env::temp_dir();
    let path = tmp.join(&dirname);
    // Clean the directory for robustness.
    remove_dir_all(&path).ok();
    path
}

/// Prepare like above, and also write some initial content to the file.
fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf {
    let path = prepare(filename);
    let mut file = File::create(&path).unwrap();
    file.write(content).unwrap();
    path
}

fn test_file() {
    let bytes = b"Hello, World!\n";
    let path = prepare("miri_test_fs_file.txt");

    // Test creating, writing and closing a file (closing is tested when `file` is dropped).
    let mut file = File::create(&path).unwrap();
    // Writing 0 bytes should not change the file contents.
    file.write(&mut []).unwrap();
    assert_eq!(file.metadata().unwrap().len(), 0);

    file.write(bytes).unwrap();
    assert_eq!(file.metadata().unwrap().len(), bytes.len() as u64);
    // Test opening, reading and closing a file.
    let mut file = File::open(&path).unwrap();
    let mut contents = Vec::new();
    // Reading 0 bytes should not move the file pointer.
    file.read(&mut []).unwrap();
    // Reading until EOF should get the whole text.
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_file_clone() {
    let bytes = b"Hello, World!\n";
    let path = prepare_with_content("miri_test_fs_file_clone.txt", bytes);

    // Cloning a file should be successful.
    let file = File::open(&path).unwrap();
    let mut cloned = file.try_clone().unwrap();
    // Reading from a cloned file should get the same text.
    let mut contents = Vec::new();
    cloned.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_seek() {
    let bytes = b"Hello, entire World!\n";
    let path = prepare_with_content("miri_test_fs_seek.txt", bytes);

    let mut file = File::open(&path).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());
    // Test that seeking to the beginning and reading until EOF gets the text again.
    file.seek(SeekFrom::Start(0)).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());
    // Test seeking relative to the end of the file.
    file.seek(SeekFrom::End(-1)).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(&bytes[bytes.len() - 1..], contents.as_slice());
    // Test seeking relative to the current position.
    file.seek(SeekFrom::Start(5)).unwrap();
    file.seek(SeekFrom::Current(-3)).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(&bytes[2..], contents.as_slice());

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn check_metadata(bytes: &[u8], path: &Path) -> Result<()> {
    // Test that the file metadata is correct.
    let metadata = path.metadata()?;
    // `path` should point to a file.
    assert!(metadata.is_file());
    // The size of the file must be equal to the number of written bytes.
    assert_eq!(bytes.len() as u64, metadata.len());
    Ok(())
}

fn test_metadata() {
    let bytes = b"Hello, meta-World!\n";
    let path = prepare_with_content("miri_test_fs_metadata.txt", bytes);

    // Test that metadata of an absolute path is correct.
    check_metadata(bytes, &path).unwrap();
    // Test that metadata of a relative path is correct.
    std::env::set_current_dir(path.parent().unwrap()).unwrap();
    check_metadata(bytes, Path::new(path.file_name().unwrap())).unwrap();

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_symlink() {
    let bytes = b"Hello, World!\n";
    let path = prepare_with_content("miri_test_fs_link_target.txt", bytes);
    let symlink_path = prepare("miri_test_fs_symlink.txt");

    // Creating a symbolic link should succeed.
    std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
    // Test that the symbolic link has the same contents as the file.
    let mut symlink_file = File::open(&symlink_path).unwrap();
    let mut contents = Vec::new();
    symlink_file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());
    // Test that metadata of a symbolic link is correct.
    check_metadata(bytes, &symlink_path).unwrap();
    // Test that the metadata of a symbolic link is correct when not following it.
    assert!(symlink_path.symlink_metadata().unwrap().file_type().is_symlink());
    // Removing symbolic link should succeed.
    remove_file(&symlink_path).unwrap();

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_errors() {
    let bytes = b"Hello, World!\n";
    let path = prepare("miri_test_fs_errors.txt");

    // The following tests also check that the `__errno_location()` shim is working properly.
    // Opening a non-existing file should fail with a "not found" error.
    assert_eq!(ErrorKind::NotFound, File::open(&path).unwrap_err().kind());
    // Removing a non-existing file should fail with a "not found" error.
    assert_eq!(ErrorKind::NotFound, remove_file(&path).unwrap_err().kind());
    // Reading the metadata of a non-existing file should fail with a "not found" error.
    assert_eq!(ErrorKind::NotFound, check_metadata(bytes, &path).unwrap_err().kind());
}

fn test_rename() {
    // Renaming a file should succeed.
    let path1 = prepare("miri_test_fs_rename_source.txt");
    let path2 = prepare("miri_test_fs_rename_destination.txt");

    let file = File::create(&path1).unwrap();
    drop(file);

    // Renaming should succeed
    rename(&path1, &path2).unwrap();
    // Check that the old file path isn't present
    assert_eq!(ErrorKind::NotFound, path1.metadata().unwrap_err().kind());
    // Check that the file has moved successfully
    assert!(path2.metadata().unwrap().is_file());

    // Renaming a nonexistent file should fail
    assert_eq!(ErrorKind::NotFound, rename(&path1, &path2).unwrap_err().kind());

    remove_file(&path2).unwrap();
}

fn test_directory() {
    let dir_path = prepare_dir("miri_test_fs_dir");
    // Creating a directory should succeed.
    create_dir(&dir_path).unwrap();
    // Test that the metadata of a directory is correct.
    assert!(dir_path.metadata().unwrap().is_dir());

    // Create some files inside the directory
    let f1_path = dir_path.join("f1");
    drop(File::create(&f1_path).unwrap());
    let f2_path = dir_path.join("f2");
    drop(File::create(&f2_path).unwrap());
    // Test that the files are present inside the directory
    let mut dir_iter = read_dir(&dir_path).unwrap();
    let first_dir_entry = dir_iter.next().unwrap().unwrap();
    let second_dir_entry = dir_iter.next().unwrap().unwrap();
    assert!(first_dir_entry.file_name() == "f1" || first_dir_entry.file_name() == "f2");
    assert!(second_dir_entry.file_name() == "f1" || second_dir_entry.file_name() == "f2");
    assert!(dir_iter.next().is_none());
    drop(dir_iter);
    // Clean up the files in the directory
    remove_file(&f1_path).unwrap();
    remove_file(&f2_path).unwrap();

    // Deleting the directory should succeed.
    remove_dir(&dir_path).unwrap();
    // Reading the metadata of a non-existent file should fail with a "not found" error.
    assert_eq!(ErrorKind::NotFound, check_metadata(&[], &dir_path).unwrap_err().kind());
}
<commit_msg>Add more information to test asserts<commit_after>// ignore-windows: File handling is not implemented yet
// compile-flags: -Zmiri-disable-isolation

use std::fs::{File, create_dir, read_dir, remove_dir, remove_dir_all, remove_file, rename};
use std::io::{Read, Write, ErrorKind, Result, Seek, SeekFrom};
use std::path::{PathBuf, Path};

fn main() {
    test_file();
    test_file_clone();
    test_seek();
    test_metadata();
    test_symlink();
    test_errors();
    test_rename();
    test_directory();
}

/// Prepare: compute filename and make sure the file does not exist.
fn prepare(filename: &str) -> PathBuf {
    let tmp = std::env::temp_dir();
    let path = tmp.join(filename);
    // Clean the paths for robustness.
    remove_file(&path).ok();
    path
}

/// Prepare directory: compute directory name and make sure it does not exist.
fn prepare_dir(dirname: &str) -> PathBuf {
    let tmp = std::env::temp_dir();
    let path = tmp.join(&dirname);
    // Clean the directory for robustness.
    remove_dir_all(&path).ok();
    path
}

/// Prepare like above, and also write some initial content to the file.
fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf {
    let path = prepare(filename);
    let mut file = File::create(&path).unwrap();
    file.write(content).unwrap();
    path
}

fn test_file() {
    let bytes = b"Hello, World!\n";
    let path = prepare("miri_test_fs_file.txt");

    // Test creating, writing and closing a file (closing is tested when `file` is dropped).
    let mut file = File::create(&path).unwrap();
    // Writing 0 bytes should not change the file contents.
    file.write(&mut []).unwrap();
    assert_eq!(file.metadata().unwrap().len(), 0);

    file.write(bytes).unwrap();
    assert_eq!(file.metadata().unwrap().len(), bytes.len() as u64);
    // Test opening, reading and closing a file.
    let mut file = File::open(&path).unwrap();
    let mut contents = Vec::new();
    // Reading 0 bytes should not move the file pointer.
    file.read(&mut []).unwrap();
    // Reading until EOF should get the whole text.
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_file_clone() {
    let bytes = b"Hello, World!\n";
    let path = prepare_with_content("miri_test_fs_file_clone.txt", bytes);

    // Cloning a file should be successful.
    let file = File::open(&path).unwrap();
    let mut cloned = file.try_clone().unwrap();
    // Reading from a cloned file should get the same text.
    let mut contents = Vec::new();
    cloned.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_seek() {
    let bytes = b"Hello, entire World!\n";
    let path = prepare_with_content("miri_test_fs_seek.txt", bytes);

    let mut file = File::open(&path).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());
    // Test that seeking to the beginning and reading until EOF gets the text again.
    file.seek(SeekFrom::Start(0)).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());
    // Test seeking relative to the end of the file.
    file.seek(SeekFrom::End(-1)).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(&bytes[bytes.len() - 1..], contents.as_slice());
    // Test seeking relative to the current position.
    file.seek(SeekFrom::Start(5)).unwrap();
    file.seek(SeekFrom::Current(-3)).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(&bytes[2..], contents.as_slice());

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn check_metadata(bytes: &[u8], path: &Path) -> Result<()> {
    // Test that the file metadata is correct.
    let metadata = path.metadata()?;
    // `path` should point to a file.
    assert!(metadata.is_file());
    // The size of the file must be equal to the number of written bytes.
    assert_eq!(bytes.len() as u64, metadata.len());
    Ok(())
}

fn test_metadata() {
    let bytes = b"Hello, meta-World!\n";
    let path = prepare_with_content("miri_test_fs_metadata.txt", bytes);

    // Test that metadata of an absolute path is correct.
    check_metadata(bytes, &path).unwrap();
    // Test that metadata of a relative path is correct.
    std::env::set_current_dir(path.parent().unwrap()).unwrap();
    check_metadata(bytes, Path::new(path.file_name().unwrap())).unwrap();

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_symlink() {
    let bytes = b"Hello, World!\n";
    let path = prepare_with_content("miri_test_fs_link_target.txt", bytes);
    let symlink_path = prepare("miri_test_fs_symlink.txt");

    // Creating a symbolic link should succeed.
    std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
    // Test that the symbolic link has the same contents as the file.
    let mut symlink_file = File::open(&symlink_path).unwrap();
    let mut contents = Vec::new();
    symlink_file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());
    // Test that metadata of a symbolic link is correct.
    check_metadata(bytes, &symlink_path).unwrap();
    // Test that the metadata of a symbolic link is correct when not following it.
    assert!(symlink_path.symlink_metadata().unwrap().file_type().is_symlink());
    // Removing symbolic link should succeed.
    remove_file(&symlink_path).unwrap();

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_errors() {
    let bytes = b"Hello, World!\n";
    let path = prepare("miri_test_fs_errors.txt");

    // The following tests also check that the `__errno_location()` shim is working properly.
    // Opening a non-existing file should fail with a "not found" error.
    assert_eq!(ErrorKind::NotFound, File::open(&path).unwrap_err().kind());
    // Removing a non-existing file should fail with a "not found" error.
    assert_eq!(ErrorKind::NotFound, remove_file(&path).unwrap_err().kind());
    // Reading the metadata of a non-existing file should fail with a "not found" error.
    assert_eq!(ErrorKind::NotFound, check_metadata(bytes, &path).unwrap_err().kind());
}

fn test_rename() {
    // Renaming a file should succeed.
    let path1 = prepare("miri_test_fs_rename_source.txt");
    let path2 = prepare("miri_test_fs_rename_destination.txt");

    let file = File::create(&path1).unwrap();
    drop(file);

    // Renaming should succeed
    rename(&path1, &path2).unwrap();
    // Check that the old file path isn't present
    assert_eq!(ErrorKind::NotFound, path1.metadata().unwrap_err().kind());
    // Check that the file has moved successfully
    assert!(path2.metadata().unwrap().is_file());

    // Renaming a nonexistent file should fail
    assert_eq!(ErrorKind::NotFound, rename(&path1, &path2).unwrap_err().kind());

    remove_file(&path2).unwrap();
}

fn test_directory() {
    let dir_path = prepare_dir("miri_test_fs_dir");
    // Creating a directory should succeed.
    create_dir(&dir_path).unwrap();
    // Test that the metadata of a directory is correct.
    assert!(dir_path.metadata().unwrap().is_dir());

    // Create some files inside the directory
    let f1_path = dir_path.join("f1");
    drop(File::create(&f1_path).unwrap());
    let f2_path = dir_path.join("f2");
    drop(File::create(&f2_path).unwrap());
    // Test that the files are present inside the directory
    let mut dir_iter = read_dir(&dir_path).unwrap();
    let first_dir_entry = dir_iter.next().unwrap().unwrap();
    let second_dir_entry = dir_iter.next().unwrap().unwrap();
    assert!(
        first_dir_entry.file_name() == "f1" || first_dir_entry.file_name() == "f2",
        "File name was {:?} instead of f1 or f2",
        first_dir_entry.file_name(),
    );
    assert!(
        second_dir_entry.file_name() == "f1" || second_dir_entry.file_name() == "f2",
        "File name was {:?} instead of f1 or f2",
        second_dir_entry.file_name(),
    );
    assert!(dir_iter.next().is_none());
    drop(dir_iter);
    // Clean up the files in the directory
    remove_file(&f1_path).unwrap();
    remove_file(&f2_path).unwrap();

    // Deleting the directory should succeed.
    remove_dir(&dir_path).unwrap();
    // Reading the metadata of a non-existent file should fail with a "not found" error.
    assert_eq!(ErrorKind::NotFound, check_metadata(&[], &dir_path).unwrap_err().kind());
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::rc::Rc;

fn rc_refcell() -> i32 {
    let r = Rc::new(RefCell::new(42));
    *r.borrow_mut() += 10;
    let x = *r.borrow();
    x
}

fn rc_raw() {
    let r = Rc::new(0);
    let r2 = Rc::into_raw(r.clone());
    let r2 = unsafe { Rc::from_raw(r2) };
    assert!(Rc::ptr_eq(&r, &r2));
    drop(r);
    assert!(Rc::try_unwrap(r2).is_ok());
}

fn main() {
    rc_refcell();
    rc_raw();
}
<commit_msg>test new From instances for Rc<commit_after>use std::cell::RefCell;
use std::rc::Rc;

fn rc_refcell() {
    let r = Rc::new(RefCell::new(42));
    *r.borrow_mut() += 10;
    let x = *r.borrow();
    assert_eq!(x, 52);
}

fn rc_raw() {
    let r = Rc::new(0);
    let r2 = Rc::into_raw(r.clone());
    let r2 = unsafe { Rc::from_raw(r2) };
    assert!(Rc::ptr_eq(&r, &r2));
    drop(r);
    assert!(Rc::try_unwrap(r2).is_ok());
}

// Make sure this Rc doesn't fall apart when touched
fn check_unique_rc<T: ?Sized>(mut r: Rc<T>) {
    let r2 = r.clone();
    assert!(Rc::get_mut(&mut r).is_none());
    drop(r2);
    assert!(Rc::get_mut(&mut r).is_some());
}

fn rc_from() {
    check_unique_rc::<[_]>(Rc::from(&[1,2,3] as &[_]));
    check_unique_rc::<[_]>(Rc::from(vec![1,2,3]));
    check_unique_rc::<[_]>(Rc::from(Box::new([1,2,3]) as Box<[_]>));
    check_unique_rc::<str>(Rc::from("Hello, World!"));
}

fn main() {
    rc_refcell();
    rc_raw();
    rc_from();
}
<|endoftext|>