text:
use std::collections::HashSet;
use std::dynamic_lib::DynamicLibrary;
use std::io::{fs, UserRWX};
use std::os;

use core::{SourceMap, Package, PackageId, PackageSet, Target, Resolve};
use util::{CargoResult, ProcessBuilder, CargoError, human, caused_human};
use util::{Config, internal, ChainError, Fresh, profile};

use self::job::{Job, Work};
use self::job_queue::{JobQueue, StageStart, StageCustomBuild, StageLibraries};
use self::job_queue::{StageBinaries, StageEnd};
use self::context::{Context, PlatformRequirement, Target, Plugin, PluginAndTarget};

pub use self::compilation::Compilation;

mod context;
mod compilation;
mod fingerprint;
mod job;
mod job_queue;
mod layout;

#[deriving(PartialEq, Eq)]
enum Kind { KindPlugin, KindTarget }

// This is a temporary assert that ensures the consistency of the arguments
// given the current limitations of Cargo. The long term fix is to have each
// Target know the absolute path to the build location.
fn uniq_target_dest<'a>(targets: &[&'a Target]) -> Option<&'a str> {
    let mut curr: Option<Option<&str>> = None;

    for t in targets.iter() {
        let dest = t.get_profile().get_dest();

        match curr {
            Some(curr) => assert!(curr == dest),
            None => curr = Some(dest)
        }
    }

    curr.unwrap()
}

// Returns a mapping of the root package plus its immediate dependencies to
// where the compiled libraries are all located.
pub fn compile_targets<'a>(env: &str, targets: &[&'a Target], pkg: &'a Package,
                           deps: &PackageSet, resolve: &'a Resolve,
                           sources: &'a SourceMap,
                           config: &'a mut Config<'a>)
                           -> CargoResult<Compilation> {
    if targets.is_empty() {
        return Ok(Compilation::new())
    }

    debug!("compile_targets; targets={}; pkg={}; deps={}", targets, pkg, deps);

    let root = pkg.get_absolute_target_dir();
    let dest = uniq_target_dest(targets).unwrap_or("");
    let host_layout = layout::Layout::new(root.join(dest));
    let target_layout = config.target().map(|target| {
        layout::Layout::new(root.join(target).join(dest))
    });

    let mut cx = try!(Context::new(env, resolve, sources, deps, config,
                                   host_layout, target_layout));
    let mut queue = JobQueue::new(cx.resolve, cx.config);

    // First ensure that the destination directory exists
    try!(cx.prepare(pkg));

    // Build up a list of pending jobs, each of which represent compiling a
    // particular package. No actual work is executed as part of this, that's
    // all done later as part of the `execute` function which will run
    // everything in order with proper parallelism.
    for dep in deps.iter() {
        if dep == pkg { continue }

        // Only compile lib targets for dependencies
        let targets = dep.get_targets().iter().filter(|target| {
            cx.is_relevant_target(*target)
        }).collect::<Vec<&Target>>();

        if targets.len() == 0 {
            return Err(human(format!("Package `{}` has no library targets", dep)))
        }

        try!(compile(targets.as_slice(), dep, &mut cx, &mut queue));
    }

    cx.primary();
    try!(compile(targets, pkg, &mut cx, &mut queue));

    // Now that we've figured out everything that we're going to do, do it!
    try!(queue.execute(cx.config));

    Ok(cx.compilation)
}

