├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE │ └── pull_request.md ├── dependabot.yml └── workflows │ ├── release.yml │ └── test.yml ├── LICENSE ├── README.md ├── _wd.sh ├── install.sh ├── test ├── shunit2 │ ├── shunit2 │ ├── shunit2_test.sh │ ├── shunit2_test_asserts.sh │ ├── shunit2_test_failures.sh │ ├── shunit2_test_helpers │ ├── shunit2_test_macros.sh │ ├── shunit2_test_misc.sh │ └── shunit2_test_standalone.sh └── tests.sh ├── tty.gif ├── wd.1 ├── wd.plugin.zsh └── wd.sh /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: You found something that doesn't work as it should 4 | title: 'Bug report: ' 5 | labels: Bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - zsh version: 28 | - wd version: 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for wd 4 | title: 'Idea: ' 5 | labels: Enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/pull_request.md: -------------------------------------------------------------------------------- 1 | **What does this PR do/change?** 2 | A nice concise description here that any user of `wd` could understand 3 | 4 | **Does this PR fix an issue?** 5 | If so, write "Will close #issue" here, so GitHub links it properly 6 | 7 | **How stable do you consider this PR?** 8 | Ready to be merged / Needs testing / I'm still working on it 9 | 10 | **Does your PR pass all tests?** 11 | If not, please post the output of running the test script, assuming you want help. 12 | The PR should be submitted as a draft 13 | 14 | If you had to change any tests, please describe the changes made to each 15 | 16 | **Do your changes need new tests to cover them, and if so, have you written the tests?** 17 | If yes, please detail what the tests do and how well they cover all casesit 18 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | description: "Version of the release" 8 | required: true 9 | type: string 10 | 11 | jobs: 12 | release: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: checkout code 16 | uses: actions/checkout@v4 17 | - name: configure git 18 | run: | 19 | git config --global user.name 'github-actions[bot]' 20 | git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com' 21 | - name: parse input 22 | run: | 23 | VERSION=$(echo "${{ inputs.version }}" | sed -E "s/^v//g") # Ex: 1.2.3 24 | echo "VERSION=$VERSION" >> $GITHUB_ENV 25 | DATE=$(date +"%Y-%m-%d") # Ex: 2022-02-22 26 | echo "DATE=$DATE" >> $GITHUB_ENV 27 | - name: bump versions 28 | run: | 29 | MANPAGE=".TH wd \"1\" \"$DATE\" \"wd $VERSION\" \"wd Manual\"" 30 | sed -i -E "1 s/.*/$MANPAGE/" wd.1 31 | sed -i -E "s/WD_VERSION=.*/WD_VERSION=$VERSION/" wd.sh 32 | - name: commit changes 33 | run: | 34 | git add wd.1 wd.sh 35 | git commit -m "chore(update): bump current version to v$VERSION" 36 | git tag "v$VERSION" 37 | git push -u origin master --tags 38 | - name: release latest 39 | run: | 40 | gh release create "v$VERSION" --generate-notes 41 | env: 42 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 43 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: checkout code 10 | uses: actions/checkout@v4 11 | - name: install zsh 12 | run: sudo apt-get install -y --no-install-recommends --no-install-suggests zsh 13 | - name: run tests 14 | run: zsh tests.sh 15 | working-directory: test 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Markus Færevaag 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wd 2 | 3 | [![Build Status](https://github.com/mfaerevaag/wd/actions/workflows/test.yml/badge.svg)](https://github.com/mfaerevaag/wd/actions) 4 | 5 | `wd` (*warp directory*) lets you jump to custom directories in zsh, without using `cd`. 6 | Why? 7 | Because `cd` seems inefficient when the folder is frequently visited or has a long path. 8 | 9 | ![Demo](https://raw.githubusercontent.com/mfaerevaag/wd/master/tty.gif) 10 | 11 | ## Setup 12 | 13 | ### [oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh) 14 | 15 | `wd` comes bundled with oh-my-zsh! 16 | 17 | Just add the plugin in your `.zshrc` file: 18 | 19 | ```zsh 20 | plugins=(... wd) 21 | ``` 22 | 23 | ### [Antigen](https://github.com/zsh-users/antigen) 24 | 25 | In your `.zshrc`: 26 | 27 | ```zsh 28 | antigen bundle mfaerevaag/wd 29 | ``` 30 | 31 | ### [Antibody](https://github.com/getantibody/antibody) 32 | 33 | In your `.zshrc`: 34 | 35 | ```zsh 36 | antibody bundle mfaerevaag/wd 37 | ``` 38 | 39 | ### [Fig](https://fig.io) 40 | 41 | Install `wd` here: [![Fig plugin store](https://fig.io/badges/install-with-fig.svg)](https://fig.io/plugins/other/wd_mfaerevaag) 42 | 43 | ### Arch ([AUR](https://aur.archlinux.org/packages/zsh-plugin-wd-git/)) 44 | 45 | 1. Install from the AUR 46 | 47 | ```zsh 48 | yay -S zsh-plugin-wd-git 49 | # or use any other AUR helper 50 | ``` 51 | 52 | 2. Then add to your `.zshrc`: 53 | 54 | ```zsh 55 | wd() { 56 | . /usr/share/wd/wd.sh 57 | } 58 | ``` 59 | 60 | ### [Home Manager](https://github.com/nix-community/home-manager) 61 | 62 | Add the following to your `home.nix` then run `home-manager switch`: 63 | 64 | ```nix 65 | programs.zsh.plugins = [ 66 | { 67 | name = "wd"; 68 | src = pkgs.fetchFromGitHub { 69 | owner = "mfaerevaag"; 70 | repo = "wd"; 71 | rev = "v0.5.2"; 72 | sha256 = "sha256-4yJ1qhqhNULbQmt6Z9G22gURfDLe30uV1ascbzqgdhg="; 73 | }; 74 | } 75 | ]; 76 | ``` 77 | 78 | ### [zplug](https://github.com/zplug/zplug) 79 | 80 | ```zsh 81 | zplug "mfaerevaag/wd", as:command, use:"wd.sh", hook-load:"wd() { . $ZPLUG_REPOS/mfaerevaag/wd/wd.sh }" 82 | ``` 83 | 84 | ### Automatic 85 | 86 | _Note: automatic install does not provide the manpage. It is also poor security practice to run remote code without first reviewing it, so you ought to look [here](https://github.com/mfaerevaag/wd/blob/master/install.sh)_ 87 | 88 | Run either command in your terminal: 89 | 90 | ```zsh 91 | curl -L https://github.com/mfaerevaag/wd/raw/master/install.sh | sh 92 | ``` 93 | 94 | or 95 | 96 | ```zsh 97 | wget --no-check-certificate https://github.com/mfaerevaag/wd/raw/master/install.sh -O - | sh 98 | ``` 99 | 100 | ### Manual 101 | 102 | 1. Clone this repository on your local machine in a sensible location (if you know what you're doing of course all of this is up to you): 103 | 104 | ```zsh 105 | git clone git@github.com:mfaerevaag/wd.git ~/.local/wd --depth 1 106 | ``` 107 | 108 | 2. Add `wd` function to `.zshrc` (or `.profile` etc.): 109 | 110 | ```zsh 111 | wd() { 112 | . ~/.local/wd/wd.sh 113 | } 114 | ``` 115 | 116 | 3. Install manpage (optional): 117 | 118 | Move manpage into an appropriate directory, then trigger `mandb` to discover it 119 | 120 | ```zsh 121 | sudo install -m 644 ~/.local/wd/wd.1 /usr/share/man/man1/wd.1 122 | sudo mandb /usr/share/man/man1 123 | ``` 124 | 125 | **Note:** when pulling and updating `wd`, you'll need to repeat step 3 should the manpage change 126 | 127 | ## Completion 128 | 129 | If you're NOT using [oh-my-zsh](https://github.com/robbyrussell/oh-my-zsh) and you want to utilize the zsh-completion feature, you will also need to add the path to your `wd` installation (`~/bin/wd` if you used the automatic installer) to your `fpath`. 130 | E.g. in your `~/.zshrc`: 131 | 132 | ```zsh 133 | fpath=(~/path/to/wd $fpath) 134 | ``` 135 | 136 | Also, you may have to force a rebuild of `zcompdump` by running: 137 | 138 | ```zsh 139 | rm -f ~/.zcompdump; compinit 140 | ``` 141 | 142 | ## Browse 143 | 144 | `wd` comes with an `fzf`-powered browse feature to fuzzy search through all your warp points. It's available through the `wd browse` command. For quick access you can set up an alias or keybind in your `.zshrc`: 145 | 146 | ```zsh 147 | # ctrl-b to open the fzf browser 148 | bindkey ${FZF_WD_BINDKEY:-'^B'} wd_browse_widget 149 | ``` 150 | 151 | ## Usage 152 | 153 | * Add warp point to current working directory: 154 | 155 | ```zsh 156 | wd add foo 157 | ``` 158 | 159 | If a warp point with the same name exists, use `wd add foo --force` to overwrite it. 160 | 161 | **Note:** a warp point cannot contain colons, or consist of only spaces and dots. 162 | The first will conflict in how `wd` stores the warp points, and the second will conflict with other features, as below. 163 | 164 | * Add warp point to any directory with default name: 165 | 166 | ```zsh 167 | wd addcd /foo/ bar 168 | ``` 169 | 170 | * Add warp point to any directory with a custom name: 171 | 172 | ```zsh 173 | wd addcd /foo/ 174 | ``` 175 | 176 | 177 | You can omit point name to automatically use the current directory's name instead. 178 | 179 | * From any directory, warp to `foo` with: 180 | 181 | ```zsh 182 | wd foo 183 | ``` 184 | 185 | * You can also warp to a directory within `foo`, with autocompletion: 186 | 187 | ```zsh 188 | wd foo some/inner/path 189 | ``` 190 | 191 | * You can warp back to previous directory and higher, with this dot syntax: 192 | 193 | ```zsh 194 | wd .. 195 | wd ... 196 | ``` 197 | 198 | This is a wrapper for the zsh's `dirs` function. 199 | _You might need to add `setopt AUTO_PUSHD` to your `.zshrc` if you are not using [oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh)._ 200 | 201 | * Remove warp point: 202 | 203 | ```zsh 204 | wd rm foo 205 | ``` 206 | 207 | You can omit point name to use the current directory's name instead. 208 | 209 | * List all warp points (stored in `~/.warprc` by default): 210 | 211 | ```zsh 212 | wd list 213 | ``` 214 | 215 | * List files in given warp point: 216 | 217 | ```zsh 218 | wd ls foo 219 | ``` 220 | 221 | * Show path of given warp point: 222 | 223 | ```zsh 224 | wd path foo 225 | ``` 226 | 227 | * List warp points to current directory, or optionally, path to given warp point: 228 | 229 | ```zsh 230 | wd show 231 | ``` 232 | 233 | * Remove warp points to non-existent directories. 234 | 235 | ```zsh 236 | wd clean 237 | ``` 238 | 239 | Use `wd clean --force` to not be prompted with confirmation. 240 | 241 | * Print usage info: 242 | 243 | ```zsh 244 | wd help 245 | ``` 246 | 247 | The usage will be printed also if you call `wd` with no command 248 | 249 | * Print the running version of `wd`: 250 | 251 | ```zsh 252 | wd --version 253 | ``` 254 | 255 | * Specifically set the config file (default being `~/.warprc`), which is useful for testing: 256 | 257 | ```zsh 258 | wd --config ./file 259 | ``` 260 | 261 | * Silence all output: 262 | 263 | ```zsh 264 | wd --quiet 265 | ``` 266 | 267 | ## Configuration 268 | 269 | You can configure `wd` with the following environment variables: 270 | 271 | ### `WD_CONFIG` 272 | 273 | Defines the path where warp points get stored. Defaults to `$HOME/.warprc`. 274 | 275 | ## Testing 276 | 277 | `wd` comes with a small test suite, run with [shunit2](https://github.com/kward/shunit2). This can be used to confirm that things are working as they should on your setup, or to demonstrate an issue. 278 | 279 | To run, simply `cd` into the `test` directory and run the `tests.sh`. 280 | 281 | ```zsh 282 | cd ./test 283 | ./tests.sh 284 | ``` 285 | 286 | ## Maintainers 287 | 288 | Following @mfaerevaag stepping away from active maintainership of this repository, the following users now are also maintainers of the repo: 289 | 290 | * @alpha-tango-kilo 291 | 292 | * @MattLewin 293 | 294 | Anyone else contributing is greatly appreciated and will be mentioned in the release notes! 295 | 296 | --- 297 | 298 | Credit to [altschuler](https://github.com/altschuler) for an awesome idea. 299 | 300 | Hope you enjoy! 301 | -------------------------------------------------------------------------------- /_wd.sh: -------------------------------------------------------------------------------- 1 | #compdef wd 2 | 3 | zstyle ':completion::complete:wd:*:descriptions' format '%B%d%b' 4 | zstyle ':completion::complete:wd:*:commands' group-name commands 5 | zstyle ':completion::complete:wd:*:warp_points' group-name warp_points 6 | zstyle ':completion::complete:wd::' list-grouped 7 | 8 | zmodload zsh/mapfile 9 | 10 | function _wd() { 11 | local WD_CONFIG=${WD_CONFIG:-$HOME/.warprc} 12 | local ret=1 13 | 14 | local -a commands 15 | local -a warp_points 16 | 17 | warp_points=( "${(f)mapfile[$WD_CONFIG]//$HOME/~}" ) 18 | 19 | typeset -A points 20 | while read -r line 21 | do 22 | arr=(${(s,:,)line}) 23 | name=${arr[1]} 24 | target_path=${arr[2]} 25 | 26 | # replace ~ from path to fix completion (#17) 27 | target_path=${target_path/#\~/$HOME} 28 | 29 | points[$name]=$target_path 30 | done < $WD_CONFIG 31 | 32 | commands=( 33 | 'add:Adds the current working directory to your warp points' 34 | 'addcd:Adds a directory to your warp points' 35 | 'add!:Overwrites existing warp point' 36 | 'export:Export warp points as static named directories' 37 | 'rm:Removes the given warp point' 38 | 'list:Outputs all stored warp points' 39 | 'ls:Show files from given warp point' 40 | 'open:Open warp point in the default file explorer' 41 | 'path:Show path to given warp point' 42 | 'show:Outputs all warp points that point to the current directory or shows a specific target directory for a point' 43 | 'help:Show this extremely helpful text' 44 | 'clean:Remove points warping to nonexistent directories' 45 | 'clean!:Remove nonexistent directories without confirmation' 46 | '..:Go back to last directory' 47 | ) 48 | 49 | _arguments -C \ 50 | '1: :->first_arg' \ 51 | '2: :->second_arg' && ret=0 52 | 53 | local target=$words[2] 54 | 55 | case $state in 56 | first_arg) 57 | _describe -t warp_points "Warp points" warp_points && ret=0 58 | _describe -t commands "Commands" commands && ret=0 59 | ;; 60 | second_arg) 61 | case $target in 62 | add\!|rm) 63 | _describe -t points "Warp points" warp_points && ret=0 64 | ;; 65 | add) 66 | _message 'Write the name of your warp point' && ret=0 67 | ;; 68 | addcd) 69 | _message 'Write the name of your path' && ret=0 70 | ;; 71 | show) 72 | _describe -t points "Warp points" warp_points && ret=0 73 | ;; 74 | ls) 75 | _describe -t points "Warp points" warp_points && ret=0 76 | ;; 77 | open) 78 | _describe -t points "Warp points" warp_points && ret=0 79 | ;; 80 | path) 81 | _describe -t points "Warp points" warp_points && ret=0 82 | ;; 83 | *) 84 | if [[ -v points[$target] ]]; then 85 | # complete sub directories from the warp point 86 | _path_files -W "(${points[$target]})" -/ && ret=0 87 | fi 88 | 89 | # don't complete anything if warp point is not valid 90 | ;; 91 | esac 92 | ;; 93 | esac 94 | 95 | return $ret 96 | } 97 | 98 | _wd "$@" 99 | 100 | # Local Variables: 101 | # mode: Shell-Script 102 | # sh-indentation: 2 103 | # indent-tabs-mode: nil 104 | # sh-basic-offset: 2 105 | # End: 106 | # vim: ft=zsh sw=2 ts=2 et 107 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | 3 | # WARP 4 | # ==== 5 | # Installation script 6 | # 7 | # @github.com/mfaerevaag/wd 8 | 9 | # variables 10 | BIN=$HOME/bin 11 | DIR=$BIN/wd 12 | REPO=https://github.com/mfaerevaag/wd.git 13 | ZSHRC=$HOME/.zshrc 14 | MANLOC=/usr/share/man/man1 15 | 16 | # make temporary log file 17 | LOG="$(mktemp -t wd_install.XXXXXXXXXX)" || exit 1 18 | exec 2> $LOG 19 | 20 | # check if already exists 21 | if [ -e $DIR ] 22 | then 23 | echo "Directory '$DIR' already exists. Backup, and try again..." 24 | exit 1 25 | fi 26 | 27 | # lazy mkdir 28 | if [ ! -d $BIN ] 29 | then 30 | echo "Making a bin directory in your home folder..." 31 | mkdir $BIN 32 | fi 33 | 34 | # clone 35 | if git clone $REPO $DIR 36 | then 37 | # add alias 38 | echo "Adding wd function to your ~/.zshrc..." 39 | echo "wd() {" >> $ZSHRC 40 | echo " . $DIR/wd.sh" >> $ZSHRC 41 | echo "}" >> $ZSHRC 42 | 43 | # TODO: we cannot process user input when piping the 44 | # script to sh, see https://github.com/mfaerevaag/wd/issues/27 45 | # install man page 46 | # while true 47 | # do 48 | # echo "Would you like to install the man page? (requires root access) (Y/n)" 49 | # read -r answer 50 | 51 | # case "$answer" in 52 | # Y|y|YES|yes|Yes ) 53 | # echo "Installing man page to ${MANLOC}/wd.1" 54 | # sudo mkdir -p ${MANLOC} 55 | # sudo cp -f ${DIR}/wd.1 ${MANLOC}/wd.1 56 | # sudo chmod 644 ${MANLOC}/wd.1 57 | # break 58 | # ;; 59 | # N|n|NO|no|No ) 60 | # echo "If you change your mind, see README for instructions" 61 | # break 62 | # ;; 63 | # * ) 64 | # echo "Please provide a valid answer (y or n)" 65 | # ;; 66 | # esac 67 | # done 68 | 69 | # remove log 70 | rm -rf $LOG 71 | 72 | # finish 73 | echo "\033[96m"' _ '"\033[m" 74 | echo "\033[96m"' | |'"\033[m" 75 | echo "\033[96m"' __ ____| |'"\033[m" 76 | echo "\033[96m"' \ \ /\ / / _` |'"\033[m" 77 | echo "\033[96m"' \ V V / (_| |'"\033[m" 78 | echo "\033[96m"' \_/\_/ \__,_|'"\033[m" 79 | echo "\033[96m"' '"\033[m" 80 | echo "\033[96m... is now installed. \033[m" 81 | echo "Remember to open new zsh to load wd." 82 | echo "For more information on usage, see README. Enjoy!" 83 | else 84 | # ops 85 | echo "\033[91m \nOps! An error occured: \033[m" 86 | cat $LOG 87 | echo "\nIt would be great of you to file an issue" 88 | echo "at GitHub so I can fix it asap. Sorry!" 89 | echo "Log file: $LOG" 90 | fi 91 | -------------------------------------------------------------------------------- /test/shunit2/shunit2: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # $Id: shunit2 335 2011-05-01 20:10:33Z kate.ward@forestent.com $ 3 | # vim:et:ft=sh:sts=2:sw=2 4 | # 5 | # Copyright 2008 Kate Ward. All Rights Reserved. 6 | # Released under the LGPL (GNU Lesser General Public License) 7 | # 8 | # shUnit2 -- Unit testing framework for Unix shell scripts. 9 | # http://code.google.com/p/shunit2/ 10 | # 11 | # Author: kate.ward@forestent.com (Kate Ward) 12 | # 13 | # shUnit2 is a xUnit based unit test framework for Bourne shell scripts. It is 14 | # based on the popular JUnit unit testing framework for Java. 15 | 16 | # return if shunit already loaded 17 | [ -n "${SHUNIT_VERSION:-}" ] && exit 0 18 | 19 | SHUNIT_VERSION='2.1.6' 20 | 21 | SHUNIT_TRUE=0 22 | SHUNIT_FALSE=1 23 | SHUNIT_ERROR=2 24 | 25 | # enable strict mode by default 26 | SHUNIT_STRICT=${SHUNIT_STRICT:-${SHUNIT_TRUE}} 27 | 28 | _shunit_warn() { echo "shunit2:WARN $@" >&2; } 29 | _shunit_error() { echo "shunit2:ERROR $@" >&2; } 30 | _shunit_fatal() { echo "shunit2:FATAL $@" >&2; exit ${SHUNIT_ERROR}; } 31 | 32 | # specific shell checks 33 | if [ -n "${ZSH_VERSION:-}" ]; then 34 | setopt |grep "^shwordsplit$" >/dev/null 35 | if [ $? -ne ${SHUNIT_TRUE} ]; then 36 | _shunit_fatal 'zsh shwordsplit option is required for proper operation' 37 | fi 38 | if [ -z "${SHUNIT_PARENT:-}" ]; then 39 | _shunit_fatal "zsh does not pass \$0 through properly. please declare \ 40 | \"SHUNIT_PARENT=\$0\" before calling shUnit2" 41 | fi 42 | fi 43 | 44 | # 45 | # constants 46 | # 47 | 48 | __SHUNIT_ASSERT_MSG_PREFIX='ASSERT:' 49 | __SHUNIT_MODE_SOURCED='sourced' 50 | __SHUNIT_MODE_STANDALONE='standalone' 51 | __SHUNIT_PARENT=${SHUNIT_PARENT:-$0} 52 | 53 | # set the constants readonly 54 | shunit_constants_=`set |grep '^__SHUNIT_' |cut -d= -f1` 55 | echo "${shunit_constants_}" |grep '^Binary file' >/dev/null && \ 56 | shunit_constants_=`set |grep -a '^__SHUNIT_' |cut -d= -f1` 57 | for shunit_constant_ in ${shunit_constants_}; do 58 | shunit_ro_opts_='' 59 | case ${ZSH_VERSION:-} in 60 | '') ;; # this isn't zsh 61 | [123].*) ;; # early versions (1.x, 2.x, 3.x) 62 | *) shunit_ro_opts_='-g' ;; # all later versions. declare readonly globally 63 | esac 64 | readonly ${shunit_ro_opts_} ${shunit_constant_} 65 | done 66 | unset shunit_constant_ shunit_constants_ shunit_ro_opts_ 67 | 68 | # variables 69 | __shunit_lineno='' # line number of executed test 70 | __shunit_mode=${__SHUNIT_MODE_SOURCED} # operating mode 71 | __shunit_reportGenerated=${SHUNIT_FALSE} # is report generated 72 | __shunit_script='' # filename of unittest script (standalone mode) 73 | __shunit_skip=${SHUNIT_FALSE} # is skipping enabled 74 | __shunit_suite='' # suite of tests to execute 75 | 76 | # counts of tests 77 | __shunit_testSuccess=${SHUNIT_TRUE} 78 | __shunit_testsTotal=0 79 | __shunit_testsPassed=0 80 | __shunit_testsFailed=0 81 | 82 | # counts of asserts 83 | __shunit_assertsTotal=0 84 | __shunit_assertsPassed=0 85 | __shunit_assertsFailed=0 86 | __shunit_assertsSkipped=0 87 | 88 | # macros 89 | _SHUNIT_LINENO_='eval __shunit_lineno=""; if [ "${1:-}" = "--lineno" ]; then [ -n "$2" ] && __shunit_lineno="[$2] "; shift 2; fi' 90 | 91 | #----------------------------------------------------------------------------- 92 | # assert functions 93 | # 94 | 95 | # Assert that two values are equal to one another. 96 | # 97 | # Args: 98 | # message: string: failure message [optional] 99 | # expected: string: expected value 100 | # actual: string: actual value 101 | # Returns: 102 | # integer: success (TRUE/FALSE/ERROR constant) 103 | assertEquals() 104 | { 105 | ${_SHUNIT_LINENO_} 106 | if [ $# -lt 2 -o $# -gt 3 ]; then 107 | _shunit_error "assertEquals() requires two or three arguments; $# given" 108 | _shunit_error "1: ${1:+$1} 2: ${2:+$2} 3: ${3:+$3}${4:+ 4: $4}" 109 | return ${SHUNIT_ERROR} 110 | fi 111 | _shunit_shouldSkip && return ${SHUNIT_TRUE} 112 | 113 | shunit_message_=${__shunit_lineno} 114 | if [ $# -eq 3 ]; then 115 | shunit_message_="${shunit_message_}$1" 116 | shift 117 | fi 118 | shunit_expected_=$1 119 | shunit_actual_=$2 120 | 121 | shunit_return=${SHUNIT_TRUE} 122 | if [ "${shunit_expected_}" = "${shunit_actual_}" ]; then 123 | _shunit_assertPass 124 | else 125 | failNotEquals "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}" 126 | shunit_return=${SHUNIT_FALSE} 127 | fi 128 | 129 | unset shunit_message_ shunit_expected_ shunit_actual_ 130 | return ${shunit_return} 131 | } 132 | _ASSERT_EQUALS_='eval assertEquals --lineno "${LINENO:-}"' 133 | 134 | # Assert that two values are not equal to one another. 135 | # 136 | # Args: 137 | # message: string: failure message [optional] 138 | # expected: string: expected value 139 | # actual: string: actual value 140 | # Returns: 141 | # integer: success (TRUE/FALSE/ERROR constant) 142 | assertNotEquals() 143 | { 144 | ${_SHUNIT_LINENO_} 145 | if [ $# -lt 2 -o $# -gt 3 ]; then 146 | _shunit_error "assertNotEquals() requires two or three arguments; $# given" 147 | return ${SHUNIT_ERROR} 148 | fi 149 | _shunit_shouldSkip && return ${SHUNIT_TRUE} 150 | 151 | shunit_message_=${__shunit_lineno} 152 | if [ $# -eq 3 ]; then 153 | shunit_message_="${shunit_message_}$1" 154 | shift 155 | fi 156 | shunit_expected_=$1 157 | shunit_actual_=$2 158 | 159 | shunit_return=${SHUNIT_TRUE} 160 | if [ "${shunit_expected_}" != "${shunit_actual_}" ]; then 161 | _shunit_assertPass 162 | else 163 | failSame "${shunit_message_}" "$@" 164 | shunit_return=${SHUNIT_FALSE} 165 | fi 166 | 167 | unset shunit_message_ shunit_expected_ shunit_actual_ 168 | return ${shunit_return} 169 | } 170 | _ASSERT_NOT_EQUALS_='eval assertNotEquals --lineno "${LINENO:-}"' 171 | 172 | # Assert that a value is null (i.e. an empty string) 173 | # 174 | # Args: 175 | # message: string: failure message [optional] 176 | # actual: string: actual value 177 | # Returns: 178 | # integer: success (TRUE/FALSE/ERROR constant) 179 | assertNull() 180 | { 181 | ${_SHUNIT_LINENO_} 182 | if [ $# -lt 1 -o $# -gt 2 ]; then 183 | _shunit_error "assertNull() requires one or two arguments; $# given" 184 | return ${SHUNIT_ERROR} 185 | fi 186 | _shunit_shouldSkip && return ${SHUNIT_TRUE} 187 | 188 | shunit_message_=${__shunit_lineno} 189 | if [ $# -eq 2 ]; then 190 | shunit_message_="${shunit_message_}$1" 191 | shift 192 | fi 193 | assertTrue "${shunit_message_}" "[ -z '$1' ]" 194 | shunit_return=$? 195 | 196 | unset shunit_message_ 197 | return ${shunit_return} 198 | } 199 | _ASSERT_NULL_='eval assertNull --lineno "${LINENO:-}"' 200 | 201 | # Assert that a value is not null (i.e. a non-empty string) 202 | # 203 | # Args: 204 | # message: string: failure message [optional] 205 | # actual: string: actual value 206 | # Returns: 207 | # integer: success (TRUE/FALSE/ERROR constant) 208 | assertNotNull() 209 | { 210 | ${_SHUNIT_LINENO_} 211 | if [ $# -gt 2 ]; then # allowing 0 arguments as $1 might actually be null 212 | _shunit_error "assertNotNull() requires one or two arguments; $# given" 213 | return ${SHUNIT_ERROR} 214 | fi 215 | _shunit_shouldSkip && return ${SHUNIT_TRUE} 216 | 217 | shunit_message_=${__shunit_lineno} 218 | if [ $# -eq 2 ]; then 219 | shunit_message_="${shunit_message_}$1" 220 | shift 221 | fi 222 | shunit_actual_=`_shunit_escapeCharactersInString "${1:-}"` 223 | test -n "${shunit_actual_}" 224 | assertTrue "${shunit_message_}" $? 225 | shunit_return=$? 226 | 227 | unset shunit_actual_ shunit_message_ 228 | return ${shunit_return} 229 | } 230 | _ASSERT_NOT_NULL_='eval assertNotNull --lineno "${LINENO:-}"' 231 | 232 | # Assert that two values are the same (i.e. equal to one another). 233 | # 234 | # Args: 235 | # message: string: failure message [optional] 236 | # expected: string: expected value 237 | # actual: string: actual value 238 | # Returns: 239 | # integer: success (TRUE/FALSE/ERROR constant) 240 | assertSame() 241 | { 242 | ${_SHUNIT_LINENO_} 243 | if [ $# -lt 2 -o $# -gt 3 ]; then 244 | _shunit_error "assertSame() requires two or three arguments; $# given" 245 | return ${SHUNIT_ERROR} 246 | fi 247 | _shunit_shouldSkip && return ${SHUNIT_TRUE} 248 | 249 | shunit_message_=${__shunit_lineno} 250 | if [ $# -eq 3 ]; then 251 | shunit_message_="${shunit_message_}$1" 252 | shift 253 | fi 254 | assertEquals "${shunit_message_}" "$1" "$2" 255 | shunit_return=$? 256 | 257 | unset shunit_message_ 258 | return ${shunit_return} 259 | } 260 | _ASSERT_SAME_='eval assertSame --lineno "${LINENO:-}"' 261 | 262 | # Assert that two values are not the same (i.e. not equal to one another). 263 | # 264 | # Args: 265 | # message: string: failure message [optional] 266 | # expected: string: expected value 267 | # actual: string: actual value 268 | # Returns: 269 | # integer: success (TRUE/FALSE/ERROR constant) 270 | assertNotSame() 271 | { 272 | ${_SHUNIT_LINENO_} 273 | if [ $# -lt 2 -o $# -gt 3 ]; then 274 | _shunit_error "assertNotSame() requires two or three arguments; $# given" 275 | return ${SHUNIT_ERROR} 276 | fi 277 | _shunit_shouldSkip && return ${SHUNIT_TRUE} 278 | 279 | shunit_message_=${__shunit_lineno} 280 | if [ $# -eq 3 ]; then 281 | shunit_message_="${shunit_message_:-}$1" 282 | shift 283 | fi 284 | assertNotEquals "${shunit_message_}" "$1" "$2" 285 | shunit_return=$? 286 | 287 | unset shunit_message_ 288 | return ${shunit_return} 289 | } 290 | _ASSERT_NOT_SAME_='eval assertNotSame --lineno "${LINENO:-}"' 291 | 292 | # Assert that a value or shell test condition is true. 293 | # 294 | # In shell, a value of 0 is true and a non-zero value is false. Any integer 295 | # value passed can thereby be tested. 296 | # 297 | # Shell supports much more complicated tests though, and a means to support 298 | # them was needed. As such, this function tests that conditions are true or 299 | # false through evaluation rather than just looking for a true or false. 300 | # 301 | # The following test will succeed: 302 | # assertTrue 0 303 | # assertTrue "[ 34 -gt 23 ]" 304 | # The folloing test will fail with a message: 305 | # assertTrue 123 306 | # assertTrue "test failed" "[ -r '/non/existant/file' ]" 307 | # 308 | # Args: 309 | # message: string: failure message [optional] 310 | # condition: string: integer value or shell conditional statement 311 | # Returns: 312 | # integer: success (TRUE/FALSE/ERROR constant) 313 | assertTrue() 314 | { 315 | ${_SHUNIT_LINENO_} 316 | if [ $# -gt 2 ]; then 317 | _shunit_error "assertTrue() takes one two arguments; $# given" 318 | return ${SHUNIT_ERROR} 319 | fi 320 | _shunit_shouldSkip && return ${SHUNIT_TRUE} 321 | 322 | shunit_message_=${__shunit_lineno} 323 | if [ $# -eq 2 ]; then 324 | shunit_message_="${shunit_message_}$1" 325 | shift 326 | fi 327 | shunit_condition_=$1 328 | 329 | # see if condition is an integer, i.e. a return value 330 | shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'` 331 | shunit_return=${SHUNIT_TRUE} 332 | if [ -z "${shunit_condition_}" ]; then 333 | # null condition 334 | shunit_return=${SHUNIT_FALSE} 335 | elif [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ] 336 | then 337 | # possible return value. treating 0 as true, and non-zero as false. 338 | [ ${shunit_condition_} -ne 0 ] && shunit_return=${SHUNIT_FALSE} 339 | else 340 | # (hopefully) a condition 341 | ( eval ${shunit_condition_} ) >/dev/null 2>&1 342 | [ $? -ne 0 ] && shunit_return=${SHUNIT_FALSE} 343 | fi 344 | 345 | # record the test 346 | if [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then 347 | _shunit_assertPass 348 | else 349 | _shunit_assertFail "${shunit_message_}" 350 | fi 351 | 352 | unset shunit_message_ shunit_condition_ shunit_match_ 353 | return ${shunit_return} 354 | } 355 | _ASSERT_TRUE_='eval assertTrue --lineno "${LINENO:-}"' 356 | 357 | # Assert that a value or shell test condition is false. 358 | # 359 | # In shell, a value of 0 is true and a non-zero value is false. Any integer 360 | # value passed can thereby be tested. 361 | # 362 | # Shell supports much more complicated tests though, and a means to support 363 | # them was needed. As such, this function tests that conditions are true or 364 | # false through evaluation rather than just looking for a true or false. 365 | # 366 | # The following test will succeed: 367 | # assertFalse 1 368 | # assertFalse "[ 'apples' = 'oranges' ]" 369 | # The folloing test will fail with a message: 370 | # assertFalse 0 371 | # assertFalse "test failed" "[ 1 -eq 1 -a 2 -eq 2 ]" 372 | # 373 | # Args: 374 | # message: string: failure message [optional] 375 | # condition: string: integer value or shell conditional statement 376 | # Returns: 377 | # integer: success (TRUE/FALSE/ERROR constant) 378 | assertFalse() 379 | { 380 | ${_SHUNIT_LINENO_} 381 | if [ $# -lt 1 -o $# -gt 2 ]; then 382 | _shunit_error "assertFalse() quires one or two arguments; $# given" 383 | return ${SHUNIT_ERROR} 384 | fi 385 | _shunit_shouldSkip && return ${SHUNIT_TRUE} 386 | 387 | shunit_message_=${__shunit_lineno} 388 | if [ $# -eq 2 ]; then 389 | shunit_message_="${shunit_message_}$1" 390 | shift 391 | fi 392 | shunit_condition_=$1 393 | 394 | # see if condition is an integer, i.e. a return value 395 | shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'` 396 | shunit_return=${SHUNIT_TRUE} 397 | if [ -z "${shunit_condition_}" ]; then 398 | # null condition 399 | shunit_return=${SHUNIT_FALSE} 400 | elif [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ] 401 | then 402 | # possible return value. treating 0 as true, and non-zero as false. 403 | [ ${shunit_condition_} -eq 0 ] && shunit_return=${SHUNIT_FALSE} 404 | else 405 | # (hopefully) a condition 406 | ( eval ${shunit_condition_} ) >/dev/null 2>&1 407 | [ $? -eq 0 ] && shunit_return=${SHUNIT_FALSE} 408 | fi 409 | 410 | # record the test 411 | if [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then 412 | _shunit_assertPass 413 | else 414 | _shunit_assertFail "${shunit_message_}" 415 | fi 416 | 417 | unset shunit_message_ shunit_condition_ shunit_match_ 418 | return ${shunit_return} 419 | } 420 | _ASSERT_FALSE_='eval assertFalse --lineno "${LINENO:-}"' 421 | 422 | #----------------------------------------------------------------------------- 423 | # failure functions 424 | # 425 | 426 | # Records a test failure. 427 | # 428 | # Args: 429 | # message: string: failure message [optional] 430 | # Returns: 431 | # integer: success (TRUE/FALSE/ERROR constant) 432 | fail() 433 | { 434 | ${_SHUNIT_LINENO_} 435 | if [ $# -gt 1 ]; then 436 | _shunit_error "fail() requires zero or one arguments; $# given" 437 | return ${SHUNIT_ERROR} 438 | fi 439 | _shunit_shouldSkip && return ${SHUNIT_TRUE} 440 | 441 | shunit_message_=${__shunit_lineno} 442 | if [ $# -eq 1 ]; then 443 | shunit_message_="${shunit_message_}$1" 444 | shift 445 | fi 446 | 447 | _shunit_assertFail "${shunit_message_}" 448 | 449 | unset shunit_message_ 450 | return ${SHUNIT_FALSE} 451 | } 452 | _FAIL_='eval fail --lineno "${LINENO:-}"' 453 | 454 | # Records a test failure, stating two values were not equal. 455 | # 456 | # Args: 457 | # message: string: failure message [optional] 458 | # expected: string: expected value 459 | # actual: string: actual value 460 | # Returns: 461 | # integer: success (TRUE/FALSE/ERROR constant) 462 | failNotEquals() 463 | { 464 | ${_SHUNIT_LINENO_} 465 | if [ $# -lt 2 -o $# -gt 3 ]; then 466 | _shunit_error "failNotEquals() requires one or two arguments; $# given" 467 | return ${SHUNIT_ERROR} 468 | fi 469 | _shunit_shouldSkip && return ${SHUNIT_TRUE} 470 | 471 | shunit_message_=${__shunit_lineno} 472 | if [ $# -eq 3 ]; then 473 | shunit_message_="${shunit_message_}$1" 474 | shift 475 | fi 476 | shunit_expected_=$1 477 | shunit_actual_=$2 478 | 479 | _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected:<${shunit_expected_}> but was:<${shunit_actual_}>" 480 | 481 | unset shunit_message_ shunit_expected_ shunit_actual_ 482 | return ${SHUNIT_FALSE} 483 | } 484 | _FAIL_NOT_EQUALS_='eval failNotEquals --lineno "${LINENO:-}"' 485 | 486 | # Records a test failure, stating two values should have been the same. 487 | # 488 | # Args: 489 | # message: string: failure message [optional] 490 | # expected: string: expected value 491 | # actual: string: actual value 492 | # Returns: 493 | # integer: success (TRUE/FALSE/ERROR constant) 494 | failSame() 495 | { 496 | ${_SHUNIT_LINENO_} 497 | if [ $# -lt 2 -o $# -gt 3 ]; then 498 | _shunit_error "failSame() requires two or three arguments; $# given" 499 | return ${SHUNIT_ERROR} 500 | fi 501 | _shunit_shouldSkip && return ${SHUNIT_TRUE} 502 | 503 | shunit_message_=${__shunit_lineno} 504 | if [ $# -eq 3 ]; then 505 | shunit_message_="${shunit_message_}$1" 506 | shift 507 | fi 508 | 509 | _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected not same" 510 | 511 | unset shunit_message_ 512 | return ${SHUNIT_FALSE} 513 | } 514 | _FAIL_SAME_='eval failSame --lineno "${LINENO:-}"' 515 | 516 | # Records a test failure, stating two values were not equal. 517 | # 518 | # This is functionally equivalent to calling failNotEquals(). 519 | # 520 | # Args: 521 | # message: string: failure message [optional] 522 | # expected: string: expected value 523 | # actual: string: actual value 524 | # Returns: 525 | # integer: success (TRUE/FALSE/ERROR constant) 526 | failNotSame() 527 | { 528 | ${_SHUNIT_LINENO_} 529 | if [ $# -lt 2 -o $# -gt 3 ]; then 530 | _shunit_error "failNotEquals() requires one or two arguments; $# given" 531 | return ${SHUNIT_ERROR} 532 | fi 533 | _shunit_shouldSkip && return ${SHUNIT_TRUE} 534 | 535 | shunit_message_=${__shunit_lineno} 536 | if [ $# -eq 3 ]; then 537 | shunit_message_="${shunit_message_}$1" 538 | shift 539 | fi 540 | failNotEquals "${shunit_message_}" "$1" "$2" 541 | shunit_return=$? 542 | 543 | unset shunit_message_ 544 | return ${shunit_return} 545 | } 546 | _FAIL_NOT_SAME_='eval failNotSame --lineno "${LINENO:-}"' 547 | 548 | #----------------------------------------------------------------------------- 549 | # skipping functions 550 | # 551 | 552 | # Force remaining assert and fail functions to be "skipped". 553 | # 554 | # This function forces the remaining assert and fail functions to be "skipped", 555 | # i.e. they will have no effect. Each function skipped will be recorded so that 556 | # the total of asserts and fails will not be altered. 557 | # 558 | # Args: 559 | # None 560 | startSkipping() 561 | { 562 | __shunit_skip=${SHUNIT_TRUE} 563 | } 564 | 565 | # Resume the normal recording behavior of assert and fail calls. 566 | # 567 | # Args: 568 | # None 569 | endSkipping() 570 | { 571 | __shunit_skip=${SHUNIT_FALSE} 572 | } 573 | 574 | # Returns the state of assert and fail call skipping. 575 | # 576 | # Args: 577 | # None 578 | # Returns: 579 | # boolean: (TRUE/FALSE constant) 580 | isSkipping() 581 | { 582 | return ${__shunit_skip} 583 | } 584 | 585 | #----------------------------------------------------------------------------- 586 | # suite functions 587 | # 588 | 589 | # Stub. This function should contains all unit test calls to be made. 590 | # 591 | # DEPRECATED (as of 2.1.0) 592 | # 593 | # This function can be optionally overridden by the user in their test suite. 594 | # 595 | # If this function exists, it will be called when shunit2 is sourced. If it 596 | # does not exist, shunit2 will search the parent script for all functions 597 | # beginning with the word 'test', and they will be added dynamically to the 598 | # test suite. 599 | # 600 | # This function should be overridden by the user in their unit test suite. 601 | # Note: see _shunit_mktempFunc() for actual implementation 602 | # 603 | # Args: 604 | # None 605 | #suite() { :; } # DO NOT UNCOMMENT THIS FUNCTION 606 | 607 | # Adds a function name to the list of tests schedule for execution. 608 | # 609 | # This function should only be called from within the suite() function. 610 | # 611 | # Args: 612 | # function: string: name of a function to add to current unit test suite 613 | suite_addTest() 614 | { 615 | shunit_func_=${1:-} 616 | 617 | __shunit_suite="${__shunit_suite:+${__shunit_suite} }${shunit_func_}" 618 | __shunit_testsTotal=`expr ${__shunit_testsTotal} + 1` 619 | 620 | unset shunit_func_ 621 | } 622 | 623 | # Stub. This function will be called once before any tests are run. 624 | # 625 | # Common one-time environment preparation tasks shared by all tests can be 626 | # defined here. 627 | # 628 | # This function should be overridden by the user in their unit test suite. 629 | # Note: see _shunit_mktempFunc() for actual implementation 630 | # 631 | # Args: 632 | # None 633 | #oneTimeSetUp() { :; } # DO NOT UNCOMMENT THIS FUNCTION 634 | 635 | # Stub. This function will be called once after all tests are finished. 636 | # 637 | # Common one-time environment cleanup tasks shared by all tests can be defined 638 | # here. 639 | # 640 | # This function should be overridden by the user in their unit test suite. 641 | # Note: see _shunit_mktempFunc() for actual implementation 642 | # 643 | # Args: 644 | # None 645 | #oneTimeTearDown() { :; } # DO NOT UNCOMMENT THIS FUNCTION 646 | 647 | # Stub. This function will be called before each test is run. 648 | # 649 | # Common environment preparation tasks shared by all tests can be defined here. 650 | # 651 | # This function should be overridden by the user in their unit test suite. 652 | # Note: see _shunit_mktempFunc() for actual implementation 653 | # 654 | # Args: 655 | # None 656 | #setUp() { :; } 657 | 658 | # Note: see _shunit_mktempFunc() for actual implementation 659 | # Stub. This function will be called after each test is run. 660 | # 661 | # Common environment cleanup tasks shared by all tests can be defined here. 662 | # 663 | # This function should be overridden by the user in their unit test suite. 664 | # Note: see _shunit_mktempFunc() for actual implementation 665 | # 666 | # Args: 667 | # None 668 | #tearDown() { :; } # DO NOT UNCOMMENT THIS FUNCTION 669 | 670 | #------------------------------------------------------------------------------ 671 | # internal shUnit2 functions 672 | # 673 | 674 | # Create a temporary directory to store various run-time files in. 675 | # 676 | # This function is a cross-platform temporary directory creation tool. Not all 677 | # OSes have the mktemp function, so one is included here. 678 | # 679 | # Args: 680 | # None 681 | # Outputs: 682 | # string: the temporary directory that was created 683 | _shunit_mktempDir() 684 | { 685 | # try the standard mktemp function 686 | ( exec mktemp -dqt shunit.XXXXXX 2>/dev/null ) && return 687 | 688 | # the standard mktemp didn't work. doing our own. 689 | if [ -r '/dev/urandom' -a -x '/usr/bin/od' ]; then 690 | _shunit_random_=`/usr/bin/od -vAn -N4 -tx4 "${_shunit_file_}" 719 | #! /bin/sh 720 | exit ${SHUNIT_TRUE} 721 | EOF 722 | chmod +x "${_shunit_file_}" 723 | done 724 | 725 | unset _shunit_file_ 726 | } 727 | 728 | # Final cleanup function to leave things as we found them. 729 | # 730 | # Besides removing the temporary directory, this function is in charge of the 731 | # final exit code of the unit test. The exit code is based on how the script 732 | # was ended (e.g. normal exit, or via Ctrl-C). 733 | # 734 | # Args: 735 | # name: string: name of the trap called (specified when trap defined) 736 | _shunit_cleanup() 737 | { 738 | _shunit_name_=$1 739 | 740 | case ${_shunit_name_} in 741 | EXIT) _shunit_signal_=0 ;; 742 | INT) _shunit_signal_=2 ;; 743 | TERM) _shunit_signal_=15 ;; 744 | *) 745 | _shunit_warn "unrecognized trap value (${_shunit_name_})" 746 | _shunit_signal_=0 747 | ;; 748 | esac 749 | 750 | # do our work 751 | rm -fr "${__shunit_tmpDir}" 752 | 753 | # exit for all non-EXIT signals 754 | if [ ${_shunit_name_} != 'EXIT' ]; then 755 | _shunit_warn "trapped and now handling the (${_shunit_name_}) signal" 756 | # disable EXIT trap 757 | trap 0 758 | # add 128 to signal and exit 759 | exit `expr ${_shunit_signal_} + 128` 760 | elif [ ${__shunit_reportGenerated} -eq ${SHUNIT_FALSE} ] ; then 761 | _shunit_assertFail 'Unknown failure encountered running a test' 762 | _shunit_generateReport 763 | exit ${SHUNIT_ERROR} 764 | fi 765 | 766 | unset _shunit_name_ _shunit_signal_ 767 | } 768 | 769 | # The actual running of the tests happens here. 770 | # 771 | # Args: 772 | # None 773 | _shunit_execSuite() 774 | { 775 | for _shunit_test_ in ${__shunit_suite}; do 776 | __shunit_testSuccess=${SHUNIT_TRUE} 777 | 778 | # disable skipping 779 | endSkipping 780 | 781 | # execute the per-test setup function 782 | setUp 783 | 784 | # execute the test 785 | echo "${_shunit_test_}" 786 | eval ${_shunit_test_} 787 | 788 | # execute the per-test tear-down function 789 | tearDown 790 | 791 | # update stats 792 | if [ ${__shunit_testSuccess} -eq ${SHUNIT_TRUE} ]; then 793 | __shunit_testsPassed=`expr ${__shunit_testsPassed} + 1` 794 | else 795 | __shunit_testsFailed=`expr ${__shunit_testsFailed} + 1` 796 | fi 797 | done 798 | 799 | unset _shunit_test_ 800 | } 801 | 802 | # Generates the user friendly report with appropriate OK/FAILED message. 803 | # 804 | # Args: 805 | # None 806 | # Output: 807 | # string: the report of successful and failed tests, as well as totals. 808 | _shunit_generateReport() 809 | { 810 | _shunit_ok_=${SHUNIT_TRUE} 811 | 812 | # if no exit code was provided one, determine an appropriate one 813 | [ ${__shunit_testsFailed} -gt 0 \ 814 | -o ${__shunit_testSuccess} -eq ${SHUNIT_FALSE} ] \ 815 | && _shunit_ok_=${SHUNIT_FALSE} 816 | 817 | echo 818 | if [ ${__shunit_testsTotal} -eq 1 ]; then 819 | echo "Ran ${__shunit_testsTotal} test." 820 | else 821 | echo "Ran ${__shunit_testsTotal} tests." 822 | fi 823 | 824 | _shunit_failures_='' 825 | _shunit_skipped_='' 826 | [ ${__shunit_assertsFailed} -gt 0 ] \ 827 | && _shunit_failures_="failures=${__shunit_assertsFailed}" 828 | [ ${__shunit_assertsSkipped} -gt 0 ] \ 829 | && _shunit_skipped_="skipped=${__shunit_assertsSkipped}" 830 | 831 | if [ ${_shunit_ok_} -eq ${SHUNIT_TRUE} ]; then 832 | _shunit_msg_='OK' 833 | [ -n "${_shunit_skipped_}" ] \ 834 | && _shunit_msg_="${_shunit_msg_} (${_shunit_skipped_})" 835 | else 836 | _shunit_msg_="FAILED (${_shunit_failures_}" 837 | [ -n "${_shunit_skipped_}" ] \ 838 | && _shunit_msg_="${_shunit_msg_},${_shunit_skipped_}" 839 | _shunit_msg_="${_shunit_msg_})" 840 | fi 841 | 842 | echo 843 | echo ${_shunit_msg_} 844 | __shunit_reportGenerated=${SHUNIT_TRUE} 845 | 846 | unset _shunit_failures_ _shunit_msg_ _shunit_ok_ _shunit_skipped_ 847 | } 848 | 849 | # Test for whether a function should be skipped. 850 | # 851 | # Args: 852 | # None 853 | # Returns: 854 | # boolean: whether the test should be skipped (TRUE/FALSE constant) 855 | _shunit_shouldSkip() 856 | { 857 | [ ${__shunit_skip} -eq ${SHUNIT_FALSE} ] && return ${SHUNIT_FALSE} 858 | _shunit_assertSkip 859 | } 860 | 861 | # Records a successful test. 862 | # 863 | # Args: 864 | # None 865 | _shunit_assertPass() 866 | { 867 | __shunit_assertsPassed=`expr ${__shunit_assertsPassed} + 1` 868 | __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1` 869 | } 870 | 871 | # Records a test failure. 872 | # 873 | # Args: 874 | # message: string: failure message to provide user 875 | _shunit_assertFail() 876 | { 877 | _shunit_msg_=$1 878 | 879 | __shunit_testSuccess=${SHUNIT_FALSE} 880 | __shunit_assertsFailed=`expr ${__shunit_assertsFailed} + 1` 881 | __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1` 882 | echo "${__SHUNIT_ASSERT_MSG_PREFIX}${_shunit_msg_}" 883 | 884 | unset _shunit_msg_ 885 | } 886 | 887 | # Records a skipped test. 888 | # 889 | # Args: 890 | # None 891 | _shunit_assertSkip() 892 | { 893 | __shunit_assertsSkipped=`expr ${__shunit_assertsSkipped} + 1` 894 | __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1` 895 | } 896 | 897 | # Prepare a script filename for sourcing. 898 | # 899 | # Args: 900 | # script: string: path to a script to source 901 | # Returns: 902 | # string: filename prefixed with ./ (if necessary) 903 | _shunit_prepForSourcing() 904 | { 905 | _shunit_script_=$1 906 | case "${_shunit_script_}" in 907 | /*|./*) echo "${_shunit_script_}" ;; 908 | *) echo "./${_shunit_script_}" ;; 909 | esac 910 | unset _shunit_script_ 911 | } 912 | 913 | # Escape a character in a string. 914 | # 915 | # Args: 916 | # c: string: unescaped character 917 | # s: string: to escape character in 918 | # Returns: 919 | # string: with escaped character(s) 920 | _shunit_escapeCharInStr() 921 | { 922 | [ -n "$2" ] || return # no point in doing work on an empty string 923 | 924 | # Note: using shorter variable names to prevent conflicts with 925 | # _shunit_escapeCharactersInString(). 926 | _shunit_c_=$1 927 | _shunit_s_=$2 928 | 929 | 930 | # escape the character 931 | echo ''${_shunit_s_}'' |sed 's/\'${_shunit_c_}'/\\\'${_shunit_c_}'/g' 932 | 933 | unset _shunit_c_ _shunit_s_ 934 | } 935 | 936 | # Escape a character in a string. 937 | # 938 | # Args: 939 | # str: string: to escape characters in 940 | # Returns: 941 | # string: with escaped character(s) 942 | _shunit_escapeCharactersInString() 943 | { 944 | [ -n "$1" ] || return # no point in doing work on an empty string 945 | 946 | _shunit_str_=$1 947 | 948 | # Note: using longer variable names to prevent conflicts with 949 | # _shunit_escapeCharInStr(). 950 | for _shunit_char_ in '"' '$' "'" '`'; do 951 | _shunit_str_=`_shunit_escapeCharInStr "${_shunit_char_}" "${_shunit_str_}"` 952 | done 953 | 954 | echo "${_shunit_str_}" 955 | unset _shunit_char_ _shunit_str_ 956 | } 957 | 958 | # Extract list of functions to run tests against. 959 | # 960 | # Args: 961 | # script: string: name of script to extract functions from 962 | # Returns: 963 | # string: of function names 964 | _shunit_extractTestFunctions() 965 | { 966 | _shunit_script_=$1 967 | 968 | # extract the lines with test function names, strip of anything besides the 969 | # function name, and output everything on a single line. 970 | _shunit_regex_='^[ ]*(function )*test[A-Za-z0-9_]* *\(\)' 971 | egrep "${_shunit_regex_}" "${_shunit_script_}" \ 972 | |sed 's/^[^A-Za-z0-9_]*//;s/^function //;s/\([A-Za-z0-9_]*\).*/\1/g' \ 973 | |xargs 974 | 975 | unset _shunit_regex_ _shunit_script_ 976 | } 977 | 978 | #------------------------------------------------------------------------------ 979 | # main 980 | # 981 | 982 | # determine the operating mode 983 | if [ $# -eq 0 ]; then 984 | __shunit_script=${__SHUNIT_PARENT} 985 | __shunit_mode=${__SHUNIT_MODE_SOURCED} 986 | else 987 | __shunit_script=$1 988 | [ -r "${__shunit_script}" ] || \ 989 | _shunit_fatal "unable to read from ${__shunit_script}" 990 | __shunit_mode=${__SHUNIT_MODE_STANDALONE} 991 | fi 992 | 993 | # create a temporary storage location 994 | __shunit_tmpDir=`_shunit_mktempDir` 995 | 996 | # provide a public temporary directory for unit test scripts 997 | # TODO(kward): document this 998 | SHUNIT_TMPDIR="${__shunit_tmpDir}/tmp" 999 | mkdir "${SHUNIT_TMPDIR}" 1000 | 1001 | # setup traps to clean up after ourselves 1002 | trap '_shunit_cleanup EXIT' 0 1003 | trap '_shunit_cleanup INT' 2 1004 | trap '_shunit_cleanup TERM' 15 1005 | 1006 | # create phantom functions to work around issues with Cygwin 1007 | _shunit_mktempFunc 1008 | PATH="${__shunit_tmpDir}:${PATH}" 1009 | 1010 | # make sure phantom functions are executable. this will bite if /tmp (or the 1011 | # current $TMPDIR) points to a path on a partition that was mounted with the 1012 | # 'noexec' option. the noexec command was created with _shunit_mktempFunc(). 1013 | noexec 2>/dev/null || _shunit_fatal \ 1014 | 'please declare TMPDIR with path on partition with exec permission' 1015 | 1016 | # we must manually source the tests in standalone mode 1017 | if [ "${__shunit_mode}" = "${__SHUNIT_MODE_STANDALONE}" ]; then 1018 | . "`_shunit_prepForSourcing \"${__shunit_script}\"`" 1019 | fi 1020 | 1021 | # execute the oneTimeSetUp function (if it exists) 1022 | oneTimeSetUp 1023 | 1024 | # execute the suite function defined in the parent test script 1025 | # deprecated as of 2.1.0 1026 | suite 1027 | 1028 | # if no suite function was defined, dynamically build a list of functions 1029 | if [ -z "${__shunit_suite}" ]; then 1030 | shunit_funcs_=`_shunit_extractTestFunctions "${__shunit_script}"` 1031 | for shunit_func_ in ${shunit_funcs_}; do 1032 | suite_addTest ${shunit_func_} 1033 | done 1034 | fi 1035 | unset shunit_func_ shunit_funcs_ 1036 | 1037 | # execute the tests 1038 | _shunit_execSuite 1039 | 1040 | # execute the oneTimeTearDown function (if it exists) 1041 | oneTimeTearDown 1042 | 1043 | # generate the report 1044 | _shunit_generateReport 1045 | 1046 | # that's it folks 1047 | [ ${__shunit_testsFailed} -eq 0 ] 1048 | exit $? 1049 | -------------------------------------------------------------------------------- /test/shunit2/shunit2_test.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # $Id: shunit2_test.sh 322 2011-04-24 00:09:45Z kate.ward@forestent.com $ 3 | # vim:et:ft=sh:sts=2:sw=2 4 | # 5 | # Copyright 2008 Kate Ward. All Rights Reserved. 6 | # Released under the LGPL (GNU Lesser General Public License) 7 | # Author: kate.ward@forestent.com (Kate Ward) 8 | # 9 | # shUnit2 unit test suite runner. 10 | # 11 | # This script runs all the unit tests that can be found, and generates a nice 12 | # report of the tests. 13 | 14 | MY_NAME=`basename $0` 15 | MY_PATH=`dirname $0` 16 | 17 | PREFIX='shunit2_test_' 18 | SHELLS='/bin/sh /bin/bash /bin/dash /bin/ksh /bin/pdksh /bin/zsh' 19 | TESTS='' 20 | for test in ${PREFIX}[a-z]*.sh; do 21 | TESTS="${TESTS} ${test}" 22 | done 23 | 24 | # load common unit test functions 25 | . ../lib/versions 26 | . ./shunit2_test_helpers 27 | 28 | usage() 29 | { 30 | echo "usage: ${MY_NAME} [-e key=val ...] [-s shell(s)] [-t test(s)]" 31 | } 32 | 33 | env='' 34 | 35 | # process command line flags 36 | while getopts 'e:hs:t:' opt; do 37 | case ${opt} in 38 | e) # set an environment variable 39 | key=`expr "${OPTARG}" : '\([^=]*\)='` 40 | val=`expr "${OPTARG}" : '[^=]*=\(.*\)'` 41 | if [ -z "${key}" -o -z "${val}" ]; then 42 | usage 43 | exit 1 44 | fi 45 | eval "${key}='${val}'" 46 | export ${key} 47 | env="${env:+${env} }${key}" 48 | ;; 49 | h) usage; exit 0 ;; # output help 50 | s) shells=${OPTARG} ;; # list of shells to run 51 | t) tests=${OPTARG} ;; # list of tests to run 52 | *) usage; exit 1 ;; 53 | esac 54 | done 55 | shift `expr ${OPTIND} - 1` 56 | 57 | # fill shells and/or tests 58 | shells=${shells:-${SHELLS}} 59 | tests=${tests:-${TESTS}} 60 | 61 | # error checking 62 | if [ -z "${tests}" ]; then 63 | th_error 'no tests found to run; exiting' 64 | exit 1 65 | fi 66 | 67 | cat <&1; ) 123 | done 124 | done 125 | -------------------------------------------------------------------------------- /test/shunit2/shunit2_test_asserts.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # $Id: shunit2_test_asserts.sh 312 2011-03-14 22:41:29Z kate.ward@forestent.com $ 3 | # vim:et:ft=sh:sts=2:sw=2 4 | # 5 | # Copyright 2008 Kate Ward. All Rights Reserved. 6 | # Released under the LGPL (GNU Lesser General Public License) 7 | # 8 | # Author: kate.ward@forestent.com (Kate Ward) 9 | # 10 | # shUnit2 unit test for assert functions 11 | 12 | # load test helpers 13 | . ./shunit2_test_helpers 14 | 15 | #------------------------------------------------------------------------------ 16 | # suite tests 17 | # 18 | 19 | commonEqualsSame() 20 | { 21 | fn=$1 22 | 23 | ( ${fn} 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) 24 | th_assertTrueWithNoOutput 'equal' $? "${stdoutF}" "${stderrF}" 25 | 26 | ( ${fn} "${MSG}" 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) 27 | th_assertTrueWithNoOutput 'equal; with msg' $? "${stdoutF}" "${stderrF}" 28 | 29 | ( ${fn} 'abc def' 'abc def' >"${stdoutF}" 2>"${stderrF}" ) 30 | th_assertTrueWithNoOutput 'equal with spaces' $? "${stdoutF}" "${stderrF}" 31 | 32 | ( ${fn} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) 33 | th_assertFalseWithOutput 'not equal' $? "${stdoutF}" "${stderrF}" 34 | 35 | ( ${fn} '' '' >"${stdoutF}" 2>"${stderrF}" ) 36 | th_assertTrueWithNoOutput 'null values' $? "${stdoutF}" "${stderrF}" 37 | 38 | ( ${fn} arg1 >"${stdoutF}" 2>"${stderrF}" ) 39 | th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" 40 | 41 | ( ${fn} arg1 arg2 arg3 arg4 >"${stdoutF}" 2>"${stderrF}" ) 42 | th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" 43 | } 44 | 45 | commonNotEqualsSame() 46 | { 47 | fn=$1 48 | 49 | ( ${fn} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) 50 | th_assertTrueWithNoOutput 'not same' $? "${stdoutF}" "${stderrF}" 51 | 52 | ( ${fn} "${MSG}" 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) 53 | th_assertTrueWithNoOutput 'not same, with msg' $? "${stdoutF}" "${stderrF}" 54 | 55 | ( ${fn} 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) 56 | th_assertFalseWithOutput 'same' $? "${stdoutF}" "${stderrF}" 57 | 58 | ( ${fn} '' '' >"${stdoutF}" 2>"${stderrF}" ) 59 | th_assertFalseWithOutput 'null values' $? "${stdoutF}" "${stderrF}" 60 | 61 | ( ${fn} arg1 >"${stdoutF}" 2>"${stderrF}" ) 62 | th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" 63 | 64 | ( ${fn} arg1 arg2 arg3 arg4 >"${stdoutF}" 2>"${stderrF}" ) 65 | th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" 66 | } 67 | 68 | testAssertEquals() 69 | { 70 | commonEqualsSame 'assertEquals' 71 | } 72 | 73 | testAssertNotEquals() 74 | { 75 | commonNotEqualsSame 'assertNotEquals' 76 | } 77 | 78 | testAssertSame() 79 | { 80 | commonEqualsSame 'assertSame' 81 | } 82 | 83 | testAssertNotSame() 84 | { 85 | commonNotEqualsSame 'assertNotSame' 86 | } 87 | 88 | testAssertNull() 89 | { 90 | ( assertNull '' >"${stdoutF}" 2>"${stderrF}" ) 91 | th_assertTrueWithNoOutput 'null' $? "${stdoutF}" "${stderrF}" 92 | 93 | ( assertNull "${MSG}" '' >"${stdoutF}" 2>"${stderrF}" ) 94 | th_assertTrueWithNoOutput 'null, with msg' $? "${stdoutF}" "${stderrF}" 95 | 96 | ( assertNull 'x' >"${stdoutF}" 2>"${stderrF}" ) 97 | th_assertFalseWithOutput 'not null' $? "${stdoutF}" "${stderrF}" 98 | 99 | ( assertNull >"${stdoutF}" 2>"${stderrF}" ) 100 | th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" 101 | 102 | ( assertNull arg1 arg2 arg3 >"${stdoutF}" 2>"${stderrF}" ) 103 | th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" 104 | } 105 | 106 | testAssertNotNull() 107 | { 108 | ( assertNotNull 'x' >"${stdoutF}" 2>"${stderrF}" ) 109 | th_assertTrueWithNoOutput 'not null' $? "${stdoutF}" "${stderrF}" 110 | 111 | ( assertNotNull "${MSG}" 'x' >"${stdoutF}" 2>"${stderrF}" ) 112 | th_assertTrueWithNoOutput 'not null, with msg' $? "${stdoutF}" "${stderrF}" 113 | 114 | ( assertNotNull 'x"b' >"${stdoutF}" 2>"${stderrF}" ) 115 | th_assertTrueWithNoOutput 'not null, with double-quote' $? \ 116 | "${stdoutF}" "${stderrF}" 117 | 118 | ( assertNotNull "x'b" >"${stdoutF}" 2>"${stderrF}" ) 119 | th_assertTrueWithNoOutput 'not null, with single-quote' $? \ 120 | "${stdoutF}" "${stderrF}" 121 | 122 | ( assertNotNull 'x$b' >"${stdoutF}" 2>"${stderrF}" ) 123 | th_assertTrueWithNoOutput 'not null, with dollar' $? \ 124 | "${stdoutF}" "${stderrF}" 125 | 126 | ( assertNotNull 'x`b' >"${stdoutF}" 2>"${stderrF}" ) 127 | th_assertTrueWithNoOutput 'not null, with backtick' $? \ 128 | "${stdoutF}" "${stderrF}" 129 | 130 | ( assertNotNull '' >"${stdoutF}" 2>"${stderrF}" ) 131 | th_assertFalseWithOutput 'null' $? "${stdoutF}" "${stderrF}" 132 | 133 | # there is no test for too few arguments as $1 might actually be null 134 | 135 | ( assertNotNull arg1 arg2 arg3 >"${stdoutF}" 2>"${stderrF}" ) 136 | th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" 137 | } 138 | 139 | testAssertTrue() 140 | { 141 | ( assertTrue 0 >"${stdoutF}" 2>"${stderrF}" ) 142 | th_assertTrueWithNoOutput 'true' $? "${stdoutF}" "${stderrF}" 143 | 144 | ( assertTrue "${MSG}" 0 >"${stdoutF}" 2>"${stderrF}" ) 145 | th_assertTrueWithNoOutput 'true, with msg' $? "${stdoutF}" "${stderrF}" 146 | 147 | ( assertTrue '[ 0 -eq 0 ]' >"${stdoutF}" 2>"${stderrF}" ) 148 | th_assertTrueWithNoOutput 'true condition' $? "${stdoutF}" "${stderrF}" 149 | 150 | ( assertTrue 1 >"${stdoutF}" 2>"${stderrF}" ) 151 | th_assertFalseWithOutput 'false' $? "${stdoutF}" "${stderrF}" 152 | 153 | ( assertTrue '[ 0 -eq 1 ]' >"${stdoutF}" 2>"${stderrF}" ) 154 | th_assertFalseWithOutput 'false condition' $? "${stdoutF}" "${stderrF}" 155 | 156 | ( assertTrue '' >"${stdoutF}" 2>"${stderrF}" ) 157 | th_assertFalseWithOutput 'null' $? "${stdoutF}" "${stderrF}" 158 | 159 | ( assertTrue >"${stdoutF}" 2>"${stderrF}" ) 160 | th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" 161 | 162 | ( assertTrue arg1 arg2 arg3 >"${stdoutF}" 2>"${stderrF}" ) 163 | th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" 164 | } 165 | 166 | testAssertFalse() 167 | { 168 | ( assertFalse 1 >"${stdoutF}" 2>"${stderrF}" ) 169 | th_assertTrueWithNoOutput 'false' $? "${stdoutF}" "${stderrF}" 170 | 171 | ( assertFalse "${MSG}" 1 >"${stdoutF}" 2>"${stderrF}" ) 172 | th_assertTrueWithNoOutput 'false, with msg' $? "${stdoutF}" "${stderrF}" 173 | 174 | ( assertFalse '[ 0 -eq 1 ]' >"${stdoutF}" 2>"${stderrF}" ) 175 | th_assertTrueWithNoOutput 'false condition' $? "${stdoutF}" "${stderrF}" 176 | 177 | ( assertFalse 0 >"${stdoutF}" 2>"${stderrF}" ) 178 | th_assertFalseWithOutput 'true' $? "${stdoutF}" "${stderrF}" 179 | 180 | ( assertFalse '[ 0 -eq 0 ]' >"${stdoutF}" 2>"${stderrF}" ) 181 | th_assertFalseWithOutput 'true condition' $? "${stdoutF}" "${stderrF}" 182 | 183 | ( assertFalse '' >"${stdoutF}" 2>"${stderrF}" ) 184 | th_assertFalseWithOutput 'true condition' $? "${stdoutF}" "${stderrF}" 185 | 186 | ( assertFalse >"${stdoutF}" 2>"${stderrF}" ) 187 | th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" 188 | 189 | ( assertFalse arg1 arg2 arg3 >"${stdoutF}" 2>"${stderrF}" ) 190 | th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" 191 | } 192 | 193 | #------------------------------------------------------------------------------ 194 | # suite functions 195 | # 196 | 197 | oneTimeSetUp() 198 | { 199 | tmpDir="${__shunit_tmpDir}/output" 200 | mkdir "${tmpDir}" 201 | stdoutF="${tmpDir}/stdout" 202 | stderrF="${tmpDir}/stderr" 203 | 204 | MSG='This is a test message' 205 | } 206 | 207 | # load and run shUnit2 208 | [ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0 209 | . ${TH_SHUNIT} 210 | -------------------------------------------------------------------------------- /test/shunit2/shunit2_test_failures.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # $Id: shunit2_test_failures.sh 286 2008-11-24 21:42:34Z kate.ward@forestent.com $ 3 | # vim:et:ft=sh:sts=2:sw=2 4 | # 5 | # Copyright 2008 Kate Ward. All Rights Reserved. 6 | # Released under the LGPL (GNU Lesser General Public License) 7 | # 8 | # Author: kate.ward@forestent.com (Kate Ward) 9 | # 10 | # shUnit2 unit test for failure functions 11 | 12 | # load common unit-test functions 13 | . ./shunit2_test_helpers 14 | 15 | #----------------------------------------------------------------------------- 16 | # suite tests 17 | # 18 | 19 | testFail() 20 | { 21 | ( fail >"${stdoutF}" 2>"${stderrF}" ) 22 | th_assertFalseWithOutput 'fail' $? "${stdoutF}" "${stderrF}" 23 | 24 | ( fail "${MSG}" >"${stdoutF}" 2>"${stderrF}" ) 25 | th_assertFalseWithOutput 'fail with msg' $? "${stdoutF}" "${stderrF}" 26 | 27 | ( fail arg1 >"${stdoutF}" 2>"${stderrF}" ) 28 | th_assertFalseWithOutput 'too many arguments' $? "${stdoutF}" "${stderrF}" 29 | } 30 | 31 | testFailNotEquals() 32 | { 33 | ( failNotEquals 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) 34 | th_assertFalseWithOutput 'same' $? "${stdoutF}" "${stderrF}" 35 | 36 | ( failNotEquals "${MSG}" 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) 37 | th_assertFalseWithOutput 'same with msg' $? "${stdoutF}" "${stderrF}" 38 | 39 | ( failNotEquals 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) 40 | th_assertFalseWithOutput 'not same' $? "${stdoutF}" "${stderrF}" 41 | 42 | ( failNotEquals '' '' >"${stdoutF}" 2>"${stderrF}" ) 43 | th_assertFalseWithOutput 'null values' $? "${stdoutF}" "${stderrF}" 44 | 45 | ( failNotEquals >"${stdoutF}" 2>"${stderrF}" ) 46 | th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" 47 | 48 | ( failNotEquals arg1 arg2 arg3 arg4 >"${stdoutF}" 2>"${stderrF}" ) 49 | th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" 50 | } 51 | 52 | testFailSame() 53 | { 54 | ( failSame 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) 55 | th_assertFalseWithOutput 'same' $? "${stdoutF}" "${stderrF}" 56 | 57 | ( failSame "${MSG}" 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) 58 | th_assertFalseWithOutput 'same with msg' $? "${stdoutF}" "${stderrF}" 59 | 60 | ( failSame 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) 61 | th_assertFalseWithOutput 'not same' $? "${stdoutF}" "${stderrF}" 62 | 63 | ( failSame '' '' >"${stdoutF}" 2>"${stderrF}" ) 64 | th_assertFalseWithOutput 'null values' $? "${stdoutF}" "${stderrF}" 65 | 66 | ( failSame >"${stdoutF}" 2>"${stderrF}" ) 67 | th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" 68 | 69 | ( failSame arg1 arg2 arg3 arg4 >"${stdoutF}" 2>"${stderrF}" ) 70 | th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" 71 | } 72 | 73 | #----------------------------------------------------------------------------- 74 | # suite functions 75 | # 76 | 77 | oneTimeSetUp() 78 | { 79 | tmpDir="${__shunit_tmpDir}/output" 80 | mkdir "${tmpDir}" 81 | stdoutF="${tmpDir}/stdout" 82 | stderrF="${tmpDir}/stderr" 83 | 84 | MSG='This is a test message' 85 | } 86 | 87 | # load and run shUnit2 88 | [ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0 89 | . ${TH_SHUNIT} 90 | -------------------------------------------------------------------------------- /test/shunit2/shunit2_test_helpers: -------------------------------------------------------------------------------- 1 | # $Id: shunit2_test_helpers 286 2008-11-24 21:42:34Z kate.ward@forestent.com $ 2 | # vim:et:ft=sh:sts=2:sw=2 3 | # 4 | # Copyright 2008 Kate Ward. All Rights Reserved. 5 | # Released under the LGPL (GNU Lesser General Public License) 6 | # 7 | # Author: kate.ward@forestent.com (Kate Ward) 8 | # 9 | # shUnit2 unit test common functions 10 | 11 | # treat unset variables as an error when performing parameter expansion 12 | set -u 13 | 14 | # set shwordsplit for zsh 15 | [ -n "${ZSH_VERSION:-}" ] && setopt shwordsplit 16 | 17 | # 18 | # constants 19 | # 20 | 21 | # path to shUnit2 library. can be overridden by setting SHUNIT_INC 22 | TH_SHUNIT=${SHUNIT_INC:-./shunit2} 23 | 24 | # configure debugging. set the DEBUG environment variable to any 25 | # non-empty value to enable debug output, or TRACE to enable trace 26 | # output. 27 | TRACE=${TRACE:+'th_trace '} 28 | [ -n "${TRACE}" ] && DEBUG=1 29 | [ -z "${TRACE}" ] && TRACE=':' 30 | 31 | DEBUG=${DEBUG:+'th_debug '} 32 | [ -z "${DEBUG}" ] && DEBUG=':' 33 | 34 | # 35 | # variables 36 | # 37 | 38 | th_RANDOM=0 39 | 40 | # 41 | # functions 42 | # 43 | 44 | # message functions 45 | th_trace() { echo "${MY_NAME}:TRACE $@" >&2; } 46 | th_debug() { echo "${MY_NAME}:DEBUG $@" >&2; } 47 | th_info() { echo "${MY_NAME}:INFO $@" >&2; } 48 | th_warn() { echo "${MY_NAME}:WARN $@" >&2; } 49 | th_error() { echo "${MY_NAME}:ERROR $@" >&2; } 50 | th_fatal() { echo "${MY_NAME}:FATAL $@" >&2; } 51 | 52 | # output subtest name 53 | th_subtest() { echo " $@" >&2; } 54 | 55 | # generate a random number 56 | th_generateRandom() 57 | { 58 | tfgr_random=${th_RANDOM} 59 | 60 | while [ "${tfgr_random}" = "${th_RANDOM}" ]; do 61 | if [ -n "${RANDOM:-}" ]; then 62 | # $RANDOM works 63 | tfgr_random=${RANDOM}${RANDOM}${RANDOM}$$ 64 | elif [ -r '/dev/urandom' ]; then 65 | tfgr_random=`od -vAn -N4 -tu4 "${stdoutF}" 2>"${stderrF}" ) 24 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 25 | rtrn=$? 26 | assertTrue '_ASSERT_EQUALS_ failure' ${rtrn} 27 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 28 | 29 | ( ${_ASSERT_EQUALS_} '"some msg"' 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) 30 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 31 | rtrn=$? 32 | assertTrue '_ASSERT_EQUALS_ w/ msg failure' ${rtrn} 33 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 34 | } 35 | 36 | testAssertNotEquals() 37 | { 38 | # start skipping if LINENO not available 39 | [ -z "${LINENO:-}" ] && startSkipping 40 | 41 | ( ${_ASSERT_NOT_EQUALS_} 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) 42 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 43 | rtrn=$? 44 | assertTrue '_ASSERT_NOT_EQUALS_ failure' ${rtrn} 45 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 46 | 47 | ( ${_ASSERT_NOT_EQUALS_} '"some msg"' 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) 48 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 49 | rtrn=$? 50 | assertTrue '_ASSERT_NOT_EQUALS_ w/ msg failure' ${rtrn} 51 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 52 | } 53 | 54 | testSame() 55 | { 56 | # start skipping if LINENO not available 57 | [ -z "${LINENO:-}" ] && startSkipping 58 | 59 | ( ${_ASSERT_SAME_} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) 60 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 61 | rtrn=$? 62 | assertTrue '_ASSERT_SAME_ failure' ${rtrn} 63 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 64 | 65 | ( ${_ASSERT_SAME_} '"some msg"' 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) 66 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 67 | rtrn=$? 68 | assertTrue '_ASSERT_SAME_ w/ msg failure' ${rtrn} 69 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 70 | } 71 | 72 | testNotSame() 73 | { 74 | # start skipping if LINENO not available 75 | [ -z "${LINENO:-}" ] && startSkipping 76 | 77 | ( ${_ASSERT_NOT_SAME_} 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) 78 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 79 | rtrn=$? 80 | assertTrue '_ASSERT_NOT_SAME_ failure' ${rtrn} 81 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 82 | 83 | ( ${_ASSERT_NOT_SAME_} '"some msg"' 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) 84 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 85 | rtrn=$? 86 | assertTrue '_ASSERT_NOT_SAME_ w/ msg failure' ${rtrn} 87 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 88 | } 89 | 90 | testNull() 91 | { 92 | # start skipping if LINENO not available 93 | [ -z "${LINENO:-}" ] && startSkipping 94 | 95 | ( ${_ASSERT_NULL_} 'x' >"${stdoutF}" 2>"${stderrF}" ) 96 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 97 | rtrn=$? 98 | assertTrue '_ASSERT_NULL_ failure' ${rtrn} 99 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 100 | 101 | ( ${_ASSERT_NULL_} '"some msg"' 'x' >"${stdoutF}" 2>"${stderrF}" ) 102 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 103 | rtrn=$? 104 | assertTrue '_ASSERT_NULL_ w/ msg failure' ${rtrn} 105 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 106 | } 107 | 108 | testNotNull() 109 | { 110 | # start skipping if LINENO not available 111 | [ -z "${LINENO:-}" ] && startSkipping 112 | 113 | ( ${_ASSERT_NOT_NULL_} '' >"${stdoutF}" 2>"${stderrF}" ) 114 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 115 | rtrn=$? 116 | assertTrue '_ASSERT_NOT_NULL_ failure' ${rtrn} 117 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 118 | 119 | ( ${_ASSERT_NOT_NULL_} '"some msg"' '""' >"${stdoutF}" 2>"${stderrF}" ) 120 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 121 | rtrn=$? 122 | assertTrue '_ASSERT_NOT_NULL_ w/ msg failure' ${rtrn} 123 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stdoutF}" "${stderrF}" >&2 124 | } 125 | 126 | testAssertTrue() 127 | { 128 | # start skipping if LINENO not available 129 | [ -z "${LINENO:-}" ] && startSkipping 130 | 131 | ( ${_ASSERT_TRUE_} ${SHUNIT_FALSE} >"${stdoutF}" 2>"${stderrF}" ) 132 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 133 | rtrn=$? 134 | assertTrue '_ASSERT_TRUE_ failure' ${rtrn} 135 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 136 | 137 | 138 | ( ${_ASSERT_TRUE_} '"some msg"' ${SHUNIT_FALSE} >"${stdoutF}" 2>"${stderrF}" ) 139 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 140 | rtrn=$? 141 | assertTrue '_ASSERT_TRUE_ w/ msg failure' ${rtrn} 142 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 143 | } 144 | 145 | testAssertFalse() 146 | { 147 | # start skipping if LINENO not available 148 | [ -z "${LINENO:-}" ] && startSkipping 149 | 150 | ( ${_ASSERT_FALSE_} ${SHUNIT_TRUE} >"${stdoutF}" 2>"${stderrF}" ) 151 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 152 | rtrn=$? 153 | assertTrue '_ASSERT_FALSE_ failure' ${rtrn} 154 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 155 | 156 | ( ${_ASSERT_FALSE_} '"some msg"' ${SHUNIT_TRUE} >"${stdoutF}" 2>"${stderrF}" ) 157 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 158 | rtrn=$? 159 | assertTrue '_ASSERT_FALSE_ w/ msg failure' ${rtrn} 160 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 161 | } 162 | 163 | testFail() 164 | { 165 | # start skipping if LINENO not available 166 | [ -z "${LINENO:-}" ] && startSkipping 167 | 168 | ( ${_FAIL_} >"${stdoutF}" 2>"${stderrF}" ) 169 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 170 | rtrn=$? 171 | assertTrue '_FAIL_ failure' ${rtrn} 172 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 173 | 174 | ( ${_FAIL_} '"some msg"' >"${stdoutF}" 2>"${stderrF}" ) 175 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 176 | rtrn=$? 177 | assertTrue '_FAIL_ w/ msg failure' ${rtrn} 178 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 179 | } 180 | 181 | testFailNotEquals() 182 | { 183 | # start skipping if LINENO not available 184 | [ -z "${LINENO:-}" ] && startSkipping 185 | 186 | ( ${_FAIL_NOT_EQUALS_} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) 187 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 188 | rtrn=$? 189 | assertTrue '_FAIL_NOT_EQUALS_ failure' ${rtrn} 190 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 191 | 192 | ( ${_FAIL_NOT_EQUALS_} '"some msg"' 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) 193 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 194 | rtrn=$? 195 | assertTrue '_FAIL_NOT_EQUALS_ w/ msg failure' ${rtrn} 196 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 197 | } 198 | 199 | testFailSame() 200 | { 201 | # start skipping if LINENO not available 202 | [ -z "${LINENO:-}" ] && startSkipping 203 | 204 | ( ${_FAIL_SAME_} 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) 205 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 206 | rtrn=$? 207 | assertTrue '_FAIL_SAME_ failure' ${rtrn} 208 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 209 | 210 | ( ${_FAIL_SAME_} '"some msg"' 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) 211 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 212 | rtrn=$? 213 | assertTrue '_FAIL_SAME_ w/ msg failure' ${rtrn} 214 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 215 | } 216 | 217 | testFailNotSame() 218 | { 219 | # start skipping if LINENO not available 220 | [ -z "${LINENO:-}" ] && startSkipping 221 | 222 | ( ${_FAIL_NOT_SAME_} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) 223 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 224 | rtrn=$? 225 | assertTrue '_FAIL_NOT_SAME_ failure' ${rtrn} 226 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 227 | 228 | ( ${_FAIL_NOT_SAME_} '"some msg"' 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) 229 | grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null 230 | rtrn=$? 231 | assertTrue '_FAIL_NOT_SAME_ w/ msg failure' ${rtrn} 232 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 233 | } 234 | 235 | #------------------------------------------------------------------------------ 236 | # suite functions 237 | # 238 | 239 | oneTimeSetUp() 240 | { 241 | tmpDir="${__shunit_tmpDir}/output" 242 | mkdir "${tmpDir}" 243 | stdoutF="${tmpDir}/stdout" 244 | stderrF="${tmpDir}/stderr" 245 | } 246 | 247 | # load and run shUnit2 248 | [ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0 249 | . ${TH_SHUNIT} 250 | -------------------------------------------------------------------------------- /test/shunit2/shunit2_test_misc.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # $Id: shunit2_test_misc.sh 322 2011-04-24 00:09:45Z kate.ward@forestent.com $ 3 | # vim:et:ft=sh:sts=2:sw=2 4 | # 5 | # Copyright 2008 Kate Ward. All Rights Reserved. 6 | # Released under the LGPL (GNU Lesser General Public License) 7 | # 8 | # Author: kate.ward@forestent.com (Kate Ward) 9 | # 10 | # shUnit2 unit tests of miscellaneous things 11 | 12 | # load test helpers 13 | . ./shunit2_test_helpers 14 | 15 | #------------------------------------------------------------------------------ 16 | # suite tests 17 | # 18 | 19 | # Note: the test script is prefixed with '#' chars so that shUnit2 does not 20 | # incorrectly interpret the embedded functions as real functions. 21 | testUnboundVariable() 22 | { 23 | sed 's/^#//' >"${unittestF}" <"${stdoutF}" 2>"${stderrF}" ) 37 | assertFalse 'expected a non-zero exit value' $? 38 | grep '^ASSERT:Unknown failure' "${stdoutF}" >/dev/null 39 | assertTrue 'assert message was not generated' $? 40 | grep '^Ran [0-9]* test' "${stdoutF}" >/dev/null 41 | assertTrue 'test count message was not generated' $? 42 | grep '^FAILED' "${stdoutF}" >/dev/null 43 | assertTrue 'failure message was not generated' $? 44 | } 45 | 46 | testIssue7() 47 | { 48 | ( assertEquals 'Some message.' 1 2 >"${stdoutF}" 2>"${stderrF}" ) 49 | diff "${stdoutF}" - >/dev/null < but was:<2> 51 | EOF 52 | rtrn=$? 53 | assertEquals ${SHUNIT_TRUE} ${rtrn} 54 | [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 55 | } 56 | 57 | testPrepForSourcing() 58 | { 59 | assertEquals '/abc' `_shunit_prepForSourcing '/abc'` 60 | assertEquals './abc' `_shunit_prepForSourcing './abc'` 61 | assertEquals './abc' `_shunit_prepForSourcing 'abc'` 62 | } 63 | 64 | testEscapeCharInStr() 65 | { 66 | actual=`_shunit_escapeCharInStr '\' ''` 67 | assertEquals '' "${actual}" 68 | assertEquals 'abc\\' `_shunit_escapeCharInStr '\' 'abc\'` 69 | assertEquals 'abc\\def' `_shunit_escapeCharInStr '\' 'abc\def'` 70 | assertEquals '\\def' `_shunit_escapeCharInStr '\' '\def'` 71 | 72 | actual=`_shunit_escapeCharInStr '"' ''` 73 | assertEquals '' "${actual}" 74 | assertEquals 'abc\"' `_shunit_escapeCharInStr '"' 'abc"'` 75 | assertEquals 'abc\"def' `_shunit_escapeCharInStr '"' 'abc"def'` 76 | assertEquals '\"def' `_shunit_escapeCharInStr '"' '"def'` 77 | 78 | actual=`_shunit_escapeCharInStr '$' ''` 79 | assertEquals '' "${actual}" 80 | assertEquals 'abc\$' `_shunit_escapeCharInStr '$' 'abc$'` 81 | assertEquals 'abc\$def' `_shunit_escapeCharInStr '$' 'abc$def'` 82 | assertEquals '\$def' `_shunit_escapeCharInStr '$' '$def'` 83 | 84 | # actual=`_shunit_escapeCharInStr "'" ''` 85 | # assertEquals '' "${actual}" 86 | # assertEquals "abc\\'" `_shunit_escapeCharInStr "'" "abc'"` 87 | # assertEquals "abc\\'def" `_shunit_escapeCharInStr "'" "abc'def"` 88 | # assertEquals "\\'def" `_shunit_escapeCharInStr "'" "'def"` 89 | 90 | # # must put the backtick in a variable so the shell doesn't misinterpret it 91 | # # while inside a backticked sequence (e.g. `echo '`'` would fail). 92 | # backtick='`' 93 | # actual=`_shunit_escapeCharInStr ${backtick} ''` 94 | # assertEquals '' "${actual}" 95 | # assertEquals '\`abc' \ 96 | # `_shunit_escapeCharInStr "${backtick}" ${backtick}'abc'` 97 | # assertEquals 'abc\`' \ 98 | # `_shunit_escapeCharInStr "${backtick}" 'abc'${backtick}` 99 | # assertEquals 'abc\`def' \ 100 | # `_shunit_escapeCharInStr "${backtick}" 'abc'${backtick}'def'` 101 | } 102 | 103 | testEscapeCharInStr_specialChars() 104 | { 105 | # make sure our forward slash doesn't upset sed 106 | assertEquals '/' `_shunit_escapeCharInStr '\' '/'` 107 | 108 | # some shells escape these differently 109 | #assertEquals '\\a' `_shunit_escapeCharInStr '\' '\a'` 110 | #assertEquals '\\b' `_shunit_escapeCharInStr '\' '\b'` 111 | } 112 | 113 | # Test the various ways of declaring functions. 114 | # 115 | # Prefixing (then stripping) with comment symbol so these functions aren't 116 | # treated as real functions by shUnit2. 117 | testExtractTestFunctions() 118 | { 119 | f="${tmpD}/extract_test_functions" 120 | sed 's/^#//' <"${f}" 121 | #testABC() { echo 'ABC'; } 122 | #test_def() { 123 | # echo 'def' 124 | #} 125 | #testG3 () 126 | #{ 127 | # echo 'G3' 128 | #} 129 | #function test4() { echo '4'; } 130 | # test5() { echo '5'; } 131 | #some_test_function() { echo 'some func'; } 132 | #func_with_test_vars() { 133 | # testVariable=1234 134 | #} 135 | EOF 136 | 137 | actual=`_shunit_extractTestFunctions "${f}"` 138 | assertEquals 'testABC test_def testG3 test4 test5' "${actual}" 139 | } 140 | 141 | #------------------------------------------------------------------------------ 142 | # suite functions 143 | # 144 | 145 | setUp() 146 | { 147 | for f in ${expectedF} ${stdoutF} ${stderrF}; do 148 | cp /dev/null ${f} 149 | done 150 | rm -fr "${tmpD}" 151 | mkdir "${tmpD}" 152 | } 153 | 154 | oneTimeSetUp() 155 | { 156 | tmpD="${SHUNIT_TMPDIR}/tmp" 157 | expectedF="${SHUNIT_TMPDIR}/expected" 158 | stdoutF="${SHUNIT_TMPDIR}/stdout" 159 | stderrF="${SHUNIT_TMPDIR}/stderr" 160 | unittestF="${SHUNIT_TMPDIR}/unittest" 161 | } 162 | 163 | # load and run shUnit2 164 | [ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0 165 | . ${TH_SHUNIT} 166 | -------------------------------------------------------------------------------- /test/shunit2/shunit2_test_standalone.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # $Id: shunit2_test_standalone.sh 303 2010-05-03 13:11:27Z kate.ward@forestent.com $ 3 | # vim:et:ft=sh:sts=2:sw=2 4 | # 5 | # Copyright 2010 Kate Ward. All Rights Reserved. 6 | # Released under the LGPL (GNU Lesser General Public License) 7 | # Author: kate.ward@forestent.com (Kate Ward) 8 | # 9 | # shUnit2 unit test for standalone operation. 10 | # 11 | # This unit test is purely to test that calling shunit2 directly, while passing 12 | # the name of a unit test script, works. When run, this script determines if it 13 | # is running as a standalone program, and calls main() if it is. 14 | 15 | ARGV0=`basename "$0"` 16 | 17 | # load test helpers 18 | . ./shunit2_test_helpers 19 | 20 | #------------------------------------------------------------------------------ 21 | # suite tests 22 | # 23 | 24 | testStandalone() 25 | { 26 | assertTrue ${SHUNIT_TRUE} 27 | } 28 | 29 | #------------------------------------------------------------------------------ 30 | # main 31 | # 32 | 33 | main() 34 | { 35 | ${TH_SHUNIT} "${ARGV0}" 36 | } 37 | 38 | # are we running as a standalone? 39 | if [ "${ARGV0}" = 'shunit2_test_standalone.sh' ]; then 40 | if [ $# -gt 0 ]; then main "$@"; else main; fi 41 | fi 42 | -------------------------------------------------------------------------------- /test/tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | 3 | # The tests avoid using wd's internal functions to check the state of 4 | # warp points. We use `--quiet` to prevent the test output being 5 | # flooded by wd's output. 6 | 7 | 8 | ### Variables 9 | 10 | # use a test config file, which is removed at the final test teardown. 11 | export WD_CONFIG="$(mktemp)" 12 | 13 | # used when testing 14 | WD_TEST_DIR=test_dir 15 | WD_TEST_DIR_2=test_dir_2 16 | WD_TEST_WP=test 17 | WD_TEST_WP_2=test2 18 | 19 | ### shUnit setup 20 | 21 | # ZSH specific shUnit stuff 22 | setopt shwordsplit 23 | SHUNIT_PARENT=$0 24 | 25 | # reset config for each test 26 | setUp() 27 | { 28 | cat /dev/null > "$WD_CONFIG" 29 | } 30 | 31 | oneTimeTearDown() 32 | { 33 | command rm -rf "$WD_TEST_DIR" "$WD_TEST_DIR_2" 34 | command rm "$WD_CONFIG" 35 | } 36 | 37 | ### Helpers 38 | 39 | WD_PATH=${PWD}/.. 40 | 41 | wd() 42 | { 43 | source "${WD_PATH}"/wd.sh "$@" 44 | } 45 | 46 | # MacOS's `wc` command adds extra padding to the front of the command. 47 | # This removes any excess spaces. 48 | wcl() 49 | { 50 | wc -l | sed 's/^ *//' 51 | } 52 | 53 | total_wps() 54 | { 55 | # total wps is the number of (non-empty) lines in the config 56 | echo "$(sed '/^\s*$/d' "$WD_CONFIG" | wcl)" 57 | } 58 | 59 | wp_exists() 60 | { 61 | wd list | command grep -q "$1[[:space:]]*->" 62 | echo $? 63 | } 64 | 65 | create_test_wp() 66 | { 67 | # create test dir 68 | mkdir "$WD_TEST_DIR" 69 | 70 | # create test wp 71 | cd "$WD_TEST_DIR" 72 | wd -q add "$WD_TEST_WP" 73 | cd .. 74 | } 75 | 76 | destroy_test_wp() 77 | { 78 | command rm -rf "$WD_TEST_DIR" 79 | wd -q rm "$WD_TEST_DIR" 80 | } 81 | 82 | 83 | ### Tests 84 | 85 | # basic functionality 86 | test_empty_config() 87 | { 88 | assertEquals "should initially be an empty config" \ 89 | 0 "$(total_wps)" 90 | } 91 | 92 | test_wd_point() 93 | { 94 | cwd="$PWD" 95 | wd -q add cwd 96 | assertTrue "should add the working directory as a custom warp point" \ 97 | "$pipestatus" 98 | command rm -rf "$WD_TEST_DIR" 99 | command mkdir "$WD_TEST_DIR" 100 | cd "$WD_TEST_DIR" 101 | wd -q add 102 | assertTrue "should add another directory using a default warp point" \ 103 | "$pipestatus" 104 | command mkdir "$WD_TEST_DIR_2" 105 | cd .. 106 | wd "$WD_TEST_DIR" 107 | assertTrue "should successfully warp to a default point" \ 108 | "$pipestatus" 109 | assertEquals "should be in the default warped directory" \ 110 | "$cwd/$WD_TEST_DIR" "$(pwd)" 111 | wd cwd 112 | assertEquals "should successfully return to cwd with a warp point" \ 113 | "$cwd" "$(pwd)" 114 | wd "$WD_TEST_DIR" "$WD_TEST_DIR_2" 115 | assertTrue "should successfully warp to nested directory of point" \ 116 | "$pipestatus" 117 | assertEquals "should be in the nested directory of the warp point" \ 118 | "$cwd/$WD_TEST_DIR/$WD_TEST_DIR_2" "$(pwd)" 119 | wd -q moon 120 | assertFalse "should fail to warp to a point that does not exist" \ 121 | "$pipestatus" 122 | wd -q "$WD_TEST_DIR" pluto 2&> /dev/null 123 | assertFalse "should fail warping to nonexistent nested directories" \ 124 | "$pipestatus" 125 | command rm -rf "$WD_TEST_DIR" 126 | } 127 | 128 | test_simple_add_remove() 129 | { 130 | wd -q add foo 131 | assertTrue "should successfully add wp 'foo'" \ 132 | "$pipestatus" 133 | 134 | assertEquals "should have 1 wps" \ 135 | 1 "$(total_wps)" 136 | 137 | assertTrue "wp 'foo' should exist" \ 138 | "$(wp_exists "foo")" 139 | 140 | wd -q rm foo 141 | assertEquals "wps should be empty" \ 142 | 0 "$(total_wps)" 143 | } 144 | 145 | test_default_add_remove() 146 | { 147 | cwd=$(basename "$PWD") 148 | 149 | wd -q add 150 | assertTrue "should successfully add wp to PWD" \ 151 | "$pipestatus" 152 | 153 | assertEquals "should have 1 wps" \ 154 | 1 "$(total_wps)" 155 | 156 | assertTrue "wp to PWD should exist" \ 157 | "$(wp_exists "$cwd")" 158 | 159 | wd -q rm 160 | assertEquals "wps should be empty" \ 161 | 0 "$(total_wps)" 162 | } 163 | 164 | test_no_duplicates() 165 | { 166 | wd -q add foo 167 | assertTrue "should successfully add 'foo'" \ 168 | "$pipestatus" 169 | 170 | wd -q add foo 171 | assertFalse "should fail when adding duplicate of 'foo'" \ 172 | "$pipestatus" 173 | } 174 | 175 | test_default_no_duplicates() 176 | { 177 | cwd=$(basename "$PWD") 178 | 179 | wd -q add 180 | assertTrue "should successfully add warp point to PWD" \ 181 | "$pipestatus" 182 | 183 | wd -q add 184 | assertFalse "should fail when adding duplicate of PWD" \ 185 | "$pipestatus" 186 | 187 | wd -q -f add 188 | assertTrue "should successfully force-add warp point to PWD" \ 189 | "$pipestatus" 190 | } 191 | 192 | test_default_multiple_directories() 193 | { 194 | command rm -rf "$WD_TEST_DIR" 195 | command mkdir "$WD_TEST_DIR" 196 | cd "$WD_TEST_DIR" 197 | wd -q add 198 | assertTrue "should successfully add warp point to PWD" \ 199 | "$pipestatus" 200 | cd .. 201 | command rmdir "$WD_TEST_DIR" 202 | 203 | command rm -rf "$WD_TEST_DIR_2" 204 | command mkdir "$WD_TEST_DIR_2" 205 | cd "$WD_TEST_DIR_2" 206 | wd -q add 207 | assertTrue "should successfully add warp point to another PWD" \ 208 | "$pipestatus" 209 | cd .. 210 | command rmdir "$WD_TEST_DIR_2" 211 | } 212 | 213 | test_valid_identifiers() 214 | { 215 | wd -q add . 216 | assertFalse "should not allow only dots" \ 217 | "$pipestatus" 218 | 219 | wd -q add .. 220 | assertFalse "should not allow only dots" \ 221 | "$pipestatus" 222 | 223 | wd -q add hej. 224 | assertTrue "should allow dots in name" \ 225 | "$pipestatus" 226 | 227 | wd -q add "foo bar" 228 | assertFalse "should not allow whitespace" \ 229 | "$pipestatus" 230 | 231 | wd -q add "foo:bar" 232 | assertFalse "should not allow colons" \ 233 | "$pipestatus" 234 | 235 | wd -q add ":foo" 236 | assertFalse "should not allow colons" \ 237 | "$pipestatus" 238 | 239 | wd -q add "foo:" 240 | assertFalse "should not allow colons" \ 241 | "$pipestatus" 242 | 243 | wd -q add "foo/bar" 244 | assertFalse "should not allow slashes" \ 245 | "$pipestatus" 246 | 247 | wd -q add "foo/" 248 | assertFalse "should not allow slashes" \ 249 | "$pipestatus" 250 | 251 | wd -q add "/foo" 252 | assertFalse "should not allow slashes" \ 253 | "$pipestatus" 254 | 255 | wd -q add "add" 256 | assertFalse "should not allow command names" \ 257 | "$pipestatus" 258 | 259 | wd -q add "rm" 260 | assertFalse "should not allow command names" \ 261 | "$pipestatus" 262 | 263 | wd -q add "show" 264 | assertFalse "should not allow command names" \ 265 | "$pipestatus" 266 | 267 | wd -q add "list" 268 | assertFalse "should not allow command names" \ 269 | "$pipestatus" 270 | 271 | wd -q add "ls" 272 | assertFalse "should not allow command names" \ 273 | "$pipestatus" 274 | 275 | wd -q add "path" 276 | assertFalse "should not allow command names" \ 277 | "$pipestatus" 278 | 279 | wd -q add "clean" 280 | assertFalse "should not allow command names" \ 281 | "$pipestatus" 282 | 283 | wd -q add "help" 284 | assertFalse "should not allow command names" \ 285 | "$pipestatus" 286 | } 287 | 288 | test_wd_addcd() 289 | { 290 | # Create a base directory for testing 291 | create_test_wp 292 | 293 | # Test with basic directory 294 | wd -q addcd "$WD_TEST_DIR" 295 | assertTrue "should successfully add default wp for directory" \ 296 | "$(wp_exists "$(basename "$WD_TEST_DIR")")" 297 | 298 | # Test with a specific warp point name 299 | wd -q addcd "$WD_TEST_DIR" "$WD_TEST_WP_2" 300 | assertTrue "should successfully add specified wp for directory" \ 301 | "$(wp_exists "$WD_TEST_WP_2")" 302 | 303 | # Test with force option 304 | wd -q addcd "$WD_TEST_DIR" -f 305 | assertTrue "should successfully force-add wp for directory" \ 306 | "$(wp_exists "$(basename "$WD_TEST_DIR")")" 307 | 308 | # Test with a specific warp point name and force option 309 | wd -q addcd "$WD_TEST_DIR" "$WD_TEST_WP_2" -f 310 | assertTrue "should successfully force-add specified wp for directory" \ 311 | "$(wp_exists "$WD_TEST_WP_2")" 312 | 313 | # Test with absolute path 314 | local abs_path="$PWD/$WD_TEST_DIR" 315 | wd -q addcd "$abs_path" 316 | assertTrue "should successfully add default wp for absolute directory path" \ 317 | "$(wp_exists "$(basename "$abs_path")")" 318 | 319 | # Test with relative path including navigation 320 | mkdir -p "$WD_TEST_DIR/nested/extra" 321 | local rel_path="../$(basename "$PWD")/$WD_TEST_DIR" 322 | cd "$WD_TEST_DIR/nested/extra" 323 | wd -q addcd "$rel_path" 324 | assertTrue "should successfully add wp for relative path with navigation" \ 325 | "$(wp_exists "$(basename "$rel_path")")" 326 | cd - > /dev/null 327 | 328 | # Test with nested directory paths 329 | local nested_path="$WD_TEST_DIR/nested/extra" 330 | wd -q addcd "$nested_path" 331 | assertTrue "should successfully add wp for nested directory path" \ 332 | "$(wp_exists "$(basename "$nested_path")")" 333 | 334 | # Cleanup 335 | destroy_test_wp 336 | } 337 | 338 | 339 | test_removal() 340 | { 341 | wd -q add foo 342 | 343 | wd -q rm bar 344 | assertFalse "should fail when removing non-existing point" \ 345 | "$pipestatus" 346 | 347 | wd -q rm foo 348 | assertTrue "should remove existing point" \ 349 | "$pipestatus" 350 | } 351 | 352 | test_list() 353 | { 354 | wd -q add foo 355 | 356 | # add one to expected number of lines, because of header msg 357 | assertEquals "should only be one warp point" \ 358 | "$(wd list | wcl)" 2 359 | 360 | wd -q add bar 361 | 362 | assertEquals "should be more than one warp point" \ 363 | "$(wd list | wcl)" 3 364 | 365 | mkdir test:dir 366 | cd test:dir 367 | wd -q add testdir 368 | cd .. 369 | rmdir test:dir 370 | 371 | assertEquals "should be three warp points" \ 372 | "$(wd list | wcl)" 4 373 | 374 | wd list | grep -q "test:dir" 375 | assertTrue "should display the full colon-holding name" \ 376 | "$?" 377 | 378 | } 379 | 380 | test_show() 381 | { 382 | if [[ ! $(wd show) =~ "No warp point to $(echo "$PWD" | sed "s:$HOME:~:")" ]] 383 | then 384 | fail "should show no warp points" 385 | fi 386 | 387 | wd -q add foo 388 | 389 | if [[ ! $(wd show) =~ '1 warp point.*foo' ]] 390 | then 391 | fail "should show 1 warp point 'foo'" 392 | fi 393 | 394 | wd -q add bar 395 | 396 | if [[ ! $(wd show) =~ '2 warp point.*foo bar' ]] 397 | then 398 | fail "should show 2 warp points 'foo bar'" 399 | fi 400 | 401 | # test with optional name argument 402 | if [[ ! $(wd show foo) =~ 'Warp point:' ]] 403 | then 404 | fail "should show warp point 'foo'" 405 | fi 406 | 407 | if [[ ! $(wd show qux) =~ 'No warp point named' ]] 408 | then 409 | fail "should not show warp point 'qux'" 410 | fi 411 | 412 | # test target in $HOME is listed/shown 413 | cd ${HOME} 414 | wd -q add home 415 | if [[ ! $(wd show) =~ '1 warp point.*:.*home' ]] 416 | then 417 | fail "should show warp point 'home'" 418 | fi 419 | } 420 | 421 | test_quiet() 422 | { 423 | if [[ ! $(wd -q add foo) == "" ]] 424 | then 425 | fail "should suppress all output from add" 426 | fi 427 | 428 | if [[ ! $(wd -q show foo) == "" ]] 429 | then 430 | fail "should suppress all output from show" 431 | fi 432 | 433 | if [[ ! $(wd -q list) == "" ]] 434 | then 435 | fail "should suppress all output from ls" 436 | fi 437 | 438 | if [[ ! $(wd -q rm foo) == "" ]] 439 | then 440 | fail "should suppress all output from rm" 441 | fi 442 | } 443 | 444 | test_clean() 445 | { 446 | dir="$HOME/.wdunittest" 447 | 448 | # create test dir 449 | command mkdir "$dir" 450 | cd "$dir" 451 | 452 | # add warp point 453 | wd -q add test 454 | 455 | # remove test dir 456 | cd .. 457 | command rmdir "$dir" 458 | 459 | if [[ ! $(echo "n" | wd clean) =~ "Cleanup aborted" ]] 460 | then 461 | fail "should be able to abort cleanup" 462 | fi 463 | 464 | if [[ ! $(echo "y" | wd clean) =~ ".*1 warp point\(s\) removed" ]] 465 | then 466 | fail "should remove one warp point when answering yes" 467 | fi 468 | 469 | # recreate test dir 470 | command mkdir "$dir" 471 | cd "$dir" 472 | 473 | # add warp point 474 | wd -q add test 475 | 476 | if [[ ! $(echo "y" | wd clean) =~ "No warp points to clean, carry on!" ]] 477 | then 478 | fail "there should be no invalid warp point" 479 | fi 480 | 481 | wd -q -f add test 482 | 483 | # remove test dir 484 | cd .. 485 | command rmdir "$dir" 486 | 487 | if [[ ! $(wd clean -f) =~ ".*1 warp point\(s\) removed" ]] 488 | then 489 | fail "should remove one warp point when using force" 490 | fi 491 | } 492 | 493 | test_ls() 494 | { 495 | # set up 496 | create_test_wp 497 | 498 | # create test files in dir 499 | touch "$WD_TEST_DIR/foo" 500 | touch "$WD_TEST_DIR/bar" 501 | 502 | # assert correct output 503 | if [[ ! $(wd ls $WD_TEST_WP) =~ "bar.*foo" ]] 504 | then 505 | fail "should list correct files" 506 | fi 507 | 508 | # clean up 509 | destroy_test_wp 510 | } 511 | 512 | test_path() 513 | { 514 | # set up 515 | create_test_wp 516 | 517 | local pwd=$(echo "$PWD" | sed "s:~:${HOME}:g") 518 | 519 | # assert correct output 520 | if [[ ! $(wd path "$WD_TEST_WP") =~ "${pwd}/${WD_TEST_DIR}" ]] 521 | then 522 | fail "should give correct path" 523 | fi 524 | 525 | # clean up 526 | destroy_test_wp 527 | } 528 | 529 | test_config() 530 | { 531 | local arg_config="$(mktemp)" 532 | local wd_config_lines=$(total_wps) 533 | 534 | wd -q --config "$arg_config" add 535 | 536 | # confirm $WD_CONFIG was unchanged 537 | assertEquals "$wd_config_lines" "$(total_wps)" 538 | assertEquals "$wd_config_lines" 0 539 | 540 | # confirm --config was changed 541 | assertEquals 1 "$(wcl < "$arg_config")" 542 | assertEquals 1 "$(WD_CONFIG=$arg_config total_wps)" 543 | 544 | command rm "$arg_config" 545 | } 546 | 547 | 548 | # Go go gadget 549 | . ./shunit2/shunit2 550 | -------------------------------------------------------------------------------- /tty.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mfaerevaag/wd/d57935d3dd385d699a75766f28657c9f3b35e944/tty.gif -------------------------------------------------------------------------------- /wd.1: -------------------------------------------------------------------------------- 1 | .TH wd "1" "2025-04-23" "wd 0.10.0" "wd Manual" 2 | . 3 | .SH NAME 4 | wd \- Quickly warp to custom directories in zsh 5 | . 6 | . 7 | .SH SYNOPSIS 8 | .B 9 | wd [\fICOMMAND\fP] [\fINAME\fP] 10 | . 11 | . 12 | .SH DESCRIPTION 13 | \fBwd\fP (warp directory) lets you quickly jump to custom directories in zsh, without using \fBcd\fP. Why? Because \fBcd\fP is slow for paths that are visited frequently or has a long path. 14 | . 15 | . 16 | .SH COMMANDS 17 | .IP "add, -a, --add" 18 | Add a new warp point which will warp to the current working directory with current directory name as identifier. 19 | . 20 | .IP "add, -a, --add " 21 | Add a new warp point which will warp to the current working directory with \fIname\fR as identifier. 22 | . 23 | .IP "addcd, -c, --addcd " 24 | Add a new warp point which will warp to the current working directory with current directory name as identifier. 25 | . 26 | .IP "addcd, -c, --addcd " 27 | Add a new warp point which will warp to the current working directory with \fIname\fR as identifier. 28 | . 29 | .IP "rm, -r, --remove" 30 | Remove any warp point with current directory name as identifier. 31 | . 32 | .IP "rm, -r, --remove " 33 | Remove any warp point with \fIname\fP as identifier. 34 | . 35 | .IP "ls, -l" 36 | List all registered warp points. 37 | . 38 | .IP "path, -p, --path " 39 | Show the path to given warp point (pwd) 40 | . 41 | .IP "open , -o , --open " 42 | Open the \fIname\fP warp point with the default file explorer. (open / xdg-open) 43 | . 44 | .IP "show" 45 | Show which warp points are registered to the current working directory. 46 | . 47 | .IP "show " 48 | Show path to given warp point. 49 | . 50 | .IP "clean" 51 | Remove warp points to non-existent directories. 52 | . 53 | .IP "help" 54 | Show help message with usage and commands. 55 | . 56 | .IP "-v, --version" 57 | Print the running version. 58 | . 59 | .IP "-c, --config" 60 | Specifically set the config file (default `~/.warprc`), which is useful when testing. 61 | . 62 | .IP "-q, --quiet" 63 | Silence all output. 64 | . 65 | .IP "-f, --force" 66 | Allows overwriting without warning. Can be applied to `add` and `clean` 67 | . 68 | .SH EXAMPLES 69 | . 70 | .IP "\fBwd add --force foo\fP" 71 | Add warp point named "foo" to current working directory, overwriting any existing points named "foo". 72 | . 73 | .IP "\fBwd rm foo\fP" 74 | Remove any warp point named \fIfoo\fP. 75 | . 76 | . 77 | .SH BUGS 78 | Issue tracking on GitHub: 79 | .nf 80 | https://github.com/mfaerevaag/wd/issues 81 | . 82 | . 83 | .SH AUTHORS 84 | Created and maintained by Markus Færevaag and Simon Altschuler, with the help of various contributors: 85 | .nf 86 | https://github.com/mfaerevaag/wd/graphs/contributors. 87 | . 88 | . 89 | .SH LICENSE 90 | wd is licensed under the MIT license. 91 | -------------------------------------------------------------------------------- /wd.plugin.zsh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | 3 | # WARP DIRECTORY 4 | # ============== 5 | # Jump to custom directories in terminal 6 | # because `cd` takes too long... 7 | # 8 | # @github.com/mfaerevaag/wd 9 | 10 | # Handle $0 according to the standard: 11 | # # https://zdharma-continuum.github.io/Zsh-100-Commits-Club/Zsh-Plugin-Standard.html 12 | 0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}" 13 | 0="${${(M)0:#/*}:-$PWD/$0}" 14 | 15 | eval "wd() { source '${0:A:h}/wd.sh' }" 16 | wd > /dev/null 17 | zle -N wd_browse_widget 18 | zle -N wd_restore_buffer 19 | autoload -Uz add-zle-hook-widget 20 | add-zle-hook-widget line-init wd_restore_buffer 21 | -------------------------------------------------------------------------------- /wd.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | 3 | # WARP DIRECTORY 4 | # ============== 5 | # Jump to custom directories in terminal 6 | # because `cd` takes too long... 7 | # 8 | # @github.com/mfaerevaag/wd 9 | 10 | # version 11 | readonly WD_VERSION=0.10.0 12 | 13 | # colors 14 | readonly WD_BLUE="\033[96m" 15 | readonly WD_GREEN="\033[92m" 16 | readonly WD_YELLOW="\033[93m" 17 | readonly WD_RED="\033[91m" 18 | readonly WD_NOC="\033[m" 19 | 20 | ## functions 21 | 22 | # helpers 23 | wd_yesorno() 24 | { 25 | # variables 26 | local question="${1}" 27 | local prompt="${question} " 28 | local yes_RETVAL="0" 29 | local no_RETVAL="3" 30 | local RETVAL="" 31 | local answer="" 32 | 33 | # read-eval loop 34 | while true ; do 35 | printf $prompt 36 | read -r answer 37 | 38 | case ${answer:=${default}} in 39 | "Y"|"y"|"YES"|"yes"|"Yes" ) 40 | RETVAL=${yes_RETVAL} && \ 41 | break 42 | ;; 43 | "N"|"n"|"NO"|"no"|"No" ) 44 | RETVAL=${no_RETVAL} && \ 45 | break 46 | ;; 47 | * ) 48 | echo "Please provide a valid answer (y or n)" 49 | ;; 50 | esac 51 | done 52 | 53 | return ${RETVAL} 54 | } 55 | 56 | wd_print_msg() 57 | { 58 | if [[ -z $wd_quiet_mode ]] 59 | then 60 | local color="${1:-$WD_BLUE}" # Default to blue if no color is provided 61 | local msg="$2" 62 | 63 | if [[ -z "$msg" ]]; then 64 | print "${WD_RED}*${WD_NOC} Could not print message. Sorry!" 65 | else 66 | print " ${color}*${WD_NOC} ${msg}" 67 | fi 68 | fi 69 | } 70 | 71 | wd_print_usage() 72 | { 73 | command cat <<- EOF 74 | Usage: wd [command] [point] 75 | 76 | Commands: 77 | Warps to the directory specified by the warp point 78 | Warps to the directory specified by the warp point with path appended 79 | add Adds the current working directory to your warp points 80 | add Adds the current working directory to your warp points with current directory's name 81 | addcd Adds a path to your warp points with the directory's name 82 | addcd Adds a path to your warp points with a custom name 83 | rm Removes the given warp point 84 | rm Removes the given warp point with current directory's name 85 | show Print path to given warp point 86 | show Print warp points to current directory 87 | list Print all stored warp points 88 | ls Show files from given warp point (ls) 89 | open Open the warp point in the default file explorer (open / xdg-open) 90 | path Show the path to given warp point (pwd) 91 | clean Remove points warping to nonexistent directories (will prompt unless --force is used) 92 | 93 | -v | --version Print version 94 | -c | --config Specify config file (default ~/.warprc) 95 | -q | --quiet Suppress all output 96 | -f | --force Allows overwriting without warning (for add & clean) 97 | 98 | help Show this extremely helpful text 99 | EOF 100 | } 101 | 102 | wd_exit_fail() 103 | { 104 | local msg=$1 105 | 106 | wd_print_msg "$WD_RED" "$msg" 107 | WD_EXIT_CODE=1 108 | } 109 | 110 | wd_exit_warn() 111 | { 112 | local msg=$1 113 | 114 | wd_print_msg "$WD_YELLOW" "$msg" 115 | WD_EXIT_CODE=1 116 | } 117 | 118 | wd_getdir() 119 | { 120 | local name_arg=$1 121 | 122 | point=$(wd_show "$name_arg") 123 | dir=${point:28+$#name_arg+7} 124 | 125 | if [[ -z $name_arg ]]; then 126 | wd_exit_fail "You must enter a warp point" 127 | break 128 | elif [[ -z $dir ]]; then 129 | wd_exit_fail "Unknown warp point '${name_arg}'" 130 | break 131 | fi 132 | } 133 | 134 | # core 135 | 136 | wd_warp() 137 | { 138 | local point=$1 139 | local sub=$2 140 | 141 | if [[ $point =~ "^\.+$" ]] 142 | then 143 | if [[ $#1 < 2 ]] 144 | then 145 | wd_exit_warn "Warping to current directory?" 146 | else 147 | (( n = $#1 - 1 )) 148 | cd -$n > /dev/null 149 | WD_EXIT_CODE=$? 150 | fi 151 | elif [[ ${points[$point]} != "" ]] 152 | then 153 | if [[ $sub != "" ]] 154 | then 155 | cd ${points[$point]/#\~/$HOME}/$sub 156 | WD_EXIT_CODE=$? 157 | else 158 | cd ${points[$point]/#\~/$HOME} 159 | WD_EXIT_CODE=$? 160 | fi 161 | else 162 | wd_exit_fail "Unknown warp point '${point}'" 163 | fi 164 | } 165 | 166 | wd_add() 167 | { 168 | local point=$1 169 | local force=$2 170 | cmdnames=(add rm show list ls path clean help) 171 | 172 | if [[ $point == "" ]] 173 | then 174 | point=$(basename "$PWD") 175 | fi 176 | 177 | if [[ $point =~ "^[\.]+$" ]] 178 | then 179 | wd_exit_fail "Warp point cannot be just dots" 180 | elif [[ $point =~ "[[:space:]]+" ]] 181 | then 182 | wd_exit_fail "Warp point should not contain whitespace" 183 | elif [[ $point =~ : ]] || [[ $point =~ / ]] 184 | then 185 | wd_exit_fail "Warp point contains illegal character (:/)" 186 | elif (($cmdnames[(Ie)$point])) 187 | then 188 | wd_exit_fail "Warp point name cannot be a wd command (see wd -h for a full list)" 189 | elif [[ ${points[$point]} == "" ]] || [ ! -z "$force" ] 190 | then 191 | wd_remove "$point" > /dev/null 192 | printf "%q:%s\n" "${point}" "${PWD/#$HOME/~}" >> "$wd_config_file" 193 | if (whence sort >/dev/null); then 194 | local config_tmp=$(mktemp "${TMPDIR:-/tmp}/wd.XXXXXXXXXX") 195 | # use 'cat' below to ensure we respect $wd_config_file as a symlink 196 | command sort -o "${config_tmp}" "$wd_config_file" && command cat "${config_tmp}" >| "$wd_config_file" && command rm "${config_tmp}" 197 | fi 198 | 199 | wd_export_static_named_directories 200 | 201 | wd_print_msg "$WD_GREEN" "Warp point added" 202 | 203 | # override exit code in case wd_remove did not remove any points 204 | # TODO: we should handle this kind of logic better 205 | WD_EXIT_CODE=0 206 | else 207 | wd_exit_warn "Warp point '${point}' already exists. Use 'add --force' to overwrite." 208 | fi 209 | } 210 | 211 | wd_addcd() { 212 | local folder="$1" 213 | local point=$2 214 | local force=$3 215 | local currentdir=$PWD 216 | 217 | if [[ -z "$folder" ]]; then 218 | wd_exit_fail "You must specify a path" 219 | return 220 | fi 221 | 222 | if [[ ! -d "$folder" ]]; then 223 | wd_exit_fail "The directory does not exist" 224 | return 225 | fi 226 | 227 | cd "$folder" || return 228 | wd_add "$point" "$force" 229 | cd "$currentdir" || return 230 | } 231 | 232 | 233 | wd_remove() 234 | { 235 | local point_list=$1 236 | 237 | if [[ "$point_list" == "" ]] 238 | then 239 | point_list=$(basename "$PWD") 240 | fi 241 | 242 | for point_name in $point_list ; do 243 | if [[ ${points[$point_name]} != "" ]] 244 | then 245 | local config_tmp=$(mktemp "${TMPDIR:-/tmp}/wd.XXXXXXXXXX") 246 | # Copy and delete in two steps in order to preserve symlinks 247 | if sed -n "/^${point_name}:.*$/!p" "$wd_config_file" >| "$config_tmp" && command cp "$config_tmp" "$wd_config_file" && command rm "$config_tmp" 248 | then 249 | wd_print_msg "$WD_GREEN" "Warp point removed" 250 | else 251 | wd_exit_fail "Something bad happened! Sorry." 252 | fi 253 | else 254 | wd_exit_fail "Warp point was not found" 255 | fi 256 | done 257 | } 258 | 259 | wd_browse() { 260 | # Check if fzf is installed 261 | if ! command -v fzf >/dev/null; then 262 | wd_print_msg "$WD_RED" "This functionality requires fzf. Please install fzf first." 263 | return 1 264 | fi 265 | 266 | # Ensure wd_config_file is properly set 267 | if [[ -z $wd_config_file ]]; then 268 | wd_config_file="${WD_CONFIG:-$HOME/.warprc}" 269 | fi 270 | 271 | # Check if config file exists 272 | if [[ ! -f $wd_config_file ]]; then 273 | wd_print_msg "$WD_RED" "Config file $wd_config_file does not exist. Please create it first." 274 | return 1 275 | fi 276 | 277 | # Read entries from the config file 278 | local entries=("${(@f)$(sed "s:${HOME}:~:g" "$wd_config_file" | awk -F ':' '{print $1 " -> " $2}')}") 279 | if [[ -z $entries ]]; then 280 | wd_print_msg "$WD_YELLOW" "You don't have any warp points to browse" 281 | return 1 282 | fi 283 | 284 | # Temp file for remove operations 285 | local script_path="${${(%):-%x}:h}" 286 | local wd_remove_output=$(mktemp "${TMPDIR:-/tmp}/wd.XXXXXXXXXX") 287 | 288 | # Create fzf bindings 289 | entries=("All warp points:" "Press enter to select. Press delete to remove" "${entries[@]}") 290 | local fzf_bind="delete:execute(echo {} | awk -F ' -> ' '{print \$1}' | xargs -I {} \"$script_path/wd.sh\" rm {} > \"$wd_remove_output\")+abort" 291 | 292 | # Run fzf 293 | local selected_entry=$(printf '%s\n' "${entries[@]}" | fzf --height 100% --reverse --header-lines=2 --bind="$fzf_bind") 294 | 295 | # Handle selection 296 | if [[ -e $wd_remove_output ]]; then 297 | cat "$wd_remove_output" 298 | rm -f "$wd_remove_output" 299 | fi 300 | 301 | if [[ -n $selected_entry ]]; then 302 | local selected_point="${selected_entry%% ->*}" 303 | selected_point=$(echo "$selected_point" | xargs) 304 | wd $selected_point 305 | fi 306 | } 307 | 308 | wd_browse_widget() { 309 | # Ensure wd_config_file is properly set 310 | if [[ -z $wd_config_file ]]; then 311 | wd_config_file="${WD_CONFIG:-$HOME/.warprc}" 312 | fi 313 | 314 | # Check if config file exists 315 | if [[ ! -f $wd_config_file ]]; then 316 | wd_print_msg "$WD_RED" "Config file $wd_config_file does not exist. Please create it first." 317 | return 1 318 | fi 319 | 320 | # Call wd_browse to handle the selection 321 | wd_browse 322 | 323 | # Restore the zsh buffer and cursor after running wd_browse 324 | saved_buffer=$BUFFER 325 | saved_cursor=$CURSOR 326 | BUFFER= 327 | zle redisplay 328 | zle accept-line 329 | } 330 | 331 | wd_restore_buffer() { 332 | if [[ -n $saved_buffer ]]; then 333 | BUFFER=$saved_buffer 334 | CURSOR=$saved_cursor 335 | fi 336 | saved_buffer= 337 | saved_cursor=1 338 | } 339 | 340 | wd_list_all() 341 | { 342 | wd_print_msg "$WD_BLUE" "All warp points:" 343 | 344 | entries=$(sed "s:${HOME}:~:g" "$wd_config_file") 345 | 346 | max_warp_point_length=0 347 | while IFS= read -r line 348 | do 349 | arr=(${(s,:,)line}) 350 | key=${arr[1]} 351 | 352 | length=${#key} 353 | if [[ length -gt max_warp_point_length ]] 354 | then 355 | max_warp_point_length=$length 356 | fi 357 | done <<< "$entries" 358 | 359 | while IFS= read -r line 360 | do 361 | if [[ $line != "" ]] 362 | then 363 | arr=(${(s,:,)line}) 364 | key=${arr[1]} 365 | val=${line#"${arr[1]}:"} 366 | 367 | if [[ -z $wd_quiet_mode ]] 368 | then 369 | printf "%${max_warp_point_length}s -> %s\n" "$key" "$val" 370 | fi 371 | fi 372 | done <<< "$entries" 373 | } 374 | 375 | wd_ls() 376 | { 377 | wd_getdir "$1" 378 | ls "${dir/#\~/$HOME}" 379 | } 380 | 381 | wd_open() 382 | { 383 | wd_getdir "$1" 384 | if command -v open >/dev/null 2>&1; then 385 | # MacOS, Ubuntu (alias) 386 | open "${dir/#\~/$HOME}" 387 | elif command -v xdg-open >/dev/null 2>&1; then 388 | # Most Linux desktops 389 | xdg-open "${dir/#\~/$HOME}" 390 | else 391 | echo "No known file opener found (need 'open' or 'xdg-open')." >&2 392 | exit 1 393 | fi 394 | } 395 | 396 | wd_path() 397 | { 398 | wd_getdir "$1" 399 | echo "$(echo "$dir" | sed "s:~:${HOME}:g")" 400 | } 401 | 402 | wd_show() 403 | { 404 | local name_arg=$1 405 | local show_pwd 406 | # if there's an argument we look up the value 407 | if [[ -n $name_arg ]] 408 | then 409 | if [[ -z $points[$name_arg] ]] 410 | then 411 | wd_print_msg "$WD_BLUE" "No warp point named $name_arg" 412 | else 413 | wd_print_msg "$WD_GREEN" "Warp point: ${WD_GREEN}$name_arg${WD_NOC} -> $points[$name_arg]" 414 | fi 415 | else 416 | # hax to create a local empty array 417 | local wd_matches 418 | wd_matches=() 419 | # do a reverse lookup to check whether PWD is in $points 420 | show_pwd="${PWD/$HOME/~}" 421 | if [[ ${points[(r)$show_pwd]} == "$show_pwd" ]] 422 | then 423 | for name in ${(k)points} 424 | do 425 | if [[ $points[$name] == "$show_pwd" ]] 426 | then 427 | wd_matches[$(($#wd_matches+1))]=$name 428 | fi 429 | done 430 | 431 | wd_print_msg "$WD_BLUE" "$#wd_matches warp point(s) to current directory: ${WD_GREEN}$wd_matches${WD_NOC}" 432 | else 433 | wd_print_msg "$WD_YELLOW" "No warp point to $show_pwd" 434 | fi 435 | fi 436 | } 437 | 438 | wd_clean() { 439 | local force=$1 440 | local count=0 441 | local wd_tmp="" 442 | 443 | while read -r line 444 | do 445 | if [[ $line != "" ]] 446 | then 447 | arr=(${(s,:,)line}) 448 | key=${arr[1]} 449 | val=${arr[2]} 450 | 451 | if [ -d "${val/#\~/$HOME}" ] 452 | then 453 | wd_tmp=$wd_tmp"\n"`echo "$line"` 454 | else 455 | wd_print_msg "$WD_YELLOW" "Nonexistent directory: ${key} -> ${val}" 456 | count=$((count+1)) 457 | fi 458 | fi 459 | done < "$wd_config_file" 460 | 461 | if [[ $count -eq 0 ]] 462 | then 463 | wd_print_msg "$WD_BLUE" "No warp points to clean, carry on!" 464 | else 465 | if [ ! -z "$force" ] || wd_yesorno "Removing ${count} warp points. Continue? (y/n)" 466 | then 467 | echo "$wd_tmp" >! "$wd_config_file" 468 | wd_print_msg "$WD_GREEN" "Cleanup complete. ${count} warp point(s) removed" 469 | else 470 | wd_print_msg "$WD_BLUE" "Cleanup aborted" 471 | fi 472 | fi 473 | } 474 | 475 | wd_export_static_named_directories() { 476 | if [[ ! -z $WD_EXPORT ]] 477 | then 478 | command grep '^[0-9a-zA-Z_-]\+:' "$wd_config_file" | sed -e "s,~,$HOME," -e 's/:/=/' | while read -r warpdir ; do 479 | hash -d "$warpdir" 480 | done 481 | fi 482 | } 483 | 484 | WD_CONFIG=${WD_CONFIG:-$HOME/.warprc} 485 | local WD_QUIET=0 486 | local WD_EXIT_CODE=0 487 | 488 | # Parse 'meta' options first to avoid the need to have them before 489 | # other commands. The `-D` flag consumes recognized options so that 490 | # the actual command parsing won't be affected. 491 | 492 | zparseopts -D -E \ 493 | c:=wd_alt_config -config:=wd_alt_config \ 494 | q=wd_quiet_mode -quiet=wd_quiet_mode \ 495 | v=wd_print_version -version=wd_print_version \ 496 | f=wd_force_mode -force=wd_force_mode 497 | 498 | if [[ ! -z $wd_print_version ]] 499 | then 500 | echo "wd version $WD_VERSION" 501 | fi 502 | 503 | # set the config file from variable or default 504 | typeset wd_config_file=${WD_CONFIG:-$HOME/.warprc} 505 | if [[ ! -z $wd_alt_config ]] 506 | then 507 | # prefer the flag if provided 508 | wd_config_file=$wd_alt_config[2] 509 | fi 510 | 511 | # check if config file exists 512 | if [ ! -e "$wd_config_file" ] 513 | then 514 | # if not, create config file 515 | touch "$wd_config_file" 516 | else 517 | wd_export_static_named_directories 518 | fi 519 | 520 | # disable extendedglob for the complete wd execution time 521 | setopt | grep -q extendedglob 522 | wd_extglob_is_set=$? 523 | if (( wd_extglob_is_set == 0 )); then 524 | setopt noextendedglob 525 | fi 526 | 527 | # load warp points 528 | typeset -A points 529 | while read -r line 530 | do 531 | arr=(${(s,:,)line}) 532 | key=${arr[1]} 533 | # join the rest, in case the path contains colons 534 | val=${(j,:,)arr[2,-1]} 535 | 536 | points[$key]=$val 537 | done < "$wd_config_file" 538 | 539 | # get opts 540 | args=$(getopt -o a:r:c:lhs -l add:,rm:,clean,list,ls:,open:,path:,help,show -- $*) 541 | 542 | # check if no arguments were given, and that version is not set 543 | if [[ ($? -ne 0 || $#* -eq 0) && -z $wd_print_version ]] 544 | then 545 | wd_print_usage 546 | 547 | # check if config file is writeable 548 | elif [ ! -w "$wd_config_file" ] 549 | then 550 | # do nothing 551 | # can't run `exit`, as this would exit the executing shell 552 | wd_exit_fail "\'$wd_config_file\' is not writeable." 553 | 554 | else 555 | # parse rest of options 556 | local wd_o 557 | for wd_o 558 | do 559 | case "$wd_o" 560 | in 561 | "-a"|"--add"|"add") 562 | wd_add "$2" "$wd_force_mode" 563 | break 564 | ;; 565 | "-b"|"browse") 566 | wd_browse 567 | break 568 | ;; 569 | "-c"|"--addcd"|"addcd") 570 | wd_addcd "$2" "$3" "$wd_force_mode" 571 | break 572 | ;; 573 | "-e"|"export") 574 | wd_export_static_named_directories 575 | break 576 | ;; 577 | "-r"|"--remove"|"rm") 578 | # Passes all the arguments as a single string separated by whitespace to wd_remove 579 | wd_remove "${@:2}" 580 | break 581 | ;; 582 | "-l"|"list") 583 | wd_list_all 584 | break 585 | ;; 586 | "-ls"|"ls") 587 | wd_ls "$2" 588 | break 589 | ;; 590 | "-o"|"--open"|"open") 591 | wd_open "$2" 592 | break 593 | ;; 594 | "-p"|"--path"|"path") 595 | wd_path "$2" 596 | break 597 | ;; 598 | "-h"|"--help"|"help") 599 | wd_print_usage 600 | break 601 | ;; 602 | "-s"|"--show"|"show") 603 | wd_show "$2" 604 | break 605 | ;; 606 | "-c"|"--clean"|"clean") 607 | wd_clean "$wd_force_mode" 608 | break 609 | ;; 610 | *) 611 | wd_warp "$wd_o" "$2" 612 | break 613 | ;; 614 | --) 615 | break 616 | ;; 617 | esac 618 | done 619 | fi 620 | 621 | ## garbage collection 622 | # if not, next time warp will pick up variables from this run 623 | # remember, there's no sub shell 624 | 625 | if (( wd_extglob_is_set == 0 )); then 626 | setopt extendedglob 627 | fi 628 | 629 | unset wd_extglob_is_set 630 | unset wd_warp 631 | unset wd_add 632 | unset wd_addcd 633 | unset wd_remove 634 | unset wd_show 635 | unset wd_list_all 636 | unset wd_print_msg 637 | unset wd_yesorno 638 | unset wd_print_usage 639 | unset wd_alt_config 640 | #unset wd_config_file do not unset this - breaks keybind 641 | unset wd_quiet_mode 642 | unset wd_print_version 643 | unset wd_force_mode 644 | unset wd_export_static_named_directories 645 | unset wd_o 646 | 647 | unset args 648 | unset points 649 | unset val &> /dev/null # fixes issue #1 650 | 651 | return $WD_EXIT_CODE 652 | --------------------------------------------------------------------------------