<commit_before>use toml_edit::Document;

#[track_caller]
fn run(toml: &str, msg: &str) {
    let doc = toml.parse::<Document>();

    let err = match doc {
        Err(e) => e.to_string(),
        _ => unreachable!("must fail"),
    };
    dbg!(&err);
    assert!(err.contains(msg));
}

macro_rules! t(
    ($name:ident, $msg:expr, $toml:expr) => (
        #[test]
        fn $name() {
            run(include_str!($toml), $msg);
        }
    )
);

t!(
    test_datetime_malformed_no_leads,
    "While parsing a Date-Time",
    "fixtures/invalid/datetime-malformed-no-leads.toml"
);
t!(
    test_datetime_malformed_no_secs,
    "While parsing a Date-Time",
    "fixtures/invalid/datetime-malformed-no-secs.toml"
);
t!(
    test_datetime_malformed_no_t,
    "",
    "fixtures/invalid/datetime-malformed-no-t.toml"
);
t!(
    test_datetime_malformed_with_milli,
    "While parsing a Date-Time",
    "fixtures/invalid/datetime-malformed-with-milli.toml"
);
t!(
    test_duplicate_keys,
    "Duplicate key",
    "fixtures/invalid/duplicate-keys.toml"
);
t!(
    test_duplicate_key_table,
    "Duplicate key",
    "fixtures/invalid/duplicate-key-table.toml"
);
t!(
    test_duplicate_tables,
    "Duplicate key",
    "fixtures/invalid/duplicate-tables.toml"
);
t!(
    test_duplicate_key_std_into_dotted,
    "Duplicate key",
    "fixtures/invalid/duplicate-key-std-into-dotted.toml"
);
t!(
    test_duplicate_key_dotted_into_std,
    "Duplicate key",
    "fixtures/invalid/duplicate-key-dotted-into-std.toml"
);
t!(
    test_empty_implicit_table,
    "While parsing a Table Header",
    "fixtures/invalid/empty-implicit-table.toml"
);
t!(
    test_empty_table,
    "While parsing a Table Header",
    "fixtures/invalid/empty-table.toml"
);
t!(
    test_float_leading_zero_neg,
    "Unexpected `3`",
    "fixtures/invalid/float-leading-zero-neg.toml"
);
t!(
    test_float_leading_zero_pos,
    "Unexpected `3`",
    "fixtures/invalid/float-leading-zero-pos.toml"
);
t!(
    test_float_leading_zero,
    "Unexpected `3`",
    "fixtures/invalid/float-leading-zero.toml"
);
t!(
    test_float_no_leading_zero,
    "Unexpected `.`",
    "fixtures/invalid/float-no-leading-zero.toml"
);
t!(
    test_float_no_trailing_digits,
    "While parsing a Float",
    "fixtures/invalid/float-no-trailing-digits.toml"
);
t!(
    test_float_underscore_after_point,
    "While parsing a Float",
    "fixtures/invalid/float-underscore-after-point.toml"
);
t!(
    test_float_underscore_after,
    "column 11",
    "fixtures/invalid/float-underscore-after.toml"
);
t!(
    test_float_underscore_before_point,
    "column 9",
    "fixtures/invalid/float-underscore-before-point.toml"
);
t!(
    test_float_underscore_before,
    "column 7",
    "fixtures/invalid/float-underscore-before.toml"
);
t!(
    test_integer_leading_zero_neg,
    "",
    "fixtures/invalid/integer-leading-zero-neg.toml"
);
t!(
    test_integer_leading_zero_pos,
    "",
    "fixtures/invalid/integer-leading-zero-pos.toml"
);
t!(
    test_integer_leading_zero,
    "",
    "fixtures/invalid/integer-leading-zero.toml"
);
t!(
    test_integer_underscore_after,
    "",
    "fixtures/invalid/integer-underscore-after.toml"
);
t!(
    test_integer_underscore_before,
    "",
    "fixtures/invalid/integer-underscore-before.toml"
);
t!(
    test_integer_underscore_double,
    "",
    "fixtures/invalid/integer-underscore-double.toml"
);
t!(
    test_integer_invalid_hex_char,
    "",
    "fixtures/invalid/integer-invalid-hex-char.toml"
);
t!(
    test_integer_invalid_octal_char,
    "",
    "fixtures/invalid/integer-invalid-octal-char.toml"
);
t!(
    test_integer_invalid_binary_char,
    "",
    "fixtures/invalid/integer-invalid-binary-char.toml"
);
t!(
    test_key_after_array,
    "",
    "fixtures/invalid/key-after-array.toml"
);
t!(
    test_key_after_table,
    "",
    "fixtures/invalid/key-after-table.toml"
);
t!(test_key_empty, "", "fixtures/invalid/key-empty.toml");
t!(test_key_hash, "", "fixtures/invalid/key-hash.toml");
t!(test_key_newline, "", "fixtures/invalid/key-newline.toml");
t!(test_key_no_eol, "", "fixtures/invalid/key-no-eol.toml");
t!(
    test_key_open_bracket,
    "",
    "fixtures/invalid/key-open-bracket.toml"
);
t!(
    test_key_single_open_bracket,
    "",
    "fixtures/invalid/key-single-open-bracket.toml"
);
t!(test_key_space, "", "fixtures/invalid/key-space.toml");
t!(
    test_key_start_bracket,
    "",
    "fixtures/invalid/key-start-bracket.toml"
);
t!(
    test_key_two_equals,
    "",
    "fixtures/invalid/key-two-equals.toml"
);
t!(test_llbrace, "", "fixtures/invalid/llbrace.toml");
t!(test_rrbrace, "", "fixtures/invalid/rrbrace.toml");
t!(
    test_string_bad_byte_escape,
    "",
    "fixtures/invalid/string-bad-byte-escape.toml"
);
t!(
    test_string_bad_escape,
    "",
    "fixtures/invalid/string-bad-escape.toml"
);
t!(
    test_string_bad_surrogate,
    "",
    "fixtures/invalid/string-bad-surrogate.toml"
);
t!(
    test_string_bad_uni_esc,
    "",
    "fixtures/invalid/string-bad-uni-esc.toml"
);
t!(
    test_string_byte_escapes,
    "",
    "fixtures/invalid/string-byte-escapes.toml"
);
t!(
    test_string_no_close,
    "",
    "fixtures/invalid/string-no-close.toml"
);
t!(
    test_table_array_implicit,
    "",
    "fixtures/invalid/table-array-implicit.toml"
);
t!(
    test_table_array_malformed_bracket,
    "",
    "fixtures/invalid/table-array-malformed-bracket.toml"
);
t!(
    test_table_array_malformed_empty,
    "",
    "fixtures/invalid/table-array-malformed-empty.toml"
);
t!(test_table_empty, "", "fixtures/invalid/table-empty.toml");
t!(
    test_table_nested_brackets_close,
    "",
    "fixtures/invalid/table-nested-brackets-close.toml"
);
t!(
    test_table_nested_brackets_open,
    "",
    "fixtures/invalid/table-nested-brackets-open.toml"
);
t!(
    test_table_whitespace,
    "",
    "fixtures/invalid/table-whitespace.toml"
);
t!(
    test_table_with_pound,
    "",
    "fixtures/invalid/table-with-pound.toml"
);
t!(
    test_text_after_array_entries,
    "",
    "fixtures/invalid/text-after-array-entries.toml"
);
t!(
    test_text_after_integer,
    "",
    "fixtures/invalid/text-after-integer.toml"
);
t!(
    test_text_after_string,
    "",
    "fixtures/invalid/text-after-string.toml"
);
t!(
    test_text_after_table,
    "",
    "fixtures/invalid/text-after-table.toml"
);
t!(
    test_text_before_array_separator,
    "",
    "fixtures/invalid/text-before-array-separator.toml"
);
t!(
    test_text_in_array,
    "",
    "fixtures/invalid/text-in-array.toml"
);
<commit_msg>refactor(test): Open up for other options<commit_after>use toml_edit::Document;

#[track_caller]
fn run_contains(toml: &str, msg: &str) {
    let doc = toml.parse::<Document>();

    let err = match doc {
        Err(e) => e.to_string(),
        _ => unreachable!("must fail"),
    };
    dbg!(msg);
    dbg!(&err);
    assert!(err.contains(msg));
}

macro_rules! t_file_contains(
    ($name:ident, $toml:expr, $msg:expr, ) => (
        #[test]
        fn $name() {
            run_contains(include_str!($toml), $msg);
        }
    )
);

