<commit_before>//! Integration tests.

extern crate assert_cmd;
extern crate escargot;
extern crate filetime;
extern crate predicates;
extern crate tempdir;

use std::fs::{create_dir_all, File};
use std::io::Write;
use std::process::Command;
use std::time::{Duration, SystemTime};

use assert_cmd::prelude::*;
use tempdir::TempDir;
use predicates::boolean::PredicateBooleanExt;
use predicates::prelude::predicate::str::{contains, is_empty, similar};

struct TestEnv {
    pub cache_dir: TempDir,
    pub config_dir: TempDir,
    pub input_dir: TempDir,
    pub default_features: bool,
    pub features: Vec<String>,
}

impl TestEnv {
    fn new() -> Self {
        TestEnv {
            cache_dir: TempDir::new(".tldr.test.cache").unwrap(),
            config_dir: TempDir::new(".tldr.test.config").unwrap(),
            input_dir: TempDir::new(".tldr.test.input").unwrap(),
            default_features: true,
            features: vec![],
        }
    }

    /// Add entry for that environment.
    fn add_entry(&self, name: &str, contents: &str) {
        let dir = self.cache_dir.path().join("tldr-master").join("pages").join("common");
        create_dir_all(&dir).unwrap();

        let mut file = File::create(&dir.join(format!("{}.md", name))).unwrap();
        file.write_all(&contents.as_bytes()).unwrap();
    }

    /// Disable default features.
    #[allow(dead_code)] // Might be useful in the future
    fn no_default_features(mut self) -> Self {
        self.default_features = false;
        self
    }

    /// Add the specified feature.
    #[allow(dead_code)] // Might be useful in the future
    fn with_feature<S: Into<String>>(mut self, feature: S) -> Self {
        self.features.push(feature.into());
        self
    }

    /// Return a new `Command` with env vars set.
    fn command(&self) -> Command {
        let mut build = escargot::CargoBuild::new()
            .bin("tldr")
            .current_release()
            .current_target();
        if !self.default_features {
            build = build.arg("--no-default-features");
        }
        if !self.features.is_empty() {
            build = build.arg(&format!("--feature {}", self.features.join(",")));
        }
        let run = build.run().unwrap();
        let mut cmd = run.command();
        cmd.env("TEALDEER_CACHE_DIR", self.cache_dir.path().to_str().unwrap());
        cmd.env("TEALDEER_CONFIG_DIR", self.config_dir.path().to_str().unwrap());
        cmd
    }
}

#[test]
fn test_missing_cache() {
    TestEnv::new()
        .command()
        .args(&["sl"])
        .assert()
        .failure()
        .stderr(contains("Cache not found. Please run `tldr --update`."));
}

#[test]
fn test_update_cache() {
    let testenv = TestEnv::new();

    testenv
        .command()
        .args(&["sl"])
        .assert()
        .failure()
        .stderr(contains("Cache not found. Please run `tldr --update`."));

    testenv
        .command()
        .args(&["--update"])
        .assert()
        .success()
        .stdout(contains("Successfully updated cache."));

    testenv
        .command()
        .args(&["sl"])
        .assert()
        .success();
}

#[test]
fn test_quiet_cache() {
    let testenv = TestEnv::new();
    testenv
        .command()
        .args(&["--update", "--quiet"])
        .assert()
        .success()
        .stdout(is_empty());

    testenv
        .command()
        .args(&["--clear-cache", "--quiet"])
        .assert()
        .success()
        .stdout(is_empty());
}

#[test]
fn test_quiet_failures() {
    let testenv = TestEnv::new();

    testenv
        .command()
        .args(&["--update", "-q"])
        .assert()
        .success()
        .stdout(is_empty());

    testenv
        .command()
        .args(&["fakeprogram", "-q"])
        .assert()
        .failure()
        .stdout(is_empty());
}

#[test]
fn test_quiet_old_cache() {
    let testenv = TestEnv::new();

    testenv
        .command()
        .args(&["--update", "-q"])
        .assert()
        .success()
        .stdout(is_empty());

    filetime::set_file_mtime(
        testenv.cache_dir.path().join("tldr-master"),
        filetime::FileTime::from_unix_time(1, 0),
    )
    .unwrap();

    testenv
        .command()
        .args(&["tldr"])
        .assert()
        .success()
        .stderr(contains("The cache hasn't been updated for more than "));

    testenv
        .command()
        .args(&["tldr", "--quiet"])
        .assert()
        .success()
        .stderr(contains("The cache hasn't been updated for more than ").not());
}

#[test]
fn test_setup_seed_config() {
    let testenv = TestEnv::new();

    testenv
        .command()
        .args(&["--seed-config"])
        .assert()
        .success()
        .stdout(contains("Successfully created seed config file"));
}

#[test]
fn test_show_config_path() {
    let testenv = TestEnv::new();

    testenv
        .command()
        .args(&["--config-path"])
        .assert()
        .success()
        .stdout(contains(format!(
            "Config path is: {}",
            testenv.config_dir.path().join("config.toml").to_str().unwrap(),
        )));
}

#[test]
fn test_markdown_rendering() {
    let testenv = TestEnv::new();

    testenv
        .command()
        .args(&["--update"])
        .assert()
        .success()
        .stdout(contains("Successfully updated cache."));

    let expected = include_str!("tar-markdown.expected");
    testenv
        .command()
        .args(&["-m", "tar"])
        .assert()
        .success()
        .stdout(similar(expected));
}

fn _test_correct_rendering(input_file: &str, filename: &str) {
    let testenv = TestEnv::new();

    // Create input file
    let file_path = testenv.input_dir.path().join(filename);
    println!("Testfile path: {:?}", &file_path);
    let mut file = File::create(&file_path).unwrap();
    file.write_all(input_file.as_bytes()).unwrap();

    let expected = include_str!("inkscape-default.expected");
    testenv
        .command()
        .args(&["-f", &file_path.to_str().unwrap()])
        .assert()
        .success()
        .stdout(similar(expected));
}

/// An end-to-end integration test for direct file rendering (v1 syntax).
#[test]
fn test_correct_rendering_v1() {
    _test_correct_rendering(include_str!("inkscape-v1.md"), "inkscape-v1.md");
}

/// An end-to-end integration test for direct file rendering (v2 syntax).
#[test]
fn test_correct_rendering_v2() {
    _test_correct_rendering(include_str!("inkscape-v2.md"), "inkscape-v2.md");
}

/// An end-to-end integration test for rendering with constom syntax config.
#[test]
fn test_correct_rendering_with_config() {
    let testenv = TestEnv::new();

    // Setup config file
    // TODO should be config::CONFIG_FILE_NAME
    let config_file_path = testenv.config_dir.path().join("config.toml");
    println!("Config path: {:?}", &config_file_path);

    let mut config_file = File::create(&config_file_path).unwrap();
    config_file
        .write(include_str!("config.toml").as_bytes())
        .unwrap();

    // Create input file
    let file_path = testenv.input_dir.path().join("inkscape-v2.md");
    println!("Testfile path: {:?}", &file_path);

    let mut file = File::create(&file_path).unwrap();
    file.write_all(include_str!("inkscape-v2.md").as_bytes()).unwrap();

    // Load expected output
    let expected = include_str!("inkscape-with-config.expected");

    testenv
        .command()
        .args(&["-f", &file_path.to_str().unwrap()])
        .assert()
        .success()
        .stdout(similar(expected));
}

#[test]
fn test_spaces_find_command() {
    let testenv = TestEnv::new();

    testenv
        .command()
        .args(&["--update"])
        .assert()
        .success()
        .stdout(contains("Successfully updated cache."));

    testenv
        .command()
        .args(&["git", "checkout"])
        .assert()
        .success();
}

#[test]
fn test_pager_flag_enable() {
    let testenv = TestEnv::new();

    testenv
        .command()
        .args(&["--update"])
        .assert()
        .success()
        .stdout(contains("Successfully updated cache."));

    testenv
        .command()
        .args(&["--pager", "tar"])
        .assert()
        .success();
}

#[test]
fn test_list_flag_rendering() {
    let testenv = TestEnv::new();

    testenv
        .command()
        .args(&["--list"])
        .assert()
        .failure()
        .stderr(contains("Cache not found. Please run `tldr --update`."));

    testenv.add_entry("foo", "");

    testenv
        .command()
        .args(&["--list"])
        .assert()
        .success()
        .stdout("foo\n");

    testenv.add_entry("bar", "");
    testenv.add_entry("baz", "");
    testenv.add_entry("qux", "");

    testenv
        .command()
        .args(&["--list"])
        .assert()
        .success()
        .stdout("bar\nbaz\nfoo\nqux\n");
}

