]> Sergey Matveev's repositories - zsh-autoenv.git/commitdiff
Merge pull request #8 from blueyed/next
authorDaniel Hahler <github@thequod.de>
Thu, 27 Nov 2014 14:52:17 +0000 (15:52 +0100)
committerDaniel Hahler <github@thequod.de>
Thu, 27 Nov 2014 14:52:17 +0000 (15:52 +0100)
Next: varstash integration, tests refactoring, s/dotenv/autoenv/ and some more

12 files changed:
Makefile
autoenv.zsh
lib/varstash [new file with mode: 0644]
tests/.zshenv
tests/_autoenv_stack.t [new file with mode: 0644]
tests/_autoenv_utils.t [new file with mode: 0644]
tests/autoenv.t
tests/cwd.t
tests/leave.t
tests/recurse-upwards.t [new file with mode: 0644]
tests/setup.sh [new file with mode: 0644]
tests/varstash.t [new file with mode: 0644]

index a769734a63e00dc2a9d5007000b860d3cdec845b..0ffe150a521014dcf2cbfc706fd7fbdbd8736800 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,17 @@
 .PHONY: itest test
 
+test:
+       ZDOTDIR="${PWD}/tests" cram --shell=zsh -v tests
+
 itest:
        ZDOTDIR="${PWD}/tests" cram -i --shell=zsh tests
 