t_file_contains!(
    test_datetime_malformed_no_leads,
    "fixtures/invalid/datetime-malformed-no-leads.toml",
    "While parsing a Date-Time",
);
t_file_contains!(
    test_datetime_malformed_no_secs,
    "fixtures/invalid/datetime-malformed-no-secs.toml",
    "While parsing a Date-Time",
);
t_file_contains!(
    test_datetime_malformed_no_t,
    "fixtures/invalid/datetime-malformed-no-t.toml",
    "",
);
t_file_contains!(
    test_datetime_malformed_with_milli,
    "fixtures/invalid/datetime-malformed-with-milli.toml",
    "While parsing a Date-Time",
);
t_file_contains!(
    test_duplicate_keys,
    "fixtures/invalid/duplicate-keys.toml",
    "Duplicate key",
);
t_file_contains!(
    test_duplicate_key_table,
    "fixtures/invalid/duplicate-key-table.toml",
    "Duplicate key",
);
t_file_contains!(
    test_duplicate_tables,
    "fixtures/invalid/duplicate-tables.toml",
    "Duplicate key",
);
t_file_contains!(
    test_duplicate_key_std_into_dotted,
    "fixtures/invalid/duplicate-key-std-into-dotted.toml",
    "Duplicate key",
);
t_file_contains!(
    test_duplicate_key_dotted_into_std,
    "fixtures/invalid/duplicate-key-dotted-into-std.toml",
    "Duplicate key",
);
t_file_contains!(
    test_empty_implicit_table,
    "fixtures/invalid/empty-implicit-table.toml",
    "While parsing a Table Header",
);
t_file_contains!(
    test_empty_table,
    "fixtures/invalid/empty-table.toml",
    "While parsing a Table Header",
);
t_file_contains!(
    test_float_leading_zero_neg,
    "fixtures/invalid/float-leading-zero-neg.toml",
    "Unexpected `3`",
);
t_file_contains!(
    test_float_leading_zero_pos,
    "fixtures/invalid/float-leading-zero-pos.toml",
    "Unexpected `3`",
);
t_file_contains!(
    test_float_leading_zero,
    "fixtures/invalid/float-leading-zero.toml",
    "Unexpected `3`",
);
t_file_contains!(
    test_float_no_leading_zero,
    "fixtures/invalid/float-no-leading-zero.toml",
    "Unexpected `.`",
);
t_file_contains!(
    test_float_no_trailing_digits,
    "fixtures/invalid/float-no-trailing-digits.toml",
    "While parsing a Float",
);
t_file_contains!(
    test_float_underscore_after_point,
    "fixtures/invalid/float-underscore-after-point.toml",
    "While parsing a Float",
);
t_file_contains!(
    test_float_underscore_after,
    "fixtures/invalid/float-underscore-after.toml",
    "column 11",
);
t_file_contains!(
    test_float_underscore_before_point,
    "fixtures/invalid/float-underscore-before-point.toml",
    "column 9",
);
t_file_contains!(
    test_float_underscore_before,
    "fixtures/invalid/float-underscore-before.toml",
    "column 7",
);
t_file_contains!(
    test_integer_leading_zero_neg,
    "fixtures/invalid/integer-leading-zero-neg.toml",
    "",
);
t_file_contains!(
    test_integer_leading_zero_pos,
    "fixtures/invalid/integer-leading-zero-pos.toml",
    "",
);
t_file_contains!(
    test_integer_leading_zero,
    "fixtures/invalid/integer-leading-zero.toml",
    "",
);
t_file_contains!(
    test_integer_underscore_after,
    "fixtures/invalid/integer-underscore-after.toml",
    "",
);
t_file_contains!(
    test_integer_underscore_before,
    "fixtures/invalid/integer-underscore-before.toml",
    "",
);
t_file_contains!(
    test_integer_underscore_double,
    "fixtures/invalid/integer-underscore-double.toml",
    "",
);
t_file_contains!(
    test_integer_invalid_hex_char,
    "fixtures/invalid/integer-invalid-hex-char.toml",
    "",
);
t_file_contains!(
    test_integer_invalid_octal_char,
    "fixtures/invalid/integer-invalid-octal-char.toml",
    "",
);
t_file_contains!(
    test_integer_invalid_binary_char,
    "fixtures/invalid/integer-invalid-binary-char.toml",
    "",
);
t_file_contains!(
    test_key_after_array,
    "fixtures/invalid/key-after-array.toml",
    "",
);
t_file_contains!(
    test_key_after_table,
    "fixtures/invalid/key-after-table.toml",
    "",
);
t_file_contains!(test_key_empty, "fixtures/invalid/key-empty.toml", "",);
t_file_contains!(test_key_hash, "fixtures/invalid/key-hash.toml", "",);
t_file_contains!(test_key_newline, "fixtures/invalid/key-newline.toml", "",);
t_file_contains!(test_key_no_eol, "fixtures/invalid/key-no-eol.toml", "",);
t_file_contains!(
    test_key_open_bracket,
    "fixtures/invalid/key-open-bracket.toml",
    "",
);
t_file_contains!(
    test_key_single_open_bracket,
    "fixtures/invalid/key-single-open-bracket.toml",
    "",
);
t_file_contains!(test_key_space, "fixtures/invalid/key-space.toml", "",);
t_file_contains!(
    test_key_start_bracket,
    "fixtures/invalid/key-start-bracket.toml",
    "",
);
t_file_contains!(
    test_key_two_equals,
    "fixtures/invalid/key-two-equals.toml",
    "",
);
t_file_contains!(test_llbrace, "fixtures/invalid/llbrace.toml", "",);
t_file_contains!(test_rrbrace, "fixtures/invalid/rrbrace.toml", "",);
t_file_contains!(
    test_string_bad_byte_escape,
    "fixtures/invalid/string-bad-byte-escape.toml",
    "",
);
t_file_contains!(
    test_string_bad_escape,
    "fixtures/invalid/string-bad-escape.toml",
    "",
);
t_file_contains!(
    test_string_bad_surrogate,
    "fixtures/invalid/string-bad-surrogate.toml",
    "",
);
t_file_contains!(
    test_string_bad_uni_esc,
    "fixtures/invalid/string-bad-uni-esc.toml",
    "",
);
t_file_contains!(
    test_string_byte_escapes,
    "fixtures/invalid/string-byte-escapes.toml",
    "",
);
t_file_contains!(
    test_string_no_close,
    "fixtures/invalid/string-no-close.toml",
    "",
);
t_file_contains!(
    test_table_array_implicit,
    "fixtures/invalid/table-array-implicit.toml",
    "",
);
t_file_contains!(
    test_table_array_malformed_bracket,
    "fixtures/invalid/table-array-malformed-bracket.toml",
    "",
);
t_file_contains!(
    test_table_array_malformed_empty,
    "fixtures/invalid/table-array-malformed-empty.toml",
    "",
);
t_file_contains!(test_table_empty, "fixtures/invalid/table-empty.toml", "",);
t_file_contains!(
    test_table_nested_brackets_close,
    "fixtures/invalid/table-nested-brackets-close.toml",
    "",
);
t_file_contains!(
    test_table_nested_brackets_open,
    "fixtures/invalid/table-nested-brackets-open.toml",
    "",
);
t_file_contains!(
    test_table_whitespace,
    "fixtures/invalid/table-whitespace.toml",
    "",
);
t_file_contains!(
    test_table_with_pound,
    "fixtures/invalid/table-with-pound.toml",
    "",
);
t_file_contains!(
    test_text_after_array_entries,
    "fixtures/invalid/text-after-array-entries.toml",
    "",
);
t_file_contains!(
    test_text_after_integer,
    "fixtures/invalid/text-after-integer.toml",
    "",
);
t_file_contains!(
    test_text_after_string,
    "fixtures/invalid/text-after-string.toml",
    "",
);
t_file_contains!(
    test_text_after_table,
    "fixtures/invalid/text-after-table.toml",
    "",
);
t_file_contains!(
    test_text_before_array_separator,
    "fixtures/invalid/text-before-array-separator.toml",
    "",
);
t_file_contains!(
    test_text_in_array,
    "fixtures/invalid/text-in-array.toml",
    "",
);
<|endoftext|>


<commit_before>#![deny(rust_2018_idioms)]

use cargo::{
    core::{
        compiler::{CompileKind, CompileTarget, TargetInfo},
        package::PackageSet,
        registry::PackageRegistry,
        resolver::{self, features::RequestedFeatures, ResolveOpts, VersionPreferences},
        source::SourceMap,
        Dependency, Package, PackageId, Source, SourceId, TargetKind,
    },
    sources::RegistrySource,
    util::{interning::InternedString, Config, VersionExt},
};
use itertools::Itertools;
use semver::Version;
use serde::{Deserialize, Serialize};
use std::{
    collections::{BTreeMap, BTreeSet, HashSet},
    io::Read,
    task::Poll,
};

const PLAYGROUND_TARGET_PLATFORM: &str = "x86_64-unknown-linux-gnu";

struct GlobalState<'cfg> {
    config: &'cfg Config,
    target_info: TargetInfo,
    registry: PackageRegistry<'cfg>,
    crates_io: SourceId,
    source: RegistrySource<'cfg>,
    modifications: &'cfg Modifications,
}

/// The list of crates from crates.io
#[derive(Debug, Deserialize)]
struct TopCrates {
    crates: Vec<Crate>,
}

/// The shared description of a crate
#[derive(Debug, Deserialize)]
struct Crate {
    #[serde(rename = "id")]
    name: InternedString,
}

/// A mapping of a crates name to its identifier used in source code
#[derive(Debug, Serialize)]
pub struct CrateInformation {
    pub name: String,
    pub version: Version,
    pub id: String,
}

/// Hand-curated changes to the crate list
#[derive(Debug, Default, Deserialize)]
pub struct Modifications {
    #[serde(default)]
    pub exclusions: Vec<InternedString>,
    #[serde(default)]
    pub additions: BTreeSet<InternedString>,
}

#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct DependencySpec {
    #[serde(skip_serializing_if = "String::is_empty")]
    pub package: String,
    #[serde(serialize_with = "exact_version")]
    pub version: Version,
    #[serde(skip_serializing_if = "BTreeSet::is_empty")]
    pub features: BTreeSet<InternedString>,
    #[serde(skip_serializing_if = "is_true")]
    pub default_features: bool,
}

fn exact_version<S>(version: &Version, serializer: S) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    semver::Comparator {
        op: semver::Op::Exact,
        major: version.major,
        minor: Some(version.minor),
        patch: Some(version.patch),
        pre: version.pre.clone(),
    }
    .serialize(serializer)
}

fn is_true(b: &bool) -> bool {
    *b
}

impl Modifications {
    fn excluded(&self, name: &str) -> bool {
        self.exclusions.iter().any(|n| n == name)
    }
}

fn simple_get(url: &str) -> reqwest::Result<reqwest::blocking::Response> {
    reqwest::blocking::ClientBuilder::new()
        .user_agent("Rust Playground - Top Crates Utility")
        .build()?
        .get(url)
        .send()
}

impl TopCrates {
    /// List top 100 crates by number of downloads on crates.io.
    fn download() -> TopCrates {
        let resp = simple_get("https://crates.io/api/v1/crates?page=1&per_page=100&sort=downloads")
            .expect("Could not fetch top crates");
        assert!(
            resp.status().is_success(),
            "Could not download top crates; HTTP status was {}",
            resp.status(),
        );

        serde_json::from_reader(resp).expect("Invalid JSON")
    }

    fn add_rust_cookbook_crates(&mut self) {
        let mut resp = simple_get(
            "https://raw.githubusercontent.com/rust-lang-nursery/rust-cookbook/master/Cargo.toml",
        )
        .expect("Could not fetch cookbook manifest");
        assert!(
            resp.status().is_success(),
            "Could not download cookbook; HTTP status was {}",
            resp.status(),
        );

        let mut content = String::new();
        resp.read_to_string(&mut content)
            .expect("could not read cookbook manifest");

        let manifest = content
            .parse::<toml::Value>()
            .expect("could not parse cookbook manifest");

        let dependencies = manifest["dependencies"]
            .as_table()
            .expect("no dependencies found for cookbook manifest");
        self.crates.extend({
            dependencies.iter().map(|(name, _)| Crate {
                name: InternedString::new(name),
            })
        })
    }

    /// Add crates that have been hand-picked
    fn add_curated_crates(&mut self, modifications: &Modifications) {
        self.crates.extend({
            modifications
                .additions
                .iter()
                .copied()
                .map(|name| Crate { name })
        });
    }
}

/// Finds the features specified by the custom metadata of `pkg`.
///
/// Our custom metadata format looks like:
///
///     [package.metadata.playground]
///     default-features = true
///     features = ["std", "extra-traits"]
///     all-features = false
///
/// All fields are optional.
fn playground_metadata_features(pkg: &Package) -> Option<(BTreeSet<InternedString>, bool)> {
    let custom_metadata = pkg.manifest().custom_metadata()?;
    let playground_metadata = custom_metadata.get("playground")?;

    #[derive(Deserialize)]
    #[serde(default, rename_all = "kebab-case")]
    struct Metadata {
        features: BTreeSet<InternedString>,
        default_features: bool,
        all_features: bool,
    }

    impl Default for Metadata {
        fn default() -> Self {
            Metadata {
                features: BTreeSet::new(),
                default_features: true,
                all_features: false,
            }
        }
    }

    let metadata = match playground_metadata.clone().try_into::<Metadata>() {
        Ok(metadata) => metadata,
        Err(err) => {
            eprintln!(
                "Failed to parse custom metadata for {} {}: {}",
                pkg.name(),
                pkg.version(),
                err
            );
            return None;
        }
    };

    // If `all-features` is set then we ignore `features`.
    let summary = pkg.summary();
    let mut enabled_features: BTreeSet<InternedString> = if metadata.all_features {
        summary.features().keys().copied().collect()
    } else {
        metadata.features
    };

    // If not opting out of default features, remove default features from the
    // explicit features list. This avoids ongoing spurious diffs in our
    // generated Cargo.toml as default features are added to a library.
    if metadata.default_features {
        if let Some(default_feature_names) = summary.features().get("default") {
            enabled_features.remove("default");
            for feature in default_feature_names {
                enabled_features.remove(&InternedString::new(&feature.to_string()));
            }
        }
    }

    if !enabled_features.is_empty() || !metadata.default_features {
        Some((enabled_features, metadata.default_features))
    } else {
        None
    }
}