#[test]
fn test_autoupdate_cache() {
    let testenv = TestEnv::new();

    // The first time, if automatic updates are disabled, the cache should not be found
    testenv
        .command()
        .args(&["--list"])
        .assert()
        .failure()
        .stderr(contains("Cache not found. Please run `tldr --update`."));

    let config_file_path = testenv.config_dir.path().join("config.toml");
    let cache_file_path = testenv.cache_dir.path().join("tldr-master");

    // Activate automatic updates, set the auto-update interval to 24 hours
    let mut config_file = File::create(&config_file_path).unwrap();
    config_file
        .write("[updates]\nauto_update = true\nauto_update_interval_hours = 24".as_bytes())
        .unwrap();
    config_file.flush().unwrap();

    // Helper function that runs `tldr --list` and asserts that the cache is automatically updated
    // or not, depending on the value of `expected`.
    let check_cache_updated = |expected| {
        let assert = testenv.command().args(&["--list"]).assert().success();
        let pred = contains("Successfully updated cache");
        if expected {
            assert.stdout(pred)
        } else {
            assert.stdout(pred.not())
        };
    };

    // The cache is updated the first time we run `tldr --list`
    check_cache_updated(true);

    // The cache is not updated with a subsequent call
    check_cache_updated(false);

    // We update the modification and access times such that they are about 23 hours from now.
    // auto-update interval is 24 hours, the cache should not be updated
    let new_mtime = SystemTime::now() - Duration::from_secs(82_800);
    filetime::set_file_mtime(&cache_file_path, new_mtime.into()).unwrap();
    check_cache_updated(false);

    // We update the modification and access times such that they are about 25 hours from now.
    // auto-update interval is 24 hours, the cache should be updated
    let new_mtime = SystemTime::now() - Duration::from_secs(90_000);
    filetime::set_file_mtime(&cache_file_path, new_mtime.into()).unwrap();
    check_cache_updated(true);

    // The cache is not updated with a subsequent call
    check_cache_updated(false);
}
<commit_msg>Add test for pager warnings on Windows (#122)<commit_after>//! Integration tests.

extern crate assert_cmd;
extern crate escargot;
extern crate filetime;
extern crate predicates;
extern crate tempdir;

use std::fs::{create_dir_all, File};
use std::io::Write;
use std::process::Command;
use std::time::{Duration, SystemTime};

use assert_cmd::prelude::*;
use tempdir::TempDir;
use predicates::boolean::PredicateBooleanExt;
use predicates::prelude::predicate::str::{contains, is_empty, similar};

struct TestEnv {
    pub cache_dir: TempDir,
    pub config_dir: TempDir,
    pub input_dir: TempDir,
    pub default_features: bool,
    pub features: Vec<String>,
}

impl TestEnv {
    fn new() -> Self {
        TestEnv {
            cache_dir: TempDir::new(".tldr.test.cache").unwrap(),
            config_dir: TempDir::new(".tldr.test.config").unwrap(),
            input_dir: TempDir::new(".tldr.test.input").unwrap(),
            default_features: true,
            features: vec![],
        }
    }

    /// Add entry for that environment.
    fn add_entry(&self, name: &str, contents: &str) {
        let dir = self.cache_dir.path().join("tldr-master").join("pages").join("common");
        create_dir_all(&dir).unwrap();

        let mut file = File::create(&dir.join(format!("{}.md", name))).unwrap();
        file.write_all(&contents.as_bytes()).unwrap();
    }

    /// Disable default features.
    #[allow(dead_code)] // Might be useful in the future
    fn no_default_features(mut self) -> Self {
        self.default_features = false;
        self
    }

    /// Add the specified feature.
    #[allow(dead_code)] // Might be useful in the future
    fn with_feature<S: Into<String>>(mut self, feature: S) -> Self {
        self.features.push(feature.into());
        self
    }

    /// Return a new `Command` with env vars set.
    fn command(&self) -> Command {
        let mut build = escargot::CargoBuild::new()
            .bin("tldr")
            .current_release()
            .current_target();
        if !self.default_features {
            build = build.arg("--no-default-features");
        }
        if !self.features.is_empty() {
            build = build.arg(&format!("--feature {}", self.features.join(",")));
        }
        let run = build.run().unwrap();
        let mut cmd = run.command();
        cmd.env("TEALDEER_CACHE_DIR", self.cache_dir.path().to_str().unwrap());
        cmd.env("TEALDEER_CONFIG_DIR", self.config_dir.path().to_str().unwrap());
        cmd
    }
}

#[test]
fn test_missing_cache() {
    TestEnv::new()
        .command()
        .args(&["sl"])
        .assert()
        .failure()
        .stderr(contains("Cache not found. Please run `tldr --update`."));
}

#[test]
fn test_update_cache() {
    let testenv = TestEnv::new();

    testenv
        .command()
        .args(&["sl"])
        .assert()
        .failure()
        .stderr(contains("Cache not found. Please run `tldr --update`."));

    testenv
        .command()
        .args(&["--update"])
        .assert()
        .success()
        .stdout(contains("Successfully updated cache."));

    testenv
        .command()
        .args(&["sl"])
        .assert()
        .success();
}

#[test]
fn test_quiet_cache() {
    let testenv = TestEnv::new();
    testenv
        .command()
        .args(&["--update", "--quiet"])
        .assert()
        .success()
        .stdout(is_empty());

    testenv
        .command()
        .args(&["--clear-cache", "--quiet"])
        .assert()
        .success()
        .stdout(is_empty());
}

#[test]
fn test_quiet_failures() {
    let testenv = TestEnv::new();

    testenv
        .command()
        .args(&["--update", "-q"])
        .assert()
        .success()
        .stdout(is_empty());

    testenv
        .command()
        .args(&["fakeprogram", "-q"])
        .assert()
        .failure()
        .stdout(is_empty());
}

#[test]
fn test_quiet_old_cache() {
    let testenv = TestEnv::new();

    testenv
        .command()
        .args(&["--update", "-q"])
        .assert()
        .success()
        .stdout(is_empty());

    filetime::set_file_mtime(
        testenv.cache_dir.path().join("tldr-master"),
        filetime::FileTime::from_unix_time(1, 0),
    )
    .unwrap();

    testenv
        .command()
        .args(&["tldr"])
        .assert()
        .success()
        .stderr(contains("The cache hasn't been updated for more than "));

    testenv
        .command()
        .args(&["tldr", "--quiet"])
        .assert()
        .success()
        .stderr(contains("The cache hasn't been updated for more than ").not());
}

#[test]
fn test_setup_seed_config() {
    let testenv = TestEnv::new();

    testenv
        .command()
        .args(&["--seed-config"])
        .assert()
        .success()
        .stdout(contains("Successfully created seed config file"));
}

#[test]
fn test_show_config_path() {
    let testenv = TestEnv::new();

    testenv
        .command()
        .args(&["--config-path"])
        .assert()
        .success()
        .stdout(contains(format!(
            "Config path is: {}",
            testenv.config_dir.path().join("config.toml").to_str().unwrap(),
        )));
}

#[test]
fn test_markdown_rendering() {
    let testenv = TestEnv::new();

    testenv
        .command()
        .args(&["--update"])
        .assert()
        .success()
        .stdout(contains("Successfully updated cache."));

    let expected = include_str!("tar-markdown.expected");
    testenv
        .command()
        .args(&["-m", "tar"])
        .assert()
        .success()
        .stdout(similar(expected));
}

fn _test_correct_rendering(input_file: &str, filename: &str) {
    let testenv = TestEnv::new();

    // Create input file
    let file_path = testenv.input_dir.path().join(filename);
    println!("Testfile path: {:?}", &file_path);
    let mut file = File::create(&file_path).unwrap();
    file.write_all(input_file.as_bytes()).unwrap();

    let expected = include_str!("inkscape-default.expected");
    testenv
        .command()
        .args(&["-f", &file_path.to_str().unwrap()])
        .assert()
        .success()
        .stdout(similar(expected));
}

/// An end-to-end integration test for direct file rendering (v1 syntax).
#[test]
fn test_correct_rendering_v1() {
    _test_correct_rendering(include_str!("inkscape-v1.md"), "inkscape-v1.md");
}

/// An end-to-end integration test for direct file rendering (v2 syntax).
#[test]
fn test_correct_rendering_v2() {
    _test_correct_rendering(include_str!("inkscape-v2.md"), "inkscape-v2.md");
}

/// An end-to-end integration test for rendering with constom syntax config.
#[test]
fn test_correct_rendering_with_config() {
    let testenv = TestEnv::new();

    // Setup config file
    // TODO should be config::CONFIG_FILE_NAME
    let config_file_path = testenv.config_dir.path().join("config.toml");
    println!("Config path: {:?}", &config_file_path);

    let mut config_file = File::create(&config_file_path).unwrap();
    config_file
        .write(include_str!("config.toml").as_bytes())
        .unwrap();

    // Create input file
    let file_path = testenv.input_dir.path().join("inkscape-v2.md");
    println!("Testfile path: {:?}", &file_path);

    let mut file = File::create(&file_path).unwrap();
    file.write_all(include_str!("inkscape-v2.md").as_bytes()).unwrap();

    // Load expected output
    let expected = include_str!("inkscape-with-config.expected");

    testenv
        .command()
        .args(&["-f", &file_path.to_str().unwrap()])
        .assert()
        .success()
        .stdout(similar(expected));
}

#[test]
fn test_spaces_find_command() {
    let testenv = TestEnv::new();

    testenv
        .command()
        .args(&["--update"])
        .assert()
        .success()
        .stdout(contains("Successfully updated cache."));

    testenv
        .command()
        .args(&["git", "checkout"])
        .assert()
        .success();
}

#[test]
fn test_pager_flag_enable() {
    let testenv = TestEnv::new();

    testenv
        .command()
        .args(&["--update"])
        .assert()
        .success()
        .stdout(contains("Successfully updated cache."));

    testenv
        .command()
        .args(&["--pager", "tar"])
        .assert()
        .success();
}

#[test]
fn test_list_flag_rendering() {
    let testenv = TestEnv::new();

    testenv
        .command()
        .args(&["--list"])
        .assert()
        .failure()
        .stderr(contains("Cache not found. Please run `tldr --update`."));

    testenv.add_entry("foo", "");

    testenv
        .command()
        .args(&["--list"])
        .assert()
        .success()
        .stdout("foo\n");

    testenv.add_entry("bar", "");
    testenv.add_entry("baz", "");
    testenv.add_entry("qux", "");

    testenv
        .command()
        .args(&["--list"])
        .assert()
        .success()
        .stdout("bar\nbaz\nfoo\nqux\n");
}

#[test]
fn test_autoupdate_cache() {
    let testenv = TestEnv::new();

    // The first time, if automatic updates are disabled, the cache should not be found
    testenv
        .command()
        .args(&["--list"])
        .assert()
        .failure()
        .stderr(contains("Cache not found. Please run `tldr --update`."));

    let config_file_path = testenv.config_dir.path().join("config.toml");
    let cache_file_path = testenv.cache_dir.path().join("tldr-master");

    // Activate automatic updates, set the auto-update interval to 24 hours
    let mut config_file = File::create(&config_file_path).unwrap();
    config_file
        .write("[updates]\nauto_update = true\nauto_update_interval_hours = 24".as_bytes())
        .unwrap();
    config_file.flush().unwrap();

    // Helper function that runs `tldr --list` and asserts that the cache is automatically updated
    // or not, depending on the value of `expected`.
    let check_cache_updated = |expected| {
        let assert = testenv.command().args(&["--list"]).assert().success();
        let pred = contains("Successfully updated cache");
        if expected {
            assert.stdout(pred)
        } else {
            assert.stdout(pred.not())
        };
    };

    // The cache is updated the first time we run `tldr --list`
    check_cache_updated(true);

    // The cache is not updated with a subsequent call
    check_cache_updated(false);

    // We update the modification and access times such that they are about 23 hours from now.
    // auto-update interval is 24 hours, the cache should not be updated
    let new_mtime = SystemTime::now() - Duration::from_secs(82_800);
    filetime::set_file_mtime(&cache_file_path, new_mtime.into()).unwrap();
    check_cache_updated(false);

    // We update the modification and access times such that they are about 25 hours from now.
    // auto-update interval is 24 hours, the cache should be updated
    let new_mtime = SystemTime::now() - Duration::from_secs(90_000);
    filetime::set_file_mtime(&cache_file_path, new_mtime.into()).unwrap();
    check_cache_updated(true);

    // The cache is not updated with a subsequent call
    check_cache_updated(false);
}

#[test]
#[cfg(target_os = "windows")]
fn test_pager_warning() {
    let testenv = TestEnv::new();
    testenv
        .command()
        .args(&["--update"])
        .assert()
        .success()
        .stdout(contains("Successfully updated cache."));

    // Regular call should not show a "pager flag not available on windows" warning
    testenv
        .command()
        .args(&["tar"])
        .assert()
        .success()
        .stderr(contains("pager flag not available on Windows").not());

    // But it should be shown if the pager flag is true
    testenv
        .command()
        .args(&["tar", "-p"])
        .assert()
        .success()
        .stderr(contains("pager flag not available on Windows"));
}
<|endoftext|>


<commit_before>#![feature(core)]
#![feature(old_io)]
#![feature(std_misc)]

extern crate time;
extern crate stopwatch;

use stopwatch::{Stopwatch};
use std::num::SignedInt;
use std::old_io::timer;
use std::time::Duration;

static SLEEP_MS: i64 = 50;
static TOLERANCE_PERCENTAGE: f64 = 0.3;

#[test]
fn repeated_stops() {
	let mut sw = Stopwatch::start_new();
	for _ in (0..1000i32) {
		sw.stop();
		sw.start();
	}
	assert_sw_near(sw, 0);
}

#[test]
fn elapsed_none() {
	let sw = Stopwatch::new();
	assert_eq!(sw.elapsed_ms(), 0);
}

#[test]
fn elapsed_ms() {
	let sw = Stopwatch::start_new();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	assert_sw_near(sw, SLEEP_MS);
}

#[test]
fn stop() {
	let mut sw = Stopwatch::start_new();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	sw.stop();
	assert_sw_near(sw, SLEEP_MS);

	timer::sleep(Duration::milliseconds(SLEEP_MS));
	assert_sw_near(sw, SLEEP_MS);
}

#[test]
fn resume_once() {
	let mut sw = Stopwatch::start_new();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	sw.stop();
	assert_sw_near(sw, SLEEP_MS);
	sw.start();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	assert_sw_near(sw, 2 * SLEEP_MS);
}

#[test]
fn resume_twice() {
	let mut sw = Stopwatch::start_new();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	sw.stop();
	assert_sw_near(sw, SLEEP_MS);
	sw.start();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	sw.stop();
	assert_sw_near(sw, 2 * SLEEP_MS);
	sw.start();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	assert_sw_near(sw, 3 * SLEEP_MS);
}

#[test]
fn is_running() {
	let mut sw = Stopwatch::new();
	assert!(!sw.is_running());
	sw.start();
	assert!(sw.is_running());
	sw.stop();
	assert!(!sw.is_running());
}

#[test]
fn restart() {
	let mut sw = Stopwatch::start_new();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	sw.restart();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	assert_sw_near(sw, SLEEP_MS);
}

#[test]
fn reset() {
	let mut sw = Stopwatch::start_new();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	sw.reset();
	assert!(!sw.is_running());
	assert_eq!(sw.elapsed_ms(), 0);
}

/////////////// helpers

fn assert_near(x: i64, y: i64, tolerance: i64) {
	let diff = (x - y).abs();
	if diff > tolerance {
		panic!("Expected {:?}, got {:?}", x, y);
	}
}

fn assert_sw_near(sw: Stopwatch, elapsed: i64) {
	let tolerance_value = (TOLERANCE_PERCENTAGE * elapsed as f64) as i64;
	assert_near(elapsed, sw.elapsed_ms(), tolerance_value);
}<commit_msg>Fix warnings in tests.<commit_after>#![feature(core)]

extern crate time;
extern crate stopwatch;

use stopwatch::{Stopwatch};

static SLEEP_MS: i64 = 50;
static TOLERANCE_PERCENTAGE: f64 = 0.3;

#[test]
fn repeated_stops() {
	let mut sw = Stopwatch::start_new();
	for _ in (0..1000i32) {
		sw.stop();
		sw.start();
	}
	assert_sw_near(sw, 0);
}

#[test]
fn elapsed_none() {
	let sw = Stopwatch::new();
	assert_eq!(sw.elapsed_ms(), 0);
}

#[test]
fn elapsed_ms() {
	let sw = Stopwatch::start_new();
	sleep_ms(SLEEP_MS);
	assert_sw_near(sw, SLEEP_MS);
}

#[test]
fn stop() {
	let mut sw = Stopwatch::start_new();
	sleep_ms(SLEEP_MS);
	sw.stop();
	assert_sw_near(sw, SLEEP_MS);

	sleep_ms(SLEEP_MS);
	assert_sw_near(sw, SLEEP_MS);
}

#[test]
fn resume_once() {
	let mut sw = Stopwatch::start_new();
	sleep_ms(SLEEP_MS);
	sw.stop();
	assert_sw_near(sw, SLEEP_MS);
	sw.start();
	sleep_ms(SLEEP_MS);
	assert_sw_near(sw, 2 * SLEEP_MS);
}

#[test]
fn resume_twice() {
	let mut sw = Stopwatch::start_new();
	sleep_ms(SLEEP_MS);
	sw.stop();
	assert_sw_near(sw, SLEEP_MS);
	sw.start();
	sleep_ms(SLEEP_MS);
	sw.stop();
	assert_sw_near(sw, 2 * SLEEP_MS);
	sw.start();
	sleep_ms(SLEEP_MS);
	assert_sw_near(sw, 3 * SLEEP_MS);
}

#[test]
fn is_running() {
	let mut sw = Stopwatch::new();
	assert!(!sw.is_running());
	sw.start();
	assert!(sw.is_running());
	sw.stop();
	assert!(!sw.is_running());
}

#[test]
fn restart() {
	let mut sw = Stopwatch::start_new();
	sleep_ms(SLEEP_MS);
	sw.restart();
	sleep_ms(SLEEP_MS);
	assert_sw_near(sw, SLEEP_MS);
}

#[test]
fn reset() {
	let mut sw = Stopwatch::start_new();
	sleep_ms(SLEEP_MS);
	sw.reset();
	assert!(!sw.is_running());
	assert_eq!(sw.elapsed_ms(), 0);
}

/////////////// helpers

fn sleep_ms(ms: i64) {
	use std::num::ToPrimitive;
	std::thread::sleep_ms(ms.to_u32().unwrap())
}

fn assert_near(x: i64, y: i64, tolerance: i64) {
	let diff = (x - y).abs();
	if diff > tolerance {
		panic!("Expected {:?}, got {:?}", x, y);
	}
}

fn assert_sw_near(sw: Stopwatch, elapsed: i64) {
	let tolerance_value = (TOLERANCE_PERCENTAGE * elapsed as f64) as i64;
	assert_near(elapsed, sw.elapsed_ms(), tolerance_value);
}
<|endoftext|>


<commit_before>
extern crate num_traits;
extern crate fftw;
extern crate ndarray;
#[macro_use]
extern crate ndarray_linalg;

use fftw::*;
use ndarray::*;
use ndarray_linalg::*;

/// Check successive forward and backward transformation conserves.
fn test_identity<R>(mut pair: Pair<R, R, Ix1>, rtol: R::Real)
where
    R: FFTWReal,
{
    let a: Array1<R> = random(pair.size());
    println!("a = {:?}", &a);
    let b = {
        let b = pair.forward(a.as_slice().unwrap());
        Array::from_vec(b.to_vec())
    };
    println!("b = {:?}", &b);
    let a2 = pair.backward(b.as_slice().unwrap());
    let a2: Array1<R> = Array::from_vec(a2.to_vec());
    println!("a2 = {:?}", &a2);
    assert_close_l2!(&a2, &a, rtol);
}

/// Check `cos(k_0 x)` is transformed `b[1] = 1.0 + 0.0i`
fn test_forward<R>(mut pair: Pair<R, R, Ix1>, rtol: R::Real)
where
    R: FFTWReal,
{
    let n = pair.size().size();
    let pi = ::std::f64::consts::PI;
    let a: Array1<R> = Array::from_iter((0..n).map(|i| {
        Scalar::from_f64((2.0 * pi * i as f64 / n as f64).cos())
    }));
    println!("a = {:?}", &a);
    let b = {
        let b = pair.forward(a.as_slice().unwrap());
        Array::from_vec(b.to_vec())
    };
    println!("b = {:?}", &b);
    let mut ans: Array1<R> = Array::zeros(b.len());
    ans[1] = Scalar::from_f64(0.5); // cos(x) = 0.5*exp(ix) + c.c.
    assert_close_l2!(&b, &ans, rtol);
}

mod _64 {
    use super::*;
    const N: usize = 32;
    const RTOL: f64 = 1e-7;

    #[test]
    fn r2hc_identity() {
        let pair: Pair<f64, f64, Ix1> = r2hc_1d(N).to_pair().unwrap();
        test_identity(pair, RTOL);
    }

    #[test]
    fn r2hc_forward() {
        let pair: Pair<f64, f64, Ix1> = r2hc_1d(N).to_pair().unwrap();
        test_forward(pair, RTOL);
    }
}

mod _32 {
    use super::*;
    const N: usize = 32;
    const RTOL: f32 = 1e-4;

    #[test]
    fn r2hc_identity() {
        let pair: Pair<f32, f32, Ix1> = r2hc_1d(N).to_pair().unwrap();
        test_identity(pair, RTOL);
    }

    #[test]
    fn r2hc_forward() {
        let pair: Pair<f32, f32, Ix1> = r2hc_1d(N).to_pair().unwrap();
        test_forward(pair, RTOL);
    }
}
<commit_msg>Add should_panic for intel-mkl<commit_after>
extern crate num_traits;
extern crate fftw;
extern crate ndarray;
#[macro_use]
extern crate ndarray_linalg;

use fftw::*;
use ndarray::*;
use ndarray_linalg::*;

/// Check successive forward and backward transformation conserves.
fn test_identity<R>(mut pair: Pair<R, R, Ix1>, rtol: R::Real)
where
    R: FFTWReal,
{
    let a: Array1<R> = random(pair.size());
    println!("a = {:?}", &a);
    let b = {
        let b = pair.forward(a.as_slice().unwrap());
        Array::from_vec(b.to_vec())
    };
    println!("b = {:?}", &b);
    let a2 = pair.backward(b.as_slice().unwrap());
    let a2: Array1<R> = Array::from_vec(a2.to_vec());
    println!("a2 = {:?}", &a2);
    assert_close_l2!(&a2, &a, rtol);
}

/// Check `cos(k_0 x)` is transformed `b[1] = 1.0 + 0.0i`
fn test_forward<R>(mut pair: Pair<R, R, Ix1>, rtol: R::Real)
where
    R: FFTWReal,
{
    let n = pair.size().size();
    let pi = ::std::f64::consts::PI;
    let a: Array1<R> = Array::from_iter((0..n).map(|i| {
        Scalar::from_f64((2.0 * pi * i as f64 / n as f64).cos())
    }));
    println!("a = {:?}", &a);
    let b = {
        let b = pair.forward(a.as_slice().unwrap());
        Array::from_vec(b.to_vec())
    };
    println!("b = {:?}", &b);
    let mut ans: Array1<R> = Array::zeros(b.len());
    ans[1] = Scalar::from_f64(0.5); // cos(x) = 0.5*exp(ix) + c.c.
    assert_close_l2!(&b, &ans, rtol);
}

mod _64 {
    use super::*;
    const N: usize = 32;
    const RTOL: f64 = 1e-7;

    #[cfg_attr(feature = "intel-mkl", should_panic)]
    #[test]
    fn r2hc_identity() {
        let pair: Pair<f64, f64, Ix1> = r2hc_1d(N).to_pair().unwrap();
        test_identity(pair, RTOL);
    }

    #[cfg_attr(feature = "intel-mkl", should_panic)]
    #[test]
    fn r2hc_forward() {
        let pair: Pair<f64, f64, Ix1> = r2hc_1d(N).to_pair().unwrap();
        test_forward(pair, RTOL);
    }
}

mod _32 {
    use super::*;
    const N: usize = 32;
    const RTOL: f32 = 1e-4;

    #[cfg_attr(feature = "intel-mkl", should_panic)]
    #[test]
    fn r2hc_identity() {
        let pair: Pair<f32, f32, Ix1> = r2hc_1d(N).to_pair().unwrap();
        test_identity(pair, RTOL);
    }

    #[cfg_attr(feature = "intel-mkl", should_panic)]
    #[test]
    fn r2hc_forward() {
        let pair: Pair<f32, f32, Ix1> = r2hc_1d(N).to_pair().unwrap();
        test_forward(pair, RTOL);
    }
}
<|endoftext|>


<commit_before>use params::Params;
use std::num::ln;

mod params;

pub trait Model {
  fn setup(params: &Params) -> Self;
  fn run(&self) -> f64;
}

 pub struct Gompertz {
  alpha: f64,
  K : f64,
  C0: f64
 }

impl Model for Gompertz {
  #[allow(dead_code)]
  fn setup(params: &Params) -> Gompertz {
    Gompertz{ alpha: params.get_float("alpha"), K: params.get_float("K"), C0 : 0.1f64}
  }

  #[allow(dead_code)]
  fn run(&self) -> f64 {
    let tmax=10f64;
    let dt=0.001;
    let mut t=0.0;
    let mut C=self.C0;
    for _ in range(0, (tmax/dt) as uint) {
      C=C+self.alpha*dt*ln(self.K/C)*C;
      t=t+dt;
    }
    C
  }
}
<commit_msg>C0 is also a parameter in the Gompertz case<commit_after>use params::Params;
use std::num::ln;

mod params;

pub trait Model {
  fn setup(params: &Params) -> Self;
  fn run(&self) -> f64;
}

 pub struct Gompertz {
  alpha: f64,
  K : f64,
  C0: f64
 }

impl Model for Gompertz {
  #[allow(dead_code)]
  fn setup(params: &Params) -> Gompertz {
    Gompertz{ alpha: params.get_float("alpha"), K: params.get_float("K"), C0 : params.get_float("C0")}
  }

  #[allow(dead_code)]
  fn run(&self) -> f64 {
    let tmax=10f64;
    let dt=0.001;
    let mut t=0.0;
    let mut C=self.C0;
    for _ in range(0, (tmax/dt) as uint) {
      C=C+self.alpha*dt*ln(self.K/C)*C;
      t=t+dt;
    }
    C
  }
}
<|endoftext|>


<commit_before>use spine;

type Env = spine::env::Env<(), usize, usize>;

pub fn check(prog: &spine::ProgDef) -> Vec<String> {
  let fun_binds = prog.fun_defs.iter().map(|fun_def| {
      (fun_def.name.clone(), fun_def.args.len())
    }).collect();
  let empty_env = spine::env::Env::new();
  let fun_env = empty_env.bind_funs(fun_binds);

  let mut errors = Vec::new();
  for fun_def in prog.fun_defs.iter() {
    errors.extend(check_fun_def(&fun_env, fun_def).into_iter());
  }

  if fun_env.lookup_fun(&prog.main_fun).is_none() {
    errors.push(format!("undefined main function: '{}'", prog.main_fun.0));
  }

  errors
}

fn check_fun_def(env: &Env, fun_def: &spine::FunDef) -> Vec<String> {
  let var_binds = fun_def.args.iter().map(|arg| {
      (arg.clone(), ())
    }).collect();
  let cont_binds = vec![
      (fun_def.ret.clone(), 1),
    ];
  let args_env = env.bind_vars(var_binds);
  let cont_env = args_env.bind_conts(cont_binds);
  check_term(&cont_env, &fun_def.body)
}

fn check_cont_def(env: &Env, cont_def: &spine::ContDef) -> Vec<String> {
  let var_binds = cont_def.args.iter().map(|arg| {
      (arg.clone(), ())
    }).collect();
  let args_env = env.bind_vars(var_binds);
  check_term(&args_env, &cont_def.body)
}

fn check_term(env: &Env, term: &spine::Term) -> Vec<String> {
  let mut errors = Vec::new();

  match *term {
    spine::Term::Letcont(ref cont_defs, ref body) => {
      let cont_binds = cont_defs.iter()
          .map(|cont_def| (cont_def.name.clone(), cont_def.args.len()))
          .collect();
      let inner_env = env.bind_conts(cont_binds);
      for cont_def in cont_defs.iter() {
        errors.extend(check_cont_def(&inner_env, cont_def).into_iter());
      }
      errors.extend(check_term(&inner_env, &**body).into_iter());
    },
    spine::Term::Call(ref fun_name, ref ret_cont, ref args) => {
      match env.lookup_fun(fun_name) {
        Some(&argc) => if argc != args.len() {
          errors.push(format!("call args mismatch: '{}' expects {} args, got {}",
            fun_name.0, argc, args.len()))
        },
        None => errors.push(format!("call to undefined fun: '{}'", fun_name.0)),
      }

      match env.lookup_cont(ret_cont) {
        Some(&retc) => if retc != 1 {
          errors.push(format!("return args mismatch: '{}' expects {} args",
            ret_cont.0, retc))
        },
        None => errors.push(format!("return to undefined cont: '{}'", ret_cont.0)),
      }

      for arg in args.iter() {
        errors.extend(check_val(env, arg).into_iter());
      }
    },
    spine::Term::ExternCall(ref _extern_name, ref ret_cont, ref args) => {
      match env.lookup_cont(ret_cont) {
        Some(&retc) => if retc != 1 {
          errors.push(format!("extern return args mismatch: '{}' expects {} args",
            ret_cont.0, retc))
        },
        None => errors.push(format!("extern return to undefined cont: '{}'", ret_cont.0)),
      }

      for arg in args.iter() {
        errors.extend(check_val(env, arg).into_iter());
      }
    },
    spine::Term::Cont(ref cont, ref args) => {
      match env.lookup_cont(cont) {
        Some(&argc) => if argc != args.len() {
          errors.push(format!("continue args mismatch: '{}' expects {} args, got {}",
            cont.0, argc, args.len()));
        },
        None => errors.push(format!("continue to undefined cont: '{}'", cont.0)),
      }

      for arg in args.iter() {
        errors.extend(check_val(env, arg).into_iter());
      }
    },
    spine::Term::Branch(ref boolval, ref then_cont, ref else_cont) => {
      match env.lookup_cont(then_cont) {
        Some(&argc) => if argc != 0 {
          errors.push(format!("then-branch args mismatch: '{}' expects {} args",
            then_cont.0, argc));
        },
        None => errors.push(format!("then-branch to undefined cont: '{}'", then_cont.0)),
      }

      match env.lookup_cont(else_cont) {
        Some(&argc) => if argc != 0 {
          errors.push(format!("else-branch args mismatch: '{}' expects {} args",
            else_cont.0, argc));
        },
        None => errors.push(format!("else-branch to undefined cont: '{}'", else_cont.0)),
      }

      errors.extend(check_boolval(env, boolval).into_iter());
    }
  }

  errors
}

fn check_val(env: &Env, val: &spine::Val) -> Vec<String> {
  match *val {
    spine::Val::Literal(_) => vec![],
    spine::Val::Var(ref var) => match env.lookup_var(var) {
      Some(&()) => vec![],
      None => vec![format!("undefined var: '{}'", var.0)],
    },
  }
}

fn check_boolval(env: &Env, boolval: &spine::Boolval) -> Vec<String> {
  match *boolval {
    spine::Boolval::IsTrue(ref val) => check_val(env, val),
    spine::Boolval::IsFalse(ref val) => check_val(env, val),
  }
}

#[cfg(test)]
mod test {
  use sexpr;
  use spine;
  use spine::helpers::*;

  fn parse_check(txt: &str) -> Vec<String> {
    let sexpr = sexpr::parse::parse_sexpr(txt).unwrap();
    let spine = sexpr::to_spine::prog_from_sexpr(&sexpr).unwrap();
    spine::check::check(&spine)
  }

  #[test]
  fn test_undefined_var() {
    let errors = parse_check("(program main (main halt () (cont halt b)))");
    assert_eq!(errors.len(), 1);
    assert!(errors[0].contains("undefined var: 'b'"));
  }

  // TODO :-)
}
<commit_msg>Write tests for spine well-formedness checker<commit_after>use spine;
use std::collections::{HashSet};

type Env = spine::env::Env<(), usize, usize>;

pub fn check(prog: &spine::ProgDef) -> Vec<String> {
  let fun_binds = prog.fun_defs.iter().map(|fun_def| {
      (fun_def.name.clone(), fun_def.args.len())
    }).collect();
  let empty_env = spine::env::Env::new();
  let fun_env = empty_env.bind_funs(fun_binds);

  let mut errors = Vec::new();
  let mut fun_names = HashSet::new();
  for fun_def in prog.fun_defs.iter() {
    errors.extend(check_fun_def(&fun_env, fun_def).into_iter());
    if !fun_names.insert(&fun_def.name) {
      errors.push(format!("redefined function: '{}'", fun_def.name.0));
    }
  }

  if fun_env.lookup_fun(&prog.main_fun).is_none() {
    errors.push(format!("undefined main function: '{}'", prog.main_fun.0));
  }

  errors
}

fn check_fun_def(env: &Env, fun_def: &spine::FunDef) -> Vec<String> {
  let var_binds = fun_def.args.iter().map(|arg| {
      (arg.clone(), ())
    }).collect();
  let cont_binds = vec![
      (fun_def.ret.clone(), 1),
    ];
  let args_env = env.bind_vars(var_binds);
  let cont_env = args_env.bind_conts(cont_binds);
  check_term(&cont_env, &fun_def.body)
}

fn check_cont_def(env: &Env, cont_def: &spine::ContDef) -> Vec<String> {
  let var_binds = cont_def.args.iter().map(|arg| {
      (arg.clone(), ())
    }).collect();
  let args_env = env.bind_vars(var_binds);
  check_term(&args_env, &cont_def.body)
}

fn check_term(env: &Env, term: &spine::Term) -> Vec<String> {
  let mut errors = Vec::new();

  match *term {
    spine::Term::Letcont(ref cont_defs, ref body) => {
      let cont_binds = cont_defs.iter()
          .map(|cont_def| (cont_def.name.clone(), cont_def.args.len()))
          .collect();
      let inner_env = env.bind_conts(cont_binds);
      for cont_def in cont_defs.iter() {
        errors.extend(check_cont_def(&inner_env, cont_def).into_iter());
      }
      errors.extend(check_term(&inner_env, &**body).into_iter());
    },
    spine::Term::Call(ref fun_name, ref ret_cont, ref args) => {
      match env.lookup_fun(fun_name) {
        Some(&argc) => if argc != args.len() {
          errors.push(format!("call args mismatch: '{}' expects {} args, got {}",
            fun_name.0, argc, args.len()))
        },
        None => errors.push(format!("call to undefined fun: '{}'", fun_name.0)),
      }

      match env.lookup_cont(ret_cont) {
        Some(&retc) => if retc != 1 {
          errors.push(format!("return args mismatch: '{}' expects {} args",
            ret_cont.0, retc))
        },
        None => errors.push(format!("return to undefined cont: '{}'", ret_cont.0)),
      }

      for arg in args.iter() {
        errors.extend(check_val(env, arg).into_iter());
      }
    },
    spine::Term::ExternCall(ref _extern_name, ref ret_cont, ref args) => {
      match env.lookup_cont(ret_cont) {
        Some(&retc) => if retc != 1 {
          errors.push(format!("extern return args mismatch: '{}' expects {} args",
            ret_cont.0, retc))
        },
        None => errors.push(format!("extern return to undefined cont: '{}'", ret_cont.0)),
      }

      for arg in args.iter() {
        errors.extend(check_val(env, arg).into_iter());
      }
    },
    spine::Term::Cont(ref cont, ref args) => {
      match env.lookup_cont(cont) {
        Some(&argc) => if argc != args.len() {
          errors.push(format!("continue args mismatch: '{}' expects {} args, got {}",
            cont.0, argc, args.len()));
        },
        None => errors.push(format!("continue to undefined cont: '{}'", cont.0)),
      }

      for arg in args.iter() {
        errors.extend(check_val(env, arg).into_iter());
      }
    },
    spine::Term::Branch(ref boolval, ref then_cont, ref else_cont) => {
      match env.lookup_cont(then_cont) {
        Some(&argc) => if argc != 0 {
          errors.push(format!("then-branch args mismatch: '{}' expects {} args",
            then_cont.0, argc));
        },
        None => errors.push(format!("then-branch to undefined cont: '{}'", then_cont.0)),
      }

      match env.lookup_cont(else_cont) {
        Some(&argc) => if argc != 0 {
          errors.push(format!("else-branch args mismatch: '{}' expects {} args",
            else_cont.0, argc));
        },
        None => errors.push(format!("else-branch to undefined cont: '{}'", else_cont.0)),
      }

      errors.extend(check_boolval(env, boolval).into_iter());
    }
  }

  errors
}

fn check_val(env: &Env, val: &spine::Val) -> Vec<String> {
  match *val {
    spine::Val::Literal(_) => vec![],
    spine::Val::Var(ref var) => match env.lookup_var(var) {
      Some(&()) => vec![],
      None => vec![format!("undefined var: '{}'", var.0)],
    },
  }
}

fn check_boolval(env: &Env, boolval: &spine::Boolval) -> Vec<String> {
  match *boolval {
    spine::Boolval::IsTrue(ref val) => check_val(env, val),
    spine::Boolval::IsFalse(ref val) => check_val(env, val),
  }
}

#[cfg(test)]
mod test {
  use sexpr;
  use spine;

  fn parse_check(txt: &str) -> Vec<String> {
    let sexpr = sexpr::parse::parse_sexpr(txt).unwrap();
    let spine = sexpr::to_spine::prog_from_sexpr(&sexpr).unwrap();
    spine::check::check(&spine)
  }

  #[test]
  fn test_missing_main() {
    let errs = parse_check("(program question (answer r () (cont r 42)))");
    assert_eq!(errs.len(), 1);
    assert!(errs[0].contains("undefined main function: 'question'"));
  }

  #[test]
  fn test_double_fun_defs() {
    let errs = parse_check("(program main
      (main k () (cont k 10))
      (main k () (cont k 20)))");
    assert_eq!(errs.len(), 1);
    assert!(errs[0].contains("redefined function: 'main'"));
  }

  #[test]
  fn test_undefined_var() {
    let errs = parse_check("(program main (main halt () (cont halt x)))");
    assert_eq!(errs.len(), 1);
    assert!(errs[0].contains("undefined var: 'x'"));
  }

  #[test]
  fn test_undefined_conts() {
    let errs = parse_check(
      "(program main
        (fun r (a b) (cont r 100))
        (main r ()
          (letcont ((bad-call () (call fun cc1 1 2))
                    (bad-ext-call () (extern-call ext_add cc2 1 2))
                    (bad-cont () (cont cc3 3))
                    (bad-branch () (branch (is-true 1) cc4 cc5)))
            (cont r 5))))");

    assert_eq!(errs.len(), 5);
    assert!(errs[0].contains("return to undefined cont: 'cc1'"));
    assert!(errs[1].contains("extern return to undefined cont: 'cc2'"));
    assert!(errs[2].contains("continue to undefined cont: 'cc3'"));
    assert!(errs[3].contains("then-branch to undefined cont: 'cc4'"));
    assert!(errs[4].contains("else-branch to undefined cont: 'cc5'"));
  }

  #[test]
  fn test_cont_args_mismatch() {
    let errs = parse_check(
      "(program main
        (main r () (cont r 0))
        (return-42 r () (cont r 42))
        (bad-return-2 ret (a b) (cont ret a b))
        (bad-return-0 ret (a b) (cont ret))
        (bad-call r ()
          (letcont ((needs-two-args (x y) (cont r x)))
            (call return-42 needs-two-args)))
        (bad-extern-call r ()
          (letcont ((needs-zero-args () (cont r 10)))
            (extern-call __boo__ needs-zero-args 1 2 3)))
        (bad-branch r ()
          (letcont ((needs-one-arg (x) (cont r x))
                    (needs-two-args (x y) (cont r y)))
            (branch (is-true 0) needs-one-arg needs-two-args))))");

    assert_eq!(errs.len(),  6);
    assert!(errs[0].contains("continue args mismatch: 'ret' expects 1 args"));
    assert!(errs[1].contains("continue args mismatch: 'ret' expects 1 args"));
    assert!(errs[2].contains("return args mismatch: 'needs-two-args' expects 2 args"));
    assert!(errs[3].contains("extern return args mismatch: \
      'needs-zero-args' expects 0 args"));
    assert!(errs[4].contains("then-branch args mismatch: \
      'needs-one-arg' expects 1 args"));
    assert!(errs[5].contains("else-branch args mismatch: \
      'needs-two-args' expects 2 args"));
  }
}
<|endoftext|>


<commit_before>use std::io;
use std::io::{Read, Seek};
use byteorder::{ReadBytesExt, LittleEndian};

use image::ImageError;
use image::ImageResult;
use image::ImageDecoder;
use image::DecodingResult;
use color::ColorType;

enum ImageType {
    NoImageData = 0,
    /// Uncompressed images
    RawColorMap = 1,
    RawTrueColor = 2,
    RawGrayScale = 3,
    /// Run length encoded images
    RunColorMap = 9,
    RunTrueColor = 10,
    RunGrayScale = 11,
    Unknown,
}

impl ImageType {
    /// Create a new image type from a u8
    fn new(img_type: u8) -> ImageType {
        match img_type {
            0  => ImageType::NoImageData,

            1  => ImageType::RawColorMap,
            2  => ImageType::RawTrueColor,
            3  => ImageType::RawGrayScale,

            9  => ImageType::RunColorMap,
            10 => ImageType::RunTrueColor,
            11 => ImageType::RunGrayScale,

            _  => ImageType::Unknown,
        }
    }

    /// Check if the image format uses colors as opposed to gray scale
    fn is_color(&self) -> bool {
        match *self {
            ImageType::RawColorMap  |
            ImageType::RawTrueColor |
            ImageType::RunTrueColor |
            ImageType::RunColorMap => true,
            _ => false,
        }
    }

    /// Does the image use a color map
    fn is_color_mapped(&self) -> bool {
        match *self {
            ImageType::RawColorMap |
            ImageType::RunColorMap => true,
            _ => false,
        }
    }

    /// Is the image run length encoded
    fn is_encoded(&self) -> bool {
        match *self {
            ImageType::RunColorMap |
            ImageType::RunTrueColor |
            ImageType::RunGrayScale => true,
            _ => false,
        }
    }
}

/// Header used by TGA image files
#[derive(Debug)]
struct Header {
    id_length: u8,         // length of ID string
    map_type: u8,          // color map type
    image_type: u8,        // image type code
    map_origin: u16,       // starting index of map
    map_length: u16,      // length of map
    map_entry_size: u8,    // size of map entries in bits
    x_origin: u16,         // x-origin of image
    y_origin: u16,         // y-origin of image
    image_width: u16,      // width of image
    image_height: u16,     // height of image
    pixel_depth: u8,       // bits per pixel
    image_desc: u8,        // image descriptor
}

impl Header {
    /// Create a header with all valuse set to zero
    fn new() -> Header {
        Header {
            id_length: 0,
            map_type: 0,
            image_type: 0,
            map_origin: 0,
            map_length: 0,
            map_entry_size: 0,
            x_origin: 0,
            y_origin: 0,
            image_width: 0,
            image_height: 0,
            pixel_depth: 0,
            image_desc: 0,
        }
    }

    /// Load the header with values from the reader
    fn from_reader(r: &mut Read) -> ImageResult<Header> {
        Ok(Header {
            id_length:         try!(r.read_u8()),
            map_type:          try!(r.read_u8()),
            image_type:        try!(r.read_u8()),
            map_origin:        try!(r.read_u16::<LittleEndian>()),
            map_length:        try!(r.read_u16::<LittleEndian>()),
            map_entry_size:    try!(r.read_u8()),
            x_origin:          try!(r.read_u16::<LittleEndian>()),
            y_origin:          try!(r.read_u16::<LittleEndian>()),
            image_width:       try!(r.read_u16::<LittleEndian>()),
            image_height:      try!(r.read_u16::<LittleEndian>()),
            pixel_depth:       try!(r.read_u8()),
            image_desc:        try!(r.read_u8()),
        })
    }
}

struct ColorMap {
    /// sizes in bytes
    start_offset: usize,
    entry_size: usize,
    bytes: Vec<u8>,
}

impl ColorMap {
    pub fn from_reader(r: &mut Read,
                       start_offset: u16,
                       num_entries: u16,
                       bits_per_entry: u8)
        -> ImageResult<ColorMap> {
            let bytes_per_entry = (bits_per_entry as usize + 7) / 8;

            let mut bytes = vec![0; bytes_per_entry * num_entries as usize];
            if try!(r.read(&mut bytes)) != bytes.len() {
                return Err(ImageError::ImageEnd);
            }

            Ok(ColorMap {
                entry_size: bytes_per_entry,
                start_offset: start_offset as usize,
                bytes: bytes,
            })
        }

    /// Get one entry from the color map
    pub fn get(&self, index: usize) -> &[u8] {
        let entry = self.start_offset + self.entry_size * index;
        &self.bytes[entry..entry + self.entry_size]
    }
}

/// The representation of a TGA decoder
pub struct TGADecoder<R> {
    r: R,

    width: usize,
    height: usize,
    bytes_per_pixel: usize,
    has_loaded_metadata: bool,

    image_type: ImageType,
    color_type: ColorType,

    header: Header,
    color_map: Option<ColorMap>,
}

impl<R: Read + Seek> TGADecoder<R> {
    /// Create a new decoder that decodes from the stream `r`
    pub fn new(r: R) -> TGADecoder<R> {
        TGADecoder {
            r: r,

            width: 0,
            height: 0,
            bytes_per_pixel: 0,
            has_loaded_metadata: false,

            image_type: ImageType::Unknown,
            color_type: ColorType::Gray(1),

            header: Header::new(),
            color_map: None,
        }
    }

    fn read_header(&mut self) -> ImageResult<()> {
        self.header = try!(Header::from_reader(&mut self.r));
        self.image_type = ImageType::new(self.header.image_type);
        self.width = self.header.image_width as usize;
        self.height = self.header.image_height as usize;
        self.bytes_per_pixel = (self.header.pixel_depth as usize + 7) / 8;
        Ok(())
    }

    fn read_metadata(&mut self) -> ImageResult<()> {
        if !self.has_loaded_metadata {
            try!(self.read_header());
            try!(self.read_image_id());
            try!(self.read_color_map());
            try!(self.read_color_information());
            self.has_loaded_metadata = true;
        }
        Ok(())
    }

    /// Loads the color information for the decoder
    ///
    /// To keep things simple, we won't handle bit depths that aren't divisible
    /// by 8 and are less than 32.
    fn read_color_information(&mut self) -> ImageResult<()> {
        if self.header.pixel_depth % 8 != 0 {
            return Err(ImageError::UnsupportedError("\
                Bit depth must be divisible by 8".to_string()));
        }
        if self.header.pixel_depth > 32 {
            return Err(ImageError::UnsupportedError("\
                Bit depth must be less than 32".to_string()));
        }

        let num_alpha_bits = self.header.image_desc & 0b1111;

        let other_channel_bits = if self.header.map_type != 0 {
            self.header.map_entry_size
        } else {
            self.header.pixel_depth - num_alpha_bits
        };
        let color = self.image_type.is_color();

        match (num_alpha_bits, other_channel_bits, color) {
            // really, the encoding is BGR and BGRA, this is fixed
            // up with `TGADecoder::reverse_encoding`.
            (8, 24, true) => self.color_type = ColorType::RGBA(8),
            (0, 24, true) => self.color_type = ColorType::RGB(8),
            (8, 8, false) => self.color_type = ColorType::GrayA(8),
            (0, 8, false) => self.color_type = ColorType::Gray(8),
            _ => return Err(ImageError::UnsupportedError(format!("\
                    Color format not supported. Bit depth: {}, Alpha bits: {}",
                    other_channel_bits, num_alpha_bits).to_string())),
        }
        Ok(())
    }

    /// Read the image id field
    ///
    /// We're not interested in this field, so this function skips it if it
    /// is present
    fn read_image_id(&mut self) -> ImageResult<()> {
        try!(self.r.seek(io::SeekFrom::Current(self.header.id_length as i64)));
        Ok(())
    }

    fn read_color_map(&mut self) -> ImageResult<()> {
        if self.header.map_type == 1 {
            self.color_map = Some(try!(
                ColorMap::from_reader(&mut self.r,
                                      self.header.map_origin,
                                      self.header.map_length,
                                      self.header.map_entry_size)));
        }
        Ok(())
    }

    /// Expands indices into its mapped color
    fn expand_color_map(&mut self, pixel_data: Vec<u8>) -> Vec<u8> {
        #[inline]
        fn bytes_to_index(bytes: &[u8]) -> usize {
            let mut result = 0usize;
            for byte in bytes.iter() {
                result = result << 8 | *byte as usize;
            }
            result
        }

        let bytes_per_entry = (self.header.map_entry_size as usize + 7) / 8;
        let mut result = Vec::with_capacity(self.width * self.height *
                                            bytes_per_entry);

        let color_map = match self.color_map {
            Some(ref color_map) => color_map,
            None => unreachable!(),
        };

        for chunk in pixel_data.chunks(self.bytes_per_pixel) {
            let index = bytes_to_index(chunk);
            result.extend(color_map.get(index).iter().map(|&c| c));
        }

        result
    }

    fn read_image_data(&mut self) -> ImageResult<Vec<u8>> {
        // read the pixels from the data region
        let mut pixel_data = if self.image_type.is_encoded() {
            try!(self.read_encoded_data())
        } else {
            let num_raw_bytes = self.width * self.height * self.bytes_per_pixel;
            let mut buf = Vec::with_capacity(num_raw_bytes);
            try!(self.r.by_ref().take(num_raw_bytes as u64).read_to_end(&mut buf));
            buf
        };

        // expand the indices using the color map if necessary
        if self.image_type.is_color_mapped() {
            pixel_data = self.expand_color_map(pixel_data)
        }

        self.reverse_encoding(&mut pixel_data);
        Ok(pixel_data)
    }

    /// Reads a run length encoded packet
    fn read_encoded_data(&mut self) -> ImageResult<Vec<u8>> {
        let num_pixels = self.width * self.height;
        let mut num_read = 0;
        let mut pixel_data = Vec::with_capacity(self.width * self.height *
                                                self.bytes_per_pixel);

        while num_read < num_pixels {
            let run_packet = try!(self.r.read_u8());
            // If the highest bit in `run_packet` is set, then we repeat pixels
            //
            // Note: the TGA format adds 1 to both counts because having a count
            // of 0 would be pointless.
            if (run_packet & 0x80) != 0 {
                // high bit set, so we will repeat the data
                let repeat_count = ((run_packet & !0x80) + 1) as usize;
                let mut data = Vec::with_capacity(self.bytes_per_pixel);
                try!(self.r.by_ref().take(self.bytes_per_pixel as u64).read_to_end(&mut data));
                for _ in 0usize..repeat_count {
                    pixel_data.extend(data.iter().map(|&c| c));
                }
                num_read += repeat_count;
            } else {
                // not set, so `run_packet+1` is the number of non-encoded bytes
                let num_raw_bytes = (run_packet + 1) as usize * self.bytes_per_pixel;
                try!(self.r.by_ref().take(num_raw_bytes as u64).read_to_end(&mut pixel_data));
                num_read += run_packet as usize;
            }
        }

        Ok(pixel_data)
    }

    /// Reverse from BGR encoding to RGB encoding
    ///
    /// TGA files are stored in the BGRA encoding. This function swaps
    /// the blue and red bytes in the `pixels` array.
    fn reverse_encoding(&mut self, pixels: &mut [u8]) {
        // We only need to reverse the encoding of color images
        match self.color_type {
            ColorType::RGB(8) => {
                for chunk in pixels.chunks_mut(self.bytes_per_pixel) {
                    let r = chunk[0];
                    chunk[0] = chunk[2];
                    chunk[2] = r;
                }
            }
            ColorType::RGBA(8) => {
                for chunk in pixels.chunks_mut(self.bytes_per_pixel) {
                    let r = chunk[0];
                    chunk[0] = chunk[2];
                    chunk[2] = r;
                }
            }
            _ => { }
        }
    }
}

impl<R: Read + Seek> ImageDecoder for TGADecoder<R> {
    fn dimensions(&mut self) -> ImageResult<(u32, u32)> {
        try!(self.read_metadata());

        Ok((self.width as u32, self.height as u32))
    }

    fn colortype(&mut self) -> ImageResult<ColorType> {
        try!(self.read_metadata());

        Ok(self.color_type)
    }

    fn row_len(&mut self) -> ImageResult<usize> {
        try!(self.read_metadata());

        Ok(self.bytes_per_pixel * 8 * self.width)
    }

    fn read_scanline(&mut self, _buf: &mut [u8]) -> ImageResult<u32> {
        unimplemented!();
    }

    fn read_image(&mut self) -> ImageResult<DecodingResult> {
        try!(self.read_metadata());
        self.read_image_data().map(|v| DecodingResult::U8(v) )
    }
}
<commit_msg>tga/decoder: fix RLE read length<commit_after>use std::io;
use std::io::{Read, Seek};
use byteorder::{ReadBytesExt, LittleEndian};

use image::ImageError;
use image::ImageResult;
use image::ImageDecoder;
use image::DecodingResult;
use color::ColorType;

enum ImageType {
    NoImageData = 0,
    /// Uncompressed images
    RawColorMap = 1,
    RawTrueColor = 2,
    RawGrayScale = 3,
    /// Run length encoded images
    RunColorMap = 9,
    RunTrueColor = 10,
    RunGrayScale = 11,
    Unknown,
}

impl ImageType {
    /// Create a new image type from a u8
    fn new(img_type: u8) -> ImageType {
        match img_type {
            0  => ImageType::NoImageData,

            1  => ImageType::RawColorMap,
            2  => ImageType::RawTrueColor,
            3  => ImageType::RawGrayScale,

            9  => ImageType::RunColorMap,
            10 => ImageType::RunTrueColor,
            11 => ImageType::RunGrayScale,

            _  => ImageType::Unknown,
        }
    }

    /// Check if the image format uses colors as opposed to gray scale
    fn is_color(&self) -> bool {
        match *self {
            ImageType::RawColorMap  |
            ImageType::RawTrueColor |
            ImageType::RunTrueColor |
            ImageType::RunColorMap => true,
            _ => false,
        }
    }

    /// Does the image use a color map
    fn is_color_mapped(&self) -> bool {
        match *self {
            ImageType::RawColorMap |
            ImageType::RunColorMap => true,
            _ => false,
        }
    }

    /// Is the image run length encoded
    fn is_encoded(&self) -> bool {
        match *self {
            ImageType::RunColorMap |
            ImageType::RunTrueColor |
            ImageType::RunGrayScale => true,
            _ => false,
        }
    }
}

/// Header used by TGA image files
#[derive(Debug)]
struct Header {
    id_length: u8,         // length of ID string
    map_type: u8,          // color map type
    image_type: u8,        // image type code
    map_origin: u16,       // starting index of map
    map_length: u16,      // length of map
    map_entry_size: u8,    // size of map entries in bits
    x_origin: u16,         // x-origin of image
    y_origin: u16,         // y-origin of image
    image_width: u16,      // width of image
    image_height: u16,     // height of image
    pixel_depth: u8,       // bits per pixel
    image_desc: u8,        // image descriptor
}

impl Header {
    /// Create a header with all valuse set to zero
    fn new() -> Header {
        Header {
            id_length: 0,
            map_type: 0,
            image_type: 0,
            map_origin: 0,
            map_length: 0,
            map_entry_size: 0,
            x_origin: 0,
            y_origin: 0,
            image_width: 0,
            image_height: 0,
            pixel_depth: 0,
            image_desc: 0,
        }
    }

    /// Load the header with values from the reader
    fn from_reader(r: &mut Read) -> ImageResult<Header> {
        Ok(Header {
            id_length:         try!(r.read_u8()),
            map_type:          try!(r.read_u8()),
            image_type:        try!(r.read_u8()),
            map_origin:        try!(r.read_u16::<LittleEndian>()),
            map_length:        try!(r.read_u16::<LittleEndian>()),
            map_entry_size:    try!(r.read_u8()),
            x_origin:          try!(r.read_u16::<LittleEndian>()),
            y_origin:          try!(r.read_u16::<LittleEndian>()),
            image_width:       try!(r.read_u16::<LittleEndian>()),
            image_height:      try!(r.read_u16::<LittleEndian>()),
            pixel_depth:       try!(r.read_u8()),
            image_desc:        try!(r.read_u8()),
        })
    }
}

struct ColorMap {
    /// sizes in bytes
    start_offset: usize,
    entry_size: usize,
    bytes: Vec<u8>,
}

impl ColorMap {
    pub fn from_reader(r: &mut Read,
                       start_offset: u16,
                       num_entries: u16,
                       bits_per_entry: u8)
        -> ImageResult<ColorMap> {
            let bytes_per_entry = (bits_per_entry as usize + 7) / 8;

            let mut bytes = vec![0; bytes_per_entry * num_entries as usize];
            if try!(r.read(&mut bytes)) != bytes.len() {
                return Err(ImageError::ImageEnd);
            }

            Ok(ColorMap {
                entry_size: bytes_per_entry,
                start_offset: start_offset as usize,
                bytes: bytes,
            })
        }

    /// Get one entry from the color map
    pub fn get(&self, index: usize) -> &[u8] {
        let entry = self.start_offset + self.entry_size * index;
        &self.bytes[entry..entry + self.entry_size]
    }
}

/// The representation of a TGA decoder
pub struct TGADecoder<R> {
    r: R,

    width: usize,
    height: usize,
    bytes_per_pixel: usize,
    has_loaded_metadata: bool,

    image_type: ImageType,
    color_type: ColorType,

    header: Header,
    color_map: Option<ColorMap>,
}

impl<R: Read + Seek> TGADecoder<R> {
    /// Create a new decoder that decodes from the stream `r`
    pub fn new(r: R) -> TGADecoder<R> {
        TGADecoder {
            r: r,

            width: 0,
            height: 0,
            bytes_per_pixel: 0,
            has_loaded_metadata: false,

            image_type: ImageType::Unknown,
            color_type: ColorType::Gray(1),

            header: Header::new(),
            color_map: None,
        }
    }

    fn read_header(&mut self) -> ImageResult<()> {
        self.header = try!(Header::from_reader(&mut self.r));
        self.image_type = ImageType::new(self.header.image_type);
        self.width = self.header.image_width as usize;
        self.height = self.header.image_height as usize;
        self.bytes_per_pixel = (self.header.pixel_depth as usize + 7) / 8;
        Ok(())
    }

    fn read_metadata(&mut self) -> ImageResult<()> {
        if !self.has_loaded_metadata {
            try!(self.read_header());
            try!(self.read_image_id());
            try!(self.read_color_map());
            try!(self.read_color_information());
            self.has_loaded_metadata = true;
        }
        Ok(())
    }

    /// Loads the color information for the decoder
    ///
    /// To keep things simple, we won't handle bit depths that aren't divisible
    /// by 8 and are less than 32.
    fn read_color_information(&mut self) -> ImageResult<()> {
        if self.header.pixel_depth % 8 != 0 {
            return Err(ImageError::UnsupportedError("\
                Bit depth must be divisible by 8".to_string()));
        }
        if self.header.pixel_depth > 32 {
            return Err(ImageError::UnsupportedError("\
                Bit depth must be less than 32".to_string()));
        }

        let num_alpha_bits = self.header.image_desc & 0b1111;

        let other_channel_bits = if self.header.map_type != 0 {
            self.header.map_entry_size
        } else {
            self.header.pixel_depth - num_alpha_bits
        };
        let color = self.image_type.is_color();

        match (num_alpha_bits, other_channel_bits, color) {
            // really, the encoding is BGR and BGRA, this is fixed
            // up with `TGADecoder::reverse_encoding`.
            (8, 24, true) => self.color_type = ColorType::RGBA(8),
            (0, 24, true) => self.color_type = ColorType::RGB(8),
            (8, 8, false) => self.color_type = ColorType::GrayA(8),
            (0, 8, false) => self.color_type = ColorType::Gray(8),
            _ => return Err(ImageError::UnsupportedError(format!("\
                    Color format not supported. Bit depth: {}, Alpha bits: {}",
                    other_channel_bits, num_alpha_bits).to_string())),
        }
        Ok(())
    }

    /// Read the image id field
    ///
    /// We're not interested in this field, so this function skips it if it
    /// is present
    fn read_image_id(&mut self) -> ImageResult<()> {
        try!(self.r.seek(io::SeekFrom::Current(self.header.id_length as i64)));
        Ok(())
    }

    fn read_color_map(&mut self) -> ImageResult<()> {
        if self.header.map_type == 1 {
            self.color_map = Some(try!(
                ColorMap::from_reader(&mut self.r,
                                      self.header.map_origin,
                                      self.header.map_length,
                                      self.header.map_entry_size)));
        }
        Ok(())
    }

    /// Expands indices into its mapped color
    fn expand_color_map(&mut self, pixel_data: Vec<u8>) -> Vec<u8> {
        #[inline]
        fn bytes_to_index(bytes: &[u8]) -> usize {
            let mut result = 0usize;
            for byte in bytes.iter() {
                result = result << 8 | *byte as usize;
            }
            result
        }

        let bytes_per_entry = (self.header.map_entry_size as usize + 7) / 8;
        let mut result = Vec::with_capacity(self.width * self.height *
                                            bytes_per_entry);

        let color_map = match self.color_map {
            Some(ref color_map) => color_map,
            None => unreachable!(),
        };

        for chunk in pixel_data.chunks(self.bytes_per_pixel) {
            let index = bytes_to_index(chunk);
            result.extend(color_map.get(index).iter().map(|&c| c));
        }

        result
    }

    fn read_image_data(&mut self) -> ImageResult<Vec<u8>> {
        // read the pixels from the data region
        let mut pixel_data = if self.image_type.is_encoded() {
            try!(self.read_encoded_data())
        } else {
            let num_raw_bytes = self.width * self.height * self.bytes_per_pixel;
            let mut buf = Vec::with_capacity(num_raw_bytes);
            try!(self.r.by_ref().take(num_raw_bytes as u64).read_to_end(&mut buf));
            buf
        };

        // expand the indices using the color map if necessary
        if self.image_type.is_color_mapped() {
            pixel_data = self.expand_color_map(pixel_data)
        }

        self.reverse_encoding(&mut pixel_data);
        Ok(pixel_data)
    }

    /// Reads a run length encoded packet
    fn read_encoded_data(&mut self) -> ImageResult<Vec<u8>> {
        let num_bytes = self.width * self.height * self.bytes_per_pixel;
        let mut pixel_data = Vec::with_capacity(num_bytes);

        while pixel_data.len() < num_bytes {
            let run_packet = try!(self.r.read_u8());
            // If the highest bit in `run_packet` is set, then we repeat pixels
            //
            // Note: the TGA format adds 1 to both counts because having a count
            // of 0 would be pointless.
            if (run_packet & 0x80) != 0 {
                // high bit set, so we will repeat the data
                let repeat_count = ((run_packet & !0x80) + 1) as usize;
                let mut data = Vec::with_capacity(self.bytes_per_pixel);
                try!(self.r.by_ref().take(self.bytes_per_pixel as u64).read_to_end(&mut data));
                for _ in 0usize..repeat_count {
                    pixel_data.extend(data.iter().map(|&c| c));
                }
            } else {
                // not set, so `run_packet+1` is the number of non-encoded pixels
                let num_raw_bytes = (run_packet + 1) as usize * self.bytes_per_pixel;
                try!(self.r.by_ref().take(num_raw_bytes as u64).read_to_end(&mut pixel_data));
            }
        }

        Ok(pixel_data)
    }

    /// Reverse from BGR encoding to RGB encoding
    ///
    /// TGA files are stored in the BGRA encoding. This function swaps
    /// the blue and red bytes in the `pixels` array.
    fn reverse_encoding(&mut self, pixels: &mut [u8]) {
        // We only need to reverse the encoding of color images
        match self.color_type {
            ColorType::RGB(8) => {
                for chunk in pixels.chunks_mut(self.bytes_per_pixel) {
                    let r = chunk[0];
                    chunk[0] = chunk[2];
                    chunk[2] = r;
                }
            }
            ColorType::RGBA(8) => {
                for chunk in pixels.chunks_mut(self.bytes_per_pixel) {
                    let r = chunk[0];
                    chunk[0] = chunk[2];
                    chunk[2] = r;
                }
            }
            _ => { }
        }
    }
}

impl<R: Read + Seek> ImageDecoder for TGADecoder<R> {
    fn dimensions(&mut self) -> ImageResult<(u32, u32)> {
        try!(self.read_metadata());

        Ok((self.width as u32, self.height as u32))
    }

    fn colortype(&mut self) -> ImageResult<ColorType> {
        try!(self.read_metadata());

        Ok(self.color_type)
    }

    fn row_len(&mut self) -> ImageResult<usize> {
        try!(self.read_metadata());

        Ok(self.bytes_per_pixel * 8 * self.width)
    }

    fn read_scanline(&mut self, _buf: &mut [u8]) -> ImageResult<u32> {
        unimplemented!();
    }

    fn read_image(&mut self) -> ImageResult<DecodingResult> {
        try!(self.read_metadata());
        self.read_image_data().map(|v| DecodingResult::U8(v) )
    }
}
<|endoftext|>


<commit_before>use std::rand::StdRng;
use std::rand::Rng;
use std::rand::SeedableRng;
use serialize::{Decodable, Decoder, Encodable, Encoder};
use std::collections::HashMap;
use terrain::TerrainType;
use location::Location;
use mapgen;
use egg::Egg;
use mob;
use mob::{MobSpec};
use {AreaSpec};
use components::{Kind};

// Note to maintainer: Due to the way serialization works, Area *must* be
// generated to have exactly the same contents every time given the same seed
// value. That means absolutely no outside sources of randomness. Only use an
// internal generator initialized with the given seed value. And watch out for
// surprise randomness. A map generator in a previous project used memory
// addresses of temporary structures as indexing keys, and ended up with a
// nondeterminism bug that depended on the numerical order of the arbitrary
// address values.

#[deriving(Decodable, Encodable)]
struct AreaSeed {
    pub rng_seed: u32,
    pub spec: AreaSpec,
}

/// Immutable procedurally generated terrain initialized on random seed.
pub struct Area {
    /// Random number generator seed. Must uniquely define the Area contents.
    pub seed: AreaSeed,
    /// Stored terrain.
    pub terrain: HashMap<Location, TerrainType>,
    /// Valid slots to spawn things in, basically open floor and connected to
    /// areas the player can reach. (Does not handle stuff like monsters that
    /// spawn in water for now.)
    _open_slots: Vec<Location>,
    /// Where the player should enter the area.
    player_entrance: Location,
    /// Non-player entities to create when first initializing the map.
    eggs: Vec<(Egg, Location)>,
}

impl<E, D:Decoder<E>> Decodable<D, E> for Area {
    fn decode(d: &mut D) -> Result<Area, E> {
        let seed: AreaSeed = try!(Decodable::decode(d));
        Ok(Area::new(seed.rng_seed, seed.spec))
    }
}

impl<E, S:Encoder<E>> Encodable<S, E> for Area {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        self.seed.encode(s)
    }
}