-test:
-       ZDOTDIR="${PWD}/tests" cram --shell=zsh tests
+# Define targets for test files, with relative and abolute path.
+# Use verbose output, which is useful with Vim's 'errorformat'.
+TESTS:=$(wildcard tests/*.t)
+
+uniq = $(if $1,$(firstword $1) $(call uniq,$(filter-out $(firstword $1),$1)))
+_TESTS_REL_AND_ABS:=$(call uniq,$(abspath $(TESTS)) $(TESTS))
+$(_TESTS_REL_AND_ABS):
+       ZDOTDIR="${PWD}/tests" cram --shell=zsh -v $@
+.PHONY: $(_TESTS_REL_AND_ABS)
index 60c8d03189d44b2640648528ce6c74cd906db6e9..f770d8a3f8f203a231a7ba817f73922f5c9079d1 100644 (file)
 # Initially based on
 # https://github.com/joshuaclayton/dotfiles/blob/master/zsh_profile.d/autoenv.zsh
 
-# TODO: move this to DOTENV_*?!
-export ENV_AUTHORIZATION_FILE=$HOME/.env_auth
+export AUTOENV_ENV_FILENAME=$HOME/.env_auth
 
 # Name of file to look for when entering directories.
-: ${DOTENV_FILE_ENTER:=.env}
+: ${AUTOENV_FILE_ENTER:=.env}
 
 # Name of file to look for when leaving directories.
-# Requires DOTENV_HANDLE_LEAVE=1.
-: ${DOTENV_FILE_LEAVE:=.env.leave}
+# Requires AUTOENV_HANDLE_LEAVE=1.
+: ${AUTOENV_FILE_LEAVE:=.env.leave}
 
 # Look for .env in parent dirs?
-: ${DOTENV_LOOK_UPWARDS:=1}
+: ${AUTOENV_LOOK_UPWARDS:=1}
 
 # Handle leave events when changing away from a subtree, where an "enter"
 # event was handled?
-: ${DOTENV_HANDLE_LEAVE:=1}
+: ${AUTOENV_HANDLE_LEAVE:=1}
 
+# Enable debugging. Multiple levels are supported (max 2).
+: ${AUTOENV_DEBUG:=0}
 
-# Internal: stack of entered (and handled) directories.
-_dotenv_stack_entered=()
+# Public helper functions, which can be used from your .env files:
+#
+# Source the next .env file from parent directories.
+# This is useful if you want to use a base .env file for a directory subtree.
+autoenv_source_parent() {
+  local parent_env_file=$(_autoenv_get_file_upwards $PWD)
 
+  if [[ -n $parent_env_file ]] \
+    && _autoenv_check_authorized_env_file $parent_env_file; then
+    _autoenv_debug "Calling autoenv_source_parent: parent_env_file:$parent_env_file"
 
-_dotenv_hash_pair() {
+    local parent_env_dir=${parent_env_file:A:h}
+
+    _autoenv_stack_entered_add $parent_env_file
+
+    _autoenv_source $parent_env_file enter $parent_env_dir
+  fi
+}
+
+# Internal functions. {{{
+# Internal: stack of entered (and handled) directories. {{{
+_autoenv_stack_entered=()
+typeset -A _autoenv_stack_entered_mtime
+_autoenv_stack_entered_mtime=()
+
+# Add an entry to the stack, and remember its mtime.
+_autoenv_stack_entered_add() {
   local env_file=$1
-  env_shasum=$(shasum $env_file | cut -d' ' -f1)
-  echo "$env_file:$env_shasum"
+
+  _autoenv_debug "[stack] adding: $env_file" 2
+
+  # Remove any existing entry.
+  _autoenv_stack_entered_remove $env_file
+
+  # Append it to the stack, and remember its mtime.
+  _autoenv_stack_entered+=($env_file)
+  _autoenv_stack_entered_mtime[$env_file]=$(_autoenv_get_file_mtime $env_file)
+}
+
+_autoenv_get_file_mtime() {
+  if [[ -f $1 ]]; then
+    zstat +mtime $1
+  else
+    echo 0
+  fi
 }
 
-_dotenv_authorized_env_file() {
+# Remove an entry from the stack.
+_autoenv_stack_entered_remove() {
   local env_file=$1
-  local pair=$(_dotenv_hash_pair $env_file)
-  test -f $ENV_AUTHORIZATION_FILE \
-    && \grep -qF $pair $ENV_AUTHORIZATION_FILE
+  _autoenv_debug "[stack] removing: $env_file" 2
+  _autoenv_stack_entered[$_autoenv_stack_entered[(i)$env_file]]=()
+  _autoenv_stack_entered_mtime[$env_file]=
 }
 
-_dotenv_authorize() {
+# Is the given entry already in the stack?
+_autoenv_stack_entered_contains() {
   local env_file=$1
-  _dotenv_deauthorize $env_file
-  _dotenv_hash_pair $env_file >> $ENV_AUTHORIZATION_FILE
+  if (( ${+_autoenv_stack_entered[(r)${env_file}]} )); then
+    # Entry is in stack.
+    if [[ $_autoenv_stack_entered_mtime[$env_file] == $(_autoenv_get_file_mtime $env_file) ]]; then
+      # Entry has the expected mtime.
+      return
+    fi
+  fi
+  return 1
 }
+# }}}
 
-_dotenv_deauthorize() {
+# Internal function for debug output. {{{
+_autoenv_debug() {
+  local msg=$1
+  local level=${2:-1}
+  if [[ $AUTOENV_DEBUG -lt $level ]]; then
+    return
+  fi
+  # Load zsh color support.
+  if [[ -z $colors ]]; then
+    autoload colors
+    colors
+  fi
+  # Build $indent prefix.
+  local indent=
+  if [[ $_autoenv_debug_indent -gt 0 ]]; then
+    for i in {1..${_autoenv_debug_indent}}; do
+      indent="  $indent"
+    done
+  fi
+
+  # Split $msg by \n (not newline).
+  lines=(${(ps:\\n:)msg})
+  for line in $lines; do
+    echo -n "${fg_bold[blue]}[autoenv]${fg_no_bold[default]} " >&2
+    echo ${indent}${line} >&2
+  done
+}
+# }}}
+
+# Load zstat module, but only its builtin `zstat`.
+zmodload -F zsh/stat b:zstat
+
+
+_autoenv_hash_pair() {
+  local env_file=${1:A}
+  local env_shasum
+  if [[ -n $2 ]]; then
+    env_shasum=$2
+  else
+    env_shasum=$(shasum $env_file | cut -d' ' -f1)
+  fi
+  echo "$env_file:$env_shasum:1"
+}
+
+_autoenv_authorized_env_file() {
   local env_file=$1
-  if [[ -f $ENV_AUTHORIZATION_FILE ]]; then
-    echo $(\grep -vF $env_file $ENV_AUTHORIZATION_FILE) > $ENV_AUTHORIZATION_FILE
+  local pair=$(_autoenv_hash_pair $env_file)
+  test -f $AUTOENV_ENV_FILENAME \
+    && \grep -qF $pair $AUTOENV_ENV_FILENAME
+}
+
+_autoenv_authorize() {
+  local env_file=$1
+  _autoenv_deauthorize $env_file
+  _autoenv_hash_pair $env_file >> $AUTOENV_ENV_FILENAME
+}
+
+_autoenv_deauthorize() {
+  local env_file=$1
+  if [[ -f $AUTOENV_ENV_FILENAME ]]; then
+    echo $(\grep -vF $env_file $AUTOENV_ENV_FILENAME) > $AUTOENV_ENV_FILENAME
   fi
 }
 
 # This function can be mocked in tests
-_dotenv_read_answer() {
+_autoenv_ask_for_yes() {
   local answer
-  read -q answer
-  echo $answer
+  read answer
+  if [[ $answer == "yes" ]]; then
+    return 0
+  else
+    return 1
+  fi
 }
 
 # Args: 1: absolute path to env file (resolved symlinks).
-_dotenv_check_authorized_env_file() {
+_autoenv_check_authorized_env_file() {
   if ! [[ -f $1 ]]; then
     return 1
   fi
-  if ! _dotenv_authorized_env_file $1; then
-    echo "Attempting to load unauthorized env file: $1"
+  if ! _autoenv_authorized_env_file $1; then
+    echo "Attempting to load unauthorized env file!"
+    command ls -l $1
     echo ""
     echo "**********************************************"
     echo ""
@@ -70,78 +179,133 @@ _dotenv_check_authorized_env_file() {
     echo ""
     echo "**********************************************"
     echo ""
-    echo -n "Would you like to authorize it? [y/N] "
+    echo -n "Would you like to authorize it? (type 'yes') "
 
-    local answer=$(_dotenv_read_answer)
-    echo
-    if [[ $answer != 'y' ]]; then
+    if ! _autoenv_ask_for_yes; then
       return 1
     fi
 
-    _dotenv_authorize $1
+    _autoenv_authorize $1
   fi
   return 0
 }
 
-_dotenv_source() {
+# Get directory of this file (absolute, with resolved symlinks).
+_autoenv_source_dir=${0:A:h}
+
+_autoenv_source() {
   local env_file=$1
-  _dotenv_event=$2
-  _dotenv_cwd=$PWD
+  _autoenv_event=$2
+  local _autoenv_envfile_dir=${3:-${1:A:h}}
+
+  _autoenv_from_dir=$_autoenv_chpwd_prev_dir
+  _autoenv_to_dir=$PWD
 
-  builtin cd -q ${env_file:h}
+  # Source varstash library once.
+  if [[ -z "$functions[(I)autostash]" ]]; then
+    source $_autoenv_source_dir/lib/varstash
+    # NOTE: Varstash uses $PWD as default for varstash_dir, we might set it to
+    # ${env_file:h}.
+  fi
+
+  # Change to directory of env file, source it and cd back.
+  local new_dir=$PWD
+  builtin cd -q $_autoenv_envfile_dir
+  _autoenv_debug "== SOURCE: ${bold_color}$env_file${reset_color}\n      PWD: $PWD"
+  (( _autoenv_debug_indent++ ))
   source $env_file
-  builtin cd -q $_dotenv_cwd
+  (( _autoenv_debug_indent-- ))
+  _autoenv_debug "== END SOURCE =="
+  builtin cd -q $new_dir
+
+  # Unset vars set for enter/leave scripts.
+  # This should not get done for recursion (via autoenv_source_parent),
+  # and can be useful to have in general after autoenv was used.
+  # unset _autoenv_event _autoenv_from_dir _autoenv_to_dir
+}
+
+_autoenv_get_file_upwards() {
+  local look_from=${1:-$PWD}
+  local look_for=${2:-$AUTOENV_FILE_ENTER}
+
+  # Manually look in parent dirs. An extended Zsh glob should use Y1 for
+  # performance reasons, which is only available in zsh-5.0.5-146-g9381bb6.
+  local last
+  local parent_dir="$look_from/.."
+  while true; do
+    parent_dir=${parent_dir:A}
+    if [[ $parent_dir == $last ]]; then
+      break
+    fi
+    parent_file="${parent_dir}/${look_for}"
 
-  unset _dotenv_event _dotenv_cwd
+    if [[ -f $parent_file ]]; then
+      echo $parent_file
+      break
+    fi
+
+    last=$parent_dir
+    parent_dir="${parent_dir}/.."
+  done
 }
 
-_dotenv_chpwd_handler() {
-  local env_file="$PWD/$DOTENV_FILE_ENTER"
+
+_autoenv_chpwd_prev_dir=$PWD
+_autoenv_chpwd_handler() {
+  local env_file="$PWD/$AUTOENV_FILE_ENTER"
+
+  _autoenv_debug "Calling chpwd handler: PWD=$PWD"
 
   # Handle leave event for previously sourced env files.
-  if [[ $DOTENV_HANDLE_LEAVE == 1 ]] && (( $#_dotenv_stack_entered )); then
-    for prev_dir in ${_dotenv_stack_entered}; do
+  if [[ $AUTOENV_HANDLE_LEAVE == 1 ]] && (( $#_autoenv_stack_entered )); then
+    local prev_file prev_dir
+    for prev_file in ${_autoenv_stack_entered}; do
+      prev_dir=${prev_file:A:h}
       if ! [[ ${PWD}/ == ${prev_dir}/* ]]; then
-        local env_file_leave=$prev_dir/$DOTENV_FILE_LEAVE
-        if _dotenv_check_authorized_env_file $env_file_leave; then
-          _dotenv_source $env_file_leave leave
+        local env_file_leave=$prev_dir/$AUTOENV_FILE_LEAVE
+        if _autoenv_check_authorized_env_file $env_file_leave; then
+          _autoenv_source $env_file_leave leave $prev_dir
         fi
-        # Remove this entry from the stack.
-        _dotenv_stack_entered=(${_dotenv_stack_entered#$prev_dir})
+        _autoenv_stack_entered_remove $prev_file
       fi
     done
   fi
 
-  if ! [[ -f $env_file ]] && [[ $DOTENV_LOOK_UPWARDS == 1 ]]; then
-    # Look for files in parent dirs, using an extended Zsh glob.
-    setopt localoptions extendedglob
-    local m
-    m=((../)#${DOTENV_FILE_ENTER}(N))
-    if (( $#m )); then
-      env_file=${${m[1]}:A}
-    else
+  if ! [[ -f $env_file ]] && [[ $AUTOENV_LOOK_UPWARDS == 1 ]]; then
+    env_file=$(_autoenv_get_file_upwards $PWD)
+    if [[ -z $env_file ]]; then
+      _autoenv_chpwd_prev_dir=$PWD
       return
     fi
   fi
 
-  if ! _dotenv_check_authorized_env_file $env_file; then
+  # Load the env file only once: check if $env_file is in the stack of entered
+  # directories.
+  if _autoenv_stack_entered_contains $env_file; then
+    _autoenv_debug "Already in stack: $env_file"
+    _autoenv_chpwd_prev_dir=$PWD
     return
   fi
 
-  # Load the env file only once: check if $env_file's parent
-  # is in $_dotenv_stack_entered.
-  local env_file_dir=${env_file:A:h}
-  if (( ${+_dotenv_stack_entered[(r)${env_file_dir}]} )); then
+  if ! _autoenv_check_authorized_env_file $env_file; then
+    _autoenv_chpwd_prev_dir=$PWD
     return
   fi
 
-  _dotenv_stack_entered+=(${env_file_dir})
+  _autoenv_stack_entered_add $env_file
+
+  # Source the enter env file.
+  _autoenv_debug "Sourcing from chpwd handler: $env_file"
+  _autoenv_source $env_file enter
+
+  _autoenv_chpwd_prev_dir=$PWD
 
-  _dotenv_source $env_file enter
+  (( _autoenv_debug_indent++ ))
 }
+# }}}
 
 autoload -U add-zsh-hook
-add-zsh-hook chpwd _dotenv_chpwd_handler
+add-zsh-hook chpwd _autoenv_chpwd_handler
 
 # Look in current directory already.
-_dotenv_chpwd_handler
+_autoenv_chpwd_handler
diff --git a/lib/varstash b/lib/varstash
new file mode 100644 (file)
index 0000000..9844c32
--- /dev/null
@@ -0,0 +1,343 @@
+################################################################################
+# Stash/unstash support for per-directory variables
+#
+# Adopted for zsh-autoenv.
+#
+#   Copyright (c) 2009,2012 Dave Olszewski <cxreg@pobox.com>
+#   http://github.com/cxreg/smartcd
+#
+#   This code is released under GPL v2 and the Artistic License, and
+#   may be redistributed under the terms of either.
+#
+#
+#   This library allows you to save the current value of a given environment
+#   variable in a temporary location, so that you can modify it, and then
+#   later restore its original value.
+#
+#   Note that you will need to be in the same directory you were in when you
+#   stashed in order to successfully unstash.  This is because the temporary
+#   variable is derived from your current working directory's path.
+#
+#   Usage:
+#       stash PATH
+#       export PATH=/something/else
+#       [...]
+#       unstash PATH
+#
+#   Note that this was written for use with, and works very well with,
+#   smartcd.  See the documentation there for examples.
+#
+#   An alternate usage is `autostash' which will trigger autounstash when
+#   leaving the directory, if combined with smartcd.  This reduces the amount
+#   of explicit configuration you need to provide:
+#
+#       autostash PATH
+#       export PATH=/something/else
+#
+#   You may also do both operations on line line, leaving only the very succinct
+#
+#       autostash PATH=/something/else
+#
+#   If you attempt to stash the same value twice, a warning will be displayed
+#   and the second stash will not occur.  To make it happen anyway, pass -f
+#   as the first argument to stash.
+#
+#       $ stash FOO
+#       $ stash FOO
+#       You have already stashed FOO, please specify "-f" if you want to overwrite another stashed value
+#       $ stash -f FOO
+#       $
+#
+#   This rule is a bit different if you are assigning a value and the variable
+#   has already been stashed.  In that case, the new value will be assigned, but
+#   the stash will not be overwritten.  This allows for non-conflicting chained
+#   stash-assign rules.
+#
+################################################################################
+
+
+# Library functions, from smartcd's lib/core/arrays. {{{
+function apush() {
+    local var=$1; shift
+    eval "$var=(\${$var[@]} \"\$@\")"
+}
+
+function alen() {
+    local var=$1
+
+    if [[ -n $var ]]; then
+        eval "echo \${#$var[@]}"
+    fi
+}
+
+function afirst() {
+    setopt localoptions && setopt ksharrays
+    local var=$1
+
+    if [[ -n $var ]] && (( $(eval "echo \${#$var[@]}") >= 1 )); then
+        eval "echo \"\${$var""[0]}\""
+    fi
+}
+
+function ashift() {
+    setopt localoptions && setopt ksharrays
+    local var=$1
+
+    local _ashift_return=
+
+    if [[ -n $var ]] && (( $(eval "echo \${#$var[@]}") >= 1 )); then
+        eval "_ashift_return=\"\${$var""[0]}\""
+        eval "$var""[0]=()"
+
+        echo "$_ashift_return"
+    fi
+}
+# }}}
+
+
+function stash() {
+    if [[ $1 == "-f" ]]; then
+        local force=1; shift
+    fi
+
+    while [[ -n $1 ]]; do
+        if [[ $1 == "alias" && $2 =~ "=" ]]; then
+            shift
+            local _stashing_alias_assign=1
+            continue
+        fi
+
+        local stash_expression=$1
+        local stash_which=${stash_expression%%'='*}
+        local stash_name=$(_mangle_var $stash_which)
+
+        # Extract the value and make it double-quote safe
+        local stash_value=${stash_expression#*'='}
+        stash_value=${stash_value//\\/\\\\}
+        stash_value=${stash_value//\"/\\\"}
+        stash_value=${stash_value//\`/\\\`}
+        stash_value=${stash_value//\$/\\\$}
+
+        if [[ ( -n "$(eval echo '$__varstash_alias__'$stash_name)"    ||
+                -n "$(eval echo '$__varstash_function__'$stash_name)" ||
+                -n "$(eval echo '$__varstash_array__'$stash_name)"    ||
+                -n "$(eval echo '$__varstash_export__'$stash_name)"   ||
+                -n "$(eval echo '$__varstash_variable__'$stash_name)" ||
+                -n "$(eval echo '$__varstash_nostash__'$stash_name)" )
+                && -z $force ]]; then
+
+            if [[ -z $already_stashed && ${already_stashed-_} == "_" ]]; then
+                local already_stashed=1
+            else
+                already_stashed=1
+            fi
+
+            if [[ $stash_which == $stash_expression ]]; then
+                if [[ -z $run_from_smartcd ]]; then
+                    echo "You have already stashed $stash_which, please specify \"-f\" if you want to overwrite another stashed value"
+                fi
+
+                # Skip remaining work if we're not doing an assignment
+                shift
+                continue
+            fi
+        fi
+
+        # Handle any alias that may exist under this name
+        if [[ -z $already_stashed ]]; then
+            local alias_def="$(eval alias $stash_which 2>/dev/null)"
+            if [[ -n $alias_def ]]; then
+                alias_def=${alias_def#alias }
+                eval "__varstash_alias__$stash_name=\"$alias_def\""
+                local stashed=1
+            fi
+        fi
+        if [[ $stash_which != $stash_expression && -n $_stashing_alias_assign ]]; then
+            eval "alias $stash_which=\"$stash_value\""
+        fi
+
+        # Handle any function that may exist under this name
+        if [[ -z $already_stashed ]]; then
+            local function_def="$(declare -f $stash_which)"
+            if [[ -n $function_def ]]; then
+                # make function definition quote-safe.  because we are going to evaluate the
+                # source with "echo -e", we need to double-escape the backslashes (so 1 -> 4)
+                function_def=${function_def//\\/\\\\\\\\}
+                function_def=${function_def//\"/\\\"}
+                function_def=${function_def//\`/\\\`}
+                function_def=${function_def//\$/\\\$}
+                eval "__varstash_function__$stash_name=\"$function_def\""
+                local stashed=1
+            fi
+        fi
+
+        # Handle any variable that may exist under this name
+        local vartype="$(declare -p $stash_which 2>/dev/null)"
+        if [[ -n $vartype ]]; then
+            if [[ -n $ZSH_VERSION ]]; then
+                local pattern="^typeset"
+            else
+                local pattern="^declare"
+            fi
+            if [[ $vartype =~ $pattern" -a" ]]; then
+                # varible is an array
+                if [[ -z $already_stashed ]]; then
+                    eval "__varstash_array__$stash_name=(\"\${$stash_which""[@]}\")"
+                fi
+
+            elif [[ $vartype =~ $pattern" -x" ]]; then
+                # variable is exported
+                if [[ -z $already_stashed ]]; then
+                    eval "__varstash_export__$stash_name=\"\$$stash_which\""
+                fi
+                if [[ $stash_which != $stash_expression && -z $_stashing_alias_assign ]]; then
+                    eval "export $stash_which=\"$stash_value\""
+                fi
+            else
+                # regular variable
+                if [[ -z $already_stashed ]]; then
+                    eval "__varstash_variable__$stash_name=\"\$$stash_which\""
+                fi
+                if [[ $stash_which != $stash_expression && -z $_stashing_alias_assign ]]; then
+                    eval "$stash_which=\"$stash_value\""
+                fi
+
+            fi
+            local stashed=1
+        fi
+
+        if [[ -z $stashed ]]; then
+            # Nothing in the variable we're stashing, but make a note that we stashed so we
+            # do the right thing when unstashing.  Without this, we take no action on unstash
+
+            # Zsh bug sometimes caues
+            # (eval):1: command not found: __varstash_nostash___tmp__home_dolszewski_src_smartcd_RANDOM_VARIABLE=1
+            # fixed in zsh commit 724fd07a67f, version 4.3.14
+            if [[ -z $already_stashed ]]; then
+                eval "__varstash_nostash__$stash_name=1"
+            fi
+
+            # In the case of a previously unset variable that we're assigning too, export it
+            if [[ $stash_which != $stash_expression && -z $_stashing_alias_assign ]]; then
+                eval "export $stash_which=\"$stash_value\""
+            fi
+        fi
+
+        shift
+        unset -v _stashing_alias_assign
+    done
+}
+
+function autostash() {
+    local run_from_autostash=1
+    while [[ -n $1 ]]; do
+        if [[ $1 == "alias" && $2 =~ "=" ]]; then
+            shift
+            local _stashing_alias_assign=1
+        fi
+
+        local already_stashed=
+        stash "$1"
+        if [[ -z $already_stashed ]]; then
+            local autostash_name=$(_mangle_var AUTOSTASH)
+            local varname=${1%%'='*}
+            apush $autostash_name "$varname"
+        fi
+        shift
+        unset -v _stashing_alias_assign
+    done
+}
+
+function unstash() {
+    while [[ -n $1 ]]; do
+        local unstash_which=$1
+        if [[ -z $unstash_which ]]; then
+            continue
+        fi
+
+        local unstash_name=$(_mangle_var $unstash_which)
+
+        # This bit is a little tricky.  Here are the rules:
+        #   1) unstash any alias, function, or variable which matches
+        #   2) if one or more matches, but not all, delete any that did not
+        #   3) if none match but nostash is found, delete all
+        #   4) if none match and nostash not found, do nothing
+
+        # Unstash any alias
+        if [[ -n "$(eval echo \$__varstash_alias__$unstash_name)" ]]; then
+            eval "alias $(eval echo \$__varstash_alias__$unstash_name)"
+            unset __varstash_alias__$unstash_name
+            local unstashed=1
+            local unstashed_alias=1
+        fi
+
+        # Unstash any function
+        if [[ -n "$(eval echo \$__varstash_function__$unstash_name)" ]]; then
+            eval "function $(eval echo -e \"\$__varstash_function__$unstash_name\")"
+            unset __varstash_function__$unstash_name
+            local unstashed=1
+            local unstashed_function=1
+        fi
+
+        # Unstash any variable
+        if [[ -n "$(declare -p __varstash_array__$unstash_name 2>/dev/null)" ]]; then
+            eval "$unstash_which=(\"\${__varstash_array__$unstash_name""[@]}\")"
+            unset __varstash_array__$unstash_name
+            local unstashed=1
+            local unstashed_variable=1
+        elif [[ -n "$(declare -p __varstash_export__$unstash_name 2>/dev/null)" ]]; then
+            eval "export $unstash_which=\"\$__varstash_export__$unstash_name\""
+            unset __varstash_export__$unstash_name
+            local unstashed=1
+            local unstashed_variable=1
+        elif [[ -n "$(declare -p __varstash_variable__$unstash_name 2>/dev/null)" ]]; then
+            # Unset variable first to reset export
+            unset -v $unstash_which
+            eval "$unstash_which=\"\$__varstash_variable__$unstash_name\""
+            unset __varstash_variable__$unstash_name
+            local unstashed=1
+            local unstashed_variable=1
+        fi
+
+        # Unset any values which did not exist at time of stash
+        local nostash="$(eval echo \$__varstash_nostash__$unstash_name)"
+        unset __varstash_nostash__$unstash_name
+        if [[ ( -n "$nostash" && -z "$unstashed" ) || ( -n "$unstashed" && -z "$unstashed_alias" ) ]]; then
+            unalias $unstash_which 2>/dev/null
+        fi
+        if [[ ( -n "$nostash" && -z "$unstashed" ) || ( -n "$unstashed" && -z "$unstashed_function" ) ]]; then
+            unset -f $unstash_which 2>/dev/null
+        fi
+        if [[ ( -n "$nostash" && -z "$unstashed" ) || ( -n "$unstashed" && -z "$unstashed_variable" ) ]]; then
+            # Don't try to unset illegal variable names
+            if ! [[ $unstash_which =~ [^a-zA-Z0-9_] || $unstash_which =~ ^[0-9] ]]; then
+                unset -v $unstash_which
+            fi
+        fi
+
+        shift
+    done
+}
+
+function autounstash() {
+    # If there is anything in (mangled) variable AUTOSTASH, then unstash it
+    local autounstash_name=$(_mangle_var AUTOSTASH)
+    if (( $(alen $autounstash_name) > 0 )); then
+        local run_from_autounstash=1
+        while (( $(alen $autounstash_name) > 0 )); do
+            local autounstash_var=$(afirst $autounstash_name)
+            ashift $autounstash_name >/dev/null
+            unstash $autounstash_var
+        done
+        unset $autounstash_name
+    fi
+}
+
+function _mangle_var() {
+    local mangle_var_where="${varstash_dir:-$PWD}"
+    mangle_var_where=${mangle_var_where//[^A-Za-z0-9]/_}
+    local mangled_name=${1//[^A-Za-z0-9]/_}
+    echo "_tmp_${mangle_var_where}_${mangled_name}"
+}
+
+# vim: filetype=sh autoindent expandtab shiftwidth=4 softtabstop=4
index 1c0a9d2492821f9b8d2e2c06e42b3600127ee71b..2311e2addf7cf46882409d6348b7c341eb116081 100644 (file)
@@ -1,4 +1,6 @@
 test -f "$TESTDIR/.zcompdump" && rm "$TESTDIR/.zcompdump"
 
+AUTOENV_DEBUG=0
+
 source "$TESTDIR/../autoenv.plugin.zsh"
-export ENV_AUTHORIZATION_FILE="$PWD/.env_auth"
+export AUTOENV_ENV_FILENAME="$PWD/.env_auth"
diff --git a/tests/_autoenv_stack.t b/tests/_autoenv_stack.t
new file mode 100644 (file)
index 0000000..6f659c6
--- /dev/null
@@ -0,0 +1,58 @@
+Tests for internal stack handling.
+
+  $ source $TESTDIR/setup.sh
+
+Non-existing entries are allowed and handled without error.
+
+  $ _autoenv_stack_entered_add non-existing
+  $ echo $_autoenv_stack_entered
+  non-existing
+
+Add existing entries.
+
+  $ mkdir -p sub/sub2
+  $ touch -t 201401010101 sub/file
+  $ _autoenv_stack_entered_add sub
+  $ _autoenv_stack_entered_add sub/file
+  $ _autoenv_stack_entered_add sub/sub2
+  $ echo $_autoenv_stack_entered
+  non-existing sub sub/file sub/sub2
+
+  $ _autoenv_stack_entered_add non-existing
+  $ echo $_autoenv_stack_entered
+  sub sub/file sub/sub2 non-existing
+
+  $ echo ${(k)_autoenv_stack_entered}
+  sub sub/file sub/sub2 non-existing
+
+  $ echo $_autoenv_stack_entered_mtime
+  0 1388538060 0 0 (glob)
+
+Touch the file and re-add it.
+
+  $ touch -t 201401012359 sub/file
+  $ _autoenv_stack_entered_add sub/file
+
+The mtime should have been updated.
+
+  $ echo ${_autoenv_stack_entered_mtime[sub/file]}
+  1388620740
+
+It should have moved to the end of the stack.
+
+  $ echo ${(k)_autoenv_stack_entered}
+  sub sub/sub2 non-existing sub/file
+
+Test lookup of containing elements.
+
+  $ _autoenv_stack_entered_contains sub/file
+  $ _autoenv_stack_entered_contains non-existing
+  $ _autoenv_stack_entered_contains not-added
+  [1]
+
+Test removing.
+
+  $ _autoenv_stack_entered_remove sub
+  $ echo ${_autoenv_stack_entered}
+  sub/sub2 non-existing sub/file
+
diff --git a/tests/_autoenv_utils.t b/tests/_autoenv_utils.t
new file mode 100644 (file)
index 0000000..79c4bf1
--- /dev/null
@@ -0,0 +1,15 @@
+Tests for internal util methods.
+
+  $ source $TESTDIR/setup.sh
+
+Non-existing entries are allowed and handled without error.
+
+  $ mkdir -p sub/sub2
+  $ touch file sub/file sub/sub2/file
+
+Should not get the file from the current dir.
+  $ _autoenv_get_file_upwards . file
+
+  $ cd sub/sub2
+  $ _autoenv_get_file_upwards . file
+  */_autoenv_utils.t/sub/file (glob)
index f3ede0cd0116baa3a64836d4fec02e9aec853e3f..19d0592d95f28c9f90c27b14da00db1b55455907 100644 (file)
@@ -1,6 +1,4 @@
-Ensure we have our mocked out ENV_AUTHORIZATION_FILE
-
-  $ [[ $ENV_AUTHORIZATION_FILE[0,4] == '/tmp' ]] || return 1
+  $ source $TESTDIR/setup.sh
 
 Lets set a simple .env action
 
@@ -8,17 +6,18 @@ Lets set a simple .env action
 
 Manually create auth file
 
-  $ echo "$PWD/.env:$(echo echo ENTERED | shasum)" > $ENV_AUTHORIZATION_FILE
+  $ test_autoenv_add_to_env $PWD/.env
   $ cd .
   ENTERED
 
 Now try to make it accept it
 
-  $ unset _dotenv_stack_entered
-  $ rm $ENV_AUTHORIZATION_FILE
-  $ _dotenv_read_answer() { echo 'y' }
+  $ unset _autoenv_stack_entered
+  $ rm $AUTOENV_ENV_FILENAME
+  $ _autoenv_ask_for_yes() { echo "yes" }
   $ cd .
-  Attempting to load unauthorized env file: /tmp/cramtests-??????/autoenv.t/.env (glob)
+  Attempting to load unauthorized env file!
+  -* /tmp/cramtests-*/autoenv.t/.env (glob)
   
   **********************************************
   
@@ -26,26 +25,24 @@ Now try to make it accept it
   
   **********************************************
   
-  Would you like to authorize it? [y/N] 
+  Would you like to authorize it? (type 'yes') yes
   ENTERED
 
 
+The last "ENTERED" is because it executed the command.
 
+Now lets see that it actually checks the shasum value.
 
-
-The last "ENTERED" is because it executed the command
-
-Now lets see that it actually checks the shasum value
-
-  $ unset _dotenv_stack_entered
+  $ unset _autoenv_stack_entered
   $ cd .
   ENTERED
 
-  $ unset _dotenv_stack_entered
-  $ rm $ENV_AUTHORIZATION_FILE
-  $ echo "$PWD/.env:$(echo mischief | shasum)" > $ENV_AUTHORIZATION_FILE
+  $ unset _autoenv_stack_entered
+  $ rm $AUTOENV_ENV_FILENAME
+  $ test_autoenv_add_to_env $PWD/.env mischief
   $ cd .
-  Attempting to load unauthorized env file: /tmp/cramtests-??????/autoenv.t/.env (glob)
+  Attempting to load unauthorized env file!
+  -* /tmp/cramtests-*/autoenv.t/.env (glob)
   
   **********************************************
   
@@ -53,20 +50,18 @@ Now lets see that it actually checks the shasum value
   
   **********************************************
   
-  Would you like to authorize it? [y/N] 
+  Would you like to authorize it? (type 'yes') yes
   ENTERED
 
 
-
-
-
 Now, will it take no for an answer?
 
-  $ unset _dotenv_stack_entered
-  $ rm $ENV_AUTHORIZATION_FILE
-  $ _dotenv_read_answer() { echo 'n' }
+  $ unset _autoenv_stack_entered
+  $ rm $AUTOENV_ENV_FILENAME
+  $ _autoenv_ask_for_yes() { echo "no"; return 1 }
   $ cd .
-  Attempting to load unauthorized env file: /tmp/cramtests-??????/autoenv.t/.env (glob)
+  Attempting to load unauthorized env file!
+  -* /tmp/cramtests-*/autoenv.t/.env (glob)
   
   **********************************************
   
@@ -74,16 +69,14 @@ Now, will it take no for an answer?
   
   **********************************************
   
-  Would you like to authorize it? [y/N] 
-
-
-
+  Would you like to authorize it? (type 'yes') no
 
 
-Lets also try one more time to ensure it didnt add it
+Lets also try one more time to ensure it didn't add it.
 
   $ cd .
-  Attempting to load unauthorized env file: /tmp/cramtests-??????/autoenv.t/.env (glob)
+  Attempting to load unauthorized env file!
+  -* /tmp/cramtests-*/autoenv.t/.env (glob)
   
   **********************************************
   
@@ -91,4 +84,4 @@ Lets also try one more time to ensure it didnt add it
   
   **********************************************
   
-  Would you like to authorize it? [y/N] 
+  Would you like to authorize it? (type 'yes') no
index 9764e740d73ded18c1c73ab12a637a0601b5f4ef..ea77aa0dffe702a7dc6056ef2273fcceb5fc049d 100644 (file)
@@ -1,29 +1,26 @@
-Test $PWD and $_dotenv_cwd.
+Test $PWD, $_autoenv_from_dir and _autoenv_to_dir.
 
-Ensure we have our mocked out ENV_AUTHORIZATION_FILE.
-
-  $ [[ $ENV_AUTHORIZATION_FILE[0,4] == '/tmp' ]] || return 1
+  $ source $TESTDIR/setup.sh
 
 Setup env actions / output.
 
-  $ DOTENV_LOOK_UPWARDS=1
+  $ AUTOENV_LOOK_UPWARDS=1
   $ mkdir -p sub/sub2
   $ cd sub
-  $ echo 'echo ENTERED: cwd:${PWD:t} ${_dotenv_cwd:t}' >> .env
-  $ echo 'echo LEFT: cwd:${PWD:t} ${_dotenv_cwd:t}' >> .env.leave
+  $ echo 'echo ENTERED: PWD:${PWD:t} from:${_autoenv_from_dir:t} to:${_autoenv_to_dir:t}' > .env
+  $ echo 'echo LEFT: PWD:${PWD:t} from:${_autoenv_from_dir:t} to:${_autoenv_to_dir:t}' > .env.leave
 
 Manually create auth files.
 
-  $ echo "$PWD/$DOTENV_FILE_ENTER:$(echo $(<$DOTENV_FILE_ENTER) | shasum)" > $ENV_AUTHORIZATION_FILE
-  $ echo "$PWD/$DOTENV_FILE_LEAVE:$(echo $(<$DOTENV_FILE_LEAVE) | shasum)" >> $ENV_AUTHORIZATION_FILE
+  $ test_autoenv_auth_env_files
 
 The actual tests.
 
   $ cd .
-  ENTERED: cwd:sub sub
+  ENTERED: PWD:sub from:sub to:sub
 
   $ cd ..
-  LEFT: cwd:sub cwd.t
+  LEFT: PWD:sub from:sub to:cwd.t
 
   $ cd sub/sub2
-  ENTERED: cwd:sub sub2
+  ENTERED: PWD:sub from:cwd.t to:sub2
index 770b310405ee2bad26210d5a9efc0c5c23ceaa17..2e4e36089d730aebd92e52405bb288ab0d6bb638 100644 (file)
@@ -1,7 +1,4 @@
-Ensure we have our mocked out ENV_AUTHORIZATION_FILE
-
-  $ [[ $ENV_AUTHORIZATION_FILE[0,4] == '/tmp' ]] || return 1
-
+  $ source $TESTDIR/setup.sh
 
 Lets set a simple .env action
 
@@ -12,9 +9,10 @@ Lets set a simple .env action
 
 Change to the directory.
 
-  $ _dotenv_read_answer() { echo 'y' }
+  $ _autoenv_ask_for_yes() { echo "yes"; return 0 }
   $ cd .
-  Attempting to load unauthorized env file: /tmp/cramtests-??????/leave.t/sub/.env (glob)
+  Attempting to load unauthorized env file!
+  -* /tmp/cramtests-*/leave.t/sub/.env (glob)
   
   **********************************************
   
@@ -22,15 +20,16 @@ Change to the directory.
   
   **********************************************
   
-  Would you like to authorize it? [y/N] 
+  Would you like to authorize it? (type 'yes') yes
   ENTERED
 
 
 Leave the directory and answer "no".
 
-  $ _dotenv_read_answer() { echo 'n' }
+  $ _autoenv_ask_for_yes() { echo "no"; return 1 }
   $ cd ..
-  Attempting to load unauthorized env file: /tmp/cramtests-??????/leave.t/sub/.env.leave (glob)
+  Attempting to load unauthorized env file!
+  -* /tmp/cramtests-*/leave.t/sub/.env.leave (glob)
   
   **********************************************
   
@@ -38,14 +37,15 @@ Leave the directory and answer "no".
   
   **********************************************
   
-  Would you like to authorize it? [y/N] 
+  Would you like to authorize it? (type 'yes') no
 
 
   $ cd sub
   ENTERED
-  $ _dotenv_read_answer() { echo 'y' }
+  $ _autoenv_ask_for_yes() { echo "yes"; return 0 }
   $ cd ..
-  Attempting to load unauthorized env file: /tmp/cramtests-??????/leave.t/sub/.env.leave (glob)
+  Attempting to load unauthorized env file!
+  -* /tmp/cramtests-*/leave.t/sub/.env.leave (glob)
   
   **********************************************
   
@@ -53,13 +53,13 @@ Leave the directory and answer "no".
   
   **********************************************
   
-  Would you like to authorize it? [y/N] 
+  Would you like to authorize it? (type 'yes') yes
   LEFT
 
 
 Now check with subdirs, looking upwards.
 
-  $ DOTENV_LOOK_UPWARDS=1
+  $ AUTOENV_LOOK_UPWARDS=1
   $ mkdir sub/child
   $ cd sub/child
   ENTERED
@@ -71,7 +71,7 @@ Now check with subdirs, looking upwards.
 
 Now check with subdirs, not looking at parent dirs.
 
-  $ DOTENV_LOOK_UPWARDS=0
+  $ AUTOENV_LOOK_UPWARDS=0
   $ cd sub/child
   $ cd ..
   ENTERED
@@ -80,10 +80,10 @@ Now check with subdirs, not looking at parent dirs.
   LEFT
 
 
-Test that .env is sourced only once with DOTENV_HANDLE_LEAVE=0.
+Test that .env is sourced only once with AUTOENV_HANDLE_LEAVE=0.
 
-  $ unset _dotenv_stack_entered
-  $ DOTENV_HANDLE_LEAVE=0
+  $ unset _autoenv_stack_entered
+  $ AUTOENV_HANDLE_LEAVE=0
   $ cd sub
   ENTERED
   $ cd ..
diff --git a/tests/recurse-upwards.t b/tests/recurse-upwards.t
new file mode 100644 (file)
index 0000000..f4c2780
--- /dev/null
@@ -0,0 +1,167 @@
+Test recursing into parent .env files.
+
+  $ source $TESTDIR/setup.sh
+
+Setup env actions / output.
+
+  $ AUTOENV_LOOK_UPWARDS=1
+
+Create env files in root dir.
+
+  $ echo 'echo ENTERED_root: PWD:${PWD:t} from:${_autoenv_from_dir:t} to:${_autoenv_to_dir:t}' > .env
+  $ echo 'echo LEFT_root: PWD:${PWD:t} from:${_autoenv_from_dir:t} to:${_autoenv_to_dir:t}' > .env.leave
+  $ test_autoenv_auth_env_files
+
+Create env files in sub dir.
+
+  $ mkdir -p sub/sub2
+  $ cd sub
+  ENTERED_root: PWD:recurse-upwards.t from:recurse-upwards.t to:sub
+
+  $ echo 'echo ENTERED_sub: PWD:${PWD:t} from:${_autoenv_from_dir:t} to:${_autoenv_to_dir:t}' > .env
+  $ echo 'echo LEFT_sub: PWD:${PWD:t} from:${_autoenv_from_dir:t} to:${_autoenv_to_dir:t}' > .env.leave
+  $ test_autoenv_auth_env_files
+
+The actual tests.
+
+  $ cd .
+  ENTERED_sub: PWD:sub from:sub to:sub
+
+  $ cd ..
+  LEFT_sub: PWD:sub from:sub to:recurse-upwards.t
+
+  $ cd sub/sub2
+  ENTERED_sub: PWD:sub from:recurse-upwards.t to:sub2
+
+  $ cd ..
+
+Changing the .env file should re-source it.
+
+  $ echo 'echo ENTER2' >> .env
+
+Set timestamp of auth file into the past, so it gets seen as new below.
+
+  $ touch -t 201401010101 .env
+
+  $ test_autoenv_auth_env_files
+  $ cd .
+  ENTERED_sub: PWD:sub from:sub to:sub
+  ENTER2
+
+Add sub/sub2/.env file, with a call to autoenv_source_parent.
+
+  $ echo "echo autoenv_source_parent_from_sub2:\nautoenv_source_parent\necho done_sub2\n" > sub2/.env
+  $ test_autoenv_add_to_env sub2/.env
+  $ cd sub2
+  autoenv_source_parent_from_sub2:
+  ENTERED_sub: PWD:sub from:sub to:sub2
+  ENTER2
+  done_sub2
+
+Move sub/.env away, now the root .env file should get sourced.
+
+  $ mv ../.env ../.env.out
+  $ touch -t 201401010102 .env
+  $ cd .
+  autoenv_source_parent_from_sub2:
+  ENTERED_root: PWD:recurse-upwards.t from:sub2 to:sub2
+  done_sub2
+  $ mv ../.env.out ../.env
+
+Prepend call to autoenv_source_parent to sub/.env file.
+
+  $ cd ..
+  $ sed -i -e "1s/^/echo autoenv_source_parent_from_sub:\nautoenv_source_parent\n/" .env
+  $ echo "echo done_sub" >> .env
+  $ touch -t 201401010103 .env
+  $ test_autoenv_auth_env_files
+
+  $ cd .
+  autoenv_source_parent_from_sub:
+  ENTERED_root: PWD:recurse-upwards.t from:sub to:sub
+  ENTERED_sub: PWD:sub from:sub to:sub
+  ENTER2
+  done_sub
+
+
+Add sub/sub2/.env file.
+
+  $ echo -e "echo autoenv_source_parent_from_sub2:\nautoenv_source_parent\necho done_sub2\n" > sub2/.env
+  $ test_autoenv_add_to_env sub2/.env
+  $ cd sub2
+  autoenv_source_parent_from_sub2:
+  autoenv_source_parent_from_sub:
+  ENTERED_root: PWD:recurse-upwards.t from:sub to:sub
+  ENTERED_sub: PWD:sub from:sub to:sub
+  ENTER2
+  done_sub
+  done_sub2
+
+Go to root.
+This should not trigger the enter event, because it was handled via
+autoenv_source_parent already.
+
+  $ cd ../..
+  LEFT_sub: PWD:sub from:sub2 to:recurse-upwards.t
+
+
+Changing the root .env should trigger re-authentication via autoenv_source_parent.
+
+First, let's answer "no".
+
+  $ echo "echo NEW" > .env
+  $ _autoenv_ask_for_yes() { echo "no"; return 1 }
+  $ cd sub
+  autoenv_source_parent_from_sub:
+  Attempting to load unauthorized env file!
+  -* /tmp/cramtests-*/recurse-upwards.t/.env (glob)
+  
+  **********************************************
+  
+  echo NEW
+  
+  **********************************************
+  
+  Would you like to authorize it? (type 'yes') no
+  ENTERED_sub: PWD:sub from:recurse-upwards.t to:sub
+  ENTER2
+  done_sub
+
+Now with "yes".
+This currently does not trigger re-execution of the .env file.
+
+  $ _autoenv_ask_for_yes() { echo "yes"; return 0 }
+  $ cd .
+
+Touching the .env file will now source the parent env file.
+
+  $ touch -t 201401010104 .env
+  $ cd .
+  autoenv_source_parent_from_sub:
+  Attempting to load unauthorized env file!
+  -* /tmp/cramtests-*/recurse-upwards.t/.env (glob)
+  
+  **********************************************
+  
+  echo NEW
+  
+  **********************************************
+  
+  Would you like to authorize it? (type 'yes') yes
+  NEW
+  ENTERED_sub: PWD:sub from:sub to:sub
+  ENTER2
+  done_sub
+
+
+  $ cd ..
+  LEFT_sub: PWD:sub from:sub to:recurse-upwards.t
+  $ mkdir sub/sub2/sub3
+  $ cd sub/sub2/sub3
+  autoenv_source_parent_from_sub2:
+  autoenv_source_parent_from_sub:
+  NEW
+  ENTERED_sub: PWD:sub from:recurse-upwards.t to:sub
+  ENTER2
+  done_sub
+  done_sub2
diff --git a/tests/setup.sh b/tests/setup.sh
new file mode 100644 (file)
index 0000000..90bfc51
--- /dev/null
@@ -0,0 +1,18 @@
+# Ensure we have our mocked out AUTOENV_ENV_FILENAME
+# (via .zshenv).
+
+[[ $AUTOENV_ENV_FILENAME[0,4] == '/tmp' ]] || return 1
+
+# Reset any authentication.
+echo -n > $AUTOENV_ENV_FILENAME
+
+# Add file $1 (with optional hash $2) to authentication file.
+test_autoenv_add_to_env() {
+  _autoenv_hash_pair $1 $2 >> $AUTOENV_ENV_FILENAME
+}
+
+# Add enter and leave env files to authentication file.
+test_autoenv_auth_env_files() {
+  test_autoenv_add_to_env $PWD/$AUTOENV_FILE_ENTER
+  test_autoenv_add_to_env $PWD/$AUTOENV_FILE_LEAVE
+}
diff --git a/tests/varstash.t b/tests/varstash.t
new file mode 100644 (file)
index 0000000..7458044
--- /dev/null
@@ -0,0 +1,32 @@
+Test varstash integration.
+
+  $ source $TESTDIR/setup.sh
+
+Setup test environment.
+
+  $ mkdir sub
+  $ cd sub
+  $ echo 'echo ENTER; autostash FOO=baz' > $AUTOENV_FILE_ENTER
+  $ echo 'echo LEAVE; autounstash' > $AUTOENV_FILE_LEAVE
+
+Manually create auth file
+
+  $ test_autoenv_auth_env_files
+
+Set environment variable.
+
+  $ FOO=bar
+
+Activating the env stashes it and applies a new value.
+
+  $ cd .
+  ENTER
+  $ echo $FOO
+  baz
+
+Leaving the directory unstashes it.
+
+  $ cd ..
+  LEAVE
+  $ echo $FOO
+  bar