fn make_global_state<'cfg>(
    config: &'cfg Config,
    modifications: &'cfg Modifications,
) -> GlobalState<'cfg> {
    // Information about the playground's target platform.
    let compile_target =
        CompileTarget::new(PLAYGROUND_TARGET_PLATFORM).expect("Unable to create a CompileTarget");
    let compile_kind = CompileKind::Target(compile_target);
    let rustc = config
        .load_global_rustc(None)
        .expect("Unable to load the global rustc");
    let target_info = TargetInfo::new(config, &[compile_kind], &rustc, compile_kind)
        .expect("Unable to create a TargetInfo");

    // Registry of known packages.
    let mut registry = PackageRegistry::new(config).expect("Unable to create package registry");
    registry.lock_patches();

    // Source for obtaining packages from the crates.io registry.
    let crates_io = SourceId::crates_io(config).expect("Unable to create crates.io source ID");
    let yanked_whitelist = HashSet::new();
    let mut source = RegistrySource::remote(crates_io, &yanked_whitelist, config)
        .expect("Unable to create registry source");
    source.invalidate_cache();
    source
        .block_until_ready()
        .expect("Unable to wait for registry to be ready");

    GlobalState {
        config,
        target_info,
        registry,
        crates_io,
        source,
        modifications,
    }
}

fn bulk_download(global: &mut GlobalState<'_>, package_ids: &[PackageId]) -> Vec<Package> {
    let mut sources = SourceMap::new();
    sources.insert(Box::new(&mut global.source));

    let package_set = PackageSet::new(package_ids, sources, global.config)
        .expect("Unable to create a PackageSet");

    package_set
        .get_many(package_set.package_ids())
        .expect("Unable to download packages")
        .into_iter()
        .map(Package::clone)
        .collect()
}

pub fn generate_info(
    modifications: &Modifications,
) -> (BTreeMap<String, DependencySpec>, Vec<CrateInformation>) {
    // Setup to interact with cargo.
    let config = Config::default().expect("Unable to create default Cargo config");
    let _lock = config.acquire_package_cache_lock();
    let mut global = make_global_state(&config, modifications);

    let mut top = TopCrates::download();
    top.add_rust_cookbook_crates();
    top.add_curated_crates(global.modifications);

    // Find the newest (non-prerelease, non-yanked) versions of all
    // the interesting crates.
    let mut summaries = Vec::new();
    for Crate { name } in top.crates {
        if global.modifications.excluded(&name) {
            continue;
        }

        // Query the registry for a summary of this crate.
        // Usefully, this doesn't seem to include yanked versions
        let version = None;
        let dep = Dependency::parse(name, version, global.crates_io)
            .unwrap_or_else(|e| panic!("Unable to parse dependency for {}: {}", name, e));

        let matches = match global.source.query_vec(&dep) {
            Poll::Ready(Ok(v)) => v,
            Poll::Ready(Err(e)) => panic!("Unable to query registry for {}: {}", name, e),
            Poll::Pending => panic!("Registry not ready to query"),
        };

        // Find the newest non-prelease version
        let summary = matches
            .into_iter()
            .filter(|summary| !summary.version().is_prerelease())
            .max_by_key(|summary| summary.version().clone())
            .unwrap_or_else(|| panic!("Registry has no viable versions of {}", name));

        // Add a dependency on this crate.
        summaries.push((
            summary,
            ResolveOpts {
                dev_deps: false,
                features: RequestedFeatures::DepFeatures {
                    features: Default::default(),
                    uses_default_features: true,
                },
            },
        ));
    }

    // Resolve transitive dependencies.
    let replacements = [];
    let version_prefs = VersionPreferences::default();
    let warnings = None;
    let check_public_visible_dependencies = true;
    let resolve = resolver::resolve(
        &summaries,
        &replacements,
        &mut global.registry,
        &version_prefs,
        warnings,
        check_public_visible_dependencies,
    )
    .expect("Unable to resolve dependencies");

    // Find crates incompatible with the playground's platform
    let mut valid_for_our_platform: BTreeSet<_> =
        summaries.iter().map(|(s, _)| s.package_id()).collect();

    let mut to_visit = valid_for_our_platform.clone();

    while !to_visit.is_empty() {
        let mut visit_next = BTreeSet::new();

        for package_id in to_visit {
            for (dep_pkg, deps) in resolve.deps(package_id) {
                let for_this_platform = deps.iter().any(|dep| {
                    dep.platform().map_or(true, |platform| {
                        platform.matches(PLAYGROUND_TARGET_PLATFORM, global.target_info.cfg())
                    })
                });

                if for_this_platform {
                    valid_for_our_platform.insert(dep_pkg);
                    visit_next.insert(dep_pkg);
                }
            }
        }

        to_visit = visit_next;
    }

    // Remove invalid and excluded packages that have been added due to resolution
    let package_ids: Vec<_> = resolve
        .iter()
        .filter(|pkg| valid_for_our_platform.contains(pkg))
        .filter(|pkg| !global.modifications.excluded(pkg.name().as_str()))
        .collect();

    let mut packages = bulk_download(&mut global, &package_ids);

    // Sort all packages by name then version (descending), so that
    // when we group them we know we get all the same crates together
    // and the newest version first.
    packages.sort_by(|a, b| {
        a.name()
            .cmp(&b.name())
            .then(a.version().cmp(&b.version()).reverse())
    });

    let mut dependencies = BTreeMap::new();
    let mut infos = Vec::new();

    for (name, pkgs) in &packages.into_iter().group_by(|pkg| pkg.name()) {
        let mut first = true;

        for pkg in pkgs {
            let version = pkg.version();

            let crate_name = pkg
                .targets()
                .iter()
                .flat_map(|target| match target.kind() {
                    TargetKind::Lib(_) => Some(target.crate_name()),
                    _ => None,
                })
                .next()
                .unwrap_or_else(|| panic!("{} did not have a library", name));

            // We see the newest version first. Any subsequent
            // versions will have their version appended so that they
            // are uniquely named
            let exposed_name = if first {
                crate_name.clone()
            } else {
                format!(
                    "{}_{}_{}_{}",
                    crate_name, version.major, version.minor, version.patch
                )
            };

            let (features, default_features) =
                playground_metadata_features(&pkg).unwrap_or_else(|| (BTreeSet::new(), true));

            dependencies.insert(
                exposed_name.clone(),
                DependencySpec {
                    package: name.to_string(),
                    version: version.clone(),
                    features,
                    default_features,
                },
            );

            infos.push(CrateInformation {
                name: name.to_string(),
                version: version.clone(),
                id: exposed_name,
            });

            first = false;
        }
    }

    (dependencies, infos)
}
<commit_msg>Factor out populate_initial_direct_dependencies<commit_after>#![deny(rust_2018_idioms)]

use cargo::{
    core::{
        compiler::{CompileKind, CompileTarget, TargetInfo},
        package::PackageSet,
        registry::PackageRegistry,
        resolver::{self, features::RequestedFeatures, ResolveOpts, VersionPreferences},
        source::SourceMap,
        Dependency, Package, PackageId, Source, SourceId, Summary, TargetKind,
    },
    sources::RegistrySource,
    util::{interning::InternedString, Config, VersionExt},
};
use itertools::Itertools;
use semver::Version;
use serde::{Deserialize, Serialize};
use std::{
    collections::{BTreeMap, BTreeSet, HashSet},
    io::Read,
    task::Poll,
};

const PLAYGROUND_TARGET_PLATFORM: &str = "x86_64-unknown-linux-gnu";

struct GlobalState<'cfg> {
    config: &'cfg Config,
    target_info: TargetInfo,
    registry: PackageRegistry<'cfg>,
    crates_io: SourceId,
    source: RegistrySource<'cfg>,
    modifications: &'cfg Modifications,
}

/// The list of crates from crates.io
#[derive(Debug, Deserialize)]
struct TopCrates {
    crates: Vec<Crate>,
}

/// The shared description of a crate
#[derive(Debug, Deserialize)]
struct Crate {
    #[serde(rename = "id")]
    name: InternedString,
}

/// A mapping of a crates name to its identifier used in source code
#[derive(Debug, Serialize)]
pub struct CrateInformation {
    pub name: String,
    pub version: Version,
    pub id: String,
}

/// Hand-curated changes to the crate list
#[derive(Debug, Default, Deserialize)]
pub struct Modifications {
    #[serde(default)]
    pub exclusions: Vec<InternedString>,
    #[serde(default)]
    pub additions: BTreeSet<InternedString>,
}

#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct DependencySpec {
    #[serde(skip_serializing_if = "String::is_empty")]
    pub package: String,
    #[serde(serialize_with = "exact_version")]
    pub version: Version,
    #[serde(skip_serializing_if = "BTreeSet::is_empty")]
    pub features: BTreeSet<InternedString>,
    #[serde(skip_serializing_if = "is_true")]
    pub default_features: bool,
}

fn exact_version<S>(version: &Version, serializer: S) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    semver::Comparator {
        op: semver::Op::Exact,
        major: version.major,
        minor: Some(version.minor),
        patch: Some(version.patch),
        pre: version.pre.clone(),
    }
    .serialize(serializer)
}

fn is_true(b: &bool) -> bool {
    *b
}

impl Modifications {
    fn excluded(&self, name: &str) -> bool {
        self.exclusions.iter().any(|n| n == name)
    }
}

fn simple_get(url: &str) -> reqwest::Result<reqwest::blocking::Response> {
    reqwest::blocking::ClientBuilder::new()
        .user_agent("Rust Playground - Top Crates Utility")
        .build()?
        .get(url)
        .send()
}

impl TopCrates {
    /// List top 100 crates by number of downloads on crates.io.
    fn download() -> TopCrates {
        let resp = simple_get("https://crates.io/api/v1/crates?page=1&per_page=100&sort=downloads")
            .expect("Could not fetch top crates");
        assert!(
            resp.status().is_success(),
            "Could not download top crates; HTTP status was {}",
            resp.status(),
        );

        serde_json::from_reader(resp).expect("Invalid JSON")
    }

    fn add_rust_cookbook_crates(&mut self) {
        let mut resp = simple_get(
            "https://raw.githubusercontent.com/rust-lang-nursery/rust-cookbook/master/Cargo.toml",
        )
        .expect("Could not fetch cookbook manifest");
        assert!(
            resp.status().is_success(),
            "Could not download cookbook; HTTP status was {}",
            resp.status(),
        );

        let mut content = String::new();
        resp.read_to_string(&mut content)
            .expect("could not read cookbook manifest");

        let manifest = content
            .parse::<toml::Value>()
            .expect("could not parse cookbook manifest");

        let dependencies = manifest["dependencies"]
            .as_table()
            .expect("no dependencies found for cookbook manifest");
        self.crates.extend({
            dependencies.iter().map(|(name, _)| Crate {
                name: InternedString::new(name),
            })
        })
    }

    /// Add crates that have been hand-picked
    fn add_curated_crates(&mut self, modifications: &Modifications) {
        self.crates.extend({
            modifications
                .additions
                .iter()
                .copied()
                .map(|name| Crate { name })
        });
    }
}