impl Area {
    pub fn new(rng_seed: u32, spec: AreaSpec) -> Area {
        let num_eggs = 32i;

        let mut terrain = HashMap::new();
        let mut rng: StdRng = SeedableRng::from_seed([rng_seed as uint + spec.depth as uint].as_slice());
        mapgen::gen_herringbone(
            &mut rng,
            &spec,
            |p, t| {terrain.insert(Location::new(0, 0) + p, t);});

        // Generate open slots that can be used to spawn stuff.
        let mut opens = Vec::new();
        for (&loc, t) in terrain.iter() {
            // No connectivity analysis yes, trusting that herringbone map has
            // total connectivity. Later on, use Dijkstra map that spreads
            // from entrance/exit as a reachability floodfill to do something
            // cleverer here.
            if !t.blocks_walk() {
                opens.push(loc);
            }
        }
        rng.shuffle(opens.as_mut_slice());

        let entrance = opens.swap_remove(0).unwrap();

        let viable_mobs: Vec<MobSpec> = mob::MOB_SPECS.iter()
            .filter(|m| m.area_spec.can_hatch_in(&spec))
            .map(|&x| x).collect();

        let mut eggs = vec![];

        assert!(viable_mobs.len() > 0, "Area has no viable mob spawns");
        for _ in range(0, num_eggs) {
            if let Some(loc) = opens.swap_remove(0) {
                let mob = rng.choose(viable_mobs.as_slice()).unwrap();
                eggs.push((Egg::new(Kind::Mob(mob.typ)), loc));
            } else {
                // Ran out of open space.
                break;
            }
        }

        Area {
            seed: AreaSeed { rng_seed: rng_seed, spec: spec },
            terrain: terrain,
            _open_slots: opens,
            player_entrance: entrance,
            eggs: eggs,
        }
    }