fn compile<'a, 'b>(targets: &[&'a Target], pkg: &'a Package,
                   cx: &mut Context<'a, 'b>,
                   jobs: &mut JobQueue<'a, 'b>) -> CargoResult<()> {
    debug!("compile_pkg; pkg={}; targets={}", pkg, targets);
    let _p = profile::start(format!("preparing: {}", pkg));

    if targets.is_empty() {
        return Ok(())
    }

    // Prepare the fingerprint directory as the first step of building a package
    let (target1, target2) = fingerprint::prepare_init(cx, pkg, KindTarget);
    let mut init = vec![(Job::new(target1, target2, String::new()), Fresh)];
    if cx.config.target().is_some() {
        let (plugin1, plugin2) = fingerprint::prepare_init(cx, pkg, KindPlugin);
        init.push((Job::new(plugin1, plugin2, String::new()), Fresh));
    }
    jobs.enqueue(pkg, StageStart, init);

    // First part of the build step of a target is to execute all of the custom
    // build commands.
    let mut build_cmds = Vec::new();
    for (i, build_cmd) in pkg.get_manifest().get_build().iter().enumerate() {
        let work = try!(compile_custom(pkg, build_cmd.as_slice(), cx, i == 0));
        build_cmds.push(work);
    }
    let (freshness, dirty, fresh) =
        try!(fingerprint::prepare_build_cmd(cx, pkg));
    let desc = match build_cmds.len() {
        0 => String::new(),
        1 => pkg.get_manifest().get_build()[0].to_string(),
        _ => format!("custom build commands"),
    };
    let dirty = proc() {
        for cmd in build_cmds.move_iter() { try!(cmd()) }
        dirty()
    };
    jobs.enqueue(pkg, StageCustomBuild, vec![(Job::new(dirty, fresh, desc),
                                              freshness)]);

    // After the custom command has run, execute rustc for all targets of our
    // package.
    //
    // Each target has its own concept of freshness to ensure incremental
    // rebuilds on the *target* granularity, not the *package* granularity.
    let (mut libs, mut bins) = (Vec::new(), Vec::new());
    for &target in targets.iter() {
        let work = if target.get_profile().is_doc() {
            let (rustdoc, desc) = try!(rustdoc(pkg, target, cx));
            vec![(rustdoc, KindTarget, desc)]
        } else {
            let req = cx.get_requirement(pkg, target);
            try!(rustc(pkg, target, cx, req))
        };

        let dst = if target.is_lib() {&mut libs} else {&mut bins};
        for (work, kind, desc) in work.move_iter() {
            let (freshness, dirty, fresh) =
                try!(fingerprint::prepare_target(cx, pkg, target, kind));

            let dirty = proc() { try!(work()); dirty() };
            dst.push((Job::new(dirty, fresh, desc), freshness));
        }
    }
    jobs.enqueue(pkg, StageLibraries, libs);
    jobs.enqueue(pkg, StageBinaries, bins);
    jobs.enqueue(pkg, StageEnd, Vec::new());
    Ok(())
}

fn compile_custom(pkg: &Package, cmd: &str,
                  cx: &Context, first: bool) -> CargoResult<Work> {
    // TODO: this needs to be smarter about splitting
    let mut cmd = cmd.split(' ');
    // TODO: this shouldn't explicitly pass `KindTarget` for dest/deps_dir, we
    //       may be building a C lib for a plugin
    let layout = cx.layout(KindTarget);
    let output = layout.native(pkg);
    let old_output = layout.proxy().old_native(pkg);
    let mut p = process(cmd.next().unwrap(), pkg, cx)
                     .env("OUT_DIR", Some(&output))
                     .env("DEPS_DIR", Some(&output))
                     .env("TARGET", Some(cx.target_triple()));
    for arg in cmd {
        p = p.arg(arg);
    }
    for &(pkg, _) in cx.dep_targets(pkg).iter() {
        let name: String = pkg.get_name().chars().map(|c| {
            match c {
                '-' => '_',
                c => c.to_uppercase(),
            }
        }).collect();
        p = p.env(format!("DEP_{}_OUT_DIR", name).as_slice(),
                  Some(&layout.native(pkg)));
    }
    let pkg = pkg.to_string();

    Ok(proc() {
        if first {
            try!(if old_output.exists() {
                fs::rename(&old_output, &output)
            } else {
                fs::mkdir(&output, UserRWX)
            }.chain_error(|| {
                internal("failed to create output directory for build command")
            }));
        }
        try!(p.exec_with_output().map(|_| ()).map_err(|mut e| {
            e.msg = format!("Failed to run custom build command for `{}`\n{}",
                            pkg, e.msg);
            e.mark_human()
        }));
        Ok(())
    })
}

fn rustc(package: &Package, target: &Target,
         cx: &mut Context, req: PlatformRequirement)
         -> CargoResult<Vec<(Work, Kind, String)> >{
    let crate_types = target.rustc_crate_types();
    let root = package.get_root();

    log!(5, "root={}; target={}; crate_types={}; verbose={}; req={}",
         root.display(), target, crate_types, cx.primary, req);

    let primary = cx.primary;
    let rustcs = try!(prepare_rustc(package, target, crate_types, cx, req));

    Ok(rustcs.move_iter().map(|(rustc, kind)| {
        let name = package.get_name().to_string();
        let desc = rustc.to_string();

        (proc() {
            if primary {
                log!(5, "executing primary");
                try!(rustc.exec().chain_error(|| {
                    human(format!("Could not compile `{}`.", name))
                }))
            } else {
                log!(5, "executing deps");
                try!(rustc.exec_with_output().and(Ok(())).map_err(|err| {
                    caused_human(format!("Could not compile `{}`.\n{}",
                                         name, err.output().unwrap()), err)
                }))
            }
            Ok(())
        }, kind, desc)
    }).collect())
}

fn prepare_rustc(package: &Package, target: &Target, crate_types: Vec<&str>,
                 cx: &Context, req: PlatformRequirement)
                 -> CargoResult<Vec<(ProcessBuilder, Kind)>> {
    let base = process("rustc", package, cx);
    let base = build_base_args(cx, base, target, crate_types.as_slice());

    let target_cmd = build_plugin_args(base.clone(), cx, package, target, KindTarget);
    let plugin_cmd = build_plugin_args(base, cx, package, target, KindPlugin);
    let target_cmd = try!(build_deps_args(target_cmd, target, package, cx,
                                          KindTarget));
    let plugin_cmd = try!(build_deps_args(plugin_cmd, target, package, cx,
                                          KindPlugin));

    Ok(match req {
        Target => vec![(target_cmd, KindTarget)],
        Plugin => vec![(plugin_cmd, KindPlugin)],
        PluginAndTarget if cx.config.target().is_none() =>
            vec![(target_cmd, KindTarget)],
        PluginAndTarget => vec![(target_cmd, KindTarget),
                                (plugin_cmd, KindPlugin)],
    })
}

fn rustdoc(package: &Package, target: &Target,
           cx: &mut Context) -> CargoResult<(Work, String)> {
    let kind = KindTarget;
    let pkg_root = package.get_root();
    let cx_root = cx.layout(kind).proxy().dest().join("doc");
    let rustdoc = process("rustdoc", package, cx).cwd(pkg_root.clone());
    let rustdoc = rustdoc.arg(target.get_src_path())
                         .arg("-o").arg(cx_root)
                         .arg("--crate-name").arg(target.get_name());
    let rustdoc = try!(build_deps_args(rustdoc, target, package, cx, kind));

    log!(5, "commands={}", rustdoc);

    let primary = cx.primary;
    let name = package.get_name().to_string();
    let desc = rustdoc.to_string();
    Ok((proc() {
        if primary {
            try!(rustdoc.exec().chain_error(|| {
                human(format!("Could not document `{}`.", name))
            }))
        } else {
            try!(rustdoc.exec_with_output().and(Ok(())).map_err(|err| {
                match err.output() {
                    Some(output) => {
                        caused_human(format!("Could not document `{}`.\n{}",
                                             name, output), err)
                    }
                    None => {
                        caused_human("Failed to run rustdoc", err)
                    }
                }
            }))
        }
        Ok(())
    }, desc))
}

fn build_base_args(cx: &Context, mut cmd: ProcessBuilder,
                   target: &Target,
                   crate_types: &[&str]) -> ProcessBuilder {
    let metadata = target.get_metadata();

    // TODO: Handle errors in converting paths into args
    cmd = cmd.arg(target.get_src_path());

    cmd = cmd.arg("--crate-name").arg(target.get_name());

    for crate_type in crate_types.iter() {
        cmd = cmd.arg("--crate-type").arg(*crate_type);
    }

    // Despite whatever this target's profile says, we need to configure it
    // based off the profile found in the root package's targets.
    let mut profile = target.get_profile().clone();
    let root_package = cx.get_package(cx.resolve.root());
    for target in root_package.get_manifest().get_targets().iter() {
        let root_profile = target.get_profile();
        if root_profile.get_env() != profile.get_env() { continue }
        profile = profile.opt_level(root_profile.get_opt_level())
                         .debug(root_profile.get_debug());
    }

    if profile.get_opt_level() != 0 {
        cmd = cmd.arg("--opt-level").arg(profile.get_opt_level().to_string());
    }

    if profile.get_debug() {
        cmd = cmd.arg("-g");
    } else {
        cmd = cmd.args(["--cfg", "ndebug"]);
    }

    if profile.is_test() && profile.uses_test_harness() {
        cmd = cmd.arg("--test");
    }

    match metadata {
        Some(m) => {
            cmd = cmd.arg("-C").arg(format!("metadata={}", m.metadata));
            cmd = cmd.arg("-C").arg(format!("extra-filename={}", m.extra_filename));
        }
        None => {}
    }

    return cmd;
}

fn build_plugin_args(mut cmd: ProcessBuilder, cx: &Context, pkg: &Package,
                     target: &Target, kind: Kind) -> ProcessBuilder {
    cmd = cmd.arg("--out-dir");
    cmd = cmd.arg(cx.layout(kind).root());

    let (_, dep_info_loc) = fingerprint::dep_info_loc(cx, pkg, target, kind);
    cmd = cmd.arg("--dep-info").arg(dep_info_loc);

    if kind == KindTarget {
        fn opt(cmd: ProcessBuilder, key: &str, prefix: &str,
               val: Option<&str>) -> ProcessBuilder {
            match val {
                Some(val) => {
                    cmd.arg(key)
                       .arg(format!("{}{}", prefix, val))
                }
                None => cmd
            }
        }

        cmd = opt(cmd, "--target", "", cx.config.target());
        cmd = opt(cmd, "-C", "ar=", cx.config.ar());
        cmd = opt(cmd, "-C", "linker=", cx.config.linker());
    }

    return cmd;
}

fn build_deps_args(mut cmd: ProcessBuilder, target: &Target, package: &Package,
                   cx: &Context,
                   kind: Kind) -> CargoResult<ProcessBuilder> {
    enum LinkReason { Dependency, LocalLib }

    let layout = cx.layout(kind);
    cmd = cmd.arg("-L").arg(layout.root());
    cmd = cmd.arg("-L").arg(layout.deps());

    // Traverse the entire dependency graph looking for -L paths to pass for
    // native dependencies.
    cmd = push_native_dirs(cmd, &layout, package, cx, &mut HashSet::new());

    for &(_, target) in cx.dep_targets(package).iter() {
        cmd = try!(link_to(cmd, target, cx, kind, Dependency));
    }

    let mut targets = package.get_targets().iter().filter(|target| {
        target.is_lib() && target.get_profile().is_compile()
    });

    if target.is_bin() {
        for target in targets {
            if target.is_staticlib() {
                continue;
            }

            cmd = try!(link_to(cmd, target, cx, kind, LocalLib));
        }
    }

    return Ok(cmd);

    fn link_to(mut cmd: ProcessBuilder, target: &Target,
               cx: &Context, kind: Kind,
               reason: LinkReason) -> CargoResult<ProcessBuilder> {
        // If this target is itself a plugin *or* if it's being linked to a
        // plugin, then we want the plugin directory. Otherwise we want the
        // target directory (hence the || here).
        let layout = cx.layout(match kind {
            KindPlugin => KindPlugin,
            KindTarget if target.get_profile().is_plugin() => KindPlugin,
            KindTarget => KindTarget,
        });

        for filename in try!(cx.target_filenames(target)).iter() {
            let mut v = Vec::new();
            v.push_all(target.get_name().as_bytes());
            v.push(b'=');
            match reason {
                Dependency => v.push_all(layout.deps().as_vec()),
                LocalLib => v.push_all(layout.root().as_vec()),
            }
            v.push(b'/');
            v.push_all(filename.as_bytes());
            cmd = cmd.arg("--extern").arg(v.as_slice());
        }
        return Ok(cmd);
    }

    fn push_native_dirs(mut cmd: ProcessBuilder, layout: &layout::LayoutProxy,
                        pkg: &Package, cx: &Context,
                        visited: &mut HashSet<PackageId>) -> ProcessBuilder {
        if !visited.insert(pkg.get_package_id().clone()) { return cmd }

        if pkg.get_manifest().get_build().len() > 0 {
            cmd = cmd.arg("-L").arg(layout.native(pkg));
        }

        match cx.resolve.deps(pkg.get_package_id()) {
            Some(mut pkgids) => {
                pkgids.fold(cmd, |cmd, dep_id| {
                    let dep = cx.get_package(dep_id);
                    push_native_dirs(cmd, layout, dep, cx, visited)
                })
            }
            None => cmd
        }
    }
}

pub fn process<T: ToCStr>(cmd: T, pkg: &Package, cx: &Context) -> ProcessBuilder {
    // When invoking a tool, we need the *host* deps directory in the dynamic
    // library search path for plugins and such which have dynamic dependencies.
    let mut search_path = DynamicLibrary::search_path();
    search_path.push(cx.layout(KindPlugin).deps().clone());
    let search_path = os::join_paths(search_path.as_slice()).unwrap();

    // We want to use the same environment and such as normal processes, but we
    // want to override the dylib search path with the one we just calculated.
    cx.compilation.process(cmd, pkg)
                  .env(DynamicLibrary::envvar(), Some(search_path.as_slice()))
}


text:
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include "cmockery.h"

#include "../idle_tracker.c"

/*
 * This sets up an expected exception that will be rethrown for
 * verification using PG_TRY(), PG_CATCH() and PG_END_TRY() macros
 */
#define EXPECT_EXCEPTION()     \
	expect_any(ExceptionalCondition,conditionName); \
	expect_any(ExceptionalCondition,errorType); \
	expect_any(ExceptionalCondition,fileName); \
	expect_any(ExceptionalCondition,lineNumber); \
    will_be_called_with_sideeffect(ExceptionalCondition, &_ExceptionalCondition, NULL);\

/* MySessionState will use the address of this global variable */
static SessionState fakeSessionState;

/* Prepares a fake MySessionState pointer for use in the vmem tracker */
static void
InitFakeSessionState(int activeProcessCount, int cleanupCountdown, RunawayStatus runawayStatus, int pinCount, int vmem)
{
	MySessionState = &fakeSessionState;

	MySessionState->activeProcessCount = activeProcessCount;
	MySessionState->cleanupCountdown = cleanupCountdown;
	MySessionState->runawayStatus = runawayStatus;
	MySessionState->next = NULL;
	MySessionState->pinCount = pinCount;
	MySessionState->sessionId = 1234;
	MySessionState->sessionVmem = vmem;
	MySessionState->spinLock = 0;
}

#undef PG_RE_THROW
#define PG_RE_THROW() siglongjmp(*PG_exception_stack, 1)

/*
 * This method will emulate the real ExceptionalCondition
 * function by re-throwing the exception, essentially falling
 * back to the next available PG_CATCH();
 */
static void
_ExceptionalCondition()
{
     PG_RE_THROW();
}

/*
 * A shared method to test proper activation during IdleTracker_Init()
 * or a direct call to IdleTracker_ActivateProcess() during regular
 * activation of an idle process.
 */
static void
CheckForActivation(void (*testFunc)(void))
{
	static EventVersion fakeCurrentVersion = 10;
	CurrentVersion = &fakeCurrentVersion;

	InitFakeSessionState(0 /* activeProcessCount */, CLEANUP_COUNTDOWN_BEFORE_RUNAWAY /* cleanupCountdown */,
			RunawayStatus_NotRunaway /* runawayStatus */, 1 /* pinCount */, 0 /* vmem */);

	activationVersion = 0;
	deactivationVersion = 0;

	assert_true(*CurrentVersion != activationVersion);
	assert_true(*CurrentVersion != deactivationVersion);

	/*
	 * Set to false as we want to verify that it gets set to true
	 * once the testFunc() call returns
	 */
	isProcessActive = false;

	assert_true(MySessionState->activeProcessCount == 0);

	testFunc();

	assert_true(activationVersion == *CurrentVersion);
	assert_true(deactivationVersion == 0);
	assert_true(isProcessActive == true);
	assert_true(MySessionState->activeProcessCount == 1);
}

/*
 * A shared method to test proper deactivation during IdleTracker_Shutdown()
 * or a direct call to IdleTracker_DeactivateProcess() during regular
 * deactivation of an active process when a proper cleanup was not possible
 * (e.g., a transaction is in progress).
 */
static void
CheckForDeactivationWithoutCleanup(void (*testFunc)(void))
{
	static EventVersion fakeCurrentVersion = 10;
	CurrentVersion = &fakeCurrentVersion;

	InitFakeSessionState(1 /* activeProcessCount */, CLEANUP_COUNTDOWN_BEFORE_RUNAWAY /* cleanupCountdown */,
			RunawayStatus_NotRunaway /* runawayStatus */, 1 /* pinCount */, 0 /* vmem */);

	/* Ensure we have a pending runaway event */
	EventVersion fakeLatestRunawayVersion = *CurrentVersion - 1;
	latestRunawayVersion = &fakeLatestRunawayVersion;

	activationVersion = 0;
	deactivationVersion = 0;

	assert_true(*CurrentVersion != activationVersion);
	assert_true(*CurrentVersion != deactivationVersion);

	/*
	 * Set to true as we want to verify that it gets set to false
	 * once the testFunc() call returns
	 */
	isProcessActive = true;

	assert_true(MySessionState->activeProcessCount == 1);

	/*
	 * Deactivation must call RunawayCleaner_StartCleanup before finishing deactivation
	 * to check for cleanup requirement for any pending runaway event. The method is
	 * supposed to throw an exception, but for this test we are mocking the function
	 * without side effect. I.e., the function behaves as if a proper cleanup is not
	 * possible
	 */
	will_be_called(RunawayCleaner_StartCleanup);
	TimestampTz lastTime = GetCurrentTimestamp();
	MySessionState->idle_start = 0;

#ifdef USE_ASSERT_CHECKING
	will_return(RunawayCleaner_IsCleanupInProgress, true);
	/*
	 * Expecting an exception as we are indicating an ongoing cleanup
	 * and yet returning to IdleTracker_DactivateProcess
	 */
	EXPECT_EXCEPTION();
#endif

	PG_TRY();
	{
		testFunc();
#ifdef USE_ASSERT_CHECKING
		assert_false("Expected an assertion failure");
#endif
	}
	PG_CATCH();
	{

	}
	PG_END_TRY();

	assert_true(activationVersion == 0);
	assert_true(deactivationVersion == *CurrentVersion);
	assert_true(isProcessActive == false);
	assert_true(MySessionState->activeProcessCount == 0);
	assert_true(lastTime > 0 && MySessionState->idle_start >= lastTime);
}

/*
 * A shared method to test proper deactivation during IdleTracker_Shutdown()
 * or a direct call to IdleTracker_DeactivateProcess() during regular
 * deactivation of an active process when a proper cleanup was done, throwing
 * and elog(ERROR,...) and therefore the call would never return to the calling
 * function
 */
static void
CheckForDeactivationWithProperCleanup(void (*testFunc)(void))
{
	static EventVersion fakeCurrentVersion = 10;
	CurrentVersion = &fakeCurrentVersion;

	InitFakeSessionState(1 /* activeProcessCount */, CLEANUP_COUNTDOWN_BEFORE_RUNAWAY /* cleanupCountdown */,
			RunawayStatus_NotRunaway /* runawayStatus */, 1 /* pinCount */, 0 /* vmem */);

	/* Ensure we have a pending runaway event */
	EventVersion fakeLatestRunawayVersion = *CurrentVersion - 1;
	latestRunawayVersion = &fakeLatestRunawayVersion;

	activationVersion = 0;
	deactivationVersion = 0;

	assert_true(*CurrentVersion != activationVersion);
	assert_true(*CurrentVersion != deactivationVersion);

	/*
	 * Set to true as we want to verify that it gets set to false
	 * once the testFunc() call returns
	 */
	isProcessActive = true;

	assert_true(MySessionState->activeProcessCount == 1);

	/*
	 * Deactivation must call RunawayCleaner_StartCleanup before finishing deactivation
	 * to check for cleanup requirement for any pending runaway event. The mocked method
	 * is throwing an exception here, which would block execution of code following the call
	 * site
	 */
	will_be_called_with_sideeffect(RunawayCleaner_StartCleanup, &_ExceptionalCondition, NULL);
	TimestampTz lastTime = GetCurrentTimestamp();
	MySessionState->idle_start = 0;
	PG_TRY();
	{
		testFunc();
		assert_false("Expected an exception");
	}
	PG_CATCH();
	{

	}
	PG_END_TRY();

	assert_true(activationVersion == 0);
	assert_true(deactivationVersion == *CurrentVersion);
	assert_true(isProcessActive == false);
	assert_true(MySessionState->activeProcessCount == 0);
	assert_true(lastTime > 0 && MySessionState->idle_start >= lastTime);
}

/*
 * A shared method to test that the IdleTracker_DeactivateProcess ignores
 * deactivation for an already idle process when the proc_exit_inprogress
 * is set to true
 */
static void
PreventDuplicateDeactivationDuringProcExit(void (*testFunc)(void))
{
	static EventVersion fakeCurrentVersion = 10;
	CurrentVersion = &fakeCurrentVersion;

	InitFakeSessionState(1 /* activeProcessCount */, CLEANUP_COUNTDOWN_BEFORE_RUNAWAY /* cleanupCountdown */,
			RunawayStatus_NotRunaway /* runawayStatus */, 1 /* pinCount */, 0 /* vmem */);

	/* Ensure we have a pending runaway event */
	EventVersion fakeLatestRunawayVersion = *CurrentVersion - 1;
	latestRunawayVersion = &fakeLatestRunawayVersion;

	activationVersion = 0;
	deactivationVersion = 0;

	assert_true(*CurrentVersion != activationVersion);
	assert_true(*CurrentVersion != deactivationVersion);

	/*
	 * Set to false to mark the process as deactivated
	 */
	isProcessActive = false;

	assert_true(MySessionState->activeProcessCount == 1);
	/*
	 * Setting proc_exit_inprogress to true means we won't try to
	 * deactivate an already idle process
	 */
	proc_exit_inprogress = true;
	/* Interrupts should be held off during proc_exit_inprogress*/
	InterruptHoldoffCount = 1;
	MySessionState->idle_start = 0;
	/* Now the deactivation should succeed as we have held off interrupts */
	testFunc();
	InterruptHoldoffCount = 0;

	assert_true(activationVersion == deactivationVersion);
	assert_true(deactivationVersion != *CurrentVersion);
	assert_true(isProcessActive == false);
	/* We haven't reduced the activeProcessCount */
	assert_true(MySessionState->activeProcessCount == 1);
	/* idle_start shouldn't change as we were already idle */
	assert_true(MySessionState->idle_start == 0);

#ifdef USE_ASSERT_CHECKING
	/*
	 * Now test that the testFunc fails assert if we try to deactivate an already
	 * idle process during a normal execution (i.e., not proc_exit_inprogress)
	 */
	proc_exit_inprogress = false;
	EXPECT_EXCEPTION();
	PG_TRY();
	{
		testFunc();
		assert_false("Expected assertion failure");
	}
	PG_CATCH();
	{

	}
	PG_END_TRY();
#endif
}

/*
 * Checks if IdleTracker_ShmemInit() properly initializes the global variables
 * as the postmaster
 */
static void
test__IdleTracker_ShmemInit__InitializesGlobalVarsWhenPostmaster(void **state)
{
	IsUnderPostmaster = false;

	static EventVersion fakeCurrentVersion = 10;
	CurrentVersion = &fakeCurrentVersion;

	activationVersion = 0;
	deactivationVersion = 0;

	assert_true(*CurrentVersion != activationVersion);
	assert_true(*CurrentVersion != deactivationVersion);

	/*
	 * Set to true as we want to verfiy that it gets set to false
	 * once the IdleTracker_ShmemInit() call returns
	 */
	isProcessActive = true;

	IdleTracker_ShmemInit();

	assert_true(activationVersion == *CurrentVersion);
	assert_true(deactivationVersion == *CurrentVersion);
	assert_true(isProcessActive == false);
}

/*
 * Checks if IdleTracker_Init() activates the current process
 */
static void
test__IdleTracker_Init__ActivatesProcess(void **state)
{
	CheckForActivation(&IdleTracker_Init);
}

/*
 * Checks if IdleTracker_ActivateProcess() activates the current process
 */
static void
test__IdleTracker_ActivateProcess__ActivatesProcess(void **state)
{
	CheckForActivation(&IdleTracker_ActivateProcess);
}

/*
 * Checks if IdleTracker_DeactivateProcess() deactivates the current process
 * when a proper cleanup was done
 */
static void
test__IdleTracker_DeactivateProcess__DeactivatesProcessWithCleanup(void **state)
{
	CheckForDeactivationWithProperCleanup(&IdleTracker_DeactivateProcess);
}

/*
 * Checks if IdleTracker_Shutdown() deactivates the current process when a
 * proper cleanup was done
 */
static void
test__IdleTracker_Shutdown__DeactivatesProcessWithCleanup(void **state)
{
	CheckForDeactivationWithProperCleanup(&IdleTracker_Shutdown);
}

/*
 * Checks if IdleTracker_DeactivateProcess() deactivates the current process
 * when a proper cleanup could not be done
 */
static void
test__IdleTracker_DeactivateProcess__DeactivatesProcessWithoutCleanup(void **state)
{
	CheckForDeactivationWithoutCleanup(&IdleTracker_DeactivateProcess);
}

/*
 * Checks if IdleTracker_Shutdown() deactivates the current process when a
 * proper cleanup could not be done
 */
static void
test__IdleTracker_Shutdown__DeactivatesProcessWithoutCleanup(void **state)
{
	CheckForDeactivationWithoutCleanup(&IdleTracker_Shutdown);
}

/*
 * Checks if IdleTracker_DeactivateProcess() ignores deactivation if the
 * proc_exit_inprogress is set to true
 */
static void
test__IdleTracker_DeactivateProcess__IgnoresDeactivationDuringProcExit(void **state)
{
	PreventDuplicateDeactivationDuringProcExit(&IdleTracker_DeactivateProcess);
}

/*
 * Checks if IdleTracker_Shutdown() ignores deactivation if the
 * proc_exit_inprogress is set to true
 */
static void
test__IdleTracker_Shutdown__IgnoresDeactivationDuringProcExit(void **state)
{
	PreventDuplicateDeactivationDuringProcExit(&IdleTracker_Shutdown);
}

int
main(int argc, char* argv[])
{
	cmockery_parse_arguments(argc, argv);

	const UnitTest tests[] = {
		unit_test(test__IdleTracker_ShmemInit__InitializesGlobalVarsWhenPostmaster),
		unit_test(test__IdleTracker_Init__ActivatesProcess),
		unit_test(test__IdleTracker_ActivateProcess__ActivatesProcess),
		unit_test(test__IdleTracker_DeactivateProcess__DeactivatesProcessWithCleanup),
		unit_test(test__IdleTracker_Shutdown__DeactivatesProcessWithCleanup),
		unit_test(test__IdleTracker_DeactivateProcess__DeactivatesProcessWithoutCleanup),
		unit_test(test__IdleTracker_Shutdown__DeactivatesProcessWithoutCleanup),
		unit_test(test__IdleTracker_DeactivateProcess__IgnoresDeactivationDuringProcExit),
		unit_test(test__IdleTracker_Shutdown__IgnoresDeactivationDuringProcExit),
	};

	return run_tests(tests);
}

redpajama_set_name: RedPajamaGithub


text:
<?php

class Property_Resource extends Pimcore_Model_Resource_Abstract {

    public function getRawData(){
        $cid = $this->model->getCid();
        $type = $this->model->getType();
        $name = $this->model->getName();
        $raw = null;
        if($cid){
            $data = $this->db->fetchRow("SELECT * FROM properties WHERE type=? AND cid = ? AND name=?",array($type,$cid,$name) );
            $raw = $data['data'];
        }
        return $raw;
    }

    /**
     * Save object to database
     *
     * @return void
     */
    public function save() {

        $data = $this->model->getData();

        if ($this->model->getType() == "object" || $this->model->getType() == "asset" || $this->model->getType() == "document") {

            if ($data instanceof Element_Interface) {
                $data = $data->getId();
            }
            else {
                $data = null;
            }
        }

        if (is_array($data) || is_object($data)) {
            $data = Pimcore_Tool_Serialize::serialize($data);
        }

        $saveData = array(
            "cid" => $this->model->getCid(),
            "ctype" => $this->model->getCtype(),
            "cpath" => $this->model->getCpath(),
            "name" => $this->model->getName(),
            "type" => $this->model->getType(),
            "inheritable" => (int)$this->model->getInheritable(),
            "data" => $data
        );

        try {
            $this->db->insert("properties", $saveData);
        }
        catch (Exception $e) {
            $this->db->update("properties", $saveData, $this->db->quoteInto("name = ?", $this->model->getName()) . " AND " . $this->db->quoteInto("cid = ?", $this->model->getCid()) . " AND " . $this->db->quoteInto("ctype = ?", $this->model->getCtype()));
        }
    }
}


text:
import { adminFetch } from "./fetch";

export const postAdminCreateDirectoryRest = (directory, course, value) => adminFetch('/rest/admin/files/directory', {
  body: JSON.stringify({
    directory,
    course,
    value
  }),
  method: 'PUT',
});

export const putAdminEditFileRest = (id, data) => adminFetch(`/rest/admin/files/${id}`, {
  body: JSON.stringify(data),
  method: 'PUT',
});

export const fetchAdminFileDetailsRest = id => adminFetch(`/rest/admin/files/${id}`);


text:
/*jslint node: true */
var fs = require("fs-extra"),
    path = require("path"),
    yaml = require("js-yaml");

var YamlFrontMatter = (function () {
    'use strict';

    /**
    * Creates a new instance of FileMerger
    * @param options Options for the generation process.
    */
    function YamlFrontMatter(options) {
        this.options = options || { verbose: 0 };
        this.stage = "Starting YAML stripping";
    }

    YamlFrontMatter.prototype.run = function (file) {
        if (this.options.verbose > 1) {
            console.log("String YAML from file " + file);
        }

        var content = fs.readFileSync(file, "utf8"),
            yamlRegexStripper = /^(---\s*\n[\s\S]*?\n?)^(---\s*$\n?)/m,
            match = yamlRegexStripper.exec(content),
            yamlData;
        if (match === null) {
            return {};
        }

        try {
            yamlData = yaml.safeLoad(match[1]);
        } catch (yamle) {
            console.error("File: " + file);
            if (this.options.verbose > 1) {
                console.error("YAML Exception during processing following content:\n" + content);
            }

            throw yamle;
        }

        content = content.substr(match[0].length);
        fs.writeFileSync(file, content);
        return yamlData;
    };

    return YamlFrontMatter;
}());
module.exports = YamlFrontMatter;


text:
@interface OnboardTests : XCTestCase

@end

@implementation OnboardTests

- (void)setUp {
    [super setUp];
}

- (void)tearDown {
    [super tearDown];
}

- (void)testContentViewControllers {
    // This tests that when we create an onboarding view controller with an array of content
    // view controllers at init-time, the view controllers are correctly set.
    NSArray *contents = [self generateStockContentVCS];
    OnboardingViewController *onboardingVC = [[OnboardingViewController alloc] initWithBackgroundImage:nil contents:contents];
    NSArray *contentsFromController = [onboardingVC contentViewControllers];

    for (NSInteger index = 0; index < contents.count; index++) {
        OnboardingContentViewController *originalContentVC = contents[index];
        OnboardingContentViewController *passedContentVC = contentsFromController[index];
        XCTAssertEqualObjects(originalContentVC, passedContentVC, @"The content view controllers from the controller don't match.");
    }
}

- (void)testDefaultValues {
    // This tests that the default values on the onboarding view controller are set properly.
    OnboardingViewController *onboardingVC = [[OnboardingViewController alloc] initWithBackgroundImage:nil contents:nil];
    XCTAssertTrue(onboardingVC.shouldMaskBackground, @"The background should be masked by default.");
    XCTAssertFalse(onboardingVC.shouldBlurBackground, @"The background should not be blurred by default.");
}

- (void)testConvenienceSetters {
    // This tests that when we use the convenience setter methods on the master onboaring view controller,
    // the properties correctly trickle down to each of the child content view controllers.
    OnboardingViewController *onboardingVC = [[OnboardingViewController alloc] initWithBackgroundImage:nil contents:[self generateStockContentVCS]];

    CGFloat testIconSize = 80;
    onboardingVC.iconSize = testIconSize;

    UIColor *testColor = [UIColor purpleColor];
    onboardingVC.titleTextColor = testColor;
    onboardingVC.bodyTextColor = testColor;
    onboardingVC.buttonTextColor = testColor;

    NSString *testFontName = @"Helvetica-LightOblique";
    onboardingVC.fontName = testFontName;

    CGFloat testFontSize = 12;
    onboardingVC.titleFontSize = testFontSize;
    onboardingVC.bodyFontSize = testFontSize;

    CGFloat testPadding = 22;
    onboardingVC.topPadding = testPadding;
    onboardingVC.underIconPadding = testPadding;
    onboardingVC.underTitlePadding = testPadding;
    onboardingVC.bottomPadding = testPadding;

    NSArray *contentsFromController = [onboardingVC contentViewControllers];

    for (OnboardingContentViewController *contentVC in contentsFromController) {
        XCTAssert(contentVC.iconSize == testIconSize, @"The content view controller's icon size is invalid.");
        XCTAssert(contentVC.titleTextColor == testColor, @"The content view controller's title text color is invalid.");
        XCTAssert(contentVC.bodyTextColor == testColor, @"The content view controller's body text color is invalid.");
        XCTAssert(contentVC.buttonTextColor == testColor, @"The content view controller's button test color is invalid.");
        XCTAssert([contentVC.fontName isEqualToString:testFontName], @"The content view controller's font name is invalid.");
        XCTAssert(contentVC.titleFontSize == testFontSize, @"The content view controller's title fotn size is invalid.");
        XCTAssert(contentVC.bodyFontSize == testFontSize, @"The content view controller's body font size is invalid.");
        XCTAssert(contentVC.topPadding == testPadding, @"The content view controller's top padding is invalid.");
        XCTAssert(contentVC.underIconPadding == testPadding, @"The content view controller's under icon padding is invalid.");
        XCTAssert(contentVC.underTitlePadding == testPadding, @"The content view controller's under title padding is invalid.");
        XCTAssert(contentVC.bottomPadding == testPadding, @"The content view controller's bottom padding is invalid.");
    }
}

#pragma mark - Test utilities

- (NSArray *)generateStockContentVCS {
    // Create and return an array of content view controllers.
    OnboardingContentViewController *contentVC1 = [[OnboardingContentViewController alloc] initWithTitle:@"T1" body:@"B1" image:nil buttonText:nil action:nil];
    OnboardingContentViewController *contentVC2 = [[OnboardingContentViewController alloc] initWithTitle:@"T2" body:@"B2" image:nil buttonText:nil action:nil];
    OnboardingContentViewController *contentVC3 = [[OnboardingContentViewController alloc] initWithTitle:@"T3" body:@"B3" image:nil buttonText:nil action:nil];
    OnboardingContentViewController *contentVC4 = [[OnboardingContentViewController alloc] initWithTitle:@"T4" body:@"B4" image:nil buttonText:nil action:nil];
    return @[contentVC1, contentVC2, contentVC3, contentVC4];
}

@end


text:
<?php include_once('../app/view/include/header.inc.php'); ?>

   <?php if(isset($_GET['mail_exist'])) {
                          if($_GET['mail_exist'] = 1)
                          {
                            ?>

                            <?php
                          }
                        }
?>

YOUR CART

<?php echo $count_articles; ?>  articles

    		<?php
    		$GrandTotal='';
    		foreach ($panier as $key => $row) {//var_dump($panier);?>

<?php echo $panier[$key][0]['lp_nom']; ?>

 : <?php echo $row[1]?>

<?php echo $panier[$key][0]['lp_prix']*$row[1]; ?>€

        <?php $prixTotal = ($panier[$key][0]['lp_prix']*$row[1]);$GrandTotal += $prixTotal;
        ?>
        <?php } ?>

Total : <?php echo $GrandTotal; ?> €

        <?php if(!isset($_SESSION['id_user']))
        {
        ?>

Summary

Finalize your booking at the restaurant <?php echo $details[0]['lr_nom']; ?>

1 - First, get logged !

                <button class="color_principal button_log button_style font_open_sansbold trigger">Log in</button>

OR

                  <form class="form_summary" method="post">

                      Civility

                        <input type="radio" name="gender_user" id="radios-0" value="Female" checked="checked">
                        Female

                          <input type="radio" name="gender_user" id="radios-1" value="Male">Male

                      First name

                        <input name="nom_user" type="text" class="form-control" placeholder="First name" required>

                      Last Name

                        <input name="prenom_user" type="text" class="form-control" placeholder="Last Name" required>

                      Email

                        <input name="email_user" type="email" class="form-control" id="inputEmail3" placeholder="Email" required>

                      Phone

                          <input name="phone_user" type="tel" class="form-control" id="inputEmail3" placeholder="Phone" required>

                        Password

                          <input name="mdp_user" type="password" class="form-control" id="inputPassword3" placeholder="Password" required>

                        Country

                          <select name="country_user" class="form-control">
                            <option selected>France</option>
                            <option>Italie</option>
                            <option>Engleterre</option>
                            <option>Espagne</option>
                            <option>Allemagne</option>
                          </select>

                          <input type="submit" class="btn btn-danger btn-default" value="Register" />

                    </form>

                <?php } else { ?>

2 - Finalize your booking by several informations !

                    <form class=" col-sm-12 form_summary_test" method="post">

Choose a date

                       <input class='form-control' name="date_resa" type="text" id="datepicker" required/>

Choose an hour

                        <select class="form-control" name="horraire_resa" id="horraire" required>
                          <?php
                            for ( $heures = $heure[0]['open_day'] ; $heures < $heure[0]['close_night'] ; $heures++ ){
                            for ( $minutes = 0 ; $minutes <= 45 ; $minutes += 15 ){
                            ?>
                            <option value="<?php echo sprintf('%02d:%02d', $heures, $minutes) ?>:00" <?php if(($heures>$heure[0]['close_day'])){
                            echo"disabled";
                            }
                            if($heures>=$heure[0]['open_night']){
                            echo"disabled";
                            }
                            ?>><?php echo sprintf('%02d:%02d', $heures, $minutes) ?></option>     <?php
                            }
                            }
                            ?>
                        </select>

                    <input id="alternate" name="alternate" type="hidden"class="form-control" />

Number of persons

                      <select name="personne_resa" class="form-control" required>
                        <option>1</option>
                        <option>2</option>
                        <option>3</option>
                        <option>4</option>
                        <option>5</option>
                      </select>

Location

<?php echo $details[0]['lr_adresse']; ?>

Do you have any comments ? ( anything about your dish , the restaurant .. )

                    <textarea class="col-lg-12" name="commentaire_commande"></textarea>

                <input type="submit" class="btn btn-danger btn-default" value="Checkout" />
              </form>

    <?php } ?>

<?php include_once('../app/view/include/footer.inc.php'); ?>
<?php
//var_dump($_SESSION);
?>


text:
<?php

namespace Silver\Database\Parts;

class Quote extends Part
{

    private $value;
    private $quote;

    public function __construct($value, $quote_char)
    {
        $this->value = $value;
        $this->quote = $quote_char;
    }

    protected static function compile($q)
    {
        $quote = $q->quote;
        $value = $q->value;

        switch($quote) {
        case '`':
        case '"':
        case "'":
            $value = str_replace($quote, $quote.$quote, $value);
            break;
        default:
            throw new \Exception("Unknown quote char: $quote");
        }

        return $quote . $value . $quote;
    }
}


text:
[![Build Status](https://travis-ci.org/escapestudios-cookbooks/newrelic.png)](https://travis-ci.org/escapestudios-cookbooks/newrelic)

Description
===========

This cookbook provides an easy way to install various New Relic agents and the New Relic server monitor.

More information?
* https://docs.newrelic.com/docs/server/new-relic-for-server-monitoring
* https://docs.newrelic.com/docs/php/new-relic-for-php
* https://docs.newrelic.com/docs/python/new-relic-for-python
* https://docs.newrelic.com/docs/dotnet/new-relic-for-net
* https://docs.newrelic.com/docs/java/new-relic-for-java
* https://docs.newrelic.com/docs/nodejs/installing-and-maintaining-nodejs
* https://docs.newrelic.com/docs/ruby/new-relic-for-ruby

* https://pypi.python.org/pypi/newrelic_plugin_agent
* https://github.com/MeetMe/newrelic-plugin-agent#installation-instructions

Requirements
============

## Chef version:

Make sure you run Chef >= 0.10.0.

## Cookbooks:

* python

This cookbook recommends on the following cookbooks:

* php
* ms_dotnet4
* nodejs
* curl

### Depending on your environment, these recommended cookbooks are actual dependencies (depends):
* Installing the PHP agent? You'll need the php cookbook to be available.
* Installing the DotNet agent? You'll need the ms_dotnet4 cookbook to be available.
* Installing the Nodejs agent? You'll need the nodejs cookbook to be available.
* Installing the MeetMe plugin? You'll need the python cookbook to be available.

* Making use of the deployment LWRP? You'll need the curl cookbook to be available.

## Platforms:

* Debian
* Ubuntu
* RHEL
* CentOS
* Fedora
* Scientific
* Amazon
* Windows
* SmartOS

Attributes
==========

## default.rb:

### BASIC
* `node['newrelic']['license']` - Your New Relic license key. Default is `nil`
* `node['newrelic']['server_monitoring']['license']` - Your New Relic license key for server monitoring purposes (defaults to value of node['newrelic']['license'])
* `node['newrelic']['application_monitoring']['license']` - Your New Relic license key for application monitoring purposes (defaults to value of node['newrelic']['license'])
* `node['newrelic']['plugin_monitoring']['license']` - Your New Relic license key for plugin purposes (defaults to value of node['newrelic']['license'])

### ADVANCED
* `node['newrelic']['server_monitoring']['logfile']`
* `node['newrelic']['server_monitoring']['loglevel']`
* `node['newrelic']['server_monitoring']['proxy']`
* `node['newrelic']['server_monitoring']['ssl']`
* `node['newrelic']['server_monitoring']['ssl_ca_bundle']`
* `node['newrelic']['server_monitoring']['ssl_ca_path']`
* `node['newrelic']['server_monitoring']['hostname']`
* `node['newrelic']['server_monitoring']['pidfile']`
* `node['newrelic']['server_monitoring']['collector_host']`
* `node['newrelic']['server_monitoring']['timeout']`

* `node['newrelic']['application_monitoring']['enabled']`
* `node['newrelic']['application_monitoring']['logfile']`
* `node['newrelic']['application_monitoring']['loglevel']`
* `node['newrelic']['application_monitoring']['appname']`
* `node['newrelic']['application_monitoring']['daemon']['logfile']`
* `node['newrelic']['application_monitoring']['daemon']['loglevel']`
* `node['newrelic']['application_monitoring']['daemon']['port']`
* `node['newrelic']['application_monitoring']['daemon']['max_threads']`
* `node['newrelic']['application_monitoring']['daemon']['ssl']`
* `node['newrelic']['application_monitoring']['daemon']['ssl_ca_path']`
* `node['newrelic']['application_monitoring']['daemon']['ssl_ca_bundle']`
* `node['newrelic']['application_monitoring']['daemon']['proxy']`
* `node['newrelic']['application_monitoring']['daemon']['pidfile']`
* `node['newrelic']['application_monitoring']['daemon']['location']`
* `node['newrelic']['application_monitoring']['daemon']['collector_host']`
* `node['newrelic']['application_monitoring']['daemon']['dont_launch']`
* `node['newrelic']['application_monitoring']['capture_params']`
* `node['newrelic']['application_monitoring']['ignored_params']`
* `node['newrelic']['application_monitoring']['error_collector']['enable']`
* `node['newrelic']['application_monitoring']['error_collector']['record_database_errors']`
* `node['newrelic']['application_monitoring']['error_collector']['prioritize_api_errors']`
* `node['newrelic']['application_monitoring']['browser_monitoring']['auto_instrument']`
* `node['newrelic']['application_monitoring']['transaction_tracer']['enable']`
* `node['newrelic']['application_monitoring']['transaction_tracer']['threshold']`
* `node['newrelic']['application_monitoring']['transaction_tracer']['detail']`
* `node['newrelic']['application_monitoring']['transaction_tracer']['slow_sql']`
* `node['newrelic']['application_monitoring']['transaction_tracer']['stack_trace_threshold']`
* `node['newrelic']['application_monitoring']['transaction_tracer']['explain_threshold']`
* `node['newrelic']['application_monitoring']['transaction_tracer']['record_sql']`
* `node['newrelic']['application_monitoring']['transaction_tracer']['custom']`
* `node['newrelic']['application_monitoring']['framework']`
* `node['newrelic']['application_monitoring']['webtransaction']['name']['remove_trailing_path']`
* `node['newrelic']['application_monitoring']['webtransaction']['name']['functions']`
* `node['newrelic']['application_monitoring']['webtransaction']['name']['files']`

## repository.rb:
* `node['newrelic']['repository']['repository_key']` - The New Relic repository key, defaults to "548C16BF"

## php-agent.rb:
* `node['newrelic']['php-agent']['startup_mode']` - The newrelic-daemon startup mode ("agent"/"external"), defaults to "agent"
* `node['newrelic']['php-agent']['web_server']['service_name']` - The web server service name, defaults to "apache2"
* `node['newrelic']['php-agent']['php_recipe']` - The php recipe to include for the php agent, defaults to "php::default"
* `node['newrelic']['php-agent']['config_file']` - The New Relic php agent config file, defaults to "#{node['php']['ext_conf_dir']}/newrelic.ini" (FYI: Opscode PHP cookbook's ext_conf_dir)

## python-agent.rb:
* `node['newrelic']['python-agent']['python_version']` - Defaults to "latest". Version numbers can be found at http://download.newrelic.com/python_agent/release/
* `node['newrelic']['python-agent']['python_recipe']` - The python recipe to include for the python agent, defaults to "python::pip"
* `node['newrelic']['python-agent']['config_file']` - The New Relic python agent config file, defaults to "/etc/newrelic/newrelic.ini"

## dotnet-agent.rb:
* `node['newrelic']['dotnet-agent']['https_download']` - The URL to download the MSI installer from New Relic. Default is to pull "latest"
* `node['newrelic']['dotnet-agent']['install_level']` - The install version of the .NET Agent. Default is '1' but can use '50' for a complete installation
* `node['newrelic']['dotnet-agent']['dotnet_recipe']` - The dotnet recipe to include for the php agent, defaults to "ms_dotnet4"

## server-monitor-agent.rb:
* `node['newrelic']['server-monitor-agent']['service_name']` - The New Relic server monitoring service name, defaults to "newrelic-sysmond"
* `node['newrelic']['server-monitor-agent']['service_notify_action']` - The New Relic server monitoring notify action, defaults to ":restart"
* `node['newrelic']['server-monitor-agent']['service_actions']` - The New Relic server monitoring service actions, defaults to "[:enable, :start]" (#starts the service if it's not running and enables it to start at system boot time)
* `node['newrelic']['server-monitor-agent']['config_path']` - The New Relic server monitoring config path, defaults to "/etc/newrelic"
* `node['newrelic']['server-monitor-agent']['config_file_group']` - The New Relic server monitoring config file group, defaults to "newrelic"
* `node['newrelic']['server-monitor-agent']['windows_version']` - the Windows version to install, defaults to "2.0.0.198"
* `node['newrelic']['server-monitor-agent']['windows64_checksum']` - checksum of the 64-bit Windows version, defaults to "5a8f3f5e8f15997463430401756d377c321c8899c2790ca85e5587a5b643651e"
* `node['newrelic']['server-monitor-agent']['windows32_checksum']` - checksum of the 32-bit Windows version, defaults to "ac2b65eecaad461fdd2e4386e3e4c9f96ea940b35bdf7a8c532c21dbd1c99ff0"

## java-agent.rb:
* `node['newrelic']['java-agent']['https_download']` - The url to download the jar vor the New Relic Java agent
* `node['newrelic']['java-agent']['jar_file']` - The name of the newrelic jar file
* `node['newrelic']['java-agent']['install_dir']` - The directory to install the newrelic jar and config file
* `node['newrelic']['java-agent']['app_user']` - The user that runs the Java application that will use the New Relic Java agent
* `node['newrelic']['java-agent']['app_group']` - The group for the app_user
* `node['newrelic']['java-agent']['audit_mode']` - Boolean, log all data to and from New Relic in plain text
* `node['newrelic']['java-agent']['log_file_count']` - The number of log files to use
* `node['newrelic']['java-agent']['log_limit_in_kbytes']` - The maximum number of bytes to write to any one log file
* `node['newrelic']['java-agent']['log_daily']` - Override other log rolling configuration and roll the logs daily

## nodejs-agent.rb
* `node['newrelic']['nodejs-agent']['apps']` - Array of Hash describing the apps to monitor

eg.
```
[
   { 'app_name' => 'My Application', 'app_path' => "/path/to/app/root" }
]
```

You then need to modify your application "main" file to add the following on the first line:

```
javascript
require('newrelic');
```

* `node['newrelic']['nodejs-agent']['nodejs_recipe']` - The nodejs recipe to include for the nodejs agent, defaults to "nodejs::npm"

## ruby-agent.rb:
* `node['newrelic']['ruby-agent']['install_dir']` - The directory to for the config file
* `node['newrelic']['ruby-agent']['app_user']` - The user that runs the Ruby application that will use the New Relic Ruby agent
* `node['newrelic']['ruby-agent']['app_group']` - The group for the app_user
* `node['newrelic']['ruby-agent']['audit_mode']` - Boolean, log all data to and from New Relic in plain text
* `node['newrelic']['ruby-agent']['log_file_count']` - The number of log files to use
* `node['newrelic']['ruby-agent']['log_limit_in_kbytes']` - The maximum number of bytes to write to any one log file
* `node['newrelic']['ruby-agent']['log_daily']` - Override other log rolling configuration and roll the logs daily

## meetme-plugin.rb:
* `node['newrelic']['meetme-plugin']['service_name']` - The New Relic MeetMe plugin service name, defaults to "newrelic-plugin-agent"
* `node['newrelic']['meetme-plugin']['services']` - A hash of New Relic MeetMe plugin services, defaults to nil

eg.
```
{
  "memcached" => {
    "name" => "localhost",
    "host" => "host",
    "port" => 11211
  },
  "redis" => [
    {
      "name" => "localhost",
      "host" => "localhost",
      "port" => 6379,
      "db_count" => 16,
      "password" => "foobar"
    },
    {
      "name" => "localhost",
      "host" => "localhost",
      "port" => 6380,
      "db_count" => 16,
      "password" => "foobar"
    }
  ]
}
```

* `node['newrelic']['meetme-plugin']['wake_interval']` - The New Relic plugin agent wake interval, defaults to 60
* `node['newrelic']['meetme-plugin']['config_file']` - The New Relic plugin agent config file, defaults to "/etc/newrelic/newrelic_plugin_agent.cfg"
* `node['newrelic']['meetme-plugin']['pid_file']` - The New Relic plugin agent PID file name, defaults to "/var/run/newrelic/newrelic_plugin_agent.pid"
* `node['newrelic']['meetme-plugin']['log_file']` - The New Relic plugin agent log file name, defaults to "/var/log/newrelic/newrelic_plugin_agent.log"
* `node['newrelic']['meetme-plugin']['user']` - The New Relic plugin agent user, defaults to "newrelic"
* `node['newrelic']['meetme-plugin']['additional_requirements']` - The New Relic plugin agent's additional requirements, eg. {"mongodb", "pgbouncer", "postgresql"} - defaults to {}

Resources / Providers
=====================

This cookbook includes an LWRP for notifying New Relic of a deployment

### `newrelic_deployment`

#### Actions
- :notify: Notify New Relic of a deployment

#### Attribute parameters
- api_key: Your New Relic API key
- app_name: The name of the application, found in the newrelic.yml file
- app_id: The ID # of the application
- description: Text annotation for the deployment (notes for you)
- revision: The revision number from your source control system (SVN, git, etc.)
- changelog: A list of changes for this deployment
- user: The name of the user/process that triggered this deployment

#### Examples
```
newrelic_deployment "my-application" do
    api_key "abcdef"
    #app_name "my-application"
    app_id 1234567
    description "some description"
    revision "some revision"
    changelog "some changelog"
    user "chef-client"
    action :notify
end
```

Usage
=====

1)
include `recipe[newrelic]` in a run list to implicly run `recipe[newrelic:repository]` and `recipe[newrelic::server-monitor-agent]`
--- OR ---
include the bits and pieces explicitly in a run list:
```ruby
`recipe[newrelic::repository]`
`recipe[newrelic::server-monitor-agent]`
`recipe[newrelic::dotnet-agent]`
`recipe[newrelic::java-agent]`
`recipe[newrelic::meetme-plugin]`
`recipe[newrelic::nodejs-agent]`
`recipe[newrelic::php-agent]`
`recipe[newrelic::python-agent]`
`recipe[newrelic::ruby-agent]`
```

2)
change the `node['newrelic']['license']` attribute to your New Relic license keys
--- OR ---
override the attributes on a higher level (http://wiki.opscode.com/display/chef/Attributes#Attributes-AttributesPrecedence)

References
==========

* [New Relic home page] (http://newrelic.com/)
* [New Relic for Server Monitoring] (https://docs.newrelic.com/docs/server/new-relic-for-server-monitoring)
* [New Relic for PHP] (https://docs.newrelic.com/docs/php/new-relic-for-php)
* [newrelic-daemon startup modes] (https://newrelic.com/docs/php/newrelic-daemon-startup-modes)
* [New Relic for Python] (https://docs.newrelic.com/docs/python/new-relic-for-python)
* [New Relic for .NET] (https://docs.newrelic.com/docs/dotnet/new-relic-for-net)
* [New Relic for Java] (https://docs.newrelic.com/docs/java/new-relic-for-java)
* ["newrelic" cookbook by heavywater on github] (https://github.com/heavywater/chef-newrelic)
* ["newrelic_monitoring" cookbook on community.opscode.com] (http://community.opscode.com/cookbooks/newrelic_monitoring)
* ["newrelic_monitoring" cookbook on github] (https://github.com/8thBridge/chef-newrelic-monitoring)
* a very big thanks to heavywater <darrin@heavywater.ca> for the original version of this cookbook

License and Authors
===================

Author: David Joos <david@escapestudios.com>
Author: Escape Studios Development <dev@escapestudios.com>
Copyright: 2012-2014, Escape Studios

Unless otherwise noted, all files are released under the MIT license,
possible exceptions will contain licensing information in them.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


text:
(function(window){

var isNode = typeof process !== 'undefined' && process.toString() === '[object process]';
var RouteRecognizer = isNode ? require('route-recognizer')['default'] : window.RouteRecognizer;
var FakeXMLHttpRequest = isNode ? require('./bower_components/FakeXMLHttpRequest/fake_xml_http_request') : window.FakeXMLHttpRequest;

function Pretender(maps){
  maps = maps || function(){};
  // Herein we keep track of RouteRecognizer instances
  // keyed by HTTP method. Feel free to add more as needed.
  this.registry = {
    GET: new RouteRecognizer(),
    PUT: new RouteRecognizer(),
    POST: new RouteRecognizer(),
    DELETE: new RouteRecognizer(),
    PATCH: new RouteRecognizer(),
    HEAD: new RouteRecognizer()
  };

  this.handlers = [];
  this.handledRequests = [];
  this.unhandledRequests = [];

  // reference the native XMLHttpRequest object so
  // it can be restored later
  this._nativeXMLHttpRequest = window.XMLHttpRequest;

  // capture xhr requests, channeling them into
  // the route map.
  window.XMLHttpRequest = interceptor(this);

  // trigger the route map DSL.
  maps.call(this);
}

function interceptor(pretender) {
  function FakeRequest(){
    // super()
    FakeXMLHttpRequest.call(this);
  }
  // extend
  var proto = new FakeXMLHttpRequest();
  proto.send = function send(){
    FakeXMLHttpRequest.prototype.send.apply(this, arguments);
    pretender.handleRequest(this);
  };

  FakeRequest.prototype = proto;
  return FakeRequest;
}

function verbify(verb){
  return function(path, handler){
    this.register(verb, path, handler);
  };
}

function throwIfURLDetected(url){
  var HTTP_REGEXP = /^https?/;
  var message;

  if(HTTP_REGEXP.test(url)) {
    var parser = window.document.createElement('a');
    parser.href = url;

    message = "Pretender will not respond to requests for URLs. It is not possible to accurately simluate the browser's CSP. "+
              "Remove the " + parser.protocol +"//"+ parser.hostname +" from " + url + " and try again";
    throw new Error(message)
  }
}

Pretender.prototype = {
  get: verbify('GET'),
  post: verbify('POST'),
  put: verbify('PUT'),
  'delete': verbify('DELETE'),
  patch: verbify('PATCH'),
  head: verbify('HEAD'),
  register: function register(verb, path, handler){
    handler.numberOfCalls = 0;
    this.handlers.push(handler);

    var registry = this.registry[verb];
    registry.add([{path: path, handler: handler}]);
  },
  handleRequest: function handleRequest(request){
    var verb = request.method.toUpperCase();
    var path = request.url;

    throwIfURLDetected(path);

    var handler = this._handlerFor(verb, path, request);

    if (handler) {
      handler.handler.numberOfCalls++;
      this.handledRequests.push(request);

      try {
        var statusHeadersAndBody = handler.handler(request),
            status = statusHeadersAndBody[0],
            headers = this.prepareHeaders(statusHeadersAndBody[1]),
            body = this.prepareBody(statusHeadersAndBody[2]);
        request.respond(status, headers, body);
        this.handledRequest(verb, path, request);
      } catch (error) {
        this.erroredRequest(verb, path, request, error);
      }
    } else {
      this.unhandledRequests.push(request);
      this.unhandledRequest(verb, path, request);
    }
  },
  prepareBody: function(body) { return body; },
  prepareHeaders: function(headers) { return headers; },
  handledRequest: function(verb, path, request) { /* no-op */},
  unhandledRequest: function(verb, path, request) {
    throw new Error("Pretender intercepted "+verb+" "+path+" but no handler was defined for this type of request");
  },
  erroredRequest: function(verb, path, request, error){
    error.message = "Pretender intercepted "+verb+" "+path+" but encountered an error: " + error.message;
    throw error;
  },
  _handlerFor: function(verb, path, request){
    var registry = this.registry[verb];
    var matches = registry.recognize(path);

    var match = matches ? matches[0] : null;
    if (match) {
      request.params = match.params;
      request.queryParams = matches.queryParams;
    }

    return match;
  },
  shutdown: function shutdown(){
    window.XMLHttpRequest = this._nativeXMLHttpRequest;
  }
};

if (isNode) {
  module.exports = Pretender;
} else {
  window.Pretender = Pretender;
}

})(window);


text:
describe('Core_selection', function () {
  var id = 'testContainer';

  beforeEach(function () {
    this.$container = $('

').appendTo('body');
  });

  afterEach(function () {
    if (this.$container) {
      destroy();
      this.$container.remove();
    }
  });

  it('should call onSelection callback', function () {
    var output = null;

    handsontable({
      afterSelection: function (r, c) {
        output = [r, c];
      }
    });
    selectCell(1, 2);

    expect(output[0]).toEqual(1);
    expect(output[1]).toEqual(2);
  });

  it('should trigger selection event', function () {
    var output = null;

    handsontable();
    Handsontable.hooks.add('afterSelection', function (r, c) {
      output = [r, c];
    });
    selectCell(1, 2);

    expect(output[0]).toEqual(1);
    expect(output[1]).toEqual(2);
  });

  it('this.rootElement should point to handsontable rootElement (onSelection)', function () {
    var output = null;

    handsontable({
      afterSelection: function () {
        output = this.rootElement;
      }
    });
    selectCell(0, 0);

    expect(output).toEqual(this.$container[0]);
  });

  it('this.rootElement should point to handsontable rootElement (onSelectionByProp)', function () {
    var output = null;

    handsontable({
      afterSelectionByProp: function () {
        output = this.rootElement;
      }
    });
    selectCell(0, 0);

    expect(output).toEqual(this.$container[0]);
  });

  it('should focus external textarea when clicked during editing', function () {
    var textarea = $('<input type="text">').prependTo($('body'));

    handsontable();
    selectCell(0, 0);

    keyDown('enter');
//    $("html").triggerHandler('mouseup');
    $("html").simulate('mouseup');
    textarea.focus();

    expect(document.activeElement).toBe(textarea[0]);
    textarea.remove();
  });

  it('should deselect currently selected cell', function () {
    handsontable();
    selectCell(0, 0);

    $('html').simulate('mousedown');

    expect(getSelected()).toBeUndefined();
  });

  it('should not deselect the currently selected cell after clicking on a scrollbar', function () {
    var hot = handsontable({
      outsideClickDeselects: false,
      minRows: 20,
      minCols: 2,
      width: 400,
      height: 100
    });
    selectCell(0, 0);

    var holderBoundingBox = hot.view.wt.wtTable.holder.getBoundingClientRect(),
      verticalScrollbarCoords = {
        x: holderBoundingBox.left + holderBoundingBox.width - 3,
        y: holderBoundingBox.top + ( holderBoundingBox.height / 2 )
      },
      horizontalScrollbarCoords = {
        x: holderBoundingBox.left + ( holderBoundingBox.width / 2 ),
        y: holderBoundingBox.top + holderBoundingBox.height - 3
      };

    $(hot.view.wt.wtTable.holder).simulate('mousedown', {
      clientX: verticalScrollbarCoords.x,
      clientY: verticalScrollbarCoords.y
    });

    expect(getSelected()).toEqual([0, 0, 0, 0]);

    $(hot.view.wt.wtTable.holder).simulate('mousedown', {
      clientX: horizontalScrollbarCoords.x,
      clientY: horizontalScrollbarCoords.y
    });

    expect(getSelected()).toEqual([0, 0, 0, 0]);
  });

  it('should not deselect currently selected cell', function () {
    handsontable({
      outsideClickDeselects: false
    });
    selectCell(0, 0);

    $("html").simulate('mousedown');

    expect(getSelected()).toEqual([0, 0, 0, 0]);
  });

  it('should allow to focus on external input and hold current selection informations', function () {
    var textarea = $('<input id="test_textarea" type="text">').prependTo($('body'));

    handsontable({
      outsideClickDeselects: false
    });
    selectCell(0, 0);

    textarea.simulate('mousedown');
    textarea.focus();

    expect(document.activeElement.id).toEqual('test_textarea');
    expect(getSelected()).toEqual([0, 0, 0, 0]);
    textarea.remove();
  });

  it('should allow to type in external input while holding current selection information', function () {
    var textarea = $('<textarea id="test_textarea"></textarea>').prependTo($('body'));
    var keyPressed;
    handsontable({
      outsideClickDeselects: false
    });
    selectCell(0, 0);

    textarea.focus();
    textarea.simulate('mousedown');
    textarea.simulate('mouseup');

    textarea.on('keydown', function (event) {
      keyPressed = event.keyCode;
    });

    var LETTER_a_KEY = 97;
//    var event = $.Event('keydown');
//    event.keyCode = LETTER_a_KEY;

//    $(document.activeElement).trigger(event);
    $(document.activeElement).simulate('keydown',{
      keyCode: LETTER_a_KEY
    });

    //textarea should receive the event and be an active element
    expect(keyPressed).toEqual(LETTER_a_KEY);
    expect(document.activeElement).toBe(document.getElementById('test_textarea'));

    //should preserve selection, close editor and save changes
    expect(getSelected()).toEqual([0, 0, 0, 0]);
    expect(getDataAtCell(0, 0)).toBeNull();

    textarea.remove();
  });

  it('should allow to type in external input after opening cell editor', function () {
    var textarea = $('<textarea id="test_textarea"></textarea>').prependTo($('body'));
    var keyPressed;
    handsontable({
      outsideClickDeselects: false
    });
    selectCell(0, 0);
    keyDown('enter');
    document.activeElement.value = 'Foo';

    textarea.focus();
    textarea.simulate('mousedown');
    textarea.simulate('mouseup');

    textarea.on('keydown', function (event) {
      keyPressed = event.keyCode;
    });

    var LETTER_a_KEY = 97;
//    var event = $.Event('keydown');
//    event.keyCode = LETTER_a_KEY;

    $(document.activeElement).simulate('keydown',{
      keyCode: LETTER_a_KEY
    });

    //textarea should receive the event and be an active element
    expect(keyPressed).toEqual(LETTER_a_KEY);
    expect(document.activeElement).toBe(document.getElementById('test_textarea'));

    //should preserve selection, close editor and save changes
    expect(getSelected()).toEqual([0, 0, 0, 0]);
    expect(getDataAtCell(0, 0)).toEqual('Foo');

    textarea.remove();
  });

  it('should deselect on outside click if outsideClickDeselects is a function that returns true', function() {
    var textarea = $('<textarea id="test_textarea"></textarea>').prependTo($('body'));
    var keyPressed;
    handsontable({
      outsideClickDeselects: function() { return true }
    });
    selectCell(0, 0);
    keyDown('enter');
    document.activeElement.value = 'Foo';

    textarea.focus();
    textarea.simulate('mousedown');
    textarea.simulate('mouseup');

    textarea.on('keydown', function (event) {
      keyPressed = event.keyCode;
    });

    var LETTER_a_KEY = 97;
//    var event = $.Event('keydown');
//    event.keyCode = LETTER_a_KEY;

    $(document.activeElement).simulate('keydown',{
      keyCode: LETTER_a_KEY
    });

    //textarea should receive the event and be an active element
    expect(keyPressed).toEqual(LETTER_a_KEY);
    expect(document.activeElement).toBe(document.getElementById('test_textarea'));

    //should NOT preserve selection
    expect(getSelected()).toEqual(undefined);
    expect(getDataAtCell(0, 0)).toEqual('Foo');

    textarea.remove();
  });

it('should not deselect on outside click if outsideClickDeselects is a function that returns false', function() {
    var textarea = $('<textarea id="test_textarea"></textarea>').prependTo($('body'));
    var keyPressed;
    handsontable({
      outsideClickDeselects: function() { return false }
    });
    selectCell(0, 0);
    keyDown('enter');
    document.activeElement.value = 'Foo';

    textarea.focus();
    textarea.simulate('mousedown');
    textarea.simulate('mouseup');

    textarea.on('keydown', function (event) {
      keyPressed = event.keyCode;
    });

    var LETTER_a_KEY = 97;
//    var event = $.Event('keydown');
//    event.keyCode = LETTER_a_KEY;

    $(document.activeElement).simulate('keydown',{
      keyCode: LETTER_a_KEY
    });

    //textarea should receive the event and be an active element
    expect(keyPressed).toEqual(LETTER_a_KEY);
    expect(document.activeElement).toBe(document.getElementById('test_textarea'));

    //should preserve selection, close editor and save changes
    expect(getSelected()).toEqual([0, 0, 0, 0]);
    expect(getDataAtCell(0, 0)).toEqual('Foo');

    textarea.remove();
  });

  it('should fix start range if provided is out of bounds (to the left)', function () {
    handsontable({
      startRows: 5,
      startCols: 5
    });
    selectCell(0, 0);
    keyDownUp('arrow_left');

    expect(getSelected()).toEqual([0, 0, 0, 0]);
  });

  it('should fix start range if provided is out of bounds (to the top)', function () {
    handsontable({
      startRows: 5,
      startCols: 5
    });
    selectCell(0, 0);
    keyDownUp('arrow_up');

    expect(getSelected()).toEqual([0, 0, 0, 0]);
  });

  it('should fix start range if provided is out of bounds (to the right)', function () {
    handsontable({
      startRows: 5,
      startCols: 5
    });
    selectCell(0, 4);
    keyDownUp('arrow_right');

    expect(getSelected()).toEqual([0, 4, 0, 4]);
  });

  it('should fix start range if provided is out of bounds (to the bottom)', function () {
    handsontable({
      startRows: 5,
      startCols: 5
    });
    selectCell(4, 0);
    keyDownUp('arrow_down');

    expect(getSelected()).toEqual([4, 0, 4, 0]);
  });

  it('should fix end range if provided is out of bounds (to the left)', function () {
    handsontable({
      startRows: 5,
      startCols: 5
    });
    selectCell(0, 1);
    keyDownUp('shift+arrow_left');
    keyDownUp('shift+arrow_left');

    expect(getSelected()).toEqual([0, 1, 0, 0]);
  });

  it('should fix end range if provided is out of bounds (to the top)', function () {
    handsontable({
      startRows: 5,
      startCols: 5
    });
    selectCell(1, 0);
    keyDownUp('shift+arrow_up');
    keyDownUp('shift+arrow_up');

    expect(getSelected()).toEqual([1, 0, 0, 0]);
  });

  it('should fix end range if provided is out of bounds (to the right)', function () {
    handsontable({
      startRows: 5,
      startCols: 5
    });
    selectCell(0, 3);
    keyDownUp('shift+arrow_right');
    keyDownUp('shift+arrow_right');

    expect(getSelected()).toEqual([0, 3, 0, 4]);
  });

  it('should fix end range if provided is out of bounds (to the bottom)', function () {
    handsontable({
      startRows: 5,
      startCols: 5
    });
    selectCell(3, 0);
    keyDownUp('shift+arrow_down');
    keyDownUp('shift+arrow_down');
    keyDownUp('shift+arrow_down');

    expect(getSelected()).toEqual([3, 0, 4, 0]);
  });

  it('should select multiple cells', function () {
    handsontable({
      startRows: 5,
      startCols: 5
    });
    selectCell(3, 0, 4, 1);

    expect(getSelected()).toEqual([3, 0, 4, 1]);
  });

  it('should call onSelectionEnd as many times as onSelection when `selectCell` is called', function () {
    var tick = 0
      , tickEnd = 0;
    handsontable({
      startRows: 5,
      startCols: 5,
      afterSelection: function () {
        tick++;
      },
      afterSelectionEnd: function () {
        tickEnd++;
      }
    });
    selectCell(3, 0);
    selectCell(1, 1);

    expect(tick).toEqual(2);
    expect(tickEnd).toEqual(2);
  });

  it('should call onSelectionEnd when user finishes selection by releasing SHIFT key (3 times)', function () {
    var tick = 0;
    handsontable({
      startRows: 5,
      startCols: 5,
      afterSelectionEnd: function () {
        tick++;
      }
    });
    selectCell(3, 0); //makes tick++
    keyDownUp('shift+arrow_down'); //makes tick++
    keyDownUp('shift+arrow_down'); //makes tick++
    keyDownUp('shift+arrow_down'); //makes tick++

    expect(getSelected()).toEqual([3, 0, 4, 0]);
    expect(tick).toEqual(4);
  });

  it('should call onSelectionEnd when user finishes selection by releasing SHIFT key (1 time)', function () {
    var tick = 0;
    handsontable({
      startRows: 5,
      startCols: 5,
      afterSelectionEnd: function () {
        tick++;
      }
    });
    selectCell(3, 0); //makes tick++
    keyDown('shift+arrow_down');
    keyDown('shift+arrow_down');
    keyDownUp('shift+arrow_down'); //makes tick++

    expect(getSelected()).toEqual([3, 0, 4, 0]);
    expect(tick).toEqual(2);
  });

  it('should select columns by click on header with SHIFT key', function () {
    handsontable({
      startRows: 5,
      startCols: 5,
      colHeaders: true
    });

    this.$container.find('.ht_clone_top tr:eq(0) th:eq(1)').simulate('mousedown');
    this.$container.find('.ht_clone_top tr:eq(0) th:eq(1)').simulate('mouseup');

    this.$container.find('.ht_clone_top tr:eq(0) th:eq(4)').simulate('mousedown', {shiftKey: true});
    this.$container.find('.ht_clone_top tr:eq(0) th:eq(4)').simulate('mouseup');

    expect(getSelected()).toEqual([0, 1, 4, 4]);

  });

  it('should select rows by click on header with SHIFT key', function () {
    handsontable({
      startRows: 5,
      startCols: 5,
      rowHeaders: true
    });

    this.$container.find('.ht_clone_left tr:eq(1) th:eq(0)').simulate('mousedown');
    this.$container.find('.ht_clone_left tr:eq(1) th:eq(0)').simulate('mouseup');

    this.$container.find('.ht_clone_left tr:eq(4) th:eq(0)').simulate('mousedown', {shiftKey: true});
    this.$container.find('.ht_clone_left tr:eq(4) th:eq(0)').simulate('mouseup');

    expect(getSelected()).toEqual([1, 0, 4, 4]);

  });

  it('should select columns by click on header with SHIFT key', function () {
    handsontable({
      startRows: 5,
      startCols: 5,
      colHeaders: true
    });

    this.$container.find('.ht_clone_top tr:eq(0) th:eq(1)').simulate('mousedown');
    this.$container.find('.ht_clone_top tr:eq(0) th:eq(1)').simulate('mouseup');

    this.$container.find('.ht_clone_top tr:eq(0) th:eq(4)').simulate('mousedown', {shiftKey: true});
    this.$container.find('.ht_clone_top tr:eq(0) th:eq(4)').simulate('mouseup');

    expect(getSelected()).toEqual([0, 1, 4, 4]);

  });

  it('should change selection after click on row header with SHIFT key', function () {
    handsontable({
      startRows: 5,
      startCols: 5,
      rowHeaders: true
    });

    selectCell(1, 1, 3, 3);

    this.$container.find('.ht_clone_left tr:eq(4) th:eq(0)').simulate('mousedown', {shiftKey: true});
    this.$container.find('.ht_clone_left tr:eq(4) th:eq(0)').simulate('mouseup');

    expect(getSelected()).toEqual([1, 0, 4, 4]);

  });

  it('should change selection after click on column header with SHIFT key', function () {
    handsontable({
      startRows: 5,
      startCols: 5,
      colHeaders: true
    });

    selectCell(1, 1, 3, 3);

    this.$container.find('.ht_clone_top tr:eq(0) th:eq(4)').simulate('mousedown', {shiftKey: true});
    this.$container.find('.ht_clone_top tr:eq(0) th:eq(4)').simulate('mouseup');

    expect(getSelected()).toEqual([0, 1, 4, 4]);

  });

  it('should call onSelection while user selects cells with mouse; onSelectionEnd when user finishes selection', function () {
    var tick = 0, tickEnd = 0;
    handsontable({
      startRows: 5,
      startCols: 5,
      afterSelection: function () {
        tick++;
      },
      afterSelectionEnd: function () {
        tickEnd++;
      }
    });

    this.$container.find('tr:eq(0) td:eq(0)').simulate('mousedown');
    this.$container.find('tr:eq(0) td:eq(1)').simulate('mouseover');
    this.$container.find('tr:eq(1) td:eq(3)').simulate('mouseover');

    this.$container.find('tr:eq(1) td:eq(3)').simulate('mouseup');

    expect(getSelected()).toEqual([0, 0, 1, 3]);
    expect(tick).toEqual(3);
    expect(tickEnd).toEqual(1);
  });

  it('should properly select columns, when the user moves the cursor over column headers across two overlays', function () {
    handsontable({
      startRows: 5,
      startCols: 5,
      colHeaders: true,
      fixedColumnsLeft: 2
    });

    this.$container.find('.ht_clone_left tr:eq(0) th:eq(1)').simulate('mousedown');
    this.$container.find('.ht_clone_left tr:eq(0) th:eq(1)').simulate('mouseover');
    this.$container.find('.ht_clone_top tr:eq(0) th:eq(2)').simulate('mouseover');
    this.$container.find('.ht_clone_left tr:eq(0) th:eq(1)').simulate('mouseover');
    this.$container.find('.ht_clone_left tr:eq(0) th:eq(1)').simulate('mouseup');

    expect(getSelected()).toEqual([0, 1, 4, 1]);
  });

  it('should move focus to selected cell', function () {
    var $input = $('<input>').appendTo(document.body);
    handsontable({
      startRows: 5,
      startCols: 5
    });
    $input[0].focus();
    selectCell(0, 0);

    keyDownUp('enter');
    expect(isEditorVisible()).toEqual(true);
    $input.remove();
  });

  //This test should cover the #893 case, but it always passes. It seems like the keydown event (with CTRL key pressed) isn't delivered.
  it("should not move focus from outside elements on CTRL keydown event, when no cell is selected", function () {
    var $input = $('<input type="text"/>');
    $('body').append($input);

    handsontable();

    selectCell(0, 0);

    expect(document.activeElement.nodeName).toBeInArray(['BODY', 'HTML']);

    $input.focus();

    expect(document.activeElement.nodeName).toBe('INPUT');

//    var keyDownEvent = $.Event('keydown', {ctrlKey: true, metaKey: true});
//    $input.trigger(keyDownEvent);

    $input.simulate('keydown',{ctrlKey: true, metaKey: true});

    expect(document.activeElement.nodeName).toBe('INPUT');

    $input.remove();

  });

  it("should select the entire column after column header is clicked", function(){
    var hot = handsontable({
      width: 200,
      height: 100,
      startRows: 50,
      startCols: 5,
      colHeaders: true
    });

    this.$container.find('thead th:eq(0)').simulate('mousedown');

    expect(getSelected()).toEqual([0, 0, 49, 0]);
    expect(hot.selection.selectedHeader.rows).toBe(false);
    expect(hot.selection.selectedHeader.cols).toBe(true);
    expect(hot.selection.selectedHeader.corner).toBe(false);
  });

  it("should add classname after select column", function(){
    var hot = handsontable({
      width: 200,
      height: 100,
      startRows: 50,
      startCols: 5,
      colHeaders: true
    });

    this.$container.find('thead th:eq(0)').simulate('mousedown');

    expect(this.$container.hasClass('ht__selection--columns')).toBeTruthy();
  });

  it("should select the entire column after column header is clicked (in fixed rows/cols corner)", function(){
    var hot = handsontable({
      width: 200,
      height: 100,
      startRows: 50,
      startCols: 5,
      colHeaders: true,
      rowHeaders: true,
      fixedRowsTop: 2,
      fixedColumnsLeft: 2
    });

    this.$container.find('.ht_master thead th:eq(1)').simulate('mousedown');

    expect(getSelected()).toEqual([0, 0, 49, 0]);
    expect(hot.selection.selectedHeader.rows).toBe(false);
    expect(hot.selection.selectedHeader.cols).toBe(true);
    expect(hot.selection.selectedHeader.corner).toBe(false);
  });

  it("should select the entire fixed column after column header is clicked, after scroll horizontally", function(){
    var hot = handsontable({
      width: 200,
      height: 100,
      startRows: 50,
      startCols: 50,
      colHeaders: true,
      rowHeaders: true,
      fixedColumnsLeft: 2
    });

    hot.render();
    hot.view.wt.scrollHorizontal(20);

    this.$container.find('.ht_master thead th:eq(2)').simulate('mousedown');
    this.$container.find('.ht_master thead th:eq(2)').simulate('mouseup');

    expect(getSelected()).toEqual([0, 1, 49, 1]);
    expect(hot.selection.selectedHeader.rows).toBe(false);
    expect(hot.selection.selectedHeader.cols).toBe(true);
    expect(hot.selection.selectedHeader.corner).toBe(false);
  });

  it("should set the selection end to the first visible row, when dragging the selection from a cell to a column header", function (done) {
    var hot = handsontable({
      width: 200,
      height: 200,
      startRows: 20,
      startCols: 20,
      colHeaders: true,
      rowHeaders: true
    });

    hot.view.wt.scrollVertical(10);
    hot.view.wt.scrollHorizontal(10);

    hot.render();

    setTimeout(function () {
      $(getCell(12,11)).simulate('mousedown');
      spec().$container.find('.ht_clone_top thead th:eq(2)').simulate('mouseover');
    }, 30);

    setTimeout(function () {
      expect(getSelected()).toEqual([12, 11, 10, 11]);
      done();
    }, 60);
  });

  it("should set the selection end to the first visible column, when dragging the selection from a cell to a row header", function (done) {
    var hot = handsontable({
      width: 200,
      height: 200,
      startRows: 20,
      startCols: 20,
      colHeaders: true,
      rowHeaders: true
    });

    hot.view.wt.scrollVertical(10);
    hot.view.wt.scrollHorizontal(10);

    hot.render();

    setTimeout(function () {
      $(getCell(12,11)).simulate('mousedown');
      spec().$container.find('.ht_clone_left tbody th:eq(12)').simulate('mouseover');
    }, 30);

    setTimeout(function () {
      expect(getSelected()).toEqual([12, 11, 12, 10]);
      done();
    }, 60);
  });

  it("should allow to scroll the table when a whole column is selected and table is longer than it's container", function (done) {
    var errCount = 0;
    $(window).on("error.selectionTest", function () {
      errCount++;
    });

    var onAfterScrollVertically = jasmine.createSpy('onAfterScrollVertically');

    var hot = handsontable({
      height: 100,
      width: 300,
      startRows: 100,
      startCols: 5,
      colHeaders: true,
      rowHeaders: true,
      afterScrollVertically: onAfterScrollVertically
    });

    var mainHolder = hot.view.wt.wtTable.holder;

    mainHolder.scrollTop = 0;

    this.$container.find('thead tr:eq(0) th:eq(2)').simulate('mousedown');
    this.$container.find('thead tr:eq(0) th:eq(2)').simulate('mouseup');

    mainHolder.scrollTop = 120;

    setTimeout(function () {
      expect(errCount).toEqual(0); // expect no errors to be thrown

      $(window).off("error.selectionTest");
      done();
    }, 100);
  });

  it("should scroll to the end of the selection, when selecting cells using the keyboard", function () {
    var hot = handsontable({
      height: 300,
      width: 300,
      startRows: 50,
      startCols: 50,
      colHeaders: true,
      rowHeaders: true,
      fixedRowsTop: 2,
      fixedColumnsLeft: 2
    });

    var mainHolder = hot.view.wt.wtTable.holder;

    mainHolder.scrollTop = 100;
    selectCell(1, 3);
    keyDownUp('arrow_down');
    expect(mainHolder.scrollTop).toEqual(0);
    mainHolder.scrollTop = 100;
    selectCell(1, 3);
    keyDownUp('shift+arrow_down');
    expect(mainHolder.scrollTop).toEqual(0);

    mainHolder.scrollLeft = 100;
    selectCell(3, 1);
    keyDownUp('arrow_right');
    expect(mainHolder.scrollLeft).toEqual(0);
    mainHolder.scrollLeft = 100;
    selectCell(3, 1);
    keyDownUp('shift+arrow_right');
    expect(mainHolder.scrollLeft).toEqual(0);

    var lastVisibleColumn = hot.view.wt.wtTable.getLastVisibleColumn();
    selectCell(3, lastVisibleColumn);
    keyDownUp('arrow_right');
    expect(hot.view.wt.wtTable.getLastVisibleColumn()).toEqual(lastVisibleColumn + 1);
    keyDownUp('arrow_right');
    expect(hot.view.wt.wtTable.getLastVisibleColumn()).toEqual(lastVisibleColumn + 2);
    keyDownUp('shift+arrow_right');
    expect(hot.view.wt.wtTable.getLastVisibleColumn()).toEqual(lastVisibleColumn + 3);

    var lastVisibleRow = hot.view.wt.wtTable.getLastVisibleRow();
    selectCell(lastVisibleRow, 3);
    keyDownUp('arrow_down');
    expect(hot.view.wt.wtTable.getLastVisibleRow()).toEqual(lastVisibleRow + 1);
    keyDownUp('arrow_down');
    expect(hot.view.wt.wtTable.getLastVisibleRow()).toEqual(lastVisibleRow + 2);
    keyDownUp('shift+arrow_down');
    expect(hot.view.wt.wtTable.getLastVisibleRow()).toEqual(lastVisibleRow + 3);

  });

  it("should select the entire row after row header is clicked", function(){
    var hot = handsontable({
      startRows: 5,
      startCols: 5,
      colHeaders: true,
      rowHeaders: true
    });

    this.$container.find('tr:eq(2) th:eq(0)').simulate('mousedown');

    expect(getSelected()).toEqual([1, 0, 1, 4]);
    expect(hot.selection.selectedHeader.rows).toBe(true);
    expect(hot.selection.selectedHeader.cols).toBe(false);
    expect(hot.selection.selectedHeader.corner).toBe(false);
  });

  it("should add classname after select row", function(){
    var hot = handsontable({
      width: 200,
      height: 100,
      startRows: 50,
      startCols: 5,
      rowHeaders: true
    });

    this.$container.find('tbody tr:eq(0) th:eq(0)').simulate('mousedown');

    expect(this.$container.hasClass('ht__selection--rows')).toBeTruthy();
  });

  it("should select the entire row of a partially fixed table after row header is clicked", function(){
    handsontable({
      startRows: 5,
      startCols: 5,
      colHeaders: true,
      rowHeaders: true,
      fixedRowsTop: 2,
      fixedColumnsLeft: 2
    });

    this.$container.find('tr:eq(2) th:eq(0)').simulate('mousedown');
    expect(getSelected()).toEqual([1, 0, 1, 4]);
    this.$container.find('tr:eq(3) th:eq(0)').simulate('mousedown');
    expect(getSelected()).toEqual([2, 0, 2, 4]);
  });

  it("should select a cell in a newly added row after automatic row adding, triggered by editing a cell in the last row with minSpareRows > 0, " +
    "unless editing happened within the fixed bottom rows", function (done) {
    var hot = handsontable({
      startRows: 5,
      startCols: 2,
      minSpareRows: 1
    });

    setTimeout(function () {
      selectCell(4,0);
      keyDownUp('enter');
    }, 10);

    setTimeout(function () {
      keyDownUp('enter');
    }, 100);

    setTimeout(function () {
      expect(countRows()).toEqual(6);
      expect(getSelected()).toEqual([5,0,5,0]);
    }, 200);

    setTimeout(function () {
      done();
    }, 250);
  });

  it("should change selected coords by modifying coords object via `modifyTransformStart` hook", function(){
    var hot = handsontable({
      startRows: 5,
      startCols: 5
    });
    selectCell(0, 0);

    hot.addHook('modifyTransformStart', function(coords) {
      coords.col += 1;
      coords.row += 1;
    });
    keyDown('arrow_down');

    expect(getSelected()).toEqual([2, 1, 2, 1]);
  });

  it("should change selected coords by modifying coords object via `modifyTransformEnd` hook", function(){
    var hot = handsontable({
      startRows: 5,
      startCols: 5
    });
    selectCell(0, 0);

    hot.addHook('modifyTransformEnd', function(coords) {
      coords.col += 2;
      coords.row += 1;
    });
    keyDown('shift+arrow_down');

    expect(getSelected()).toEqual([0, 0, 2, 2]);
  });

  it('should indicate is coords is out of bounds via `afterModifyTransformStart` hook', function () {
    var spy = jasmine.createSpy();

    var hot = handsontable({
      startRows: 5,
      startCols: 5
    });
    hot.addHook('afterModifyTransformStart', spy);

    selectCell(2, 0);
    keyDownUp('arrow_left');

    expect(spy.calls.mostRecent().args[1]).toBe(0);
    expect(spy.calls.mostRecent().args[2]).toBe(-1);

    spy.calls.reset();
    selectCell(2, 4);
    keyDownUp('arrow_right');

    expect(spy.calls.mostRecent().args[1]).toBe(0);
    expect(spy.calls.mostRecent().args[2]).toBe(1);

    spy.calls.reset();
    selectCell(4, 2);
    keyDownUp('arrow_down');

    expect(spy.calls.mostRecent().args[1]).toBe(1);
    expect(spy.calls.mostRecent().args[2]).toBe(0);

    spy.calls.reset();
    selectCell(0, 2);
    keyDownUp('arrow_up');

    expect(spy.calls.mostRecent().args[1]).toBe(-1);
    expect(spy.calls.mostRecent().args[2]).toBe(0);
  });

  it('should indicate is coords is out of bounds via `afterModifyTransformEnd` hook', function () {
    var spy = jasmine.createSpy();

    var hot = handsontable({
      startRows: 5,
      startCols: 5
    });
    hot.addHook('afterModifyTransformEnd', spy);

    selectCell(2, 0);
    keyDownUp('shift+arrow_left');

    expect(spy.calls.mostRecent().args[1]).toBe(0);
    expect(spy.calls.mostRecent().args[2]).toBe(-1);

    spy.calls.reset();
    selectCell(2, 4);
    keyDownUp('shift+arrow_right');

    expect(spy.calls.mostRecent().args[1]).toBe(0);
    expect(spy.calls.mostRecent().args[2]).toBe(1);

    spy.calls.reset();
    selectCell(4, 2);
    keyDownUp('shift+arrow_down');

    expect(spy.calls.mostRecent().args[1]).toBe(1);
    expect(spy.calls.mostRecent().args[2]).toBe(0);

    spy.calls.reset();
    selectCell(0, 2);
    keyDownUp('shift+arrow_up');

    expect(spy.calls.mostRecent().args[1]).toBe(-1);
    expect(spy.calls.mostRecent().args[2]).toBe(0);
  });

  it('should change selection after left mouse button on one of selected cell', function () {
    var hot = handsontable({
      startRows: 5,
      startCols: 5
    });

    var cells = $('.ht_master.handsontable td');

    cells.eq(6).simulate('mousedown');
    cells.eq(18).simulate('mouseover');
    cells.eq(18).simulate('mouseup');

    expect(hot.getSelected()).toEqual([1, 1, 3, 3]);

    cells.eq(16).simulate('mousedown');
    cells.eq(16).simulate('mouseup');

    expect(hot.getSelected()).toEqual([3, 1, 3, 1]);
  });

  it("should select the first row after corner header is clicked", function(){
    var hot = handsontable({
      startRows: 5,
      startCols: 5,
      colHeaders: true,
      rowHeaders: true
    });

    this.$container.find('thead').find('th').eq(0).simulate('mousedown');

    expect(getSelected()).toEqual([0, 0, 0, 0]);
    expect(hot.selection.selectedHeader.rows).toBe(false);
    expect(hot.selection.selectedHeader.cols).toBe(false);
    expect(hot.selection.selectedHeader.corner).toBe(true);
  });
});