/// Finds the features specified by the custom metadata of `pkg`.
///
/// Our custom metadata format looks like:
///
///     [package.metadata.playground]
///     default-features = true
///     features = ["std", "extra-traits"]
///     all-features = false
///
/// All fields are optional.
fn playground_metadata_features(pkg: &Package) -> Option<(BTreeSet<InternedString>, bool)> {
    let custom_metadata = pkg.manifest().custom_metadata()?;
    let playground_metadata = custom_metadata.get("playground")?;

    #[derive(Deserialize)]
    #[serde(default, rename_all = "kebab-case")]
    struct Metadata {
        features: BTreeSet<InternedString>,
        default_features: bool,
        all_features: bool,
    }

    impl Default for Metadata {
        fn default() -> Self {
            Metadata {
                features: BTreeSet::new(),
                default_features: true,
                all_features: false,
            }
        }
    }

    let metadata = match playground_metadata.clone().try_into::<Metadata>() {
        Ok(metadata) => metadata,
        Err(err) => {
            eprintln!(
                "Failed to parse custom metadata for {} {}: {}",
                pkg.name(),
                pkg.version(),
                err
            );
            return None;
        }
    };

    // If `all-features` is set then we ignore `features`.
    let summary = pkg.summary();
    let mut enabled_features: BTreeSet<InternedString> = if metadata.all_features {
        summary.features().keys().copied().collect()
    } else {
        metadata.features
    };

    // If not opting out of default features, remove default features from the
    // explicit features list. This avoids ongoing spurious diffs in our
    // generated Cargo.toml as default features are added to a library.
    if metadata.default_features {
        if let Some(default_feature_names) = summary.features().get("default") {
            enabled_features.remove("default");
            for feature in default_feature_names {
                enabled_features.remove(&InternedString::new(&feature.to_string()));
            }
        }
    }

    if !enabled_features.is_empty() || !metadata.default_features {
        Some((enabled_features, metadata.default_features))
    } else {
        None
    }
}

fn make_global_state<'cfg>(
    config: &'cfg Config,
    modifications: &'cfg Modifications,
) -> GlobalState<'cfg> {
    // Information about the playground's target platform.
    let compile_target =
        CompileTarget::new(PLAYGROUND_TARGET_PLATFORM).expect("Unable to create a CompileTarget");
    let compile_kind = CompileKind::Target(compile_target);
    let rustc = config
        .load_global_rustc(None)
        .expect("Unable to load the global rustc");
    let target_info = TargetInfo::new(config, &[compile_kind], &rustc, compile_kind)
        .expect("Unable to create a TargetInfo");

    // Registry of known packages.
    let mut registry = PackageRegistry::new(config).expect("Unable to create package registry");
    registry.lock_patches();

    // Source for obtaining packages from the crates.io registry.
    let crates_io = SourceId::crates_io(config).expect("Unable to create crates.io source ID");
    let yanked_whitelist = HashSet::new();
    let mut source = RegistrySource::remote(crates_io, &yanked_whitelist, config)
        .expect("Unable to create registry source");
    source.invalidate_cache();
    source
        .block_until_ready()
        .expect("Unable to wait for registry to be ready");

    GlobalState {
        config,
        target_info,
        registry,
        crates_io,
        source,
        modifications,
    }
}

fn bulk_download(global: &mut GlobalState<'_>, package_ids: &[PackageId]) -> Vec<Package> {
    let mut sources = SourceMap::new();
    sources.insert(Box::new(&mut global.source));

    let package_set = PackageSet::new(package_ids, sources, global.config)
        .expect("Unable to create a PackageSet");

    package_set
        .get_many(package_set.package_ids())
        .expect("Unable to download packages")
        .into_iter()
        .map(Package::clone)
        .collect()
}

fn populate_initial_direct_dependencies(
    global: &mut GlobalState<'_>,
) -> Vec<(Summary, ResolveOpts)> {
    let mut top = TopCrates::download();
    top.add_rust_cookbook_crates();
    top.add_curated_crates(global.modifications);

    // Find the newest (non-prerelease, non-yanked) versions of all
    // the interesting crates.
    let mut summaries = Vec::new();
    for Crate { name } in top.crates {
        if global.modifications.excluded(&name) {
            continue;
        }

        // Query the registry for a summary of this crate.
        // Usefully, this doesn't seem to include yanked versions
        let version = None;
        let dep = Dependency::parse(name, version, global.crates_io)
            .unwrap_or_else(|e| panic!("Unable to parse dependency for {}: {}", name, e));

        let matches = match global.source.query_vec(&dep) {
            Poll::Ready(Ok(v)) => v,
            Poll::Ready(Err(e)) => panic!("Unable to query registry for {}: {}", name, e),
            Poll::Pending => panic!("Registry not ready to query"),
        };

        // Find the newest non-prelease version
        let summary = matches
            .into_iter()
            .filter(|summary| !summary.version().is_prerelease())
            .max_by_key(|summary| summary.version().clone())
            .unwrap_or_else(|| panic!("Registry has no viable versions of {}", name));

        // Add a dependency on this crate.
        summaries.push((
            summary,
            ResolveOpts {
                dev_deps: false,
                features: RequestedFeatures::DepFeatures {
                    features: Default::default(),
                    uses_default_features: true,
                },
            },
        ));
    }

    summaries
}

pub fn generate_info(
    modifications: &Modifications,
) -> (BTreeMap<String, DependencySpec>, Vec<CrateInformation>) {
    // Setup to interact with cargo.
    let config = Config::default().expect("Unable to create default Cargo config");
    let _lock = config.acquire_package_cache_lock();
    let mut global = make_global_state(&config, modifications);

    let summaries = populate_initial_direct_dependencies(&mut global);

    // Resolve transitive dependencies.
    let replacements = [];
    let version_prefs = VersionPreferences::default();
    let warnings = None;
    let check_public_visible_dependencies = true;
    let resolve = resolver::resolve(
        &summaries,
        &replacements,
        &mut global.registry,
        &version_prefs,
        warnings,
        check_public_visible_dependencies,
    )
    .expect("Unable to resolve dependencies");

    // Find crates incompatible with the playground's platform
    let mut valid_for_our_platform: BTreeSet<_> =
        summaries.iter().map(|(s, _)| s.package_id()).collect();

    let mut to_visit = valid_for_our_platform.clone();

    while !to_visit.is_empty() {
        let mut visit_next = BTreeSet::new();

        for package_id in to_visit {
            for (dep_pkg, deps) in resolve.deps(package_id) {
                let for_this_platform = deps.iter().any(|dep| {
                    dep.platform().map_or(true, |platform| {
                        platform.matches(PLAYGROUND_TARGET_PLATFORM, global.target_info.cfg())
                    })
                });

                if for_this_platform {
                    valid_for_our_platform.insert(dep_pkg);
                    visit_next.insert(dep_pkg);
                }
            }
        }

        to_visit = visit_next;
    }

    // Remove invalid and excluded packages that have been added due to resolution
    let package_ids: Vec<_> = resolve
        .iter()
        .filter(|pkg| valid_for_our_platform.contains(pkg))
        .filter(|pkg| !global.modifications.excluded(pkg.name().as_str()))
        .collect();

    let mut packages = bulk_download(&mut global, &package_ids);

    // Sort all packages by name then version (descending), so that
    // when we group them we know we get all the same crates together
    // and the newest version first.
    packages.sort_by(|a, b| {
        a.name()
            .cmp(&b.name())
            .then(a.version().cmp(&b.version()).reverse())
    });

    let mut dependencies = BTreeMap::new();
    let mut infos = Vec::new();

    for (name, pkgs) in &packages.into_iter().group_by(|pkg| pkg.name()) {
        let mut first = true;

        for pkg in pkgs {
            let version = pkg.version();

            let crate_name = pkg
                .targets()
                .iter()
                .flat_map(|target| match target.kind() {
                    TargetKind::Lib(_) => Some(target.crate_name()),
                    _ => None,
                })
                .next()
                .unwrap_or_else(|| panic!("{} did not have a library", name));

            // We see the newest version first. Any subsequent
            // versions will have their version appended so that they
            // are uniquely named
            let exposed_name = if first {
                crate_name.clone()
            } else {
                format!(
                    "{}_{}_{}_{}",
                    crate_name, version.major, version.minor, version.patch
                )
            };

            let (features, default_features) =
                playground_metadata_features(&pkg).unwrap_or_else(|| (BTreeSet::new(), true));

            dependencies.insert(
                exposed_name.clone(),
                DependencySpec {
                    package: name.to_string(),
                    version: version.clone(),
                    features,
                    default_features,
                },
            );

            infos.push(CrateInformation {
                name: name.to_string(),
                version: version.clone(),
                id: exposed_name,
            });

            first = false;
        }
    }

    (dependencies, infos)
}
<|endoftext|>


<commit_before>use futures::{Async, AsyncSink, Future, Poll};
use futures::stream::Stream;
use futures::sink::Sink;
use state_machine_future::RentToOwn;
use tokio_core::reactor::Handle;
use tokio_retry::{self, Retry};
use tokio_retry::strategy::{FibonacciBackoff, jitter};
use std::io;
use std::error;
use std::time::{Duration, Instant};
use std::fmt;
use std::collections::VecDeque;

pub type ConnectionError = io::Error;
pub type SessionId = u32;

#[derive(Debug, Clone, PartialEq)]
pub enum Event {
    Connected {
        session_id: SessionId,
    }
}

pub enum Message<Item> {
    Packet {
        session_id: SessionId,
        content: Item,
    },
    Control {
        event: Event,
    }
}

pub struct TimestampedItem<Item> {
    pub timestamp: Instant,
    pub item: Item,
}

impl <Item> TimestampedItem<Item> {
    pub fn new(item: Item) -> TimestampedItem<Item> {
        TimestampedItem {
            timestamp: Instant::now(),
            item: item,
        }
    }

    pub fn age(&self) -> Duration {
        Instant::now().duration_since(self.timestamp)
    }

    pub fn older_than(&self, age: Duration) -> bool {
        self.age().gt(&age)
    }
}

impl <Item> Clone for Message<Item> where Item: Clone {
    fn clone(&self) -> Message<Item> {
        match self {
            &Message::Packet { session_id, ref content } => {
                Message::Packet {
                    session_id: session_id,
                    content: content.clone()
                }
            },
            &Message::Control { ref event } => {
                Message::Control {
                    event: event.clone()
                }
            }
        }
    }
}

impl <Item> fmt::Debug for Message<Item> where Item: fmt::Debug {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Message::Packet { session_id, ref content } => {
                write!(f, "Message::Packet<session_id={:?}, content={:?}>", session_id, content)
            },
            &Message::Control { ref event } => {
                write!(f, "Message::Control<event={:?}>", event)
            }
        }
    }
}

pub trait NewTransport {
    type Future: Future<Item=Self::Transport,Error=Self::Error>;
    type Transport;
    type Error: error::Error + Sync + Send;

    fn new_transport(&self) -> Self::Future;
}