    /// Return terrain at given location.
    pub fn terrain(&self, loc: Location) -> TerrainType {
        match self.terrain.get(&loc) {
            Some(&t) => t,
            None => self.default_terrain(loc)
        }
    }

    /// List the objects that should be hatched in the world during init. This
    /// is tied to map generation, so it goes in the area module.
    pub fn get_eggs(&self) -> Vec<(Egg, Location)> { self.eggs.clone() }

    pub fn player_entrance(&self) -> Location {
        self.player_entrance
    }

    fn default_terrain(&self, _loc: Location) -> TerrainType {
        self.seed.spec.biome.default_terrain()
    }
}
<commit_msg>Fix typo<commit_after>use std::rand::StdRng;
use std::rand::Rng;
use std::rand::SeedableRng;
use serialize::{Decodable, Decoder, Encodable, Encoder};
use std::collections::HashMap;
use terrain::TerrainType;
use location::Location;
use mapgen;
use egg::Egg;
use mob;
use mob::{MobSpec};
use {AreaSpec};
use components::{Kind};

// Note to maintainer: Due to the way serialization works, Area *must* be
// generated to have exactly the same contents every time given the same seed
// value. That means absolutely no outside sources of randomness. Only use an
// internal generator initialized with the given seed value. And watch out for
// surprise randomness. A map generator in a previous project used memory
// addresses of temporary structures as indexing keys, and ended up with a
// nondeterminism bug that depended on the numerical order of the arbitrary
// address values.

