Skip to content
This repository has been archived by the owner on Oct 15, 2022. It is now read-only.

Commit

Permalink
Add integration test of lorri
Browse files Browse the repository at this point in the history
A trivial initial test uses Lorri's evaluator and direnv output
to verify environment variables are set from the shell.
  • Loading branch information
grahamc committed Apr 24, 2019
1 parent 6a04076 commit a0b421f
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 0 deletions.
6 changes: 6 additions & 0 deletions default.nix
Expand Up @@ -28,6 +28,12 @@ pkgs.rustPlatform.buildRustPackage rec {

preConfigure = ''
. ${./nix/pre-check.sh}
# Do an immediate, light-weight test to ensure logged-evaluation
# is valid, prior to doing expensive compilations.
nix-build --show-trace ./src/logged-evaluation.nix \
--arg src ./tests/direnv/basic/shell.nix \
--arg coreutils "$COREUTILS" --no-out-link
'';

# Darwin fails to build doctests with:
Expand Down
1 change: 1 addition & 0 deletions tests/direnv/.gitignore
@@ -0,0 +1 @@
.envrc
3 changes: 3 additions & 0 deletions tests/direnv/basic/bin/hello
@@ -0,0 +1,3 @@
#!/bin/sh

echo "Hello, world!"
7 changes: 7 additions & 0 deletions tests/direnv/basic/shell.nix
@@ -0,0 +1,7 @@
with import ../../../nix/bogus-nixpkgs {};
mkShell {
env = {
MARKER = "present";
PATH = ./bin;
};
}
126 changes: 126 additions & 0 deletions tests/direnv/direnvtestcase.rs
@@ -0,0 +1,126 @@
//! Implement a wrapper around setup and tear-down of Direnv-based test
//! cases.

use lorri::{
build_loop::{BuildError, BuildLoop, BuildResults},
ops::direnv,
project::Project,
roots::Roots,
};
use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::process::Command;
use tempfile::{tempdir, TempDir};

pub struct DirenvTestCase {
tempdir: TempDir,
project: Project,
build_loop: BuildLoop,
}

impl DirenvTestCase {
pub fn new(name: &str) -> DirenvTestCase {
let tempdir = tempdir().expect("tempfile::tempdir() failed us!");

let test_root = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("direnv")
.join(name);

let project =
Project::load(test_root.join("shell.nix"), tempdir.path().to_path_buf()).unwrap();

let build_loop =
BuildLoop::new(project.expression(), Roots::from_project(&project).unwrap());

DirenvTestCase {
tempdir,
project,
build_loop,
}
}

/// Execute the build loop one time
pub fn evaluate(&mut self) -> Result<BuildResults, BuildError> {
self.build_loop.once()
}

/// Run `direnv allow` and then `direnv export json`, and return
/// the environment DirEnv would produce.
pub fn get_direnv_variables(&self) -> DirenvEnv {
let shell = direnv::main(&self.project)
.unwrap()
.expect("direnv::main should return a string of shell");

File::create(self.project.project_root.join(".envrc"))
.unwrap()
.write_all(shell.as_bytes())
.unwrap();

{
let mut allow = self.direnv_cmd();
allow.arg("allow");
let result = allow.status().expect("Failed to run direnv allow");
assert!(result.success());
}

let mut env = self.direnv_cmd();
env.args(&["export", "json"]);
let result = env.output().expect("Failed to run direnv allow");
assert!(result.status.success());

serde_json::from_slice(&result.stdout).unwrap()
}

fn direnv_cmd(&self) -> Command {
let mut d = Command::new("direnv");
// From: https://github.com/direnv/direnv/blob/1423e495c54de3adafde8e26218908010c955514/test/direnv-test.bash
d.env_remove("DIRENV_BASH");
d.env_remove("DIRENV_DIR");
d.env_remove("DIRENV_MTIME");
d.env_remove("DIRENV_WATCHES");
d.env_remove("DIRENV_DIFF");
d.env("DIRENV_CONFIG", &self.tempdir.path());
d.env("XDG_CONFIG_HOME", &self.tempdir.path());
d.current_dir(&self.project.project_root);

d
}
}

/// The resulting environment Direnv after running Direnv. Note:
/// Direnv returns `{ "varname": null, "varname": "something" }`
/// so the value type is `Option<String>`. This makes `.get()`
/// operations clunky, so be prepared to check for `Some(None)` and
/// `Some(Some("val"))`.
#[derive(Deserialize)]
pub struct DirenvEnv(HashMap<String, Option<String>>);

impl DirenvEnv {
/// Get an environment value with a borrowed str in the deepest Option.
/// Makes asserts nicer, like:
///
/// assert!(env.get_env("foo"), Value("bar"));
pub fn get_env<'a, 'b>(&'a self, key: &'b str) -> DirenvValue {
match self.0.get(key) {
Some(Some(val)) => DirenvValue::Value(&val),
Some(None) => DirenvValue::Unset,
None => DirenvValue::NotSet,
}
}
}

/// Environemnt Values from Direnv
#[derive(Debug, PartialEq)]
pub enum DirenvValue<'a> {
/// This variable will not be modified.
NotSet,

/// This variable will be unset when entering direnv.
Unset,

/// This variable will be set to exactly.
Value(&'a str),
}
8 changes: 8 additions & 0 deletions tests/direnv/main.rs
@@ -0,0 +1,8 @@
extern crate lorri;
extern crate serde_json;
extern crate tempfile;
#[macro_use]
extern crate serde_derive;

mod direnvtestcase;
mod trivial;
7 changes: 7 additions & 0 deletions tests/direnv/mod.rs
@@ -0,0 +1,7 @@
extern crate lorri;
extern crate tempfile;
extern crate serde_json;
#[macro_use] extern crate serde_derive;

mod direnvtestcase;
use direnvtestcase::DirenvTestCase;
10 changes: 10 additions & 0 deletions tests/direnv/trivial.rs
@@ -0,0 +1,10 @@
use direnvtestcase::{DirenvTestCase, DirenvValue};

#[test]
fn trivial() {
let mut testcase = DirenvTestCase::new("basic");
testcase.evaluate().expect("Failed to build the first time");

let env = testcase.get_direnv_variables();
assert_eq!(env.get_env("MARKER"), DirenvValue::Value("present"));
}

0 comments on commit a0b421f

Please sign in to comment.