#[derive(StateMachineFuture)]
pub enum Connection<Item, S, K, T, N> where S: Stream<Item=Item,Error=io::Error>, K: Sink<SinkItem=Message<Item>,SinkError=io::Error>, T: Stream<Item=Item,Error=io::Error>+Sink<SinkItem=Item,SinkError=io::Error>, N: NewTransport<Transport=T>+Clone+'static {
    #[state_machine_future(start, transitions(Connecting, Finished, Error))]
    NotConnected {
        handle: Handle,
        stream: S,
        sink: K,
        error_count: u32,
        session_id: u32,
        new_transport: N,
        inbound_max: usize,
        outbound_max: usize,
        outbound_max_age: Duration,
    },

    #[state_machine_future(transitions(NotConnected, Connected, Finished, Error))]
    Connecting {
        handle: Handle,
        stream: S,
        sink: K,
        tcp_future: Box<Future<Item=T,Error=io::Error>>,
        error_count: u32,
        session_id: u32,
        new_transport: N,
        inbound_max: usize,
        outbound_max: usize,
        outbound_max_age: Duration,
    },

    #[state_machine_future(transitions(Connected, NotConnected, Finished, Error))]
    Connected {
        handle: Handle,
        stream: S,
        sink: K,
        tcp: T,
        outbound: VecDeque<TimestampedItem<Item>>,
        outbound_max: usize,
        outbound_max_age: Duration,
        outbound_inflight: bool,
        inbound: VecDeque<Message<Item>>,
        inbound_max: usize,
        inbound_inflight: bool,
        error_count: u32,
        session_id: u32,
        new_transport: N,
    },

    #[state_machine_future(ready)]
    Finished(()),

    #[state_machine_future(error)]
    Error(ConnectionError),
}

impl <Item, S, K, T, N> Connection<Item, S, K, T, N> where S: Stream<Item=Item,Error=io::Error>, K: Sink<SinkItem=Message<Item>,SinkError=io::Error>, T: Stream<Item=Item,Error=io::Error>+Sink<SinkItem=Item,SinkError=io::Error>, N: NewTransport<Transport=T>+Clone+'static {
    pub fn new(handle: Handle, stream: S, sink: K, new_transport: N, inbound_max: usize, outbound_max: usize, outbound_max_age: Duration) -> ConnectionFuture<Item, S, K, T, N> {
        debug!("constructing new connection future");
        Connection::start(handle, stream, sink, 0, 0, new_transport, inbound_max, outbound_max, outbound_max_age)
    }
}

impl <Item, S, K, T, N> PollConnection<Item, S, K, T, N> for Connection<Item, S, K, T, N> where S: Stream<Item=Item,Error=io::Error>, K: Sink<SinkItem=Message<Item>,SinkError=io::Error>, T: Stream<Item=Item,Error=io::Error>+Sink<SinkItem=Item,SinkError=io::Error>, N: NewTransport<Transport=T>+Clone+'static {
    fn poll_not_connected<'a>(not_connected: &'a mut RentToOwn<'a, NotConnected<Item, S, K, T, N>>) -> Poll<AfterNotConnected<Item, S, K, T, N>, ConnectionError> {
        trace!("not connected");

        let retry_strategy = FibonacciBackoff ::from_millis(1)
            .max_delay(Duration::from_millis(2000))
            .map(jitter);

        let handle = not_connected.handle.clone();
        let new_transport = not_connected.new_transport.clone();

        let tcp_future = Box::new(Retry::spawn(handle, retry_strategy, move || {
            new_transport.new_transport()
        }).map_err(|e| {
            match e {
                tokio_retry::Error::OperationError(e) => io::Error::new(io::ErrorKind::Other, e),
                tokio_retry::Error::TimerError(e) => e,
            }
        })) as (Box<Future<Item=T,Error=io::Error>>);

        let not_connected = not_connected.take();
        Ok(Async::Ready(Connecting {
            handle: not_connected.handle,
            stream: not_connected.stream,
            sink: not_connected.sink,
            tcp_future: tcp_future,
            error_count: not_connected.error_count,
            session_id: not_connected.session_id,
            new_transport: not_connected.new_transport,
            inbound_max: not_connected.inbound_max,
            outbound_max: not_connected.outbound_max,
            outbound_max_age: not_connected.outbound_max_age,
        }.into()))
    }

    fn poll_connecting<'a>(connecting: &'a mut RentToOwn<'a, Connecting<Item, S, K, T, N>>) -> Poll<AfterConnecting<Item, S, K, T, N>, ConnectionError> {
        trace!("connecting");

        let tcp = try_ready!(connecting.tcp_future.poll());

        let connecting = connecting.take();
        Ok(Async::Ready(Connected {
            handle: connecting.handle,
            stream: connecting.stream,
            sink: connecting.sink,
            tcp: tcp,
            inbound: VecDeque::from(vec![
                Message::Control {
                    event: Event::Connected {
                        session_id: connecting.session_id
                    }
                }
            ]),
            inbound_max: connecting.inbound_max,
            inbound_inflight: false,
            outbound: VecDeque::new(),
            outbound_inflight: false,
            outbound_max: connecting.outbound_max,
            outbound_max_age: connecting.outbound_max_age,
            error_count: connecting.error_count,
            session_id: connecting.session_id,
            new_transport: connecting.new_transport,
        }.into()))
    }

    fn poll_connected<'a>(connected: &'a mut RentToOwn<'a, Connected<Item, S, K, T, N>>) -> Poll<AfterConnected<Item, S, K, T, N>, ConnectionError> {
        trace!("connected");

        // we make progress if any poll returns Ready
        // if we make progress then we return a state or state change
        // otherwise return NotReady
        let mut progress = false;
        let session_id = connected.session_id;

        while connected.inbound.len() < connected.inbound_max {
            match connected.tcp.poll() {
                Ok(Async::Ready(Some(msg))) => {
                    trace!("transport received msg");
                    progress = true;
                    connected.inbound.push_back(Message::Packet{session_id, content: msg});
                },
                Ok(Async::Ready(None)) => {
                    trace!("transport returned end of stream");

                    let connected = connected.take();

                    return Ok(Async::Ready(NotConnected {
                        handle: connected.handle,
                        stream: connected.stream,
                        sink: connected.sink,
                        error_count: connected.error_count,
                        session_id: connected.session_id + 1,
                        new_transport: connected.new_transport,
                        inbound_max: connected.inbound_max,
                        outbound_max: connected.outbound_max,
                        outbound_max_age: connected.outbound_max_age,
                    }.into()));
                },
                Ok(Async::NotReady) => {
                    trace!("transport not ready to read");
                },
                Err(e) => {
                    trace!("transport read error: {:?}", e);
                    let connected = connected.take();

                    return Ok(Async::Ready(NotConnected {
                        handle: connected.handle,
                        stream: connected.stream,
                        sink: connected.sink,
                        error_count: connected.error_count + 1,
                        session_id: connected.session_id + 1,
                        new_transport: connected.new_transport,
                        inbound_max: connected.inbound_max,
                        outbound_max: connected.outbound_max,
                        outbound_max_age: connected.outbound_max_age,
                    }.into()));
                }
            };
        }

        while connected.outbound.len() < connected.outbound_max {
            match try!(connected.stream.poll()) {
                Async::Ready(Some(msg)) => {
                    progress = true;
                    connected.outbound.push_back(TimestampedItem::new(msg));
                },
                Async::Ready(None) => {
                    trace!("stream finished");
                    return Ok(Async::Ready(Finished(()).into()));
                },
                Async::NotReady => {
                    trace!("stream not ready to read");
                    break;
                }
            };
        }

        if let Some(TimestampedItem{ timestamp, item: sending }) = connected.outbound.pop_front() {
            match connected.tcp.start_send(sending) {
                Ok(AsyncSink::Ready) => {
                    trace!("transport wrote message");
                    progress = true;
                    connected.outbound_inflight = true;
                },
                Ok(AsyncSink::NotReady(sending)) => {
                    trace!("tcpstream not ready to write");

                    let timestamped_msg = TimestampedItem { timestamp: timestamp, item: sending };
                    if timestamped_msg.older_than(connected.outbound_max_age) {
                        trace!("message older than max age {:?}. reconnecting", timestamped_msg.age());

                        let connected = connected.take();

                        return Ok(Async::Ready(NotConnected {
                            handle: connected.handle,
                            stream: connected.stream,
                            sink: connected.sink,
                            error_count: connected.error_count,
                            session_id: connected.session_id + 1,
                            new_transport: connected.new_transport,
                            inbound_max: connected.inbound_max,
                            outbound_max: connected.outbound_max,
                            outbound_max_age: connected.outbound_max_age,
                        }.into()));
                    } else {
                        connected.outbound.push_front(timestamped_msg);
                    }
                },
                Err(err) => {
                    trace!("transport write error: {:?}", err);
                    let connected = connected.take();

                    return Ok(Async::Ready(NotConnected {
                        handle: connected.handle,
                        stream: connected.stream,
                        sink: connected.sink,
                        error_count: connected.error_count + 1,
                        session_id: connected.session_id + 1,
                        new_transport: connected.new_transport,
                        inbound_max: connected.inbound_max,
                        outbound_max: connected.outbound_max,
                        outbound_max_age: connected.outbound_max_age,
                    }.into()));
                }
            }
        }

        if let Some(msg) = connected.inbound.pop_front() {
            match try!(connected.sink.start_send(msg)) {
                AsyncSink::Ready => {
                    trace!("sink started send");
                    progress = true;
                    connected.inbound_inflight = true;
                },
                AsyncSink::NotReady(msg) => {
                    trace!("sink not ready");
                    connected.inbound.push_front(msg);
                },
            }
        }

        if connected.inbound_inflight {
            match try!(connected.sink.poll_complete()) {
                Async::Ready(()) => {
                    trace!("sink polled complete");
                    progress = true;
                    connected.inbound_inflight = false;
                },
                Async::NotReady => {
                    trace!("sink polled not ready");
                    // do nothing
                }
            }
        }

        if connected.outbound_inflight {
            match try!(connected.tcp.poll_complete()) {
                Async::Ready(()) => {
                    trace!("transport poll complete");
                    progress = true;
                    connected.outbound_inflight = false;
                },
                Async::NotReady => {
                    trace!("transport sink polled not ready");
                    // do nothing
                }
            }
        }

        if progress {
            trace!("made progress");

            let connected = connected.take();

            return Ok(Async::Ready(Connected {
                handle: connected.handle,
                stream: connected.stream,
                sink: connected.sink,
                tcp: connected.tcp,
                inbound: connected.inbound,
                inbound_max: connected.inbound_max,
                inbound_inflight: connected.inbound_inflight,
                outbound: connected.outbound,
                outbound_max: connected.outbound_max,
                outbound_max_age: connected.outbound_max_age,
                outbound_inflight: connected.outbound_inflight,
                error_count: connected.error_count,
                session_id: connected.session_id,
                new_transport: connected.new_transport,
            }.into()));
        } else {
            trace!("did not make progress");

            return Ok(Async::NotReady);
        }
    }
}
<commit_msg>fix non-terminating loop<commit_after>use futures::{Async, AsyncSink, Future, Poll};
use futures::stream::Stream;
use futures::sink::Sink;
use state_machine_future::RentToOwn;
use tokio_core::reactor::Handle;
use tokio_retry::{self, Retry};
use tokio_retry::strategy::{FibonacciBackoff, jitter};
use std::io;
use std::error;
use std::time::{Duration, Instant};
use std::fmt;
use std::collections::VecDeque;

pub type ConnectionError = io::Error;
pub type SessionId = u32;

#[derive(Debug, Clone, PartialEq)]
pub enum Event {
    Connected {
        session_id: SessionId,
    }
}

pub enum Message<Item> {
    Packet {
        session_id: SessionId,
        content: Item,
    },
    Control {
        event: Event,
    }
}

pub struct TimestampedItem<Item> {
    pub timestamp: Instant,
    pub item: Item,
}

impl <Item> TimestampedItem<Item> {
    pub fn new(item: Item) -> TimestampedItem<Item> {
        TimestampedItem {
            timestamp: Instant::now(),
            item: item,
        }
    }