#[deriving(Decodable, Encodable)]
struct AreaSeed {
    pub rng_seed: u32,
    pub spec: AreaSpec,
}

/// Immutable procedurally generated terrain initialized on random seed.
pub struct Area {
    /// Random number generator seed. Must uniquely define the Area contents.
    pub seed: AreaSeed,
    /// Stored terrain.
    pub terrain: HashMap<Location, TerrainType>,
    /// Valid slots to spawn things in, basically open floor and connected to
    /// areas the player can reach. (Does not handle stuff like monsters that
    /// spawn in water for now.)
    _open_slots: Vec<Location>,
    /// Where the player should enter the area.
    player_entrance: Location,
    /// Non-player entities to create when first initializing the map.
    eggs: Vec<(Egg, Location)>,
}

impl<E, D:Decoder<E>> Decodable<D, E> for Area {
    fn decode(d: &mut D) -> Result<Area, E> {
        let seed: AreaSeed = try!(Decodable::decode(d));
        Ok(Area::new(seed.rng_seed, seed.spec))
    }
}

impl<E, S:Encoder<E>> Encodable<S, E> for Area {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        self.seed.encode(s)
    }
}

impl Area {
    pub fn new(rng_seed: u32, spec: AreaSpec) -> Area {
        let num_eggs = 32i;

        let mut terrain = HashMap::new();
        let mut rng: StdRng = SeedableRng::from_seed([rng_seed as uint + spec.depth as uint].as_slice());
        mapgen::gen_herringbone(
            &mut rng,
            &spec,
            |p, t| {terrain.insert(Location::new(0, 0) + p, t);});

        // Generate open slots that can be used to spawn stuff.
        let mut opens = Vec::new();
        for (&loc, t) in terrain.iter() {
            // No connectivity analysis yet, trusting that herringbone map has
            // total connectivity. Later on, use Dijkstra map that spreads
            // from entrance/exit as a reachability floodfill to do something
            // cleverer here.
            if !t.blocks_walk() {
                opens.push(loc);
            }
        }
        rng.shuffle(opens.as_mut_slice());

        let entrance = opens.swap_remove(0).unwrap();

        let viable_mobs: Vec<MobSpec> = mob::MOB_SPECS.iter()
            .filter(|m| m.area_spec.can_hatch_in(&spec))
            .map(|&x| x).collect();

        let mut eggs = vec![];

        assert!(viable_mobs.len() > 0, "Area has no viable mob spawns");
        for _ in range(0, num_eggs) {
            if let Some(loc) = opens.swap_remove(0) {
                let mob = rng.choose(viable_mobs.as_slice()).unwrap();
                eggs.push((Egg::new(Kind::Mob(mob.typ)), loc));
            } else {
                // Ran out of open space.
                break;
            }
        }

        Area {
            seed: AreaSeed { rng_seed: rng_seed, spec: spec },
            terrain: terrain,
            _open_slots: opens,
            player_entrance: entrance,
            eggs: eggs,
        }
    }

