diff options
-rw-r--r-- | .editorconfig | 22 | ||||
-rw-r--r-- | .gitignore | 16 | ||||
-rw-r--r-- | Cargo.toml | 20 | ||||
-rw-r--r-- | Makefile | 41 | ||||
-rw-r--r-- | README.md | 5 | ||||
-rw-r--r-- | etc/developer.Dockerfile | 8 | ||||
-rw-r--r-- | src/lib.rs | 7 | ||||
-rw-r--r-- | src/main.rs | 40 | ||||
-rwxr-xr-x | tests/stateless-journey.sh | 25 | ||||
-rw-r--r-- | tests/utilities.sh | 160 |
10 files changed, 344 insertions, 0 deletions
diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..762b67e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,22 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + +[*.sh] +indent_style = space +indent_size = 2 + +# Tab indentation (no size specified) +[Makefile] +indent_style = tab + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..25adb76 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# Created by .ignore support plugin (hsz.mobi) +### Rust template +# Generated by Cargo +# will have compiled files and executables +/target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +/.idea/ + +/callgrind.profile diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..efb8ccc --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "dua" +version = "1.0.0" +authors = ["Sebastian Thiel <byronimo@gmail.com>"] +publish = false +edition = "2018" + +[dependencies] +failure = "0.1.1" +failure-tools = "4.0.2" +structopt = "0.2.14" + +[[bin]] +name="dua" +path="src/main.rs" + +[profile.release] +panic = 'unwind' +incremental = false +overflow-checks = true diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..405efb0 --- /dev/null +++ b/Makefile @@ -0,0 +1,41 @@ +docker_image = docker_developer_environment + +help: + $(info -Targets -----------------------------------------------------------------------------) + $(info -Development Targets -----------------------------------------------------------------) + $(info lint | run lints with clippy) + $(info benchmark | just for fun, really) + $(info profile | only on linux - run callgrind and annotate it) + $(info journey-tests | run all stateless journey test) + $(info continuous-journey-tests | run all stateless journey test whenever something changes) + $(info -- Use docker for all dependencies - run make interactively from there ----------------) + $(info interactive-developer-environment-in-docker | gives you everything you need to run all targets) + +always: + +interactive-developer-environment-in-docker: + docker build -t $(docker_image) - < etc/developer.Dockerfile + docker run -v $$PWD:/volume -w /volume -it $(docker_image) + +target/debug/dua: always + cargo build + +target/release/dua: always + cargo build --release + +lint: + cargo clippy + +profile: target/release/dua + valgrind --callgrind-out-file=callgrind.profile --tool=callgrind $< >/dev/null + callgrind_annotate --auto=yes callgrind.profile + +benchmark: target/release/dua + hyperfine '$<' + +journey-tests: target/debug/dua + ./tests/stateless-journey.sh $< + +continuous-journey-tests: + watchexec $(MAKE) journey-tests + diff --git a/README.md b/README.md new file mode 100644 index 0000000..ac9bdaa --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +## Getting Started + +Find-and-replace the term `dua` with the name of your command-line application. + + diff --git a/etc/developer.Dockerfile b/etc/developer.Dockerfile new file mode 100644 index 0000000..42b52bc --- /dev/null +++ b/etc/developer.Dockerfile @@ -0,0 +1,8 @@ +from guangie88/rustfmt-clippy:nightly + +run cargo install hyperfine watchexec + +run apt-get update +run apt-get install -y valgrind + +env PATH=$PATH:/root/.cargo/bin diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..a1ed88e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,7 @@ +extern crate failure; + +use failure::Error; + +pub fn fun() -> Result<(), Error> { + unimplemented!(); +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..57406a9 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,40 @@ +extern crate failure; +extern crate failure_tools; +extern crate dua; +#[macro_use] +extern crate structopt; + +use failure::Error; +use failure_tools::ok_or_exit; +use structopt::StructOpt; + +mod options { + use std::path::PathBuf; + + #[derive(Debug, StructOpt)] + #[structopt(name = "example", about = "An example of StructOpt usage.")] + pub struct Args { + /// Activate debug mode + #[structopt(short = "d", long = "debug")] + debug: bool, + /// Set speed + #[structopt(short = "s", long = "speed", default_value = "42")] + speed: f64, + /// Input file + #[structopt(parse(from_os_str))] + input: PathBuf, + /// Output file, stdout if not present + #[structopt(parse(from_os_str))] + output: Option<PathBuf>, + } +} + +fn run() -> Result<(), Error> { + let opt = options::Args::from_args(); + println!("{:?}", opt); + dua::fun() +} + +fn main() { + ok_or_exit(run()) +} diff --git a/tests/stateless-journey.sh b/tests/stateless-journey.sh new file mode 100755 index 0000000..13b5753 --- /dev/null +++ b/tests/stateless-journey.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -eu + +exe=${1:?First argument must be the executable to test} + +root="$(cd "${0%/*}" && pwd)" +# shellcheck disable=1090 +source "$root/utilities.sh" +snapshot="$root/snapshots" + +SUCCESSFULLY=0 +WITH_FAILURE=1 + +(with "no input file" + it "fails with an error message" && { + WITH_SNAPSHOT="$snapshot/failure-missing-input-file" \ + expect_run ${WITH_FAILURE} "$exe" + } +) +(with "a valid input file" + it "produces the expected output" && { + WITH_SNAPSHOT="$snapshot/success-input-file-produces-correct-output" \ + expect_run ${SUCCESSFULLY} "$exe" <(echo this is probably not what you want) + } +) diff --git a/tests/utilities.sh b/tests/utilities.sh new file mode 100644 index 0000000..86084cf --- /dev/null +++ b/tests/utilities.sh @@ -0,0 +1,160 @@ +#!/bin/bash + +WHITE="$(tput setaf 9 2>/dev/null || echo -n '')" +YELLOW="$(tput setaf 3 2>/dev/null || echo -n '')" +GREEN="$(tput setaf 2 2>/dev/null || echo -n '')" +RED="$(tput setaf 1 2>/dev/null || echo -n '')" +OFFSET=( ) +STEP=" " + +function with_program () { + local program="${1:?}" + hash "$program" &>/dev/null || { + function expect_run () { + echo 1>&2 "${WHITE} - skipped (missing program)" + } + function expect_run_sh () { + echo 1>&2 "${WHITE} - skipped (missing program)" + } + } +} + +function title () { + echo "$WHITE-----------------------------------------------------" + echo "${GREEN}$*" + echo "$WHITE-----------------------------------------------------" +} + +function _context () { + local name="${1:?}" + shift + echo 1>&2 "${YELLOW}${OFFSET[*]:-}[$name] $*" + OFFSET+=("$STEP") +} + +function step () { + _note step "${WHITE}" "$*" +} + +function stepn () { + step "$*" $'\n' +} + +function with () { + _context with "$*" +} + +function when () { + _context when "$*" +} + +function _note () { + local name="${1:?}" + local color="${2:-}" + shift 2 + echo 1>&2 -n "${OFFSET[*]:-}${color}[$name] ${*// /}" +} + +function it () { + _note it "${GREEN}" "$*" +} + +function precondition () { + _note precondition "${WHITE}" "$*" +} + +function shortcoming () { + _note shortcoming "${RED}" "$*" +} + +function step () { + _note step "${WHITE}" "$*" +} + +function stepn () { + step "$*" $'\n' +} + +function fail () { + echo 1>&2 "${RED} $*" + exit 1 +} + +function sandbox () { + sandbox_tempdir="$(mktemp -t sandbox.XXXXXX -d)" + # shellcheck disable=2064 + trap "popd >/dev/null" EXIT + pushd "$sandbox_tempdir" >/dev/null \ + || fail "Could not change directory into temporary directory." + + local custom_init="${1:-}" + if [ -n "$custom_init" ]; then + eval "$custom_init" + fi +} + +function expect_equals () { + expect_run 0 test "${1:?}" = "${2:?}" +} + +function expect_exists () { + expect_run 0 test -e "${1:?}" +} + +function expect_run_sh () { + expect_run "${1:?}" bash -c -eu -o pipefail "${2:?}" +} + +function expect_snapshot () { + local expected=${1:?} + local actual=${2:?} + if ! [ -e "$expected" ]; then + mkdir -p "${expected%/*}" + cp -R "$actual" "$expected" + fi + expect_run 0 diff -r -N "$expected" "$actual" +} + +function expect_run () { + local expected_exit_code=$1 + shift + local output= + set +e + if [[ -n "${SNAPSHOT_FILTER-}" ]]; then + output="$("$@" 2>&1 | "$SNAPSHOT_FILTER")" + else + output="$("$@" 2>&1)" + fi + + local actual_exit_code=$? + if [[ "$actual_exit_code" == "$expected_exit_code" ]]; then + if [[ -n "${WITH_SNAPSHOT-}" ]]; then + local expected="$WITH_SNAPSHOT" + if ! [ -f "$expected" ]; then + mkdir -p "${expected%/*}" + echo -n "$output" > "$expected" || exit 1 + fi + if ! diff "$expected" <(echo -n "$output"); then + echo 1>&2 "${RED} - FAIL" + echo 1>&2 "${WHITE}\$ $*" + echo 1>&2 "Output snapshot did not match snapshot at '$expected'" + echo 1>&2 "$output" + if [ -n "${ON_ERROR:-}" ]; then + eval "$ON_ERROR" + fi + exit 1 + fi + fi + echo 1>&2 + else + echo 1>&2 "${RED} - FAIL" + echo 1>&2 "${WHITE}\$ $*" + echo 1>&2 "${RED}Expected actual status $actual_exit_code to be $expected_exit_code" + echo 1>&2 "$output" + if [ -n "${ON_ERROR:-}" ]; then + eval "$ON_ERROR" + fi + exit 1 + fi + set -e +} |