    pub fn age(&self) -> Duration {
        Instant::now().duration_since(self.timestamp)
    }

    pub fn older_than(&self, age: Duration) -> bool {
        self.age().gt(&age)
    }
}

impl <Item> Clone for Message<Item> where Item: Clone {
    fn clone(&self) -> Message<Item> {
        match self {
            &Message::Packet { session_id, ref content } => {
                Message::Packet {
                    session_id: session_id,
                    content: content.clone()
                }
            },
            &Message::Control { ref event } => {
                Message::Control {
                    event: event.clone()
                }
            }
        }
    }
}

impl <Item> fmt::Debug for Message<Item> where Item: fmt::Debug {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Message::Packet { session_id, ref content } => {
                write!(f, "Message::Packet<session_id={:?}, content={:?}>", session_id, content)
            },
            &Message::Control { ref event } => {
                write!(f, "Message::Control<event={:?}>", event)
            }
        }
    }
}

pub trait NewTransport {
    type Future: Future<Item=Self::Transport,Error=Self::Error>;
    type Transport;
    type Error: error::Error + Sync + Send;

    fn new_transport(&self) -> Self::Future;
}

#[derive(StateMachineFuture)]
pub enum Connection<Item, S, K, T, N> where S: Stream<Item=Item,Error=io::Error>, K: Sink<SinkItem=Message<Item>,SinkError=io::Error>, T: Stream<Item=Item,Error=io::Error>+Sink<SinkItem=Item,SinkError=io::Error>, N: NewTransport<Transport=T>+Clone+'static {
    #[state_machine_future(start, transitions(Connecting, Finished, Error))]
    NotConnected {
        handle: Handle,
        stream: S,
        sink: K,
        error_count: u32,
        session_id: u32,
        new_transport: N,
        inbound_max: usize,
        outbound_max: usize,
        outbound_max_age: Duration,
    },

    #[state_machine_future(transitions(NotConnected, Connected, Finished, Error))]
    Connecting {
        handle: Handle,
        stream: S,
        sink: K,
        tcp_future: Box<Future<Item=T,Error=io::Error>>,
        error_count: u32,
        session_id: u32,
        new_transport: N,
        inbound_max: usize,
        outbound_max: usize,
        outbound_max_age: Duration,
    },

    #[state_machine_future(transitions(Connected, NotConnected, Finished, Error))]
    Connected {
        handle: Handle,
        stream: S,
        sink: K,
        tcp: T,
        outbound: VecDeque<TimestampedItem<Item>>,
        outbound_max: usize,
        outbound_max_age: Duration,
        outbound_inflight: bool,
        inbound: VecDeque<Message<Item>>,
        inbound_max: usize,
        inbound_inflight: bool,
        error_count: u32,
        session_id: u32,
        new_transport: N,
    },

    #[state_machine_future(ready)]
    Finished(()),

    #[state_machine_future(error)]
    Error(ConnectionError),
}

impl <Item, S, K, T, N> Connection<Item, S, K, T, N> where S: Stream<Item=Item,Error=io::Error>, K: Sink<SinkItem=Message<Item>,SinkError=io::Error>, T: Stream<Item=Item,Error=io::Error>+Sink<SinkItem=Item,SinkError=io::Error>, N: NewTransport<Transport=T>+Clone+'static {
    pub fn new(handle: Handle, stream: S, sink: K, new_transport: N, inbound_max: usize, outbound_max: usize, outbound_max_age: Duration) -> ConnectionFuture<Item, S, K, T, N> {
        debug!("constructing new connection future");
        Connection::start(handle, stream, sink, 0, 0, new_transport, inbound_max, outbound_max, outbound_max_age)
    }
}

impl <Item, S, K, T, N> PollConnection<Item, S, K, T, N> for Connection<Item, S, K, T, N> where S: Stream<Item=Item,Error=io::Error>, K: Sink<SinkItem=Message<Item>,SinkError=io::Error>, T: Stream<Item=Item,Error=io::Error>+Sink<SinkItem=Item,SinkError=io::Error>, N: NewTransport<Transport=T>+Clone+'static {
    fn poll_not_connected<'a>(not_connected: &'a mut RentToOwn<'a, NotConnected<Item, S, K, T, N>>) -> Poll<AfterNotConnected<Item, S, K, T, N>, ConnectionError> {
        trace!("not connected");

        let retry_strategy = FibonacciBackoff ::from_millis(1)
            .max_delay(Duration::from_millis(2000))
            .map(jitter);

        let handle = not_connected.handle.clone();
        let new_transport = not_connected.new_transport.clone();

        let tcp_future = Box::new(Retry::spawn(handle, retry_strategy, move || {
            new_transport.new_transport()
        }).map_err(|e| {
            match e {
                tokio_retry::Error::OperationError(e) => io::Error::new(io::ErrorKind::Other, e),
                tokio_retry::Error::TimerError(e) => e,
            }
        })) as (Box<Future<Item=T,Error=io::Error>>);

        let not_connected = not_connected.take();
        Ok(Async::Ready(Connecting {
            handle: not_connected.handle,
            stream: not_connected.stream,
            sink: not_connected.sink,
            tcp_future: tcp_future,
            error_count: not_connected.error_count,
            session_id: not_connected.session_id,
            new_transport: not_connected.new_transport,
            inbound_max: not_connected.inbound_max,
            outbound_max: not_connected.outbound_max,
            outbound_max_age: not_connected.outbound_max_age,
        }.into()))
    }

    fn poll_connecting<'a>(connecting: &'a mut RentToOwn<'a, Connecting<Item, S, K, T, N>>) -> Poll<AfterConnecting<Item, S, K, T, N>, ConnectionError> {
        trace!("connecting");

        let tcp = try_ready!(connecting.tcp_future.poll());

        let connecting = connecting.take();
        Ok(Async::Ready(Connected {
            handle: connecting.handle,
            stream: connecting.stream,
            sink: connecting.sink,
            tcp: tcp,
            inbound: VecDeque::from(vec![
                Message::Control {
                    event: Event::Connected {
                        session_id: connecting.session_id
                    }
                }
            ]),
            inbound_max: connecting.inbound_max,
            inbound_inflight: false,
            outbound: VecDeque::new(),
            outbound_inflight: false,
            outbound_max: connecting.outbound_max,
            outbound_max_age: connecting.outbound_max_age,
            error_count: connecting.error_count,
            session_id: connecting.session_id,
            new_transport: connecting.new_transport,
        }.into()))
    }

    fn poll_connected<'a>(connected: &'a mut RentToOwn<'a, Connected<Item, S, K, T, N>>) -> Poll<AfterConnected<Item, S, K, T, N>, ConnectionError> {
        trace!("connected");

        // we make progress if any poll returns Ready
        // if we make progress then we return a state or state change
        // otherwise return NotReady
        let mut progress = false;
        let session_id = connected.session_id;

        while connected.inbound.len() < connected.inbound_max {
            match connected.tcp.poll() {
                Ok(Async::Ready(Some(msg))) => {
                    trace!("transport received msg");
                    progress = true;
                    connected.inbound.push_back(Message::Packet{session_id, content: msg});
                },
                Ok(Async::Ready(None)) => {
                    trace!("transport returned end of stream");

                    let connected = connected.take();

                    return Ok(Async::Ready(NotConnected {
                        handle: connected.handle,
                        stream: connected.stream,
                        sink: connected.sink,
                        error_count: connected.error_count,
                        session_id: connected.session_id + 1,
                        new_transport: connected.new_transport,
                        inbound_max: connected.inbound_max,
                        outbound_max: connected.outbound_max,
                        outbound_max_age: connected.outbound_max_age,
                    }.into()));
                },
                Ok(Async::NotReady) => {
                    trace!("transport not ready to read");
                    break;
                },
                Err(e) => {
                    trace!("transport read error: {:?}", e);
                    let connected = connected.take();

                    return Ok(Async::Ready(NotConnected {
                        handle: connected.handle,
                        stream: connected.stream,
                        sink: connected.sink,
                        error_count: connected.error_count + 1,
                        session_id: connected.session_id + 1,
                        new_transport: connected.new_transport,
                        inbound_max: connected.inbound_max,
                        outbound_max: connected.outbound_max,
                        outbound_max_age: connected.outbound_max_age,
                    }.into()));
                }
            };
        }

        while connected.outbound.len() < connected.outbound_max {
            match try!(connected.stream.poll()) {
                Async::Ready(Some(msg)) => {
                    progress = true;
                    connected.outbound.push_back(TimestampedItem::new(msg));
                },
                Async::Ready(None) => {
                    trace!("stream finished");
                    return Ok(Async::Ready(Finished(()).into()));
                },
                Async::NotReady => {
                    trace!("stream not ready to read");
                    break;
                }
            };
        }

        if let Some(TimestampedItem{ timestamp, item: sending }) = connected.outbound.pop_front() {
            match connected.tcp.start_send(sending) {
                Ok(AsyncSink::Ready) => {
                    trace!("transport wrote message");
                    progress = true;
                    connected.outbound_inflight = true;
                },
                Ok(AsyncSink::NotReady(sending)) => {
                    trace!("tcpstream not ready to write");

                    let timestamped_msg = TimestampedItem { timestamp: timestamp, item: sending };
                    if timestamped_msg.older_than(connected.outbound_max_age) {
                        trace!("message older than max age {:?}. reconnecting", timestamped_msg.age());

                        let connected = connected.take();

                        return Ok(Async::Ready(NotConnected {
                            handle: connected.handle,
                            stream: connected.stream,
                            sink: connected.sink,
                            error_count: connected.error_count,
                            session_id: connected.session_id + 1,
                            new_transport: connected.new_transport,
                            inbound_max: connected.inbound_max,
                            outbound_max: connected.outbound_max,
                            outbound_max_age: connected.outbound_max_age,
                        }.into()));
                    } else {
                        connected.outbound.push_front(timestamped_msg);
                    }
                },
                Err(err) => {
                    trace!("transport write error: {:?}", err);
                    let connected = connected.take();

                    return Ok(Async::Ready(NotConnected {
                        handle: connected.handle,
                        stream: connected.stream,
                        sink: connected.sink,
                        error_count: connected.error_count + 1,
                        session_id: connected.session_id + 1,
                        new_transport: connected.new_transport,
                        inbound_max: connected.inbound_max,
                        outbound_max: connected.outbound_max,
                        outbound_max_age: connected.outbound_max_age,
                    }.into()));
                }
            }
        }

        if let Some(msg) = connected.inbound.pop_front() {
            match try!(connected.sink.start_send(msg)) {
                AsyncSink::Ready => {
                    trace!("sink started send");
                    progress = true;
                    connected.inbound_inflight = true;
                },
                AsyncSink::NotReady(msg) => {
                    trace!("sink not ready");
                    connected.inbound.push_front(msg);
                },
            }
        }

        if connected.inbound_inflight {
            match try!(connected.sink.poll_complete()) {
                Async::Ready(()) => {
                    trace!("sink polled complete");
                    progress = true;
                    connected.inbound_inflight = false;
                },
                Async::NotReady => {
                    trace!("sink polled not ready");
                    // do nothing
                }
            }
        }

        if connected.outbound_inflight {
            match try!(connected.tcp.poll_complete()) {
                Async::Ready(()) => {
                    trace!("transport poll complete");
                    progress = true;
                    connected.outbound_inflight = false;
                },
                Async::NotReady => {
                    trace!("transport sink polled not ready");
                    // do nothing
                }
            }
        }

        if progress {
            trace!("made progress");

            let connected = connected.take();

            return Ok(Async::Ready(Connected {
                handle: connected.handle,
                stream: connected.stream,
                sink: connected.sink,
                tcp: connected.tcp,
                inbound: connected.inbound,
                inbound_max: connected.inbound_max,
                inbound_inflight: connected.inbound_inflight,
                outbound: connected.outbound,
                outbound_max: connected.outbound_max,
                outbound_max_age: connected.outbound_max_age,
                outbound_inflight: connected.outbound_inflight,
                error_count: connected.error_count,
                session_id: connected.session_id,
                new_transport: connected.new_transport,
            }.into()));
        } else {
            trace!("did not make progress");

            return Ok(Async::NotReady);
        }
    }
}
<|endoftext|>