    /// Return terrain at given location.
    pub fn terrain(&self, loc: Location) -> TerrainType {
        match self.terrain.get(&loc) {
            Some(&t) => t,
            None => self.default_terrain(loc)
        }
    }

    /// List the objects that should be hatched in the world during init. This
    /// is tied to map generation, so it goes in the area module.
    pub fn get_eggs(&self) -> Vec<(Egg, Location)> { self.eggs.clone() }

    pub fn player_entrance(&self) -> Location {
        self.player_entrance
    }

    fn default_terrain(&self, _loc: Location) -> TerrainType {
        self.seed.spec.biome.default_terrain()
    }
}
<|endoftext|>


<commit_before>//! Implement source for .sdc, Riegl's simple file-based discrete return format.

use std::ffi::OsStr;
use std::io::Read;
use std::path::Path;

use sdc;

use Result;
use point::{Intensity, Point};
use source::{FileSource, Source};

impl<R: Read> Source for sdc::Reader<R> {
    fn source(&mut self, want: usize) -> Result<Option<Vec<Point>>> {
        let mut points = Vec::with_capacity(want);
        for _ in 0..want {
            match try!(self.next_point()) {
                Some(point) => points.push(Point::from(point)),
                None => {
                    if points.is_empty() {
                        return Ok(None);
                    } else {
                        break;
                    }
                }
            }
        }
        Ok(Some(points))
    }