<commit_before>use crate::common::util::*;
use regex::Regex;
use std::os::unix::process::ExitStatusExt;
use std::process::{Child, Command};

// A child process the tests will try to kill.
struct Target {
    child: Child,
    killed: bool,
}

impl Target {
    // Creates a target that will naturally die after some time if not killed
    // fast enough.
    // This timeout avoids hanging failing tests.
    fn new() -> Target {
        Target {
            child: Command::new("sleep")
                .arg("30")
                .spawn()
                .expect("cannot spawn target"),
            killed: false,
        }
    }

    // Waits for the target to complete and returns the signal it received if any.
    fn wait_for_signal(&mut self) -> Option<i32> {
        let sig = self.child.wait().expect("cannot wait on target").signal();
        self.killed = true;
        sig
    }

    fn pid(&self) -> u32 {
        self.child.id()
    }
}

impl Drop for Target {
    // Terminates this target to avoid littering test boxes with zombi processes
    // when a test fails after creating a target but before killing it.
    fn drop(&mut self) {
        if !self.killed {
            self.child.kill().expect("cannot kill target");
        }
    }
}

#[test]
fn test_kill_list_all_signals() {
    // Check for a few signals.  Do not try to be comprehensive.
    new_ucmd!()
        .arg("-l")
        .succeeds()
        .stdout_contains("KILL")
        .stdout_contains("TERM")
        .stdout_contains("HUP");
}

#[test]
fn test_kill_list_final_new_line() {
    new_ucmd!()
        .arg("-l")
        .succeeds()
        .stdout_matches(&Regex::new("[\\n\\r]$").unwrap());
}

#[test]
fn test_kill_list_all_signals_as_table() {
    // Check for a few signals.  Do not try to be comprehensive.
    new_ucmd!()
        .arg("-t")
        .succeeds()
        .stdout_contains("KILL")
        .stdout_contains("TERM")
        .stdout_contains("HUP");
}

#[test]
fn test_kill_list_one_signal_from_name() {
    // Use SIGKILL because it is 9 on all unixes.
    new_ucmd!()
        .arg("-l")
        .arg("KILL")
        .succeeds()
        .stdout_matches(&Regex::new("\\b9\\b").unwrap());
}

#[test]
fn test_kill_set_bad_signal_name() {
    // spell-checker:disable-line
    new_ucmd!()
        .arg("-s")
        .arg("IAMNOTASIGNAL") // spell-checker:disable-line
        .fails()
        .stderr_contains("unknown signal");
}

#[test]
fn test_kill_with_default_signal() {
    let mut target = Target::new();
    new_ucmd!().arg(format!("{}", target.pid())).succeeds();
    assert_eq!(target.wait_for_signal(), Some(libc::SIGTERM));
}

#[test]
fn test_kill_with_signal_number_old_form() {
    let mut target = Target::new();
    new_ucmd!()
        .arg("-9")
        .arg(format!("{}", target.pid()))
        .succeeds();
    assert_eq!(target.wait_for_signal(), Some(9));
}

#[test]
fn test_kill_with_signal_number_new_form() {
    let mut target = Target::new();
    new_ucmd!()
        .arg("-s")
        .arg("9")
        .arg(format!("{}", target.pid()))
        .succeeds();
    assert_eq!(target.wait_for_signal(), Some(9));
}

#[test]
fn test_kill_with_signal_name_new_form() {
    let mut target = Target::new();
    new_ucmd!()
        .arg("-s")
        .arg("KILL")
        .arg(format!("{}", target.pid()))
        .succeeds();
    assert_eq!(target.wait_for_signal(), Some(libc::SIGKILL));
}
<commit_msg>fix kill list final new line test<commit_after>use crate::common::util::*;
use regex::Regex;
use std::os::unix::process::ExitStatusExt;
use std::process::{Child, Command};

// A child process the tests will try to kill.
struct Target {
    child: Child,
    killed: bool,
}

impl Target {
    // Creates a target that will naturally die after some time if not killed
    // fast enough.
    // This timeout avoids hanging failing tests.
    fn new() -> Target {
        Target {
            child: Command::new("sleep")
                .arg("30")
                .spawn()
                .expect("cannot spawn target"),
            killed: false,
        }
    }

    // Waits for the target to complete and returns the signal it received if any.
    fn wait_for_signal(&mut self) -> Option<i32> {
        let sig = self.child.wait().expect("cannot wait on target").signal();
        self.killed = true;
        sig
    }

    fn pid(&self) -> u32 {
        self.child.id()
    }
}

impl Drop for Target {
    // Terminates this target to avoid littering test boxes with zombi processes
    // when a test fails after creating a target but before killing it.
    fn drop(&mut self) {
        if !self.killed {
            self.child.kill().expect("cannot kill target");
        }
    }
}

#[test]
fn test_kill_list_all_signals() {
    // Check for a few signals.  Do not try to be comprehensive.
    new_ucmd!()
        .arg("-l")
        .succeeds()
        .stdout_contains("KILL")
        .stdout_contains("TERM")
        .stdout_contains("HUP");
}

#[test]
fn test_kill_list_final_new_line() {
    let re = Regex::new("\\n$").unwrap();
    assert!(re.is_match(new_ucmd!().arg("-l").succeeds().stdout_str()));
}

#[test]
fn test_kill_list_all_signals_as_table() {
    // Check for a few signals.  Do not try to be comprehensive.
    new_ucmd!()
        .arg("-t")
        .succeeds()
        .stdout_contains("KILL")
        .stdout_contains("TERM")
        .stdout_contains("HUP");
}

#[test]
fn test_kill_list_one_signal_from_name() {
    // Use SIGKILL because it is 9 on all unixes.
    new_ucmd!()
        .arg("-l")
        .arg("KILL")
        .succeeds()
        .stdout_matches(&Regex::new("\\b9\\b").unwrap());
}

#[test]
fn test_kill_set_bad_signal_name() {
    // spell-checker:disable-line
    new_ucmd!()
        .arg("-s")
        .arg("IAMNOTASIGNAL") // spell-checker:disable-line
        .fails()
        .stderr_contains("unknown signal");
}

#[test]
fn test_kill_with_default_signal() {
    let mut target = Target::new();
    new_ucmd!().arg(format!("{}", target.pid())).succeeds();
    assert_eq!(target.wait_for_signal(), Some(libc::SIGTERM));
}

#[test]
fn test_kill_with_signal_number_old_form() {
    let mut target = Target::new();
    new_ucmd!()
        .arg("-9")
        .arg(format!("{}", target.pid()))
        .succeeds();
    assert_eq!(target.wait_for_signal(), Some(9));
}

#[test]
fn test_kill_with_signal_number_new_form() {
    let mut target = Target::new();
    new_ucmd!()
        .arg("-s")
        .arg("9")
        .arg(format!("{}", target.pid()))
        .succeeds();
    assert_eq!(target.wait_for_signal(), Some(9));
}

#[test]
fn test_kill_with_signal_name_new_form() {
    let mut target = Target::new();
    new_ucmd!()
        .arg("-s")
        .arg("KILL")
        .arg(format!("{}", target.pid()))
        .succeeds();
    assert_eq!(target.wait_for_signal(), Some(libc::SIGKILL));
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::error;
use std::fmt;
use std::mem;
use std::ptr;
use std::sync::Arc;
use smallvec::SmallVec;

use device::Device;
use framebuffer::RenderPass;
use framebuffer::RenderPassAttachmentsList;
use image::Layout as ImageLayout;
use image::traits::Image;
use image::traits::ImageView;

use Error;
use OomError;
use VulkanObject;
use VulkanPointers;
use check_errors;
use vk;

/// Contains the list of images attached to a render pass.
///
/// This is a structure that you must pass when you start recording draw commands in a
/// command buffer.
///
/// A framebuffer can be used alongside with any other render pass object as long as it is
/// compatible with the render pass that his framebuffer was created with. You can determine
/// whether two renderpass objects are compatible by calling `is_compatible_with`.
pub struct Framebuffer<L> {
    device: Arc<Device>,
    render_pass: Arc<L>,
    framebuffer: vk::Framebuffer,
    dimensions: (u32, u32, u32),
    resources: SmallVec<[(Arc<ImageView>, Arc<Image>, ImageLayout, ImageLayout); 8]>,
}

impl<L> Framebuffer<L> {
    /// Builds a new framebuffer.
    ///
    /// The `attachments` parameter depends on which `RenderPass` implementation is used.
    ///
    /// # Panic
    ///
    /// - Panicks if one of the attachments has a different sample count than what the render pass
    ///   describes.
    /// - Additionally, some methods in the `RenderPassAttachmentsList` implementation may panic
    ///   if you pass invalid attachments.      // TODO: should be error instead
    ///
    pub fn new<'a, A>(render_pass: &Arc<L>, dimensions: (u32, u32, u32),        // TODO: what about [u32; 3] instead?
                      attachments: A) -> Result<Arc<Framebuffer<L>>, FramebufferCreationError>
        where L: RenderPass + RenderPassAttachmentsList
    {
        let vk = render_pass.render_pass().device().pointers();
        let device = render_pass.render_pass().device().clone();

        let attachments = render_pass.convert_attachments_list(attachments).collect::<SmallVec<[_; 8]>>();

        // checking the dimensions against the limits
        {
            let limits = render_pass.render_pass().device().physical_device().limits();
            let limits = [limits.max_framebuffer_width(), limits.max_framebuffer_height(),
                          limits.max_framebuffer_layers()];
            if dimensions.0 > limits[0] || dimensions.1 > limits[1] || dimensions.2 > limits[2] {
                return Err(FramebufferCreationError::DimensionsTooLarge);
            }
        }

        let ids = attachments.iter().map(|&(ref a, _, _, _)| {
            assert!(a.identity_swizzle());
            a.inner_view().internal_object()
        }).collect::<SmallVec<[_; 8]>>();

        let framebuffer = unsafe {
            let infos = vk::FramebufferCreateInfo {
                sType: vk::STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,   // reserved
                renderPass: render_pass.render_pass().internal_object(),
                attachmentCount: ids.len() as u32,
                pAttachments: ids.as_ptr(),
                width: dimensions.0,
                height: dimensions.1,
                layers: dimensions.2,
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateFramebuffer(device.internal_object(), &infos,
                                                   ptr::null(), &mut output)));
            output
        };

        Ok(Arc::new(Framebuffer {
            device: device,
            render_pass: render_pass.clone(),
            framebuffer: framebuffer,
            dimensions: dimensions,
            resources: attachments,
        }))
    }

    /// Returns true if this framebuffer can be used with the specified renderpass.
    #[inline]
    pub fn is_compatible_with<R>(&self, render_pass: &Arc<R>) -> bool
        where R: RenderPass, L: RenderPass
    {
        // FIXME:
        true
        /*(&*self.renderpass as *const UnsafeRenderPass<L> as usize ==
         &**renderpass as *const UnsafeRenderPass<R> as usize) ||
            self.renderpass.is_compatible_with(renderpass)*/
    }

    /// Returns the width, height and layers of this framebuffer.
    #[inline]
    pub fn dimensions(&self) -> [u32; 3] {
        [self.dimensions.0, self.dimensions.1, self.dimensions.2]
    }

    /// Returns the width of the framebuffer in pixels.
    #[inline]
    pub fn width(&self) -> u32 {
        self.dimensions.0
    }

    /// Returns the height of the framebuffer in pixels.
    #[inline]
    pub fn height(&self) -> u32 {
        self.dimensions.1
    }

    /// Returns the number of layers (or depth) of the framebuffer.
    #[inline]
    pub fn layers(&self) -> u32 {
        self.dimensions.2
    }

    /// Returns the device that was used to create this framebuffer.
    #[inline]
    pub fn device(&self) -> &Arc<Device> {
        &self.device
    }

    /// Returns the renderpass that was used to create this framebuffer.
    #[inline]
    pub fn render_pass(&self) -> &Arc<L> {
        &self.render_pass
    }

    /// Returns all the resources attached to that framebuffer.
    #[inline]
    pub fn attachments(&self) -> &[(Arc<ImageView>, Arc<Image>, ImageLayout, ImageLayout)] {
        &self.resources
    }
}

unsafe impl<L> VulkanObject for Framebuffer<L> {
    type Object = vk::Framebuffer;

    #[inline]
    fn internal_object(&self) -> vk::Framebuffer {
        self.framebuffer
    }
}

impl<L> Drop for Framebuffer<L> {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            vk.DestroyFramebuffer(self.device.internal_object(), self.framebuffer, ptr::null());
        }
    }
}

/// Error that can happen when creating a framebuffer object.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum FramebufferCreationError {
    /// Out of memory.
    OomError(OomError),
    /// The requested dimensions exceed the device's limits.
    DimensionsTooLarge,
}

impl From<OomError> for FramebufferCreationError {
    #[inline]
    fn from(err: OomError) -> FramebufferCreationError {
        FramebufferCreationError::OomError(err)
    }
}

impl error::Error for FramebufferCreationError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            FramebufferCreationError::OomError(_) => "no memory available",
            FramebufferCreationError::DimensionsTooLarge => "the dimensions of the framebuffer \
                                                             are too large",
        }
    }

    #[inline]
    fn cause(&self) -> Option<&error::Error> {
        match *self {
            FramebufferCreationError::OomError(ref err) => Some(err),
            _ => None,
        }
    }
}

impl fmt::Display for FramebufferCreationError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<Error> for FramebufferCreationError {
    #[inline]
    fn from(err: Error) -> FramebufferCreationError {
        FramebufferCreationError::from(OomError::from(err))
    }
}

#[cfg(test)]
mod tests {
    // TODO: restore these tests
    /*use framebuffer::Framebuffer;
    use framebuffer::UnsafeRenderPass;
    use framebuffer::FramebufferCreationError;

    #[test]
    #[ignore]       // TODO: crashes on AMD+Windows
    fn empty_renderpass_create() {
        let (device, _) = gfx_dev_and_queue!();
        let _ = UnsafeRenderPass::empty_single_pass(&device).unwrap();
    }

    #[test]
    #[ignore]       // TODO: crashes on AMD+Windows
    fn framebuffer_too_large() {
        let (device, _) = gfx_dev_and_queue!();
        let renderpass = UnsafeRenderPass::empty_single_pass(&device).unwrap();

        match Framebuffer::new(&renderpass, (0xffffffff, 0xffffffff, 0xffffffff), ()) {
            Err(FramebufferCreationError::DimensionsTooLarge) => (),
            _ => panic!()
        }
    }*/
}
<commit_msg>Restore the tests in framebuffer/framebuffer<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::error;
use std::fmt;
use std::mem;
use std::ptr;
use std::sync::Arc;
use smallvec::SmallVec;

use device::Device;
use framebuffer::RenderPass;
use framebuffer::RenderPassAttachmentsList;
use image::Layout as ImageLayout;
use image::traits::Image;
use image::traits::ImageView;

use Error;
use OomError;
use VulkanObject;
use VulkanPointers;
use check_errors;
use vk;

/// Contains the list of images attached to a render pass.
///
/// This is a structure that you must pass when you start recording draw commands in a
/// command buffer.
///
/// A framebuffer can be used alongside with any other render pass object as long as it is
/// compatible with the render pass that his framebuffer was created with. You can determine
/// whether two renderpass objects are compatible by calling `is_compatible_with`.
pub struct Framebuffer<L> {
    device: Arc<Device>,
    render_pass: Arc<L>,
    framebuffer: vk::Framebuffer,
    dimensions: (u32, u32, u32),
    resources: SmallVec<[(Arc<ImageView>, Arc<Image>, ImageLayout, ImageLayout); 8]>,
}

impl<L> Framebuffer<L> {
    /// Builds a new framebuffer.
    ///
    /// The `attachments` parameter depends on which `RenderPass` implementation is used.
    ///
    /// # Panic
    ///
    /// - Panicks if one of the attachments has a different sample count than what the render pass
    ///   describes.
    /// - Additionally, some methods in the `RenderPassAttachmentsList` implementation may panic
    ///   if you pass invalid attachments.      // TODO: should be error instead
    ///
    pub fn new<'a, A>(render_pass: &Arc<L>, dimensions: (u32, u32, u32),        // TODO: what about [u32; 3] instead?
                      attachments: A) -> Result<Arc<Framebuffer<L>>, FramebufferCreationError>
        where L: RenderPass + RenderPassAttachmentsList
    {
        let vk = render_pass.render_pass().device().pointers();
        let device = render_pass.render_pass().device().clone();

        let attachments = render_pass.convert_attachments_list(attachments).collect::<SmallVec<[_; 8]>>();

        // checking the dimensions against the limits
        {
            let limits = render_pass.render_pass().device().physical_device().limits();
            let limits = [limits.max_framebuffer_width(), limits.max_framebuffer_height(),
                          limits.max_framebuffer_layers()];
            if dimensions.0 > limits[0] || dimensions.1 > limits[1] || dimensions.2 > limits[2] {
                return Err(FramebufferCreationError::DimensionsTooLarge);
            }
        }

        let ids = attachments.iter().map(|&(ref a, _, _, _)| {
            assert!(a.identity_swizzle());
            a.inner_view().internal_object()
        }).collect::<SmallVec<[_; 8]>>();

        let framebuffer = unsafe {
            let infos = vk::FramebufferCreateInfo {
                sType: vk::STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,   // reserved
                renderPass: render_pass.render_pass().internal_object(),
                attachmentCount: ids.len() as u32,
                pAttachments: ids.as_ptr(),
                width: dimensions.0,
                height: dimensions.1,
                layers: dimensions.2,
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateFramebuffer(device.internal_object(), &infos,
                                                   ptr::null(), &mut output)));
            output
        };

        Ok(Arc::new(Framebuffer {
            device: device,
            render_pass: render_pass.clone(),
            framebuffer: framebuffer,
            dimensions: dimensions,
            resources: attachments,
        }))
    }

    /// Returns true if this framebuffer can be used with the specified renderpass.
    #[inline]
    pub fn is_compatible_with<R>(&self, render_pass: &Arc<R>) -> bool
        where R: RenderPass, L: RenderPass
    {
        // FIXME:
        true
        /*(&*self.renderpass as *const UnsafeRenderPass<L> as usize ==
         &**renderpass as *const UnsafeRenderPass<R> as usize) ||
            self.renderpass.is_compatible_with(renderpass)*/
    }

    /// Returns the width, height and layers of this framebuffer.
    #[inline]
    pub fn dimensions(&self) -> [u32; 3] {
        [self.dimensions.0, self.dimensions.1, self.dimensions.2]
    }

    /// Returns the width of the framebuffer in pixels.
    #[inline]
    pub fn width(&self) -> u32 {
        self.dimensions.0
    }

    /// Returns the height of the framebuffer in pixels.
    #[inline]
    pub fn height(&self) -> u32 {
        self.dimensions.1
    }

    /// Returns the number of layers (or depth) of the framebuffer.
    #[inline]
    pub fn layers(&self) -> u32 {
        self.dimensions.2
    }

    /// Returns the device that was used to create this framebuffer.
    #[inline]
    pub fn device(&self) -> &Arc<Device> {
        &self.device
    }

    /// Returns the renderpass that was used to create this framebuffer.
    #[inline]
    pub fn render_pass(&self) -> &Arc<L> {
        &self.render_pass
    }

    /// Returns all the resources attached to that framebuffer.
    #[inline]
    pub fn attachments(&self) -> &[(Arc<ImageView>, Arc<Image>, ImageLayout, ImageLayout)] {
        &self.resources
    }
}

unsafe impl<L> VulkanObject for Framebuffer<L> {
    type Object = vk::Framebuffer;

    #[inline]
    fn internal_object(&self) -> vk::Framebuffer {
        self.framebuffer
    }
}

impl<L> Drop for Framebuffer<L> {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            vk.DestroyFramebuffer(self.device.internal_object(), self.framebuffer, ptr::null());
        }
    }
}

/// Error that can happen when creating a framebuffer object.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum FramebufferCreationError {
    /// Out of memory.
    OomError(OomError),
    /// The requested dimensions exceed the device's limits.
    DimensionsTooLarge,
}

impl From<OomError> for FramebufferCreationError {
    #[inline]
    fn from(err: OomError) -> FramebufferCreationError {
        FramebufferCreationError::OomError(err)
    }
}

impl error::Error for FramebufferCreationError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            FramebufferCreationError::OomError(_) => "no memory available",
            FramebufferCreationError::DimensionsTooLarge => "the dimensions of the framebuffer \
                                                             are too large",
        }
    }

    #[inline]
    fn cause(&self) -> Option<&error::Error> {
        match *self {
            FramebufferCreationError::OomError(ref err) => Some(err),
            _ => None,
        }
    }
}

impl fmt::Display for FramebufferCreationError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<Error> for FramebufferCreationError {
    #[inline]
    fn from(err: Error) -> FramebufferCreationError {
        FramebufferCreationError::from(OomError::from(err))
    }
}

#[cfg(test)]
mod tests {
    use framebuffer::EmptySinglePassRenderPass;
    use framebuffer::Framebuffer;
    use framebuffer::FramebufferCreationError;

    #[test]
    #[ignore]       // TODO: crashes on AMD+Windows
    fn empty_renderpass_create() {
        let (device, _) = gfx_dev_and_queue!();
        let _ = EmptySinglePassRenderPass::new(&device).unwrap();
    }

    #[test]
    #[ignore]       // TODO: crashes on AMD+Windows
    fn framebuffer_too_large() {
        let (device, _) = gfx_dev_and_queue!();
        let renderpass = EmptySinglePassRenderPass::new(&device).unwrap();

        match Framebuffer::new(&renderpass, (0xffffffff, 0xffffffff, 0xffffffff), ()) {
            Err(FramebufferCreationError::DimensionsTooLarge) => (),
            _ => panic!()
        }
    }
}
<|endoftext|>