    fn source_len(&mut self) -> Option<usize> {
        None
    }
}

impl From<sdc::Point> for Point {
    fn from(point: sdc::Point) -> Point {
        Point {
            x: point.x as f64,
            y: point.y as f64,
            z: point.z as f64,
            intensity: Intensity::from_u16(point.amplitude),
            return_number: Some(point.target as usize),
            number_of_returns: Some(point.num_target as usize),
            gps_time: Some(point.time),
            ..Default::default()
        }
    }
}

impl<R: Read> FileSource for sdc::Reader<R> {
    type Config = SdcConfig;
    fn open_file_source<P>(path: P, _: Self::Config) -> Result<Box<Source>> where P: AsRef<Path> + AsRef<OsStr> {
        Ok(Box::new(try!(sdc::Reader::from_path(path))))
    }
}

/// Configuration structure for an sdc reader.
#[derive(Clone, Copy, Debug, Default, RustcDecodable)]
pub struct SdcConfig;

#[cfg(test)]
mod tests {

    use sdc;

    use source::{open_file_source, Source};

    #[test]
    fn simple_read_format_5_0() {
        let mut source = sdc::Reader::from_path("data/4-points.sdc").unwrap();
        let points = source.source_to_end(100).unwrap();
        assert_eq!(4, points.len());
    }

    #[test]
    fn fs() {
        let mut source = open_file_source("data/4-points.sdc", None).unwrap();
        let points = source.source_to_end(100).unwrap();
        assert_eq!(4, points.len());
    }
}
<commit_msg>Add more dimensions to sdc points<commit_after>//! Implement source for .sdc, Riegl's simple file-based discrete return format.

use std::ffi::OsStr;
use std::io::Read;
use std::path::Path;

use sdc;

use Result;
use point::{Intensity, Point};
use source::{FileSource, Source};

impl<R: Read> Source for sdc::Reader<R> {
    fn source(&mut self, want: usize) -> Result<Option<Vec<Point>>> {
        let mut points = Vec::with_capacity(want);
        for _ in 0..want {
            match try!(self.next_point()) {
                Some(point) => points.push(Point::from(point)),
                None => {
                    if points.is_empty() {
                        return Ok(None);
                    } else {
                        break;
                    }
                }
            }
        }
        Ok(Some(points))
    }

    fn source_len(&mut self) -> Option<usize> {
        None
    }
}

impl From<sdc::Point> for Point {
    fn from(point: sdc::Point) -> Point {
        Point {
            x: point.x as f64,
            y: point.y as f64,
            z: point.z as f64,
            intensity: Intensity::from_u16(point.amplitude),
            return_number: Some(point.target as usize),
            number_of_returns: Some(point.num_target as usize),
            gps_time: Some(point.time),
            range: Some(point.range as f64),
            scan_angle: Some(point.theta as f64),
            rg_index: Some(point.rg_index as f64),
            facet_number: Some(point.facet_number),
            high_channel: Some(point.high_channel),
            ..Default::default()
        }
    }
}

impl<R: Read> FileSource for sdc::Reader<R> {
    type Config = SdcConfig;
    fn open_file_source<P>(path: P, _: Self::Config) -> Result<Box<Source>> where P: AsRef<Path> + AsRef<OsStr> {
        Ok(Box::new(try!(sdc::Reader::from_path(path))))
    }
}

/// Configuration structure for an sdc reader.
#[derive(Clone, Copy, Debug, Default, RustcDecodable)]
pub struct SdcConfig;

#[cfg(test)]
mod tests {

    use sdc;

    use source::{open_file_source, Source};

    #[test]
    fn simple_read_format_5_0() {
        let mut source = sdc::Reader::from_path("data/4-points.sdc").unwrap();
        let points = source.source_to_end(100).unwrap();
        assert_eq!(4, points.len());
    }

    #[test]
    fn fs() {
        let mut source = open_file_source("data/4-points.sdc", None).unwrap();
        let points = source.source_to_end(100).unwrap();
        assert_eq!(4, points.len());
    }
}
<|endoftext|>