diff --git a/.editorconfig b/.editorconfig index ddabb17..51c4765 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,11 +8,3 @@ indent_size = 4 [*.md] indent_style = space - -[*.rb] -indent_style = space -indent_size = 2 - -[*.yml] -indent_style = space -indent_size = 2 diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md deleted file mode 100644 index 7663df6..0000000 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: bug -assignees: '' - ---- - -### Describe the bug - - -### To Reproduce -Steps to reproduce the behavior: - - - -```sh -% zsh -df -% source path/to/zsh-autosuggestions.zsh -% ... # what do you do to reproduce? -``` - -### Expected behavior - - -### Screenshots - - -### Desktop - - OS + distribution: - - Zsh version: - - Plugin version: - -### Additional context - diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 5874625..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: enhancement -assignees: '' - ---- - -### Is your feature request related to a problem? Please describe. - - -### Describe the solution you'd like - - -### Describe alternatives you've considered - - -### Additional context - diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml deleted file mode 100644 index ec7bc75..0000000 --- a/.github/workflows/integration.yml +++ /dev/null @@ -1,51 +0,0 @@ -on: [push, pull_request] -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true -env: - IMAGE_CACHE_PATH: /tmp/.image-cache - IMAGE_CACHE_NAME: zsh-autosuggestions-test -jobs: - determine-versions: - runs-on: ubuntu-22.04 - outputs: - versions: ${{ steps.set-versions.outputs.versions }} - steps: - - uses: actions/checkout@v3 - - id: set-versions - run: | - echo "versions=$( - grep "^[^#]" ZSH_VERSIONS \ - | sed -E 's/(^|$)/"/g' \ - | paste -sd ',' - \ - | sed -e 's/^/[/' -e 's/$/]/' - )" >> $GITHUB_OUTPUT - test: - needs: determine-versions - runs-on: ubuntu-22.04 - strategy: - matrix: - version: ${{ fromJson(needs.determine-versions.outputs.versions) }} - steps: - - uses: actions/checkout@v3 - - name: Docker image cache - id: image-cache - uses: actions/cache@v3 - with: - path: ${{ env.IMAGE_CACHE_PATH }} - key: image-cache-${{ matrix.version }}-${{ hashFiles('Dockerfile', 'install_test_zsh.sh', 'Gemfile.lock') }} - - name: Load cached docker image if available - if: ${{ steps.image-cache.outputs.cache-hit }} - run: gunzip < $IMAGE_CACHE_PATH/$IMAGE_CACHE_NAME.tar.gz | docker load - - name: Build the docker image if necessary - if: ${{ !steps.image-cache.outputs.cache-hit }} - run: | - docker build --build-arg TEST_ZSH_VERSION=${{ matrix.version }} -t $IMAGE_CACHE_NAME . - mkdir -p $IMAGE_CACHE_PATH - docker save $IMAGE_CACHE_NAME | gzip > $IMAGE_CACHE_PATH/$IMAGE_CACHE_NAME.tar.gz - - name: Run the tests - run: | - docker run --rm \ - -v $PWD:/zsh-autosuggestions \ - $IMAGE_CACHE_NAME \ - make test diff --git a/.gitignore b/.gitignore deleted file mode 100644 index d8decde..0000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# zsh word code files -*.zwc diff --git a/.rspec b/.rspec deleted file mode 100644 index 43ae203..0000000 --- a/.rspec +++ /dev/null @@ -1,3 +0,0 @@ ---color ---require spec_helper ---format documentation diff --git a/.rubocop.yml b/.rubocop.yml deleted file mode 100644 index 97facac..0000000 --- a/.rubocop.yml +++ /dev/null @@ -1,30 +0,0 @@ -# Rails: -# Enabled: true - -AllCops: - TargetRubyVersion: 2.3 - Include: - - '**/Rakefile' - - '**/config.ru' - - '**/Gemfile' - -Metrics/LineLength: - Max: 120 - -Style/Documentation: - Enabled: false - -Style/DotPosition: - EnforcedStyle: trailing - -Style/FrozenStringLiteralComment: - Enabled: false - -Style/Lambda: - Enabled: false - -Style/MultilineMethodCallIndentation: - EnforcedStyle: indented - -Style/TrailingUnderscoreVariable: - Enabled: false diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index aedc15b..0000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -2.5.3 diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 30c7735..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,124 +0,0 @@ -# Changelog - -## v0.7.1 -- Clear POSTDISPLAY instead of unsetting (#634) -- Always reset async file descriptor after consuming it (#630) -- Always use builtin `exec` (#628) -- Add `history-beginning-search-*-end` widgets to clear widget list (#619) -- Switch CI from Circle CI to GitHub Actions - -## v0.7.0 -- Enable asynchronous mode by default (#498) -- No longer wrap user widgets starting with `autosuggest-` prefix (#496) -- Fix a bug wrapping widgets that modify the buffer (#541) - - -## v0.6.4 -- Fix `vi-forward-char` triggering a bell when using it to accept a suggestion (#488) -- New configuration option to skip completion suggestions when buffer matches a pattern (#487) -- New configuration option to ignore history entries matching a pattern (#456) - -## v0.6.3 -- Fixed bug moving cursor to end of buffer after accepting suggestion (#453) - -## v0.6.2 -- Fixed bug deleting the last character in the buffer in vi mode (#450) -- Degrade gracefully when user doesn't have `zsh/system` module installed (#447) - -## v0.6.1 -- Fixed bug occurring when `_complete` had been aliased (#443) - -## v0.6.0 -- Added `completion` suggestion strategy powered by completion system (#111) -- Allow setting `ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE` to an empty string (#422) -- Don't fetch suggestions after copy-earlier-word (#439) -- Allow users to unignore zle-\* widgets (e.g. zle-line-init) (#432) - - -## v0.5.2 -- Allow disabling automatic widget re-binding for better performance (#418) -- Fix async suggestions when `SH_WORD_SPLIT` is set -- Refactor async mode to use process substitution instead of zpty (#417) - -## v0.5.1 -- Speed up widget rebinding (#413) -- Clean up global variable creations (#403) -- Respect user's set options when running original widget (#402) - -## v0.5.0 -- Don't overwrite config with default values (#335) -- Support fallback strategies by supplying array to suggestion config var -- Rename "default" suggestion strategy to "history" to name it based on what it actually does -- Reset opts in some functions affected by `GLOB_SUBST` (#334) -- Support widgets starting with dashes (ex: `-a-widget`) (#337) -- Skip async tests in zsh versions less than 5.0.8 because of reliability issues -- Fix handling of newline + carriage return in async pty (#333) - - -## v0.4.3 -- Avoid bell when accepting suggestions with `autosuggest-accept` (#228) -- Don't fetch suggestions after [up,down]-line-or-beginning-search (#227, #241) -- We are now running CI against new 5.5.1 version -- Fix partial-accept in vi mode (#188) -- Fix suggestion disappearing on fast movement after switching to `vicmd` mode (#290) -- Fix issue rotating through kill ring with `yank-pop` (#301) -- Fix issue creating new pty for async mode when previous pty is not properly cleaned up (#249) - -## v0.4.2 -- Fix bug in zsh versions older than 5.0.8 (#296) -- Officially support back to zsh v4.3.11 - -## v0.4.1 -- Switch to [[ and (( conditionals instead of [ (#257) -- Avoid warnnestedvar warnings with `typeset -g` (#275) -- Replace tabs with spaces in yaml (#268) -- Clean up and fix escaping of special characters (#267) -- Add `emacs-forward-word` to default list of partial accept widgets (#246) - -## v0.4.0 -- High-level integration tests using RSpec and tmux -- Add continuous integration with Circle CI -- Experimental support for asynchronous suggestions (#170) -- Fix problems with multi-line suggestions (#225) -- Optimize case where manually typing in suggestion -- Avoid wrapping any zle-\* widgets (#206) -- Remove support for deprecated options from v0.0.x -- Handle history entries that begin with dashes -- Gracefully handle being sourced multiple times (#126) -- Add enable/disable/toggle widgets to disable/enable suggestions (#219) - - -## v0.3.3 -- Switch from $history array to fc builtin for better performance with large HISTFILEs (#164) -- Fix tilde handling when extended_glob is set (#168) -- Add config option for maximum buffer length to fetch suggestions for (#178) -- Add config option for list of widgets to ignore (#184) -- Don't fetch a new suggestion unless a modification widget actually modifies the buffer (#183) - -## v0.3.2 -- Test runner now supports running specific tests and choosing zsh binary -- Return code from original widget is now correctly passed through (#135) -- Add `vi-add-eol` to list of accept widgets (#143) -- Escapes widget names within evals to fix problems with irregular widget names (#152) -- Plugin now clears suggestion while within a completion menu (#149) -- .plugin file no longer relies on symbolic link support, fixing issues on Windows (#156) - -## v0.3.1 - -- Fixes issue with `vi-next-char` not accepting suggestion (#137). -- Fixes global variable warning when WARN_CREATE_GLOBAL option enabled (#133). -- Split out a separate test file for each widget. - -## v0.3.0 - -- Adds `autosuggest-execute` widget (PR #124). -- Adds concept of suggestion "strategies" for different ways of fetching suggestions. -- Adds "match_prev_cmd" strategy (PR #131). -- Uses git submodules for testing dependencies. -- Lots of test cleanup. -- Various bug fixes for zsh 5.0.x and `sh_word_split` option. - - -## v0.2.17 - -Start of changelog. diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index f5dd3c4..0000000 --- a/Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -FROM ruby:2.5.3-alpine - -ARG TEST_ZSH_VERSION -RUN : "${TEST_ZSH_VERSION:?}" - -RUN apk add --no-cache autoconf -RUN apk add --no-cache libtool -RUN apk add --no-cache libcap-dev -RUN apk add --no-cache pcre-dev -RUN apk add --no-cache curl -RUN apk add --no-cache build-base -RUN apk add --no-cache ncurses-dev -RUN apk add --no-cache tmux - -WORKDIR /zsh-autosuggestions - -ADD install_test_zsh.sh ./ -RUN ./install_test_zsh.sh - -ADD Gemfile Gemfile.lock ./ -RUN bundle install diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 8b5deec..0000000 --- a/Gemfile +++ /dev/null @@ -1,5 +0,0 @@ -source 'https://rubygems.org' - -gem 'rspec' -gem 'rspec-wait' -gem 'pry-byebug' diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 63ee778..0000000 --- a/Gemfile.lock +++ /dev/null @@ -1,41 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - byebug (9.0.5) - coderay (1.1.1) - diff-lcs (1.3) - method_source (0.8.2) - pry (0.10.4) - coderay (~> 1.1.0) - method_source (~> 0.8.1) - slop (~> 3.4) - pry-byebug (3.4.0) - byebug (~> 9.0) - pry (~> 0.10) - rspec (3.5.0) - rspec-core (~> 3.5.0) - rspec-expectations (~> 3.5.0) - rspec-mocks (~> 3.5.0) - rspec-core (3.5.4) - rspec-support (~> 3.5.0) - rspec-expectations (3.5.0) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.5.0) - rspec-mocks (3.5.0) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.5.0) - rspec-support (3.5.0) - rspec-wait (0.0.9) - rspec (>= 3, < 4) - slop (3.6.0) - -PLATFORMS - ruby - -DEPENDENCIES - pry-byebug - rspec - rspec-wait - -BUNDLED WITH - 1.13.6 diff --git a/INSTALL.md b/INSTALL.md deleted file mode 100644 index 7f0a395..0000000 --- a/INSTALL.md +++ /dev/null @@ -1,82 +0,0 @@ -# Installation - -* [Packages](#packages) -* [Antigen](#antigen) -* [Oh My Zsh](#oh-my-zsh) -* [HomeBrew](#homebrew) -* [Manual](#manual-git-clone) - -## Packages - -| System | Package | -| ------------- | ------------- | -| Alpine Linux | [zsh-autosuggestions](https://pkgs.alpinelinux.org/packages?name=zsh-autosuggestions) | -| Debian / Ubuntu | [zsh-autosuggestions OBS repository](https://software.opensuse.org/download.html?project=shells%3Azsh-users%3Azsh-autosuggestions&package=zsh-autosuggestions) | -| Fedora / CentOS / RHEL / Scientific Linux | [zsh-autosuggestions OBS repository](https://software.opensuse.org/download.html?project=shells%3Azsh-users%3Azsh-autosuggestions&package=zsh-autosuggestions) | -| OpenSUSE / SLE | [zsh-autosuggestions OBS repository](https://software.opensuse.org/download.html?project=shells%3Azsh-users%3Azsh-autosuggestions&package=zsh-autosuggestions) | -| Arch Linux / Manjaro / Antergos / Hyperbola | [zsh-autosuggestions](https://www.archlinux.org/packages/zsh-autosuggestions), [zsh-autosuggestions-git](https://aur.archlinux.org/packages/zsh-autosuggestions-git) | -| NixOS | [zsh-autosuggestions](https://github.com/NixOS/nixpkgs/blob/master/pkgs/by-name/zs/zsh-autosuggestions/package.nix) | -| Void Linux | [zsh-autosuggestions](https://github.com/void-linux/void-packages/blob/master/srcpkgs/zsh-autosuggestions/template) | -| Mac OS | [homebrew](https://formulae.brew.sh/formula/zsh-autosuggestions) | -| NetBSD | [pkgsrc](http://ftp.netbsd.org/pub/pkgsrc/current/pkgsrc/shells/zsh-autosuggestions/README.html) | -| FreeBSD | [pkg](https://cgit.freebsd.org/ports/tree/shells/zsh-autosuggestions) | - -## Antigen - -1. Add the following to your `.zshrc`: - - ```sh - antigen bundle zsh-users/zsh-autosuggestions - ``` - -2. Start a new terminal session. - -## Oh My Zsh - -1. Clone this repository into `$ZSH_CUSTOM/plugins` (by default `~/.oh-my-zsh/custom/plugins`) - - ```sh - git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions - ``` - -2. Add the plugin to the list of plugins for Oh My Zsh to load (inside `~/.zshrc`): - - ```sh - plugins=( - # other plugins... - zsh-autosuggestions - ) - ``` - -3. Start a new terminal session. - -## Homebrew - -1. Install command: - ```sh - brew install zsh-autosuggestions - ``` - -2. To activate the autosuggestions, add the following at the end of your .zshrc: - - ```sh - source $(brew --prefix)/share/zsh-autosuggestions/zsh-autosuggestions.zsh - ``` - -3. Start a new terminal session. - -## Manual (Git Clone) - -1. Clone this repository somewhere on your machine. This guide will assume `~/.zsh/zsh-autosuggestions`. - - ```sh - git clone https://github.com/zsh-users/zsh-autosuggestions ~/.zsh/zsh-autosuggestions - ``` - -2. Add the following to your `.zshrc`: - - ```sh - source ~/.zsh/zsh-autosuggestions/zsh-autosuggestions.zsh - ``` - -3. Start a new terminal session. diff --git a/LICENSE b/LICENSE index 7ea78cc..ee52ee2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ Copyright (c) 2013 Thiago de Arruda -Copyright (c) 2016-2021 Eric Freese +Copyright (c) 2016 Eric Freese Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/Makefile b/Makefile index 6f5431e..9d3ae6c 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,13 @@ -SRC_DIR := ./src +SRC_DIR := ./src +SCRIPT_DIR := ./script SRC_FILES := \ $(SRC_DIR)/config.zsh \ - $(SRC_DIR)/util.zsh \ + $(SRC_DIR)/deprecated.zsh \ $(SRC_DIR)/bind.zsh \ $(SRC_DIR)/highlight.zsh \ $(SRC_DIR)/widgets.zsh \ - $(SRC_DIR)/strategies/*.zsh \ - $(SRC_DIR)/fetch.zsh \ - $(SRC_DIR)/async.zsh \ + $(SRC_DIR)/suggestion.zsh \ $(SRC_DIR)/start.zsh HEADER_FILES := \ @@ -18,18 +17,25 @@ HEADER_FILES := \ LICENSE PLUGIN_TARGET := zsh-autosuggestions.zsh +OH_MY_ZSH_LINK_TARGET := zsh-autosuggestions.plugin.zsh -all: $(PLUGIN_TARGET) +ALL_TARGETS := \ + $(PLUGIN_TARGET) \ + $(OH_MY_ZSH_LINK_TARGET) + +all: $(ALL_TARGETS) $(PLUGIN_TARGET): $(HEADER_FILES) $(SRC_FILES) cat $(HEADER_FILES) | sed -e 's/^/# /g' > $@ cat $(SRC_FILES) >> $@ +$(OH_MY_ZSH_LINK_TARGET): $(PLUGIN_TARGET) + ln -s $(PLUGIN_TARGET) $@ + .PHONY: clean clean: - rm $(PLUGIN_TARGET) + rm $(ALL_TARGETS) .PHONY: test test: all - @test -n "$$TEST_ZSH_BIN" && echo "Testing zsh binary: $(TEST_ZSH_BIN)" || true - bundle exec rspec $(TESTS) + $(SCRIPT_DIR)/test.zsh diff --git a/README.md b/README.md index a8c1b6c..4364419 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,45 @@ _[Fish](http://fishshell.com/)-like fast/unobtrusive autosuggestions for zsh._ -It suggests commands as you type based on history and completions. - -Requirements: Zsh v4.3.11 or later - -[![Chat on Gitter](https://img.shields.io/gitter/room/zsh-users/zsh-autosuggestions.svg)](https://gitter.im/zsh-users/zsh-autosuggestions) - - +It suggests commands as you type, based on command history. ## Installation -See [INSTALL.md](INSTALL.md). +### Manual + +1. Clone this repository somewhere on your machine. This guide will assume `~/.zsh/zsh-autosuggestions`. + + ```sh + git clone git://github.com/tarruda/zsh-autosuggestions ~/.zsh/zsh-autosuggestions + ``` + +2. Add the following to your `.zshrc`: + + ```sh + source ~/.zsh/zsh-autosuggestions/zsh-autosuggestions.zsh + ``` + +3. Start a new terminal session. + + +### Oh My Zsh + +1. Clone this repository into `$ZSH_CUSTOM/plugins` (by default `~/.oh-my-zsh/custom/plugins`) + + ```sh + git clone git://github.com/tarruda/zsh-autosuggestions $ZSH_CUSTOM/plugins/zsh-autosuggestions + ``` + +2. Add the plugin to the list of plugins for Oh My Zsh to load: + + ```sh + plugins=(zsh-autosuggestions) + ``` + +3. Start a new terminal session. + +**Note:** There is an open issue ([#102](https://github.com/tarruda/zsh-autosuggestions/issues/102)) when using this plugin with `bracketed-paste-magic`, which is enabled by default by Oh My Zsh. See the comments in that issue for a workaround. ## Usage @@ -27,35 +54,14 @@ If you invoke the `forward-word` widget, it will partially accept the suggestion ## Configuration -You may want to override the default global config variables. Default values of these variables can be found [here](src/config.zsh). +You may want to override the default global config variables after sourcing the plugin. Default values of these variables can be found [here](src/config.zsh). **Note:** If you are using Oh My Zsh, you can put this configuration in a file in the `$ZSH_CUSTOM` directory. See their comments on [overriding internals](https://github.com/robbyrussell/oh-my-zsh/wiki/Customization#overriding-internals). ### Suggestion Highlight Style -Set `ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE` to configure the style that the suggestion is shown with. The default is `fg=8`, which will set the foreground color to color 8 from the [256-color palette](https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.svg). If your terminal only supports 8 colors, you will need to use a number between 0 and 7. - -Background color can also be set, and the suggestion can be styled bold, underlined, or standout. For example, this would show suggestions with bold, underlined, pink text on a cyan background: - -```sh -ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=#ff00ff,bg=cyan,bold,underline" -``` - -For more info, read the Character Highlighting section of the zsh manual: `man zshzle` or [online](http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Character-Highlighting). - -**Note:** Some iTerm2 users have reported [not being able to see the suggestions](https://github.com/zsh-users/zsh-autosuggestions/issues/416#issuecomment-486516333). If this affects you, the problem is likely caused by incorrect color settings. In order to correct this, go into iTerm2's setting, navigate to profile > colors and make sure that the colors for Basic Colors > Background and ANSI Colors > Bright Black are **different**. - - -### Suggestion Strategy - -`ZSH_AUTOSUGGEST_STRATEGY` is an array that specifies how suggestions should be generated. The strategies in the array are tried successively until a suggestion is found. There are currently three built-in strategies to choose from: - -- `history`: Chooses the most recent match from history. -- `completion`: Chooses a suggestion based on what tab-completion would suggest. (requires `zpty` module, which is included with zsh since 4.0.1) -- `match_prev_cmd`: Like `history`, but chooses the most recent match whose preceding history item matches the most recently executed command ([more info](src/strategies/match_prev_cmd.zsh)). Note that this strategy won't work as expected with ZSH options that don't preserve the history order such as `HIST_IGNORE_ALL_DUPS` or `HIST_EXPIRE_DUPS_FIRST`. - -For example, setting `ZSH_AUTOSUGGEST_STRATEGY=(history completion)` will first try to find a suggestion from your history, but, if it can't find a match, will find a suggestion from the completion engine. +Set `ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE` to configure the style that the suggestion is shown with. The default is `fg=8`. ### Widget Mapping @@ -64,54 +70,19 @@ This plugin works by triggering custom behavior when certain [zle widgets](http: - `ZSH_AUTOSUGGEST_CLEAR_WIDGETS`: Widgets in this array will clear the suggestion when invoked. - `ZSH_AUTOSUGGEST_ACCEPT_WIDGETS`: Widgets in this array will accept the suggestion when invoked. -- `ZSH_AUTOSUGGEST_EXECUTE_WIDGETS`: Widgets in this array will execute the suggestion when invoked. - `ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS`: Widgets in this array will partially accept the suggestion when invoked. -- `ZSH_AUTOSUGGEST_IGNORE_WIDGETS`: Widgets in this array will not trigger any custom behavior. -Widgets that modify the buffer and are not found in any of these arrays will fetch a new suggestion after they are invoked. +Widgets not in any of these lists will update the suggestion when invoked. **Note:** A widget shouldn't belong to more than one of the above arrays. -### Disabling suggestion for large buffers - -Set `ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE` to an integer value to disable autosuggestion for large buffers. The default is unset, which means that autosuggestion will be tried for any buffer size. Recommended value is 20. -This can be useful when pasting large amount of text in the terminal, to avoid triggering autosuggestion for strings that are too long. - -### Asynchronous Mode - -Suggestions are fetched asynchronously by default in zsh versions 5.0.8 and greater. To disable asynchronous suggestions and fetch them synchronously instead, `unset ZSH_AUTOSUGGEST_USE_ASYNC` after sourcing the plugin. - -Alternatively, if you are using a version of zsh older than 5.0.8 and want to enable asynchronous mode, set the `ZSH_AUTOSUGGEST_USE_ASYNC` variable after sourcing the plugin (it can be set to anything). Note that there is [a bug](https://github.com/zsh-users/zsh-autosuggestions/issues/364#issuecomment-481423232) in versions of zsh older than 5.0.8 where ctrl + c will fail to reset the prompt immediately after fetching a suggestion asynchronously. - -### Disabling automatic widget re-binding - -Set `ZSH_AUTOSUGGEST_MANUAL_REBIND` (it can be set to anything) to disable automatic widget re-binding on each precmd. This can be a big boost to performance, but you'll need to handle re-binding yourself if any of the widget lists change or if you or another plugin wrap any of the autosuggest widgets. To re-bind widgets, run `_zsh_autosuggest_bind_widgets`. - -### Ignoring history suggestions that match a pattern - -Set `ZSH_AUTOSUGGEST_HISTORY_IGNORE` to a [glob pattern](http://zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Operators) to prevent offering suggestions for history entries that match the pattern. For example, set it to `"cd *"` to never suggest any `cd` commands from history. Or set to `"?(#c50,)"` to never suggest anything 50 characters or longer. - -**Note:** This only affects the `history` and `match_prev_cmd` suggestion strategies. - -### Skipping completion suggestions for certain cases - -Set `ZSH_AUTOSUGGEST_COMPLETION_IGNORE` to a [glob pattern](http://zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Operators) to prevent offering completion suggestions when the buffer matches that pattern. For example, set it to `"git *"` to disable completion suggestions for git subcommands. - -**Note:** This only affects the `completion` suggestion strategy. - - ### Key Bindings -This plugin provides a few widgets that you can use with `bindkey`: +This plugin provides two widgets that you can use with `bindkey`: 1. `autosuggest-accept`: Accepts the current suggestion. -2. `autosuggest-execute`: Accepts and executes the current suggestion. -3. `autosuggest-clear`: Clears the current suggestion. -4. `autosuggest-fetch`: Fetches a suggestion (works even when suggestions are disabled). -5. `autosuggest-disable`: Disables suggestions. -6. `autosuggest-enable`: Re-enables suggestions. -7. `autosuggest-toggle`: Toggles between enabled/disabled suggestions. +2. `autosuggest-clear`: Clears the current suggestion. For example, this would bind ctrl + space to accept the current suggestion. @@ -120,9 +91,41 @@ bindkey '^ ' autosuggest-accept ``` +## Compatibility With Other ZLE Plugins + +### [`zsh-history-substring-search`](https://github.com/zsh-users/zsh-history-substring-search) + +When the buffer is empty and one of the `history-substring-search-up/down` widgets is invoked, it will call the `up/down-line-or-history` widget. If the `up/down-line-or-history` widgets are in `ZSH_AUTOSUGGEST_CLEAR_WIDGETS` (the list of widgets that clear the suggestion), this can create an infinite recursion, crashing the shell session. + +For best results, you'll want to remove `up-line-or-history` and `down-line-or-history` from `ZSH_AUTOSUGGEST_CLEAR_WIDGETS`: + +``` +# Remove *-line-or-history widgets from list of widgets that clear the autosuggestion to avoid conflict with history-substring-search-* widgets +ZSH_AUTOSUGGEST_CLEAR_WIDGETS=("${(@)ZSH_AUTOSUGGEST_CLEAR_WIDGETS:#(up|down)-line-or-history}") +``` + +Additionally, the `history-substring-search-up` and `history-substring-search-down` widgets are not bound by default. You'll probably want to add them to `ZSH_AUTOSUGGEST_CLEAR_WIDGETS` so that the suggestion will be cleared when you start searching through history: + +```sh +# Add history-substring-search-* widgets to list of widgets that clear the autosuggestion +ZSH_AUTOSUGGEST_CLEAR_WIDGETS+=(history-substring-search-up history-substring-search-down) +``` + +For example: + +```sh +source ~/.zsh/zsh-autosuggestions/zsh-autosuggestions.zsh +source ~/Code/zsh-history-substring-search/zsh-history-substring-search.zsh + +ZSH_AUTOSUGGEST_CLEAR_WIDGETS=("${(@)ZSH_AUTOSUGGEST_CLEAR_WIDGETS:#(up|down)-line-or-history}") +ZSH_AUTOSUGGEST_CLEAR_WIDGETS+=(history-substring-search-up history-substring-search-down) +``` + + ## Troubleshooting -If you have a problem, please search through [the list of issues on GitHub](https://github.com/zsh-users/zsh-autosuggestions/issues?q=) to see if someone else has already reported it. +If you have a problem, please search through [the list of issues on GitHub](https://github.com/tarruda/zsh-autosuggestions/issues) to see if someone else has already reported it. + ### Reporting an Issue @@ -130,7 +133,7 @@ Before reporting an issue, please try temporarily disabling sections of your con When reporting an issue, please include: -- The smallest, simplest `.zshrc` configuration that will reproduce the problem. See [this comment](https://github.com/zsh-users/zsh-autosuggestions/issues/102#issuecomment-180944764) for a good example of what this means. +- The smallest, simplest `.zshrc` configuration that will reproduce the problem. See [this comment](https://github.com/tarruda/zsh-autosuggestions/issues/102#issuecomment-180944764) for a good example of what this means. - The version of zsh you're using (`zsh --version`) - Which operating system you're running @@ -157,7 +160,6 @@ Edit the source files in `src/`. Run `make` to build `zsh-autosuggestions.zsh` f Pull requests are welcome! If you send a pull request, please: -- Request to merge into the `develop` branch (*NOT* `master`) - Match the existing coding conventions. - Include helpful comments to keep the barrier-to-entry low for people new to the project. - Write tests that cover your code as much as possible. @@ -165,21 +167,9 @@ Pull requests are welcome! If you send a pull request, please: ### Testing -Tests are written in ruby using the [`rspec`](http://rspec.info/) framework. They use [`tmux`](https://tmux.github.io/) to drive a pseudoterminal, sending simulated keystrokes and making assertions on the terminal content. +Testing is performed with [`shunit2`](https://github.com/kward/shunit2) (v2.1.6). Documentation can be found [here](http://shunit2.googlecode.com/svn/trunk/source/2.1/doc/shunit2.html). -Test files live in `spec/`. To run the tests, run `make test`. To run a specific test, run `TESTS=spec/some_spec.rb make test`. You can also specify a `zsh` binary to use by setting the `TEST_ZSH_BIN` environment variable (ex: `TEST_ZSH_BIN=/bin/zsh make test`). - -It's possible to run the tests for any supported version of zsh in a Docker image by building an image from the provided Dockerfile. To build the docker image for a specific version of zsh (where `` below is substituted with the contents of a line from the [`ZSH_VERSIONS`](ZSH_VERSIONS) file), run: - -```sh -docker build --build-arg TEST_ZSH_VERSION= -t zsh-autosuggestions-test . -``` - -After building the image, run the tests via: - -```sh -docker run -it -v $PWD:/zsh-autosuggestions zsh-autosuggestions-test make test -``` +The test script lives at `script/test.zsh`. To run the tests, run `make test`. ## License diff --git a/URL b/URL index 4e2bd94..00a4330 100644 --- a/URL +++ b/URL @@ -1 +1 @@ -https://github.com/zsh-users/zsh-autosuggestions +https://github.com/tarruda/zsh-autosuggestions diff --git a/VERSION b/VERSION index 63f2359..b88fb90 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v0.7.1 +v0.2.5 diff --git a/ZSH_VERSIONS b/ZSH_VERSIONS deleted file mode 100644 index 23006db..0000000 --- a/ZSH_VERSIONS +++ /dev/null @@ -1,14 +0,0 @@ -# Zsh releases to run tests against -# See https://github.com/zsh-users/zsh/releases -4.3.11 -5.0.2 -5.0.8 -5.1.1 -5.2 -5.3.1 -5.4.2 -5.5.1 -5.6.2 -5.7.1 -5.8.1 -5.9 diff --git a/install_test_zsh.sh b/install_test_zsh.sh deleted file mode 100755 index 6cac9f5..0000000 --- a/install_test_zsh.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh - -set -ex - -mkdir zsh-build -cd zsh-build - -curl -L https://api.github.com/repos/zsh-users/zsh/tarball/zsh-$TEST_ZSH_VERSION | tar xz --strip=1 - -./Util/preconfig -./configure --enable-pcre \ - --enable-cap \ - --enable-multibyte \ - --with-term-lib='ncursesw tinfo' \ - --with-tcsetpgrp - -make install.bin -make install.modules -make install.fns - -cd .. - -rm -rf zsh-build diff --git a/script/test.zsh b/script/test.zsh new file mode 100755 index 0000000..23f2793 --- /dev/null +++ b/script/test.zsh @@ -0,0 +1,288 @@ +#!/usr/bin/env zsh + +SCRIPT_DIR=$(dirname "$0") +TEST_DIR=$SCRIPT_DIR/../test +DIST_DIR=$SCRIPT_DIR/../ + +# Use stub.sh for stubbing/mocking +source $TEST_DIR/stub-1.0.2.sh + +source $DIST_DIR/zsh-autosuggestions.zsh + +#--------------------------------------------------------------------# +# Highlighting # +#--------------------------------------------------------------------# + +testHighlightDefaultStyle() { + assertEquals \ + "fg=8" \ + "$ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE" +} + +testHighlightApplyWithSuggestion() { + orig_style=ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE + ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=4" + + BUFFER="ec" + POSTDISPLAY="ho hello" + region_highlight=("0 2 fg=1") + + _zsh_autosuggest_highlight_apply + + assertEquals \ + "highlight did not use correct style" \ + "0 2 fg=1 2 10 $ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE" \ + "$region_highlight" + + assertEquals \ + "higlight was not saved to be removed later" \ + "2 10 $ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE" \ + "$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT" + + ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE=orig_style +} + +testHighlightApplyWithoutSuggestion() { + BUFFER="echo hello" + POSTDISPLAY="" + region_highlight=("0 4 fg=1") + + _zsh_autosuggest_highlight_apply + + assertEquals \ + "region_highlight was modified" \ + "0 4 fg=1" \ + "$region_highlight" + + assertNull \ + "last highlight region was not cleared" \ + "$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT" +} + +testHighlightReset() { + BUFFER="ec" + POSTDISPLAY="ho hello" + region_highlight=("0 1 fg=1" "2 10 fg=8" "1 2 fg=1") + _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT="2 10 fg=8" + + _zsh_autosuggest_highlight_reset + + assertEquals \ + "last highlight region was not removed" \ + "0 1 fg=1 1 2 fg=1" \ + "$region_highlight" + + assertNull \ + "last highlight variable was not cleared" \ + "$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT" +} + +#--------------------------------------------------------------------# +# Widgets # +#--------------------------------------------------------------------# + +testWidgetFunctionClear() { + BUFFER="ec" + POSTDISPLAY="ho hello" + + _zsh_autosuggest_clear + + assertEquals \ + "BUFFER was modified" \ + "ec" \ + "$BUFFER" + + assertNull \ + "POSTDISPLAY was not cleared" \ + "$POSTDISPLAY" +} + +testWidgetFunctionModify() { + BUFFER="" + POSTDISPLAY="" + + stub_and_eval \ + _zsh_autosuggest_invoke_original_widget \ + 'BUFFER+="e"' + + stub_and_echo \ + _zsh_autosuggest_suggestion \ + "echo hello" + + _zsh_autosuggest_modify + + assertTrue \ + "original widget not invoked" \ + "stub_called _zsh_autosuggest_invoke_original_widget" + + assertEquals \ + "BUFFER was not modified" \ + "e" \ + "$BUFFER" + + assertEquals \ + "POSTDISPLAY does not contain suggestion" \ + "cho hello" \ + "$POSTDISPLAY" + + restore _zsh_autosuggest_invoke_original_widget + restore _zsh_autosuggest_suggestion +} + +testWidgetFunctionAcceptCursorAtEnd() { + BUFFER="echo" + POSTDISPLAY=" hello" + CURSOR=4 + + stub _zsh_autosuggest_invoke_original_widget + + _zsh_autosuggest_accept + + assertTrue \ + "original widget not invoked" \ + "stub_called _zsh_autosuggest_invoke_original_widget" + + assertEquals \ + "BUFFER was not modified" \ + "echo hello" \ + "$BUFFER" + + assertEquals \ + "POSTDISPLAY was not cleared" \ + "" \ + "$POSTDISPLAY" +} + +testWidgetFunctionAcceptCursorNotAtEnd() { + BUFFER="echo" + POSTDISPLAY=" hello" + CURSOR=2 + + stub _zsh_autosuggest_invoke_original_widget + + _zsh_autosuggest_accept + + assertTrue \ + "original widget not invoked" \ + "stub_called _zsh_autosuggest_invoke_original_widget" + + assertEquals \ + "BUFFER was modified" \ + "echo" \ + "$BUFFER" + + assertEquals \ + "POSTDISPLAY was modified" \ + " hello" \ + "$POSTDISPLAY" +} + +testWidgetFunctionPartialAcceptCursorMovesOutOfBuffer() { + BUFFER="ec" + POSTDISPLAY="ho hello" + CURSOR=1 + + stub_and_eval \ + _zsh_autosuggest_invoke_original_widget \ + 'CURSOR=5; LBUFFER="echo "; RBUFFER="hello"' + + _zsh_autosuggest_partial_accept + + assertTrue \ + "original widget not invoked" \ + "stub_called _zsh_autosuggest_invoke_original_widget" + + assertEquals \ + "BUFFER was not modified correctly" \ + "echo " \ + "$BUFFER" + + assertEquals \ + "POSTDISPLAY was not modified correctly" \ + "hello" \ + "$POSTDISPLAY" +} + +testWidgetFunctionPartialAcceptCursorStaysInBuffer() { + BUFFER="echo hello" + POSTDISPLAY=" world" + CURSOR=1 + + stub_and_eval \ + _zsh_autosuggest_invoke_original_widget \ + 'CURSOR=5; LBUFFER="echo "; RBUFFER="hello"' + + _zsh_autosuggest_partial_accept + + assertTrue \ + "original widget not invoked" \ + "stub_called _zsh_autosuggest_invoke_original_widget" + + assertEquals \ + "BUFFER was modified" \ + "echo hello" \ + "$BUFFER" + + assertEquals \ + "POSTDISPLAY was modified" \ + " world" \ + "$POSTDISPLAY" +} + +testWidgetAccept() { + stub _zsh_autosuggest_highlight_reset + stub _zsh_autosuggest_accept + stub _zsh_autosuggest_highlight_apply + + # Call the function pointed to by the widget since we can't call + # the widget itself when zle is not active + ${widgets[autosuggest-accept]#*:} + + assertTrue \ + "autosuggest-accept widget does not exist" \ + "zle -l autosuggest-accept" + + assertTrue \ + "highlight_reset was not called" \ + "stub_called _zsh_autosuggest_highlight_reset" + + assertTrue \ + "widget function was not called" \ + "stub_called _zsh_autosuggest_accept" + + assertTrue \ + "highlight_apply was not called" \ + "stub_called _zsh_autosuggest_highlight_apply" +} + +testWidgetClear() { + stub _zsh_autosuggest_highlight_reset + stub _zsh_autosuggest_clear + stub _zsh_autosuggest_highlight_apply + + # Call the function pointed to by the widget since we can't call + # the widget itself when zle is not active + ${widgets[autosuggest-clear]#*:} + + assertTrue \ + "autosuggest-clear widget does not exist" \ + "zle -l autosuggest-clear" + + assertTrue \ + "highlight_reset was not called" \ + "stub_called _zsh_autosuggest_highlight_reset" + + assertTrue \ + "widget function was not called" \ + "stub_called _zsh_autosuggest_clear" + + assertTrue \ + "highlight_apply was not called" \ + "stub_called _zsh_autosuggest_highlight_apply" +} + +# For zsh compatibility +setopt shwordsplit +SHUNIT_PARENT=$0 + +source $TEST_DIR/shunit2-2.1.6/src/shunit2 diff --git a/spec/async_spec.rb b/spec/async_spec.rb deleted file mode 100644 index 0af7232..0000000 --- a/spec/async_spec.rb +++ /dev/null @@ -1,70 +0,0 @@ -context 'with asynchronous suggestions enabled' do - let(:options) { ["ZSH_AUTOSUGGEST_USE_ASYNC="] } - - describe '`up-line-or-beginning-search`' do - let(:before_sourcing) do - -> do - session. - run_command('autoload -U up-line-or-beginning-search'). - run_command('zle -N up-line-or-beginning-search'). - send_string('bindkey "'). - send_keys('C-v').send_keys('up'). - send_string('" up-line-or-beginning-search'). - send_keys('enter') - end - end - - it 'should show previous history entries' do - with_history( - 'echo foo', - 'echo bar', - 'echo baz' - ) do - session.clear_screen - 3.times { session.send_keys('up') } - wait_for { session.content }.to eq("echo foo") - end - end - end - - describe '`copy-earlier-word`' do - let(:before_sourcing) do - -> do - session. - run_command('autoload -Uz copy-earlier-word'). - run_command('zle -N copy-earlier-word'). - run_command('bindkey "^N" copy-earlier-word') - end - end - - it 'should cycle through previous words in the buffer' do - session.clear_screen - session.send_string('foo bar baz') - sleep 0.5 - session.send_keys('C-n') - wait_for { session.content }.to eq('foo bar bazbaz') - session.send_keys('C-n') - wait_for { session.content }.to eq('foo bar bazbar') - session.send_keys('C-n') - wait_for { session.content }.to eq('foo bar bazfoo') - end - end - - describe 'pressing ^C after fetching a suggestion' do - before do - skip 'Workaround does not work below v5.0.8' if session.zsh_version < Gem::Version.new('5.0.8') - end - - it 'terminates the prompt and begins a new one' do - session.send_keys('e') - sleep 0.5 - session.send_keys('C-c') - sleep 0.5 - session.send_keys('echo') - - wait_for { session.content }.to eq("e\necho") - end - end -end - - diff --git a/spec/integrations/auto_cd_spec.rb b/spec/integrations/auto_cd_spec.rb deleted file mode 100644 index 94bd24b..0000000 --- a/spec/integrations/auto_cd_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -describe 'with `AUTO_CD` option set' do - let(:after_sourcing) do - -> { - session.run_command('setopt AUTO_CD') - session.run_command('autoload compinit && compinit') - } - end - - it 'directory names are still completed' do - session.send_string('sr') - session.send_keys('C-i') - wait_for { session.content }.to eq('src/') - end -end diff --git a/spec/integrations/bracketed_paste_magic_spec.rb b/spec/integrations/bracketed_paste_magic_spec.rb deleted file mode 100644 index 41ff267..0000000 --- a/spec/integrations/bracketed_paste_magic_spec.rb +++ /dev/null @@ -1,43 +0,0 @@ -describe 'pasting using bracketed-paste-magic' do - let(:before_sourcing) do - -> do - session. - run_command('autoload -Uz bracketed-paste-magic'). - run_command('zle -N bracketed-paste bracketed-paste-magic') - end - end - - context 'with suggestions disabled while pasting' do - before do - session. - run_command('bpm_init() { zle autosuggest-disable }'). - run_command('bpm_finish() { zle autosuggest-enable }'). - run_command('zstyle :bracketed-paste-magic paste-init bpm_init'). - run_command('zstyle :bracketed-paste-magic paste-finish bpm_finish') - end - - it 'does not show an incorrect suggestion' do - with_history('echo hello') do - session.paste_string("echo #{'a' * 60}") - sleep 1 - expect(session.content).to eq("echo #{'a' * 60}") - end - end - end - - context 'with `bracketed-paste` added to the list of widgets that clear the suggestion' do - let(:options) { ['ZSH_AUTOSUGGEST_CLEAR_WIDGETS+=(bracketed-paste)'] } - - it 'does not retain an old suggestion' do - with_history ('echo foo') do - session.send_string('echo ') - wait_for { session.content }.to eq('echo foo') - session.paste_string('bar') - wait_for { session.content }.to eq('echo bar') - session.send_keys('C-a') # Any cursor movement works - sleep 1 - expect(session.content).to eq('echo bar') - end - end - end -end diff --git a/spec/integrations/client_zpty_spec.rb b/spec/integrations/client_zpty_spec.rb deleted file mode 100644 index b8abb37..0000000 --- a/spec/integrations/client_zpty_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -describe 'a running zpty command' do - let(:before_sourcing) { -> { session.run_command('zmodload zsh/zpty && zpty -b kitty cat') } } - - context 'when using `completion` strategy' do - let(:options) { ["ZSH_AUTOSUGGEST_STRATEGY=completion"] } - - it 'is not affected' do - session.send_keys('a').send_keys('C-h') - session.run_command('zpty -t kitty; echo $?') - - wait_for { session.content }.to end_with("\n0") - end - end -end diff --git a/spec/integrations/glob_subst_spec.rb b/spec/integrations/glob_subst_spec.rb deleted file mode 100644 index c3dd671..0000000 --- a/spec/integrations/glob_subst_spec.rb +++ /dev/null @@ -1,12 +0,0 @@ -describe 'with `GLOB_SUBST` option set' do - let(:after_sourcing) do - -> { - session.run_command('setopt GLOB_SUBST') - } - end - - it 'error messages are not printed' do - session.send_string('[[') - wait_for { session.content }.to eq('[[') - end -end diff --git a/spec/integrations/rebound_bracket_spec.rb b/spec/integrations/rebound_bracket_spec.rb deleted file mode 100644 index 8b420f0..0000000 --- a/spec/integrations/rebound_bracket_spec.rb +++ /dev/null @@ -1,13 +0,0 @@ -describe 'rebinding [' do - context 'initialized before sourcing the plugin' do - before do - session.run_command("function [ { $commands[\\[] \"$@\" }") - session.clear_screen - end - - it 'executes the custom behavior and the built-in behavior' do - session.send_string('asdf') - wait_for { session.content }.to eq('asdf') - end - end -end diff --git a/spec/integrations/vi_mode_spec.rb b/spec/integrations/vi_mode_spec.rb deleted file mode 100644 index 0a295c2..0000000 --- a/spec/integrations/vi_mode_spec.rb +++ /dev/null @@ -1,80 +0,0 @@ -describe 'when using vi mode' do - let(:before_sourcing) do - -> do - session.run_command('bindkey -v') - end - end - - describe 'moving the cursor after exiting insert mode' do - it 'should not clear the current suggestion' do - with_history('foobar foo') do - session. - send_string('foo'). - send_keys('escape'). - send_keys('h') - - wait_for { session.content }.to eq('foobar foo') - end - end - end - - describe '`vi-forward-word-end`' do - it 'should accept through the end of the current word' do - with_history('foobar foo') do - session. - send_string('foo'). - send_keys('escape'). - send_keys('e'). # vi-forward-word-end - send_keys('a'). # vi-add-next - send_string('baz') - - wait_for { session.content }.to eq('foobarbaz') - end - end - end - - describe '`vi-forward-word`' do - it 'should accept through the first character of the next word' do - with_history('foobar foo') do - session. - send_string('foo'). - send_keys('escape'). - send_keys('w'). # vi-forward-word - send_keys('a'). # vi-add-next - send_string('az') - - wait_for { session.content }.to eq('foobar faz') - end - end - end - - describe '`vi-find-next-char`' do - it 'should accept through the next occurrence of the character' do - with_history('foobar foo') do - session. - send_string('foo'). - send_keys('escape'). - send_keys('f'). # vi-find-next-char - send_keys('o'). - send_keys('a'). # vi-add-next - send_string('b') - - wait_for { session.content }.to eq('foobar fob') - end - end - end - - describe '`vi-delete`' do - it 'should be able to remove the last character in the buffer' do - skip 'deleting last char did not work below zsh version 5.0.8' if session.zsh_version < Gem::Version.new('5.0.8') - - session. - send_string('echo foo'). - send_keys('escape'). - send_keys('d'). - send_keys('l') - - wait_for { session.content }.to eq('echo fo') - end - end -end diff --git a/spec/integrations/wrapped_widget_spec.rb b/spec/integrations/wrapped_widget_spec.rb deleted file mode 100644 index 61dfc2d..0000000 --- a/spec/integrations/wrapped_widget_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -describe 'a wrapped widget' do - let(:widget) { 'backward-delete-char' } - - context 'initialized before sourcing the plugin' do - let(:before_sourcing) do - -> do - session. - run_command("_orig_#{widget}() { zle .#{widget} }"). - run_command("zle -N orig-#{widget} _orig_#{widget}"). - run_command("#{widget}-magic() { zle orig-#{widget}; BUFFER+=b }"). - run_command("zle -N #{widget} #{widget}-magic") - end - end - - it 'executes the custom behavior and the built-in behavior' do - with_history('foobar', 'foodar') do - session.send_string('food').send_keys('C-h') - wait_for { session.content }.to eq('foobar') - end - end - end - - context 'initialized after sourcing the plugin' do - before do - session. - run_command("zle -N orig-#{widget} ${widgets[#{widget}]#*:}"). - run_command("#{widget}-magic() { zle orig-#{widget}; BUFFER+=b }"). - run_command("zle -N #{widget} #{widget}-magic"). - clear_screen - end - - it 'executes the custom behavior and the built-in behavior' do - with_history('foobar', 'foodar') do - session.send_string('food').send_keys('C-h') - wait_for { session.content }.to eq('foobar') - end - end - end -end diff --git a/spec/integrations/zle_input_stack_spec.rb b/spec/integrations/zle_input_stack_spec.rb deleted file mode 100644 index 12cfbc7..0000000 --- a/spec/integrations/zle_input_stack_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -describe 'using `zle -U`' do - let(:before_sourcing) do - -> do - session. - run_command('_zsh_autosuggest_strategy_test() { sleep 1; _zsh_autosuggest_strategy_history "$1" }'). - run_command('foo() { zle -U - "echo hello" }; zle -N foo; bindkey ^B foo') - end - end - - let(:options) { ['unset ZSH_AUTOSUGGEST_USE_ASYNC', 'ZSH_AUTOSUGGEST_STRATEGY=test'] } - - # TODO: This is only possible with the $KEYS_QUEUED_COUNT widget parameter, coming soon... - xit 'does not fetch a suggestion for every inserted character' do - session.send_keys('C-b') - wait_for { session.content }.to eq('echo hello') - end - - it 'shows a suggestion when the widget completes' do - with_history('echo hello world') do - session.send_keys('C-b') - wait_for { session.content(esc_seqs: true) }.to match(/\Aecho hello\e\[[0-9]+m world/) - end - end -end diff --git a/spec/kill_ring_spec.rb b/spec/kill_ring_spec.rb deleted file mode 100644 index 4d0178f..0000000 --- a/spec/kill_ring_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -context 'with some items in the kill ring' do - before do - session. - send_string('echo foo'). - send_keys('C-u'). - send_string('echo bar'). - send_keys('C-u') - end - - describe '`yank-pop`' do - it 'should cycle through all items in the kill ring' do - session.send_keys('C-y') - wait_for { session.content }.to eq('echo bar') - - session.send_keys('escape').send_keys('y') - wait_for { session.content }.to eq('echo foo') - - session.send_keys('escape').send_keys('y') - wait_for { session.content }.to eq('echo bar') - end - end -end - diff --git a/spec/line_init_spec.rb b/spec/line_init_spec.rb deleted file mode 100644 index 826277f..0000000 --- a/spec/line_init_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -context 'with zle-line-init unignored' do - let(:after_sourcing) do - -> do - session. - run_command('setopt extendedglob'). - run_command('ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(${(@)ZSH_AUTOSUGGEST_IGNORE_WIDGETS:#zle-\*} zle-\^line-init)'). - run_command('zle-line-init() { BUFFER="echo" }') - end - end - - it 'should fetch a suggestion on each line initialization' do - with_history('echo foo') do - session.run_command('zle -N zle-line-init') - wait_for { session.content }.to end_with('echo foo') - end - end -end diff --git a/spec/multi_line_spec.rb b/spec/multi_line_spec.rb deleted file mode 100644 index 364780a..0000000 --- a/spec/multi_line_spec.rb +++ /dev/null @@ -1,8 +0,0 @@ -describe 'a multi-line suggestion' do - it 'should be displayed on multiple lines' do - with_history("echo \"\n\"") do - session.send_keys('e') - wait_for { session.content }.to eq("echo \"\n\"") - end - end -end diff --git a/spec/options/buffer_max_size_spec.rb b/spec/options/buffer_max_size_spec.rb deleted file mode 100644 index 29ca8bc..0000000 --- a/spec/options/buffer_max_size_spec.rb +++ /dev/null @@ -1,30 +0,0 @@ -describe 'a suggestion' do - let(:term_opts) { { width: 200 } } - let(:long_command) { "echo #{'a' * 100}" } - - around do |example| - with_history(long_command) { example.run } - end - - it 'is provided for any buffer length' do - session.send_string(long_command[0...-1]) - wait_for { session.content }.to eq(long_command) - end - - context 'when ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE is specified' do - let(:buffer_max_size) { 10 } - let(:options) { ["ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=#{buffer_max_size}"] } - - it 'is provided when the buffer is shorter than the specified length' do - session.send_string(long_command[0...(buffer_max_size - 1)]) - wait_for { session.content }.to eq(long_command) - end - - it 'is provided when the buffer is equal to the specified length' do - session.send_string(long_command[0...(buffer_max_size)]) - wait_for { session.content }.to eq(long_command) - end - - it 'is not provided when the buffer is longer than the specified length' - end -end diff --git a/spec/options/highlight_style_spec.rb b/spec/options/highlight_style_spec.rb deleted file mode 100644 index a7e39b3..0000000 --- a/spec/options/highlight_style_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -describe 'a displayed suggestion' do - it 'is shown in the default style' - - describe 'when ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE is set to a zle_highlight string' do - it 'is shown in the specified style' - end -end diff --git a/spec/options/original_widget_prefix_spec.rb b/spec/options/original_widget_prefix_spec.rb deleted file mode 100644 index a4b6e98..0000000 --- a/spec/options/original_widget_prefix_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -describe 'an original zle widget' do - context 'is accessible with the default prefix' - - context 'when ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX is set' do - it 'is accessible with the specified prefix' - end -end diff --git a/spec/options/strategy_spec.rb b/spec/options/strategy_spec.rb deleted file mode 100644 index 58562d0..0000000 --- a/spec/options/strategy_spec.rb +++ /dev/null @@ -1,55 +0,0 @@ -describe 'a suggestion for a given prefix' do - let(:history_strategy) { '_zsh_autosuggest_strategy_history() { suggestion="history" }' } - let(:foobar_strategy) { '_zsh_autosuggest_strategy_foobar() { [[ "foobar baz" = $1* ]] && suggestion="foobar baz" }' } - let(:foobaz_strategy) { '_zsh_autosuggest_strategy_foobaz() { [[ "foobaz bar" = $1* ]] && suggestion="foobaz bar" }' } - - let(:after_sourcing) do - -> do - session.run_command(history_strategy) - end - end - - it 'by default is determined by calling the `history` strategy function' do - session.send_string('h') - wait_for { session.content }.to eq('history') - end - - context 'when ZSH_AUTOSUGGEST_STRATEGY is set to an array' do - let(:after_sourcing) do - -> do - session. - run_command(foobar_strategy). - run_command(foobaz_strategy). - run_command('ZSH_AUTOSUGGEST_STRATEGY=(foobar foobaz)') - end - end - - it 'is determined by the first strategy function to return a suggestion' do - session.send_string('foo') - wait_for { session.content }.to eq('foobar baz') - - session.send_string('baz') - wait_for { session.content }.to eq('foobaz bar') - end - end - - context 'when ZSH_AUTOSUGGEST_STRATEGY is set to a string' do - let(:after_sourcing) do - -> do - session. - run_command(foobar_strategy). - run_command(foobaz_strategy). - run_command('ZSH_AUTOSUGGEST_STRATEGY="foobar foobaz"') - end - end - - it 'is determined by the first strategy function to return a suggestion' do - session.send_string('foo') - wait_for { session.content }.to eq('foobar baz') - - session.send_string('baz') - wait_for { session.content }.to eq('foobaz bar') - end - end -end - diff --git a/spec/options/widget_lists_spec.rb b/spec/options/widget_lists_spec.rb deleted file mode 100644 index 421b84e..0000000 --- a/spec/options/widget_lists_spec.rb +++ /dev/null @@ -1,121 +0,0 @@ -describe 'a zle widget' do - let(:widget) { 'my-widget' } - let(:before_sourcing) { -> { session.run_command("#{widget}() {}; zle -N #{widget}; bindkey ^B #{widget}") } } - - context 'when added to ZSH_AUTOSUGGEST_ACCEPT_WIDGETS' do - let(:options) { ["ZSH_AUTOSUGGEST_ACCEPT_WIDGETS+=(#{widget})"] } - - it 'accepts the suggestion and moves the cursor to the end of the buffer when invoked' do - with_history('echo hello') do - session.send_string('e') - wait_for { session.content }.to eq('echo hello') - session.send_keys('C-b') - wait_for { session.content(esc_seqs: true) }.to eq('echo hello') - wait_for { session.cursor }.to eq([10, 0]) - end - end - end - - context 'when added to ZSH_AUTOSUGGEST_CLEAR_WIDGETS' do - let(:options) { ["ZSH_AUTOSUGGEST_CLEAR_WIDGETS+=(#{widget})"] } - - it 'clears the suggestion when invoked' do - with_history('echo hello') do - session.send_string('e') - wait_for { session.content }.to eq('echo hello') - session.send_keys('C-b') - wait_for { session.content }.to eq('e') - end - end - end - - context 'when added to ZSH_AUTOSUGGEST_EXECUTE_WIDGETS' do - let(:options) { ["ZSH_AUTOSUGGEST_EXECUTE_WIDGETS+=(#{widget})"] } - - it 'executes the suggestion when invoked' do - with_history('echo hello') do - session.send_string('e') - wait_for { session.content }.to eq('echo hello') - session.send_keys('C-b') - wait_for { session.content }.to end_with("\nhello") - end - end - end - - context 'when added to ZSH_AUTOSUGGEST_IGNORE_WIDGETS' do - let(:options) { ["ZSH_AUTOSUGGEST_IGNORE_WIDGETS=(#{widget})"] } - - it 'should not be wrapped with an autosuggest widget' do - session.run_command("echo $widgets[#{widget}]") - wait_for { session.content }.to end_with("\nuser:#{widget}") - end - end - - context 'that moves the cursor forward' do - before { session.run_command("#{widget}() { zle forward-char }") } - - context 'when added to ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS' do - let(:options) { ["ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=(#{widget})"] } - - it 'accepts the suggestion as far as the cursor is moved when invoked' do - with_history('echo hello') do - session.send_string('e') - wait_for { session.content }.to start_with('echo hello') - session.send_keys('C-b') - wait_for { session.content(esc_seqs: true) }.to match(/\Aec\e\[[0-9]+mho hello/) - end - end - end - end - - context 'that modifies the buffer' do - before { session.run_command("#{widget}() { BUFFER=\"foo\" }") } - - context 'when not added to any of the widget lists' do - it 'modifies the buffer and fetches a new suggestion' do - with_history('foobar') do - session.send_keys('C-b') - wait_for { session.content }.to eq('foobar') - end - end - end - end -end - -describe 'a modification to the widget lists' do - let(:widget) { 'my-widget' } - let(:before_sourcing) { -> { session.run_command("#{widget}() {}; zle -N #{widget}; bindkey ^B #{widget}") } } - before { session.run_command("ZSH_AUTOSUGGEST_ACCEPT_WIDGETS+=(#{widget})") } - - it 'takes effect on the next cmd line' do - with_history('echo hello') do - session.send_string('e') - wait_for { session.content }.to eq('echo hello') - session.send_keys('C-b') - wait_for { session.content(esc_seqs: true) }.to eq('echo hello') - end - end - - context 'when manual rebind is enabled' do - let(:options) { ["ZSH_AUTOSUGGEST_MANUAL_REBIND=true"] } - - it 'does not take effect until bind command is re-run' do - with_history('echo hello') do - session.send_string('e') - wait_for { session.content }.to eq('echo hello') - session.send_keys('C-b') - sleep 1 - expect(session.content(esc_seqs: true)).not_to eq('echo hello') - - session.send_keys('C-c') - session.run_command('_zsh_autosuggest_bind_widgets').clear_screen - wait_for { session.content }.to eq('') - - session.send_string('e') - wait_for { session.content }.to eq('echo hello') - session.send_keys('C-b') - wait_for { session.content(esc_seqs: true) }.to eq('echo hello') - end - end - end -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb deleted file mode 100644 index dc1abb0..0000000 --- a/spec/spec_helper.rb +++ /dev/null @@ -1,54 +0,0 @@ -require 'pry' -require 'rspec/wait' -require 'terminal_session' -require 'tempfile' - -RSpec.shared_context 'terminal session' do - let(:term_opts) { {} } - let(:session) { TerminalSession.new(term_opts) } - let(:before_sourcing) { -> {} } - let(:after_sourcing) { -> {} } - let(:options) { [] } - - around do |example| - before_sourcing.call - session.run_command(['source zsh-autosuggestions.zsh', *options].join('; ')) - after_sourcing.call - session.clear_screen - - example.run - - session.destroy - end - - def with_history(*commands, &block) - Tempfile.create do |f| - f.write(commands.map{|c| c.gsub("\n", "\\\n")}.join("\n")) - f.flush - - session.run_command('fc -p') - session.run_command("fc -R #{f.path}") - - session.clear_screen - - yield block - - session.send_keys('C-c') - session.run_command('fc -P') - end - end -end - -RSpec.configure do |config| - config.expect_with :rspec do |expectations| - expectations.include_chain_clauses_in_custom_matcher_descriptions = true - end - - config.mock_with :rspec do |mocks| - mocks.verify_partial_doubles = true - end - - config.wait_timeout = 2 - - config.include_context 'terminal session' -end diff --git a/spec/strategies/completion_spec.rb b/spec/strategies/completion_spec.rb deleted file mode 100644 index 92794d6..0000000 --- a/spec/strategies/completion_spec.rb +++ /dev/null @@ -1,72 +0,0 @@ -describe 'the `completion` suggestion strategy' do - let(:options) { ['ZSH_AUTOSUGGEST_STRATEGY=completion'] } - let(:before_sourcing) do - -> do - session. - run_command('autoload compinit && compinit'). - run_command('_foo() { compadd bar; compadd bat }'). - run_command('_num() { compadd two; compadd three }'). - run_command('compdef _foo baz'). - run_command('compdef _num one') - end - end - - it 'suggests the first completion result' do - session.send_string('baz ') - wait_for { session.content }.to eq('baz bar') - end - - it 'does not add extra carriage returns when prefix has a line feed' do - skip '`stty` does not work inside zpty below zsh version 5.0.3' if session.zsh_version < Gem::Version.new('5.0.3') - session.send_string('baz \\').send_keys('C-v', 'C-j') - wait_for { session.content }.to eq("baz \\\nbar") - end - - context 'when `_complete` is aliased' do - let(:before_sourcing) do - -> do - session. - run_command('autoload compinit && compinit'). - run_command('_foo() { compadd bar; compadd bat }'). - run_command('compdef _foo baz'). - run_command('alias _complete=_complete') - end - end - - it 'suggests the first completion result' do - session.send_string('baz ') - wait_for { session.content }.to eq('baz bar') - end - end - - context 'when ZSH_AUTOSUGGEST_COMPLETION_IGNORE is set to a pattern' do - let(:options) { ['ZSH_AUTOSUGGEST_STRATEGY=completion', 'ZSH_AUTOSUGGEST_COMPLETION_IGNORE="one *"'] } - - it 'makes suggestions when the buffer does not match the pattern' do - session.send_string('baz ') - wait_for { session.content }.to eq('baz bar') - end - - it 'does not make suggestions when the buffer matches the pattern' do - session.send_string('one t') - sleep 1 - expect(session.content).to eq('one t') - end - end - - context 'when async mode is enabled' do - let(:options) { ['ZSH_AUTOSUGGEST_USE_ASYNC=true', 'ZSH_AUTOSUGGEST_STRATEGY=completion'] } - - it 'suggests the first completion result' do - session.send_string('baz ') - wait_for { session.content }.to eq('baz bar') - end - - it 'does not add extra carriage returns when prefix has a line feed' do - skip '`stty` does not work inside zpty below zsh version 5.0.3' if session.zsh_version < Gem::Version.new('5.0.3') - session.send_string('baz \\').send_keys('C-v', 'C-j') - wait_for { session.content }.to eq("baz \\\nbar") - end - end -end - diff --git a/spec/strategies/history_spec.rb b/spec/strategies/history_spec.rb deleted file mode 100644 index eee8efd..0000000 --- a/spec/strategies/history_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'strategies/special_characters_helper' - -describe 'the `history` suggestion strategy' do - it 'suggests the last matching history entry' do - with_history('ls foo', 'ls bar', 'echo baz') do - session.send_string('ls') - wait_for { session.content }.to eq('ls bar') - end - end - - context 'when ZSH_AUTOSUGGEST_HISTORY_IGNORE is set to a pattern' do - let(:options) { ['ZSH_AUTOSUGGEST_HISTORY_IGNORE="* bar"'] } - - it 'does not make suggestions that match the pattern' do - with_history('ls foo', 'ls bar', 'echo baz') do - session.send_string('ls') - wait_for { session.content }.to eq('ls foo') - end - end - end - - include_examples 'special characters' -end diff --git a/spec/strategies/match_prev_cmd_spec.rb b/spec/strategies/match_prev_cmd_spec.rb deleted file mode 100644 index c435f16..0000000 --- a/spec/strategies/match_prev_cmd_spec.rb +++ /dev/null @@ -1,34 +0,0 @@ -require 'strategies/special_characters_helper' - -describe 'the `match_prev_cmd` strategy' do - let(:options) { ['ZSH_AUTOSUGGEST_STRATEGY=match_prev_cmd'] } - - let(:history) { [ - 'echo what', - 'ls foo', - 'echo what', - 'ls bar', - 'ls baz', - 'echo what' - ] } - - it 'suggests the last matching history entry after the previous command' do - with_history(*history) do - session.send_string('ls') - wait_for { session.content }.to eq('ls bar') - end - end - - context 'when ZSH_AUTOSUGGEST_HISTORY_IGNORE is set to a pattern' do - let(:options) { ['ZSH_AUTOSUGGEST_STRATEGY=match_prev_cmd', 'ZSH_AUTOSUGGEST_HISTORY_IGNORE="* bar"'] } - - it 'does not make suggestions that match the pattern' do - with_history(*history) do - session.send_string('ls') - wait_for { session.content }.to eq('ls foo') - end - end - end - - include_examples 'special characters' -end diff --git a/spec/strategies/special_characters_helper.rb b/spec/strategies/special_characters_helper.rb deleted file mode 100644 index eb1f0cd..0000000 --- a/spec/strategies/special_characters_helper.rb +++ /dev/null @@ -1,75 +0,0 @@ -shared_examples 'special characters' do - describe 'a special character in the buffer should be treated like any other character' do - it 'asterisk' do - with_history('echo "hello*"', 'echo "hello."') do - session.send_string('echo "hello*') - wait_for { session.content }.to eq('echo "hello*"') - end - end - - it 'question mark' do - with_history('echo "hello?"', 'echo "hello."') do - session.send_string('echo "hello?') - wait_for { session.content }.to eq('echo "hello?"') - end - end - - it 'backslash' do - with_history('echo "hello\nworld"') do - session.send_string('echo "hello\\') - wait_for { session.content }.to eq('echo "hello\nworld"') - end - end - - it 'double backslash' do - with_history('echo "\\\\"') do - session.send_string('echo "\\\\') - wait_for { session.content }.to eq('echo "\\\\"') - end - end - - it 'tilde' do - with_history('echo ~/foo') do - session.send_string('echo ~') - wait_for { session.content }.to eq('echo ~/foo') - end - end - - it 'parentheses' do - with_history('echo "$(ls foo)"') do - session.send_string('echo "$(') - wait_for { session.content }.to eq('echo "$(ls foo)"') - end - end - - it 'square bracket' do - with_history('echo "$history[123]"') do - session.send_string('echo "$history[') - wait_for { session.content }.to eq('echo "$history[123]"') - session.send_string('123]') - wait_for { session.content }.to eq('echo "$history[123]"') - end - end - - it 'octothorpe' do - with_history('echo "#yolo"') do - session.send_string('echo "#') - wait_for { session.content }.to eq('echo "#yolo"') - end - end - - it 'caret' do - with_history('echo "^A"', 'echo "^B"') do - session.send_string('echo "^A') - wait_for { session.content }.to eq('echo "^A"') - end - end - - it 'dash' do - with_history('-foo() {}') do - session.send_string('-') - wait_for { session.content }.to eq('-foo() {}') - end - end - end -end diff --git a/spec/terminal_session.rb b/spec/terminal_session.rb deleted file mode 100644 index f91ee6c..0000000 --- a/spec/terminal_session.rb +++ /dev/null @@ -1,99 +0,0 @@ -require 'securerandom' - -class TerminalSession - ZSH_BIN = ENV['TEST_ZSH_BIN'] || 'zsh' - - def initialize(opts = {}) - opts = { - width: 80, - height: 24, - prompt: '', - term: 'xterm-256color', - zsh_bin: ZSH_BIN - }.merge(opts) - - @opts = opts - - cmd="PS1=\"#{opts[:prompt]}\" TERM=#{opts[:term]} #{ZSH_BIN} -f" - tmux_command("new-session -d -x #{opts[:width]} -y #{opts[:height]} '#{cmd}'") - end - - def zsh_version - @zsh_version ||= Gem::Version.new(`#{ZSH_BIN} -c 'echo -n $ZSH_VERSION'`) - end - - def tmux_socket_name - @tmux_socket_name ||= SecureRandom.hex(6) - end - - def run_command(command) - send_string(command) - send_keys('enter') - - self - end - - def send_string(str) - tmux_command("send-keys -t 0 -l -- '#{str.gsub("'", "\\'")}'") - - self - end - - def send_keys(*keys) - tmux_command("send-keys -t 0 #{keys.join(' ')}") - - self - end - - def paste_string(str) - tmux_command("set-buffer -- '#{str}'") - tmux_command("paste-buffer -dpr -t 0") - - self - end - - def content(esc_seqs: false) - cmd = 'capture-pane -p -t 0' - cmd += ' -e' if esc_seqs - tmux_command(cmd).strip - end - - def clear_screen - send_keys('C-l') - - i = 0 - until content == opts[:prompt] || i > 20 do - sleep(0.1) - i = i + 1 - end - - self - end - - def destroy - tmux_command('kill-session') - end - - def cursor - tmux_command("display-message -t 0 -p '\#{cursor_x},\#{cursor_y}'"). - strip. - split(','). - map(&:to_i) - end - - def attach! - tmux_command('attach-session') - end - - private - - attr_reader :opts - - def tmux_command(cmd) - out = `tmux -u -L #{tmux_socket_name} #{cmd}` - - raise("tmux error running: '#{cmd}'") unless $?.success? - - out - end -end diff --git a/spec/widgets/disable_spec.rb b/spec/widgets/disable_spec.rb deleted file mode 100644 index b387a59..0000000 --- a/spec/widgets/disable_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -describe 'the `autosuggest-disable` widget' do - before do - session.run_command('bindkey ^B autosuggest-disable') - end - - it 'disables suggestions and clears the suggestion' do - with_history('echo hello') do - session.send_string('echo') - wait_for { session.content }.to eq('echo hello') - - session.send_keys('C-b') - wait_for { session.content }.to eq('echo') - - session.send_string(' h') - sleep 1 - expect(session.content).to eq('echo h') - end - end -end diff --git a/spec/widgets/enable_spec.rb b/spec/widgets/enable_spec.rb deleted file mode 100644 index 3ad35a8..0000000 --- a/spec/widgets/enable_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -describe 'the `autosuggest-enable` widget' do - before do - session. - run_command('typeset -g _ZSH_AUTOSUGGEST_DISABLED'). - run_command('bindkey ^B autosuggest-enable') - end - - it 'enables suggestions and fetches a suggestion' do - with_history('echo hello') do - session.send_string('e') - sleep 1 - expect(session.content).to eq('e') - - session.send_keys('C-b') - session.send_string('c') - wait_for { session.content }.to eq('echo hello') - end - end - - context 'invoked on an empty buffer' do - it 'does not fetch a suggestion' do - with_history('echo hello') do - session.send_keys('C-b') - sleep 1 - expect(session.content).to eq('') - end - end - end - - context 'invoked on a non-empty buffer' do - it 'fetches a suggestion' do - with_history('echo hello') do - session.send_string('e') - sleep 1 - expect(session.content).to eq('e') - - session.send_keys('C-b') - wait_for { session.content }.to eq('echo hello') - end - end - end -end diff --git a/spec/widgets/fetch_spec.rb b/spec/widgets/fetch_spec.rb deleted file mode 100644 index eb8f2ba..0000000 --- a/spec/widgets/fetch_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -describe 'the `autosuggest-fetch` widget' do - context 'when suggestions are disabled' do - before do - session. - run_command('bindkey ^B autosuggest-disable'). - run_command('bindkey ^F autosuggest-fetch'). - send_keys('C-b') - end - - it 'will fetch and display a suggestion' do - with_history('echo hello') do - session.send_string('echo h') - sleep 1 - expect(session.content).to eq('echo h') - - session.send_keys('C-f') - wait_for { session.content }.to eq('echo hello') - - session.send_string('e') - wait_for { session.content }.to eq('echo hello') - end - end - end -end diff --git a/spec/widgets/toggle_spec.rb b/spec/widgets/toggle_spec.rb deleted file mode 100644 index 8f9f3c3..0000000 --- a/spec/widgets/toggle_spec.rb +++ /dev/null @@ -1,26 +0,0 @@ -describe 'the `autosuggest-toggle` widget' do - before do - session.run_command('bindkey ^B autosuggest-toggle') - end - - it 'toggles suggestions' do - with_history('echo world', 'echo hello') do - session.send_string('echo') - wait_for { session.content }.to eq('echo hello') - - session.send_keys('C-b') - wait_for { session.content }.to eq('echo') - - session.send_string(' h') - sleep 1 - expect(session.content).to eq('echo h') - - session.send_keys('C-b') - wait_for { session.content }.to eq('echo hello') - - session.send_keys('C-h') - session.send_string('w') - wait_for { session.content }.to eq('echo world') - end - end -end diff --git a/src/async.zsh b/src/async.zsh deleted file mode 100644 index e179734..0000000 --- a/src/async.zsh +++ /dev/null @@ -1,77 +0,0 @@ - -#--------------------------------------------------------------------# -# Async # -#--------------------------------------------------------------------# - -_zsh_autosuggest_async_request() { - zmodload zsh/system 2>/dev/null # For `$sysparams` - - typeset -g _ZSH_AUTOSUGGEST_ASYNC_FD _ZSH_AUTOSUGGEST_CHILD_PID - - # If we've got a pending request, cancel it - if [[ -n "$_ZSH_AUTOSUGGEST_ASYNC_FD" ]] && { true <&$_ZSH_AUTOSUGGEST_ASYNC_FD } 2>/dev/null; then - # Close the file descriptor and remove the handler - builtin exec {_ZSH_AUTOSUGGEST_ASYNC_FD}<&- - zle -F $_ZSH_AUTOSUGGEST_ASYNC_FD - - # We won't know the pid unless the user has zsh/system module installed - if [[ -n "$_ZSH_AUTOSUGGEST_CHILD_PID" ]]; then - # Zsh will make a new process group for the child process only if job - # control is enabled (MONITOR option) - if [[ -o MONITOR ]]; then - # Send the signal to the process group to kill any processes that may - # have been forked by the suggestion strategy - kill -TERM -$_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null - else - # Kill just the child process since it wasn't placed in a new process - # group. If the suggestion strategy forked any child processes they may - # be orphaned and left behind. - kill -TERM $_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null - fi - fi - fi - - # Fork a process to fetch a suggestion and open a pipe to read from it - builtin exec {_ZSH_AUTOSUGGEST_ASYNC_FD}< <( - # Tell parent process our pid - echo $sysparams[pid] - - # Fetch and print the suggestion - local suggestion - _zsh_autosuggest_fetch_suggestion "$1" - echo -nE "$suggestion" - ) - - # There's a weird bug here where ^C stops working unless we force a fork - # See https://github.com/zsh-users/zsh-autosuggestions/issues/364 - autoload -Uz is-at-least - is-at-least 5.8 || command true - - # Read the pid from the child process - read _ZSH_AUTOSUGGEST_CHILD_PID <&$_ZSH_AUTOSUGGEST_ASYNC_FD - - # When the fd is readable, call the response handler - zle -F "$_ZSH_AUTOSUGGEST_ASYNC_FD" _zsh_autosuggest_async_response -} - -# Called when new data is ready to be read from the pipe -# First arg will be fd ready for reading -# Second arg will be passed in case of error -_zsh_autosuggest_async_response() { - emulate -L zsh - - local suggestion - - if [[ -z "$2" || "$2" == "hup" ]]; then - # Read everything from the fd and give it as a suggestion - IFS='' read -rd '' -u $1 suggestion - zle autosuggest-suggest -- "$suggestion" - - # Close the fd - builtin exec {1}<&- - fi - - # Always remove the handler - zle -F "$1" - _ZSH_AUTOSUGGEST_ASYNC_FD= -} diff --git a/src/bind.zsh b/src/bind.zsh index 1dde137..01a211a 100644 --- a/src/bind.zsh +++ b/src/bind.zsh @@ -3,86 +3,49 @@ # Widget Helpers # #--------------------------------------------------------------------# -_zsh_autosuggest_incr_bind_count() { - typeset -gi bind_count=$((_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]+1)) - _ZSH_AUTOSUGGEST_BIND_COUNTS[$1]=$bind_count -} - # Bind a single widget to an autosuggest widget, saving a reference to the original widget _zsh_autosuggest_bind_widget() { - typeset -gA _ZSH_AUTOSUGGEST_BIND_COUNTS - local widget=$1 local autosuggest_action=$2 local prefix=$ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX - local -i bind_count - # Save a reference to the original widget case $widgets[$widget] in # Already bound - user:_zsh_autosuggest_(bound|orig)_*) - bind_count=$((_ZSH_AUTOSUGGEST_BIND_COUNTS[$widget])) - ;; + user:_zsh_autosuggest_(widget|orig)_*);; # User-defined widget user:*) - _zsh_autosuggest_incr_bind_count $widget - zle -N $prefix$bind_count-$widget ${widgets[$widget]#*:} + zle -N $prefix$widget ${widgets[$widget]#*:} ;; # Built-in widget builtin) - _zsh_autosuggest_incr_bind_count $widget - eval "_zsh_autosuggest_orig_${(q)widget}() { zle .${(q)widget} }" - zle -N $prefix$bind_count-$widget _zsh_autosuggest_orig_$widget + eval "_zsh_autosuggest_orig_$widget() { zle .$widget }" + zle -N $prefix$widget _zsh_autosuggest_orig_$widget ;; # Completion widget completion:*) - _zsh_autosuggest_incr_bind_count $widget - eval "zle -C $prefix$bind_count-${(q)widget} ${${(s.:.)widgets[$widget]}[2,3]}" + eval "zle -C $prefix$widget ${${widgets[$widget]#*:}/:/ }" ;; esac - # Pass the original widget's name explicitly into the autosuggest - # function. Use this passed in widget name to call the original - # widget instead of relying on the $WIDGET variable being set - # correctly. $WIDGET cannot be trusted because other plugins call - # zle without the `-w` flag (e.g. `zle self-insert` instead of - # `zle self-insert -w`). - eval "_zsh_autosuggest_bound_${bind_count}_${(q)widget}() { - _zsh_autosuggest_widget_$autosuggest_action $prefix$bind_count-${(q)widget} \$@ - }" - # Create the bound widget - zle -N -- $widget _zsh_autosuggest_bound_${bind_count}_$widget + zle -N $widget _zsh_autosuggest_widget_$autosuggest_action } # Map all configured widgets to the right autosuggest widgets _zsh_autosuggest_bind_widgets() { - emulate -L zsh - - local widget - local ignore_widgets - - ignore_widgets=( - .\* - _\* - ${_ZSH_AUTOSUGGEST_BUILTIN_ACTIONS/#/autosuggest-} - $ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX\* - $ZSH_AUTOSUGGEST_IGNORE_WIDGETS - ) + local widget; # Find every widget we might want to bind and bind it appropriately - for widget in ${${(f)"$(builtin zle -la)"}:#${(j:|:)~ignore_widgets}}; do - if [[ -n ${ZSH_AUTOSUGGEST_CLEAR_WIDGETS[(r)$widget]} ]]; then + for widget in ${${(f)"$(builtin zle -la)"}:#(.*|_*|orig-*|autosuggest-*|$ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX*|zle-line-*|run-help|which-command|beep|set-local-history|yank)}; do + if [ ${ZSH_AUTOSUGGEST_CLEAR_WIDGETS[(r)$widget]} ]; then _zsh_autosuggest_bind_widget $widget clear - elif [[ -n ${ZSH_AUTOSUGGEST_ACCEPT_WIDGETS[(r)$widget]} ]]; then + elif [ ${ZSH_AUTOSUGGEST_ACCEPT_WIDGETS[(r)$widget]} ]; then _zsh_autosuggest_bind_widget $widget accept - elif [[ -n ${ZSH_AUTOSUGGEST_EXECUTE_WIDGETS[(r)$widget]} ]]; then - _zsh_autosuggest_bind_widget $widget execute - elif [[ -n ${ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS[(r)$widget]} ]]; then + elif [ ${ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS[(r)$widget]} ]; then _zsh_autosuggest_bind_widget $widget partial_accept else # Assume any unspecified widget might modify the buffer @@ -91,16 +54,11 @@ _zsh_autosuggest_bind_widgets() { done } -# Given the name of an original widget and args, invoke it, if it exists +# Given the name of a widget, invoke the original we saved, if it exists _zsh_autosuggest_invoke_original_widget() { - # Do nothing unless called with at least one arg - (( $# )) || return 0 + local original_widget_name="$ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX$WIDGET" - local original_widget_name="$1" - - shift - - if (( ${+widgets[$original_widget_name]} )); then + if [ $widgets[$original_widget_name] ]; then zle $original_widget_name -- $@ fi } diff --git a/src/config.zsh b/src/config.zsh index 32d32b2..c8d6f0a 100644 --- a/src/config.zsh +++ b/src/config.zsh @@ -6,90 +6,35 @@ # Color to use when highlighting suggestion # Uses format of `region_highlight` # More info: http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets -(( ! ${+ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE} )) && -typeset -g ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8' +ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8' # Prefix to use when saving original versions of bound widgets -(( ! ${+ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX} )) && -typeset -g ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig- - -# Strategies to use to fetch a suggestion -# Will try each strategy in order until a suggestion is returned -(( ! ${+ZSH_AUTOSUGGEST_STRATEGY} )) && { - typeset -ga ZSH_AUTOSUGGEST_STRATEGY - ZSH_AUTOSUGGEST_STRATEGY=(history) -} +ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig- # Widgets that clear the suggestion -(( ! ${+ZSH_AUTOSUGGEST_CLEAR_WIDGETS} )) && { - typeset -ga ZSH_AUTOSUGGEST_CLEAR_WIDGETS - ZSH_AUTOSUGGEST_CLEAR_WIDGETS=( - history-search-forward - history-search-backward - history-beginning-search-forward - history-beginning-search-backward - history-beginning-search-forward-end - history-beginning-search-backward-end - history-substring-search-up - history-substring-search-down - up-line-or-beginning-search - down-line-or-beginning-search - up-line-or-history - down-line-or-history - accept-line - copy-earlier-word - ) -} +ZSH_AUTOSUGGEST_CLEAR_WIDGETS=( + history-search-forward + history-search-backward + history-beginning-search-forward + history-beginning-search-backward + up-line-or-history + down-line-or-history + accept-line +) # Widgets that accept the entire suggestion -(( ! ${+ZSH_AUTOSUGGEST_ACCEPT_WIDGETS} )) && { - typeset -ga ZSH_AUTOSUGGEST_ACCEPT_WIDGETS - ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=( - forward-char - end-of-line - vi-forward-char - vi-end-of-line - vi-add-eol - ) -} - -# Widgets that accept the entire suggestion and execute it -(( ! ${+ZSH_AUTOSUGGEST_EXECUTE_WIDGETS} )) && { - typeset -ga ZSH_AUTOSUGGEST_EXECUTE_WIDGETS - ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=( - ) -} +ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=( + forward-char + end-of-line + vi-forward-char + vi-end-of-line +) # Widgets that accept the suggestion as far as the cursor moves -(( ! ${+ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS} )) && { - typeset -ga ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS - ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=( - forward-word - emacs-forward-word - vi-forward-word - vi-forward-word-end - vi-forward-blank-word - vi-forward-blank-word-end - vi-find-next-char - vi-find-next-char-skip - ) -} - -# Widgets that should be ignored (globbing supported but must be escaped) -(( ! ${+ZSH_AUTOSUGGEST_IGNORE_WIDGETS} )) && { - typeset -ga ZSH_AUTOSUGGEST_IGNORE_WIDGETS - ZSH_AUTOSUGGEST_IGNORE_WIDGETS=( - orig-\* - beep - run-help - set-local-history - which-command - yank - yank-pop - zle-\* - ) -} - -# Pty name for capturing completions for completion suggestion strategy -(( ! ${+ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME} )) && -typeset -g ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME=zsh_autosuggest_completion_pty +ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=( + forward-word + vi-forward-word + vi-forward-word-end + vi-forward-blank-word + vi-forward-blank-word-end +) diff --git a/src/deprecated.zsh b/src/deprecated.zsh new file mode 100644 index 0000000..5ebe9a9 --- /dev/null +++ b/src/deprecated.zsh @@ -0,0 +1,36 @@ + +#--------------------------------------------------------------------# +# Handle Deprecated Variables/Widgets # +#--------------------------------------------------------------------# + +_zsh_autosuggest_deprecated_warning() { + >&2 echo "zsh-autosuggestions: $@" +} + +_zsh_autosuggest_check_deprecated_config() { + if [ -n "$AUTOSUGGESTION_HIGHLIGHT_COLOR" ]; then + _zsh_autosuggest_deprecated_warning "AUTOSUGGESTION_HIGHLIGHT_COLOR is deprecated. Use ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE instead." + [ -z "$ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE" ] && ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE=$AUTOSUGGESTION_HIGHLIGHT_STYLE + unset AUTOSUGGESTION_HIGHLIGHT_STYLE + fi + + if [ -n "$AUTOSUGGESTION_HIGHLIGHT_CURSOR" ]; then + _zsh_autosuggest_deprecated_warning "AUTOSUGGESTION_HIGHLIGHT_CURSOR is deprecated." + unset AUTOSUGGESTION_HIGHLIGHT_CURSOR + fi + + if [ -n "$AUTOSUGGESTION_ACCEPT_RIGHT_ARROW" ]; then + _zsh_autosuggest_deprecated_warning "AUTOSUGGESTION_ACCEPT_RIGHT_ARROW is deprecated. The right arrow now accepts the suggestion by default." + unset AUTOSUGGESTION_ACCEPT_RIGHT_ARROW + fi +} + +_zsh_autosuggest_deprecated_start_widget() { + _zsh_autosuggest_deprecated_warning "The autosuggest-start widget is deprecated. For more info, see the README at https://github.com/tarruda/zsh-autosuggestions." + zle -D autosuggest-start + eval "zle-line-init() { + $(echo $functions[${widgets[zle-line-init]#*:}] | sed -e 's/zle autosuggest-start//g') + }" +} + +zle -N autosuggest-start _zsh_autosuggest_deprecated_start_widget diff --git a/src/fetch.zsh b/src/fetch.zsh deleted file mode 100644 index fef2715..0000000 --- a/src/fetch.zsh +++ /dev/null @@ -1,27 +0,0 @@ - -#--------------------------------------------------------------------# -# Fetch Suggestion # -#--------------------------------------------------------------------# -# Loops through all specified strategies and returns a suggestion -# from the first strategy to provide one. -# - -_zsh_autosuggest_fetch_suggestion() { - typeset -g suggestion - local -a strategies - local strategy - - # Ensure we are working with an array - strategies=(${=ZSH_AUTOSUGGEST_STRATEGY}) - - for strategy in $strategies; do - # Try to get a suggestion from this strategy - _zsh_autosuggest_strategy_$strategy "$1" - - # Ensure the suggestion matches the prefix - [[ "$suggestion" != "$1"* ]] && unset suggestion - - # Break once we've found a valid suggestion - [[ -n "$suggestion" ]] && break - done -} diff --git a/src/highlight.zsh b/src/highlight.zsh index 273c03d..41cc0ac 100644 --- a/src/highlight.zsh +++ b/src/highlight.zsh @@ -5,9 +5,7 @@ # If there was a highlight, remove it _zsh_autosuggest_highlight_reset() { - typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT - - if [[ -n "$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT" ]]; then + if [ -n "$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT" ]; then region_highlight=("${(@)region_highlight:#$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT}") unset _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT fi @@ -15,11 +13,9 @@ _zsh_autosuggest_highlight_reset() { # If there's a suggestion, highlight it _zsh_autosuggest_highlight_apply() { - typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT - - if (( $#POSTDISPLAY )); then - typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT="$#BUFFER $(($#BUFFER + $#POSTDISPLAY)) $ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE" - region_highlight+=("$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT") + if [ $#POSTDISPLAY -gt 0 ]; then + _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT="$#BUFFER $(($#BUFFER + $#POSTDISPLAY)) $ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE" + region_highlight+=($_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT) else unset _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT fi diff --git a/src/start.zsh b/src/start.zsh index 5d4ee52..54f5bb8 100644 --- a/src/start.zsh +++ b/src/start.zsh @@ -5,29 +5,9 @@ # Start the autosuggestion widgets _zsh_autosuggest_start() { - # By default we re-bind widgets on every precmd to ensure we wrap other - # wrappers. Specifically, highlighting breaks if our widgets are wrapped by - # zsh-syntax-highlighting widgets. This also allows modifications to the - # widget list variables to take effect on the next precmd. However this has - # a decent performance hit, so users can set ZSH_AUTOSUGGEST_MANUAL_REBIND - # to disable the automatic re-binding. - if (( ${+ZSH_AUTOSUGGEST_MANUAL_REBIND} )); then - add-zsh-hook -d precmd _zsh_autosuggest_start - fi - + _zsh_autosuggest_check_deprecated_config _zsh_autosuggest_bind_widgets } -# Mark for auto-loading the functions that we use -autoload -Uz add-zsh-hook is-at-least - -# Automatically enable asynchronous mode in newer versions of zsh. Disable for -# older versions because there is a bug when using async mode where ^C does not -# work immediately after fetching a suggestion. -# See https://github.com/zsh-users/zsh-autosuggestions/issues/364 -if is-at-least 5.0.8; then - typeset -g ZSH_AUTOSUGGEST_USE_ASYNC= -fi - -# Start the autosuggestion widgets on the next precmd +autoload -Uz add-zsh-hook add-zsh-hook precmd _zsh_autosuggest_start diff --git a/src/strategies/completion.zsh b/src/strategies/completion.zsh deleted file mode 100644 index e2d114c..0000000 --- a/src/strategies/completion.zsh +++ /dev/null @@ -1,137 +0,0 @@ - -#--------------------------------------------------------------------# -# Completion Suggestion Strategy # -#--------------------------------------------------------------------# -# Fetches a suggestion from the completion engine -# - -_zsh_autosuggest_capture_postcompletion() { - # Always insert the first completion into the buffer - compstate[insert]=1 - - # Don't list completions - unset 'compstate[list]' -} - -_zsh_autosuggest_capture_completion_widget() { - # Add a post-completion hook to be called after all completions have been - # gathered. The hook can modify compstate to affect what is done with the - # gathered completions. - local -a +h comppostfuncs - comppostfuncs=(_zsh_autosuggest_capture_postcompletion) - - # Only capture completions at the end of the buffer - CURSOR=$#BUFFER - - # Run the original widget wrapping `.complete-word` so we don't - # recursively try to fetch suggestions, since our pty is forked - # after autosuggestions is initialized. - zle -- ${(k)widgets[(r)completion:.complete-word:_main_complete]} - - if is-at-least 5.0.3; then - # Don't do any cr/lf transformations. We need to do this immediately before - # output because if we do it in setup, onlcr will be re-enabled when we enter - # vared in the async code path. There is a bug in zpty module in older versions - # where the tty is not properly attached to the pty slave, resulting in stty - # getting stopped with a SIGTTOU. See zsh-workers thread 31660 and upstream - # commit f75904a38 - stty -onlcr -ocrnl -F /dev/tty - fi - - # The completion has been added, print the buffer as the suggestion - echo -nE - $'\0'$BUFFER$'\0' -} - -zle -N autosuggest-capture-completion _zsh_autosuggest_capture_completion_widget - -_zsh_autosuggest_capture_setup() { - # There is a bug in zpty module in older zsh versions by which a - # zpty that exits will kill all zpty processes that were forked - # before it. Here we set up a zsh exit hook to SIGKILL the zpty - # process immediately, before it has a chance to kill any other - # zpty processes. - if ! is-at-least 5.4; then - zshexit() { - # The zsh builtin `kill` fails sometimes in older versions - # https://unix.stackexchange.com/a/477647/156673 - kill -KILL $$ 2>&- || command kill -KILL $$ - - # Block for long enough for the signal to come through - sleep 1 - } - fi - - # Try to avoid any suggestions that wouldn't match the prefix - zstyle ':completion:*' matcher-list '' - zstyle ':completion:*' path-completion false - zstyle ':completion:*' max-errors 0 not-numeric - - bindkey '^I' autosuggest-capture-completion -} - -_zsh_autosuggest_capture_completion_sync() { - _zsh_autosuggest_capture_setup - - zle autosuggest-capture-completion -} - -_zsh_autosuggest_capture_completion_async() { - _zsh_autosuggest_capture_setup - - zmodload zsh/parameter 2>/dev/null || return # For `$functions` - - # Make vared completion work as if for a normal command line - # https://stackoverflow.com/a/7057118/154703 - autoload +X _complete - functions[_original_complete]=$functions[_complete] - function _complete() { - unset 'compstate[vared]' - _original_complete "$@" - } - - # Open zle with buffer set so we can capture completions for it - vared 1 -} - -_zsh_autosuggest_strategy_completion() { - # Reset options to defaults and enable LOCAL_OPTIONS - emulate -L zsh - - # Enable extended glob for completion ignore pattern - setopt EXTENDED_GLOB - - typeset -g suggestion - local line REPLY - - # Exit if we don't have completions - whence compdef >/dev/null || return - - # Exit if we don't have zpty - zmodload zsh/zpty 2>/dev/null || return - - # Exit if our search string matches the ignore pattern - [[ -n "$ZSH_AUTOSUGGEST_COMPLETION_IGNORE" ]] && [[ "$1" == $~ZSH_AUTOSUGGEST_COMPLETION_IGNORE ]] && return - - # Zle will be inactive if we are in async mode - if zle; then - zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_completion_sync - else - zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_completion_async "\$1" - zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME $'\t' - fi - - { - # The completion result is surrounded by null bytes, so read the - # content between the first two null bytes. - zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0''*'$'\0' - - # Extract the suggestion from between the null bytes. On older - # versions of zsh (older than 5.3), we sometimes get extra bytes after - # the second null byte, so trim those off the end. - # See http://www.zsh.org/mla/workers/2015/msg03290.html - suggestion="${${(@0)line}[2]}" - } always { - # Destroy the pty - zpty -d $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME - } -} diff --git a/src/strategies/history.zsh b/src/strategies/history.zsh deleted file mode 100644 index 0672a13..0000000 --- a/src/strategies/history.zsh +++ /dev/null @@ -1,32 +0,0 @@ - -#--------------------------------------------------------------------# -# History Suggestion Strategy # -#--------------------------------------------------------------------# -# Suggests the most recent history item that matches the given -# prefix. -# - -_zsh_autosuggest_strategy_history() { - # Reset options to defaults and enable LOCAL_OPTIONS - emulate -L zsh - - # Enable globbing flags so that we can use (#m) and (x~y) glob operator - setopt EXTENDED_GLOB - - # Escape backslashes and all of the glob operators so we can use - # this string as a pattern to search the $history associative array. - # - (#m) globbing flag enables setting references for match data - # TODO: Use (b) flag when we can drop support for zsh older than v5.0.8 - local prefix="${1//(#m)[\\*?[\]<>()|^~#]/\\$MATCH}" - - # Get the history items that match the prefix, excluding those that match - # the ignore pattern - local pattern="$prefix*" - if [[ -n $ZSH_AUTOSUGGEST_HISTORY_IGNORE ]]; then - pattern="($pattern)~($ZSH_AUTOSUGGEST_HISTORY_IGNORE)" - fi - - # Give the first history item matching the pattern as the suggestion - # - (r) subscript flag makes the pattern match on values - typeset -g suggestion="${history[(r)$pattern]}" -} diff --git a/src/strategies/match_prev_cmd.zsh b/src/strategies/match_prev_cmd.zsh deleted file mode 100644 index b709783..0000000 --- a/src/strategies/match_prev_cmd.zsh +++ /dev/null @@ -1,66 +0,0 @@ - -#--------------------------------------------------------------------# -# Match Previous Command Suggestion Strategy # -#--------------------------------------------------------------------# -# Suggests the most recent history item that matches the given -# prefix and whose preceding history item also matches the most -# recently executed command. -# -# For example, suppose your history has the following entries: -# - pwd -# - ls foo -# - ls bar -# - pwd -# -# Given the history list above, when you type 'ls', the suggestion -# will be 'ls foo' rather than 'ls bar' because your most recently -# executed command (pwd) was previously followed by 'ls foo'. -# -# Note that this strategy won't work as expected with ZSH options that don't -# preserve the history order such as `HIST_IGNORE_ALL_DUPS` or -# `HIST_EXPIRE_DUPS_FIRST`. - -_zsh_autosuggest_strategy_match_prev_cmd() { - # Reset options to defaults and enable LOCAL_OPTIONS - emulate -L zsh - - # Enable globbing flags so that we can use (#m) and (x~y) glob operator - setopt EXTENDED_GLOB - - # TODO: Use (b) flag when we can drop support for zsh older than v5.0.8 - local prefix="${1//(#m)[\\*?[\]<>()|^~#]/\\$MATCH}" - - # Get the history items that match the prefix, excluding those that match - # the ignore pattern - local pattern="$prefix*" - if [[ -n $ZSH_AUTOSUGGEST_HISTORY_IGNORE ]]; then - pattern="($pattern)~($ZSH_AUTOSUGGEST_HISTORY_IGNORE)" - fi - - # Get all history event numbers that correspond to history - # entries that match the pattern - local history_match_keys - history_match_keys=(${(k)history[(R)$~pattern]}) - - # By default we use the first history number (most recent history entry) - local histkey="${history_match_keys[1]}" - - # Get the previously executed command - local prev_cmd="$(_zsh_autosuggest_escape_command "${history[$((HISTCMD-1))]}")" - - # Iterate up to the first 200 history event numbers that match $prefix - for key in "${(@)history_match_keys[1,200]}"; do - # Stop if we ran out of history - [[ $key -gt 1 ]] || break - - # See if the history entry preceding the suggestion matches the - # previous command, and use it if it does - if [[ "${history[$((key - 1))]}" == "$prev_cmd" ]]; then - histkey="$key" - break - fi - done - - # Give back the matched history entry - typeset -g suggestion="$history[$histkey]" -} diff --git a/src/suggestion.zsh b/src/suggestion.zsh new file mode 100644 index 0000000..fd94d4c --- /dev/null +++ b/src/suggestion.zsh @@ -0,0 +1,19 @@ + +#--------------------------------------------------------------------# +# Suggestion # +#--------------------------------------------------------------------# + +# Get a suggestion from history that matches a given prefix +_zsh_autosuggest_suggestion() { + setopt localoptions extendedglob + + # Escape the prefix (requires EXTENDED_GLOB) + local prefix=${1//(#m)[\][()|\\*?#<>~^]/\\$MATCH} + + # Get all history items (reversed) that match pattern $prefix* + local history_matches + history_matches=(${history[(R)$prefix*]}) + + # Echo the first item that matches + echo ${history_matches[1]} +} diff --git a/src/util.zsh b/src/util.zsh deleted file mode 100644 index 1f55d36..0000000 --- a/src/util.zsh +++ /dev/null @@ -1,11 +0,0 @@ - -#--------------------------------------------------------------------# -# Utility Functions # -#--------------------------------------------------------------------# - -_zsh_autosuggest_escape_command() { - setopt localoptions EXTENDED_GLOB - - # Escape special chars in the string (requires EXTENDED_GLOB) - echo -E "${1//(#m)[\"\'\\()\[\]|*?~]/\\$MATCH}" -} diff --git a/src/widgets.zsh b/src/widgets.zsh index 7562897..40e954b 100644 --- a/src/widgets.zsh +++ b/src/widgets.zsh @@ -3,229 +3,81 @@ # Autosuggest Widget Implementations # #--------------------------------------------------------------------# -# Disable suggestions -_zsh_autosuggest_disable() { - typeset -g _ZSH_AUTOSUGGEST_DISABLED - _zsh_autosuggest_clear -} - -# Enable suggestions -_zsh_autosuggest_enable() { - unset _ZSH_AUTOSUGGEST_DISABLED - - if (( $#BUFFER )); then - _zsh_autosuggest_fetch - fi -} - -# Toggle suggestions (enable/disable) -_zsh_autosuggest_toggle() { - if (( ${+_ZSH_AUTOSUGGEST_DISABLED} )); then - _zsh_autosuggest_enable - else - _zsh_autosuggest_disable - fi -} - # Clear the suggestion _zsh_autosuggest_clear() { # Remove the suggestion - POSTDISPLAY= + unset POSTDISPLAY _zsh_autosuggest_invoke_original_widget $@ } # Modify the buffer and get a new suggestion _zsh_autosuggest_modify() { - local -i retval - - # Only available in zsh >= 5.4 - local -i KEYS_QUEUED_COUNT - - # Save the contents of the buffer/postdisplay - local orig_buffer="$BUFFER" - local orig_postdisplay="$POSTDISPLAY" - - # Clear suggestion while waiting for next one - POSTDISPLAY= - - # Original widget may modify the buffer + # Original widget modifies the buffer _zsh_autosuggest_invoke_original_widget $@ - retval=$? - - emulate -L zsh - - # Don't fetch a new suggestion if there's more input to be read immediately - if (( $PENDING > 0 || $KEYS_QUEUED_COUNT > 0 )); then - POSTDISPLAY="$orig_postdisplay" - return $retval - fi - - # Optimize if manually typing in the suggestion or if buffer hasn't changed - if [[ "$BUFFER" = "$orig_buffer"* && "$orig_postdisplay" = "${BUFFER:$#orig_buffer}"* ]]; then - POSTDISPLAY="${orig_postdisplay:$(($#BUFFER - $#orig_buffer))}" - return $retval - fi - - # Bail out if suggestions are disabled - if (( ${+_ZSH_AUTOSUGGEST_DISABLED} )); then - return $? - fi # Get a new suggestion if the buffer is not empty after modification - if (( $#BUFFER > 0 )); then - if [[ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]] || (( $#BUFFER <= $ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE )); then - _zsh_autosuggest_fetch - fi + local suggestion + if [ $#BUFFER -gt 0 ]; then + suggestion=$(_zsh_autosuggest_suggestion $BUFFER) fi - return $retval -} - -# Fetch a new suggestion based on what's currently in the buffer -_zsh_autosuggest_fetch() { - if (( ${+ZSH_AUTOSUGGEST_USE_ASYNC} )); then - _zsh_autosuggest_async_request "$BUFFER" + # Add the suggestion to the POSTDISPLAY + if [ -n "$suggestion" ]; then + POSTDISPLAY=${suggestion#$BUFFER} else - local suggestion - _zsh_autosuggest_fetch_suggestion "$BUFFER" - _zsh_autosuggest_suggest "$suggestion" - fi -} - -# Offer a suggestion -_zsh_autosuggest_suggest() { - emulate -L zsh - - local suggestion="$1" - - if [[ -n "$suggestion" ]] && (( $#BUFFER )); then - POSTDISPLAY="${suggestion#$BUFFER}" - else - POSTDISPLAY= + unset POSTDISPLAY fi } # Accept the entire suggestion _zsh_autosuggest_accept() { - local -i retval max_cursor_pos=$#BUFFER - - # When vicmd keymap is active, the cursor can't move all the way - # to the end of the buffer - if [[ "$KEYMAP" = "vicmd" ]]; then - max_cursor_pos=$((max_cursor_pos - 1)) - fi - - # If we're not in a valid state to accept a suggestion, just run the - # original widget and bail out - if (( $CURSOR != $max_cursor_pos || !$#POSTDISPLAY )); then - _zsh_autosuggest_invoke_original_widget $@ - return - fi - # Only accept if the cursor is at the end of the buffer - # Add the suggestion to the buffer - BUFFER="$BUFFER$POSTDISPLAY" + if [ $CURSOR -eq $#BUFFER ]; then + # Add the suggestion to the buffer + BUFFER="$BUFFER$POSTDISPLAY" - # Remove the suggestion - POSTDISPLAY= + # Remove the suggestion + unset POSTDISPLAY - # Run the original widget before manually moving the cursor so that the - # cursor movement doesn't make the widget do something unexpected - _zsh_autosuggest_invoke_original_widget $@ - retval=$? - - # Move the cursor to the end of the buffer - if [[ "$KEYMAP" = "vicmd" ]]; then - CURSOR=$(($#BUFFER - 1)) - else - CURSOR=$#BUFFER + # Move the cursor to the end of the buffer + CURSOR=${#BUFFER} fi - return $retval -} - -# Accept the entire suggestion and execute it -_zsh_autosuggest_execute() { - # Add the suggestion to the buffer - BUFFER="$BUFFER$POSTDISPLAY" - - # Remove the suggestion - POSTDISPLAY= - - # Call the original `accept-line` to handle syntax highlighting or - # other potential custom behavior - _zsh_autosuggest_invoke_original_widget "accept-line" + _zsh_autosuggest_invoke_original_widget $@ } # Partially accept the suggestion _zsh_autosuggest_partial_accept() { - local -i retval cursor_loc - # Save the contents of the buffer so we can restore later if needed - local original_buffer="$BUFFER" + local original_buffer=$BUFFER # Temporarily accept the suggestion. BUFFER="$BUFFER$POSTDISPLAY" # Original widget moves the cursor _zsh_autosuggest_invoke_original_widget $@ - retval=$? - - # Normalize cursor location across vi/emacs modes - cursor_loc=$CURSOR - if [[ "$KEYMAP" = "vicmd" ]]; then - cursor_loc=$((cursor_loc + 1)) - fi # If we've moved past the end of the original buffer - if (( $cursor_loc > $#original_buffer )); then + if [ $CURSOR -gt $#original_buffer ]; then # Set POSTDISPLAY to text right of the cursor - POSTDISPLAY="${BUFFER[$(($cursor_loc + 1)),$#BUFFER]}" + POSTDISPLAY=$RBUFFER # Clip the buffer at the cursor - BUFFER="${BUFFER[1,$cursor_loc]}" + BUFFER=$LBUFFER else # Restore the original buffer - BUFFER="$original_buffer" + BUFFER=$original_buffer fi - - return $retval } -() { - typeset -ga _ZSH_AUTOSUGGEST_BUILTIN_ACTIONS +for action in clear modify accept partial_accept; do + eval "_zsh_autosuggest_widget_$action() { + _zsh_autosuggest_highlight_reset + _zsh_autosuggest_$action \$@ + _zsh_autosuggest_highlight_apply + }" +done - _ZSH_AUTOSUGGEST_BUILTIN_ACTIONS=( - clear - fetch - suggest - accept - execute - enable - disable - toggle - ) - - local action - for action in $_ZSH_AUTOSUGGEST_BUILTIN_ACTIONS modify partial_accept; do - eval "_zsh_autosuggest_widget_$action() { - local -i retval - - _zsh_autosuggest_highlight_reset - - _zsh_autosuggest_$action \$@ - retval=\$? - - _zsh_autosuggest_highlight_apply - - zle -R - - return \$retval - }" - done - - for action in $_ZSH_AUTOSUGGEST_BUILTIN_ACTIONS; do - zle -N autosuggest-$action _zsh_autosuggest_widget_$action - done -} +zle -N autosuggest-accept _zsh_autosuggest_widget_accept +zle -N autosuggest-clear _zsh_autosuggest_widget_clear diff --git a/test/shunit2-2.1.6/bin/gen_test_results.sh b/test/shunit2-2.1.6/bin/gen_test_results.sh new file mode 100755 index 0000000..dc825ac --- /dev/null +++ b/test/shunit2-2.1.6/bin/gen_test_results.sh @@ -0,0 +1,62 @@ +#! /bin/sh +# $Id: gen_test_results.sh 54 2008-10-21 23:29:23Z kate.ward@forestent.com $ +# vim:et:ft=sh:sts=2:sw=2 +# +# Copyright 2008 Kate Ward. All Rights Reserved. +# Released under the LGPL (GNU Lesser General Public License) +# +# Author: kate.ward@forestent.com (Kate Ward) +# +# This script runs the provided unit tests and sends the output to the +# appropriate file. +# + +# treat unset variables as an error +set -u + +die() +{ + [ $# -gt 0 ] && echo "error: $@" >&2 + exit 1 +} + +BASE_DIR="`dirname $0`/.." +LIB_DIR="${BASE_DIR}/lib" + +# load libraries +. ${LIB_DIR}/shflags || die 'unable to load shflags library' +. ${LIB_DIR}/shlib || die 'unable to load shlib library' +. ${LIB_DIR}/versions || die 'unable to load versions library' + +BASE_DIR=`shlib_relToAbsPath "${BASE_DIR}"` +SRC_DIR="${BASE_DIR}/src" + +os_name=`versions_osName |sed 's/ /_/g'` +os_version=`versions_osVersion` + +DEFINE_boolean force false 'force overwrite' f +DEFINE_string output_dir "`pwd`" 'output dir' d +DEFINE_string output_file "${os_name}-${os_version}.txt" 'output file' o +DEFINE_string suite 'shunit2_test.sh' 'unit test suite' s +FLAGS "${@:-}" || exit $?; shift ${FLAGS_ARGC} + +# determine output filename +output="${FLAGS_output_dir:+${FLAGS_output_dir}/}${FLAGS_output_file}" +output=`shlib_relToAbsPath "${output}"` + +# checks +if [ -f "${output}" ]; then + if [ ${FLAGS_force} -eq ${FLAGS_TRUE} ]; then + rm -f "${output}" + else + echo "not overwriting '${output}'" >&2 + exit ${FLAGS_ERROR} + fi +fi +touch "${output}" 2>/dev/null || die "unable to write to '${output}'" + +# run tests +( cd "${SRC_DIR}"; ./${FLAGS_suite} |tee "${output}" ) + +echo >&2 +echo "output written to '${output}'" >&2 diff --git a/test/shunit2-2.1.6/bin/which b/test/shunit2-2.1.6/bin/which new file mode 100755 index 0000000..4eefe74 --- /dev/null +++ b/test/shunit2-2.1.6/bin/which @@ -0,0 +1,36 @@ +#! /bin/sh +# $Id: which 12 2007-02-18 03:31:14Z sfsetse $ +# +# This is a simple implementation of the 'which' command for those OSes that +# don't have one. +# + +true; TRUE=$? +false; FALSE=$? + +showAll=${FALSE} + +# process command line flags +while getopts 'a' opt; do + case ${opt} in + a) showAll=${TRUE} + esac +done +shift `expr ${OPTIND} - 1` + +# exit if no arguments were given +[ $# -eq 0 ] && exit 1 + +command=$1 + +# search for command +out=`echo "${PATH}" |sed "s/:/\n/g" |\ +while read path; do + fullPath="${path}/${command}" + if [ -x "${fullPath}" ]; then + echo "${fullPath}" + [ ${showAll} -eq ${FALSE} ] && break + fi +done` +[ -z "${out}" ] && exit 1 +echo "${out}" diff --git a/test/shunit2-2.1.6/doc/CHANGES-2.1.txt b/test/shunit2-2.1.6/doc/CHANGES-2.1.txt new file mode 100644 index 0000000..14764b1 --- /dev/null +++ b/test/shunit2-2.1.6/doc/CHANGES-2.1.txt @@ -0,0 +1,214 @@ +Changes in shUnit2 2.1.X +======================== + +Changes with 2.1.6 +------------------ + +Removed all references to the DocBook documentation. + +Simplified the 'src' structure. + +Fixed error message in fail() that stated wrong number of required arguments. + +Updated lib/versions. + +Fixed bug in _shunit_mktempDir() where a failure occurred when the 'od' command was not present in /usr/bin. + +Renamed shunit_tmpDir variable to SHUNIT_TMPDIR to closer match the standard +TMPDIR variable. + +Added support for calling shunit2 as an executable, in addition to the existing +method of sourcing it in as a library. This allows users to keep tests working +despite the location of the shunit2 executable being different for each OS +distribution. + +Issue #14: Improved handling of some strange chars (e.g. single and double +quotes) in messages. + +Issue# 27: Fixed error message for assertSame(). + +Issue# 25: Added check and error message to user when phantom functions are +written to a partition mounted with noexec. + +Issue# 11: Added support for defining functions like 'function someFunction()'. + + +Changes with 2.1.5 +------------------ + +Issue# 1: Fixed bug pointed out by R Bernstein in the trap code where certain +types of exit conditions did not generate the ending report. + +Issue# 2: Added assertNotEquals() assert. + +Issue# 3: Moved check for unset variables out of shUnit2 into the unit tests. +Testing poorly written software blows up if this check is in, but it is only +interesting for shUnit2 itself. Added shunit_test_output.sh unit test for this. +Some shells still do not catch such errors properly (e.g. Bourne shell and BASH +2.x). + +Added new custom assert in test_helpers to check for output to STDOUT, and none +to STDERR. + +Replaced fatal message in the temp directory creation with a _shunit_fatal() +function call. + +Fixed test_output unit test so it works now that the 'set -u' stuff was removed +for Issue# 3. + +Flushed out the coding standards in the README.txt a bit more, and brought the +shunit2 code up to par with the documented standards. + +Issue# 4: Completely changed the reporting output to be a closer match for +JUnit and PyUnit. As a result, tests are counted separately from assertions. + +Provide public shunit_tmpDir variable that can be used by unit test scripts that +need automated and guaranteed cleanup. + +Issue# 7: Fixed duplicated printing of messages passed to asserts. + +Per code review, fixed wording of failSame() and failNotSame() messages. + +Replaced version_info.sh with versions library and made appropriate changes in +other scripts to use it. + +Added gen_test_results.sh to make releases easier. + +Fixed bugs in shlib_relToAbsPath() in shlib. + +Converted DocBook documentation to reStructuredText for easier maintenance. The +DocBook documentation is now considered obsolete, and will be removed in a +future release. + +Issue# 5: Fixed the documentation around the usage of failures. + +Issue# 9: Added unit tests and updated documentation to demonstrate the +requirement of quoting values twice when macros are used. This is due to how +shell parses arguments. + +When an invalid number of arguments is passed to a function, the invalid number +is returned to the user so they are more aware of what the cause might be. + + +Changes with 2.1.4 +------------------ + +Removed the _shunit_functionExists() function as it was dead code. + +Fixed zsh version number check in version_info. + +Fixed bug in last resort temporary directory creation. + +Fixed off-by-one in exit value for scripts caught by the trap handler. + +Added argument count error checking to all functions. + +Added mkdir_test.sh example. + +Moved src/test into src/shell to better match structure used with shFlags. + +Fixed problem where null values were not handled properly under ksh. + +Added support for outputting line numbers as part of assert messages. + +Started documenting the coding standards, and changed some variable names as a +result. + +Improved zsh version and option checks. + +Renamed the __SHUNIT_VERSION variable to SHUNIT_VERSION. + + +Changes with 2.1.3 +------------------ + +Added some explicit variable defaults, even though the variables are set, as +they sometimes behave strange when the script is canceled. + +Additional workarounds for zsh compatibility. + +shUnit2 now exits with a non-zero exit code if any of the tests failed. This was +done for automated testing frameworks. Tests that were skipped are not +considered failures, and do not affect the exit code. + +Changed detection of STDERR output in unit tests. + + +Changes with 2.1.2 +------------------ + +Unset additional variables that were missed. + +Added checks and workarounds to improve zsh compatibility. + +Added some argument count checks ``assertEquals()``, ``assertNull()``, and +``assertSame()`` + + +Changes with 2.1.1 +------------------ + +Fixed bug where ``fail()`` was not honoring skipping. + +Fixed problem with ``docs-docbook-prep`` target that prevented it from working. +(Thanks to Bryan Larsen for pointing this out.) + +Changed the test in ``assertFalse()`` so that any non-zero value registers as +false. (Credits to Bryan Larsen) + +Major fiddling to bring more in line with `JUnit `. Asserts +give better output when no message is given, and failures now just fail. + +It was pointed out that the simple 'failed' message for a failed assert was not +only insufficient, it was nonstandard (when compared to JUnit) and didn't +provide the user with an expected vs actual result. The code was revised +somewhat to bring closer into alignment with JUnit (v4.3.1 specifically) so +that it feels more "normal". (Credits to Richard Jensen) + +As part of the JUnit realignment, it was noticed that fail*() functions in +JUnit don't actually do any comparisons themselves. They only generate a +failure message. Updated the code to match. + +Added self-testing unit tests. Kinda horkey, but they did find bugs during the +JUnit realignment. + +Fixed the code for returning from asserts as the return was being called before +the unsetting of variables occurred. (Credits to Mathias Goldau) + +The assert(True|False)() functions now accept an integer value for a +conditional test. A value of '0' is considered 'true', while any non-zero value +is considered 'false'. + +All public functions now fill use default values to work properly with the '-x' +shell debugging flag. + +Fixed the method of percent calculation for the report to get achieve better +accuracy. + + +Changes with 2.1.0 (since 2.0.1) +-------------------------------- + +This release is a branch of the 2.0.1 release. + +Moving to `reStructured Text `_ for +the documentation. + +Fixed problem with ``fail()``. The failure message was not properly printed. + +Fixed the ``Makefile`` so that the DocBook XML and XSLT files would be +downloaded before parsing can continue. + +Renamed the internal ``__SHUNIT_TRUE`` and ``__SHUNIT_FALSE`` variables to +``SHUNIT_TRUE`` and ``SHUNIT_FALSE`` so that unit tests can "use" them. + +Added support for test "skipping". If skipping is turned on with the +``startSkip()`` function, ``assert`` and ``fail`` functions will return +immediately, and the skip will be recorded. + +The report output format was changed to include the percentage for each test +result, rather than just those successful. + + +.. $Revision: 326 $ +.. vim:fileencoding=latin1:ft=text:spell:tw=80 diff --git a/test/shunit2-2.1.6/doc/LGPL-2.1 b/test/shunit2-2.1.6/doc/LGPL-2.1 new file mode 100644 index 0000000..b1e3f5a --- /dev/null +++ b/test/shunit2-2.1.6/doc/LGPL-2.1 @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/test/shunit2-2.1.6/doc/README.html b/test/shunit2-2.1.6/doc/README.html new file mode 100644 index 0000000..b7b4672 --- /dev/null +++ b/test/shunit2-2.1.6/doc/README.html @@ -0,0 +1,540 @@ + + + + + + +shUnit2 2.1.x README + + + +
+

shUnit2 2.1.x README

+ +
+

code.google.com

+

This project is stored on code.google.com as http://code.google.com/p/shunit2/. +All releases as of 2.1.4 and full source are available there. Documentation is +included as part of the source and each release. Source code is stored in +Subversion and can be accessed using the following information.

+

Browse the code in a web browser:

+ +

Check out the code locally

+
+$ svn checkout http://shunit2.googlecode.com/svn/trunk/ shflags-read-only
+
+
+
+

SourceForge

+

DEPRECATED

+

This project is stored on SourceForge as http://sf.net/projects/shunit2. The +source code is stored in Subversion and can be accessed using the following +information.

+

Check out the code locally

+
+$ svn co https://shunit2.svn.sourceforge.net/svnroot/shunit2/trunk/source/2.1 shunit2
+
+

Browse the code in a web browser:

+ +
+
+

Making a release

+

For these steps, it is assumed we are working with release 2.0.0.

+

Steps:

+
    +
  • write release notes
  • +
  • update version
  • +
  • finish changelog
  • +
  • check all the code in
  • +
  • tag the release
  • +
  • export the release
  • +
  • create tarball
  • +
  • md5sum the tarball and sign with gpg
  • +
  • update website
  • +
  • post to SourceForge and Freshmeat
  • +
+
+

Write Release Notes

+

This should be pretty self explanatory. Use one of the release notes from a +previous release as an example.

+

The versions of the various platforms and shells are included when the +master unit test script is run, or when bin/gen_test_results.sh is +used. To determine the versions of the installed shells by hand, use the +lib/versions script.

+

Alternatively, do the following:

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ShellOSNotes
bash $ bash --version
dashLinux$ dpkg -l |grep dash
ksh $ ksh --version +-or- +$ echo 'echo $KSH_VERSION' |ksh
Cygwinsee pdksh
Solaris$ strings /usr/bin/ksh |grep 'Version'
pdksh $ strings /bin/pdksh |grep 'PD KSH'
Cygwinlook in the downloaded Cygwin directory
shSolarisnot possible
zsh $ zsh --version
+
+
+

Update Version

+

Edit src/shell/shunit2 and change the version number in the comment, as well +as in the SHUNIT_VERSION variable.

+
+
+

Finish Documentation

+

Make sure that any remaining changes get put into the CHANGES-X.X.txt file.

+

Finish writing the RELEASE_NOTES-X.X.X.txt. If necessary, run it +through the fmt command to make it pretty (hopefully it is already).

+
+$ fmt -w 80 RELEASE_NOTES-2.0.0.txt >RELEASE_NOTES-2.0.0.txt.new
+$ mv RELEASE_NOTES-2.0.0.txt.new RELEASE_NOTES-2.0.0.txt
+
+

We want to have an up-to-date version of the documentation in the release, so +we'd better build it.

+
+$ pwd
+.../shunit2/source/2.1
+$ cd doc
+$ RST2HTML_OPTS='--stylesheet-path=rst2html.css'
+$ rst2html ${RST2HTML_OPTS} shunit2.txt >shunit2.html
+$ rst2html ${RST2HTML_OPTS} README.txt >README.html
+
+
+
+

Check In All the Code

+

This step is pretty self-explanatory

+
+$ pwd
+.../shunit2/source/2.0
+$ svn ci -m "finalizing release"
+
+
+
+

Tag the Release

+
+$ pwd
+.../shunit2/source
+$ ls
+2.0  2.1
+$ svn cp -m "Release 2.0.0" 2.0 https://shunit2.googlecode.com/svn/tags/source/2.0.0
+
+
+
+

Export the Release

+
+$ pwd
+.../shunit2/builds
+$ svn export https://shunit2.googlecode.com/svn/tags/source/2.0.0 shunit2-2.0.0
+
+
+
+

Create Tarball

+
+$ tar cfz ../releases/shunit2-2.0.0.tgz shunit2-2.0.0
+
+
+
+

Sign the Tarball with gpg

+
+$ cd ../releases
+$ gpg --default-key kate.ward@forestent.com --detach-sign shunit2-2.0.0.tgz
+
+
+
+

Update Website

+

Again, pretty self-explanatory. Make sure to copy the GPG signature file. Once +that is done, make sure to tag the website so we can go back in time if needed.

+
+$ pwd
+.../shunit2
+$ ls
+source  website
+$ svn cp -m "Release 2.0.0" \
+website https://shunit2.googlecode.com/svn/tags/website/20060916
+
+

Now, update the website. It too is held in Subversion, so ssh into the web +server and use svn up to grab the latest version.

+
+
+

Post to code.google.com and Freshmeat

+ +
+
+ +
+ + diff --git a/test/shunit2-2.1.6/doc/README.txt b/test/shunit2-2.1.6/doc/README.txt new file mode 100644 index 0000000..2d8d534 --- /dev/null +++ b/test/shunit2-2.1.6/doc/README.txt @@ -0,0 +1,214 @@ +==================== +shUnit2 2.1.x README +==================== + +code.google.com +=============== + +This project is stored on code.google.com as http://code.google.com/p/shunit2/. +All releases as of 2.1.4 and full source are available there. Documentation is +included as part of the source and each release. Source code is stored in +Subversion and can be accessed using the following information. + +Browse the code in a web browser: + +- http://code.google.com/p/shunit2/source/browse +- svn > trunk > source > 2.1 + +Check out the code locally :: + + $ svn checkout http://shunit2.googlecode.com/svn/trunk/ shflags-read-only + + +SourceForge +=========== + +DEPRECATED + +This project is stored on SourceForge as http://sf.net/projects/shunit2. The +source code is stored in Subversion and can be accessed using the following +information. + +Check out the code locally :: + + $ svn co https://shunit2.svn.sourceforge.net/svnroot/shunit2/trunk/source/2.1 shunit2 + +Browse the code in a web browser: + +- http://shunit2.svn.sourceforge.net/viewvc/shunit2/trunk/source/2.1/ +- http://shunit2.svn.sourceforge.net/svnroot/shunit2/trunk/source/2.1/ + + +Making a release +================ + +For these steps, it is assumed we are working with release 2.0.0. + +Steps: + +- write release notes +- update version +- finish changelog +- check all the code in +- tag the release +- export the release +- create tarball +- md5sum the tarball and sign with gpg +- update website +- post to SourceForge and Freshmeat + +Write Release Notes +------------------- + +This should be pretty self explanatory. Use one of the release notes from a +previous release as an example. + +The versions of the various platforms and shells are included when the +master unit test script is run, or when ``bin/gen_test_results.sh`` is +used. To determine the versions of the installed shells by hand, use the +``lib/versions`` script. + +Alternatively, do the following: + ++-------+---------+-----------------------------------------------------------+ +| Shell | OS | Notes | ++=======+=========+===========================================================+ +| bash | | ``$ bash --version`` | ++-------+---------+-----------------------------------------------------------+ +| dash | Linux | ``$ dpkg -l |grep dash`` | ++-------+---------+-----------------------------------------------------------+ +| ksh | | ``$ ksh --version`` | +| | | -or- | +| | | ``$ echo 'echo $KSH_VERSION' |ksh`` | +| +---------+-----------------------------------------------------------+ +| | Cygwin | see pdksh | +| +---------+-----------------------------------------------------------+ +| | Solaris | ``$ strings /usr/bin/ksh |grep 'Version'`` | ++-------+---------+-----------------------------------------------------------+ +| pdksh | | ``$ strings /bin/pdksh |grep 'PD KSH'`` | +| +---------+-----------------------------------------------------------+ +| | Cygwin | look in the downloaded Cygwin directory | ++-------+---------+-----------------------------------------------------------+ +| sh | Solaris | not possible | ++-------+---------+-----------------------------------------------------------+ +| zsh | | ``$ zsh --version`` | ++-------+---------+-----------------------------------------------------------+ + +Update Version +-------------- + +Edit ``src/shell/shunit2`` and change the version number in the comment, as well +as in the ``SHUNIT_VERSION`` variable. + +Finish Documentation +-------------------- + +Make sure that any remaining changes get put into the ``CHANGES-X.X.txt`` file. + +Finish writing the ``RELEASE_NOTES-X.X.X.txt``. If necessary, run it +through the **fmt** command to make it pretty (hopefully it is already). :: + + $ fmt -w 80 RELEASE_NOTES-2.0.0.txt >RELEASE_NOTES-2.0.0.txt.new + $ mv RELEASE_NOTES-2.0.0.txt.new RELEASE_NOTES-2.0.0.txt + +We want to have an up-to-date version of the documentation in the release, so +we'd better build it. :: + + $ pwd + .../shunit2/source/2.1 + $ cd doc + $ RST2HTML_OPTS='--stylesheet-path=rst2html.css' + $ rst2html ${RST2HTML_OPTS} shunit2.txt >shunit2.html + $ rst2html ${RST2HTML_OPTS} README.txt >README.html + +Check In All the Code +--------------------- + +This step is pretty self-explanatory :: + + $ pwd + .../shunit2/source/2.0 + $ svn ci -m "finalizing release" + +Tag the Release +--------------- +:: + + $ pwd + .../shunit2/source + $ ls + 2.0 2.1 + $ svn cp -m "Release 2.0.0" 2.0 https://shunit2.googlecode.com/svn/tags/source/2.0.0 + +Export the Release +------------------ +:: + + $ pwd + .../shunit2/builds + $ svn export https://shunit2.googlecode.com/svn/tags/source/2.0.0 shunit2-2.0.0 + +Create Tarball +-------------- +:: + + $ tar cfz ../releases/shunit2-2.0.0.tgz shunit2-2.0.0 + +Sign the Tarball with gpg +------------------------- +:: + + $ cd ../releases + $ gpg --default-key kate.ward@forestent.com --detach-sign shunit2-2.0.0.tgz + +Update Website +-------------- + +Again, pretty self-explanatory. Make sure to copy the GPG signature file. Once +that is done, make sure to tag the website so we can go back in time if needed. +:: + + $ pwd + .../shunit2 + $ ls + source website + $ svn cp -m "Release 2.0.0" \ + website https://shunit2.googlecode.com/svn/tags/website/20060916 + +Now, update the website. It too is held in Subversion, so **ssh** into the web +server and use ``svn up`` to grab the latest version. + +Post to code.google.com and Freshmeat +------------------------------------- + +- http://code.google.com/p/shunit2/ +- http://freshmeat.net/ + + +Related Documentation +===================== + +Docbook: + http://www.docbook.org/ + +Docbook XML + docbook-xml-4.4.zip: + http://www.docbook.org/xml/4.4/docbook-xml-4.4.zip + http://www.oasis-open.org/docbook/xml/4.4/docbook-xml-4.4.zip + docbook-xml-4.5.zip: + http://www.docbook.org/xml/4.5/docbook-xml-4.5.zip +Docbook XSL + docbook-xsl-1.71.0.tar.bz2: + http://prdownloads.sourceforge.net/docbook/docbook-xsl-1.71.0.tar.bz2?download + docbook-xsl-1.71.1.tar.bz2: + http://downloads.sourceforge.net/docbook/docbook-xsl-1.71.1.tar.bz2?use_mirror=puzzle +JUnit: + http://www.junit.org/ +reStructuredText: + http://docutils.sourceforge.net/docs/user/rst/quickstart.html + +.. generate HTML using rst2html from Docutils of +.. http://docutils.sourceforge.net/ +.. +.. vim:fileencoding=latin1:ft=rst:spell:tw=80 +.. $Revision: 310 $ diff --git a/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.0.txt b/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.0.txt new file mode 100644 index 0000000..9aba387 --- /dev/null +++ b/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.0.txt @@ -0,0 +1,104 @@ +Release Notes for shUnit2 2.1.0 +=============================== + +This release was branched from shUnit2 2.0.1. It mostly adds new functionality, +but there are couple of bugs fixed from the previous release. + +See the ``CHANGES-2.1.rst`` file for a full list of changes. + + +Tested Platforms +---------------- + +This list of platforms comes from the latest version of log4sh as shUnit2 is +used in the testing of log4sh on each of these platforms. + +Cygwin + +- bash 3.2.9(10) +- pdksh 5.2.14 + +Linux + +- bash 3.1.17(1), 3.2.10(1) +- dash 0.5.3 +- ksh 1993-12-28 +- pdksh 5.2.14 +- zsh 4.3.2 (does not work) + +Mac OS X 10.4.8 (Darwin 8.8) + +- bash 2.05b.0(1) +- ksh 1993-12-28 + +Solaris 8 U3 (x86) + +- /bin/sh +- bash 2.03.0(1) +- ksh M-11/16/88i + +Solaris 10 U2 (sparc) + +- /bin/sh +- bash 3.00.16(1) +- ksh M-11/16/88i + +Solaris 10 U2 (x86) + +- /bin/sh +- bash 3.00.16(1) +- ksh M-11/16/88i + + +New Features +------------ + +Test skipping + + Support added for test "skipping". A skip mode can be enabled so that + subsequent ``assert`` and ``fail`` functions that are called will be recorded + as "skipped" rather than as "passed" or "failed". This functionality can be + used such that when a set of tests makes sense on one platform but not on + another, they can be effectively disabled without altering the total number + of tests. + + One example might be when something is supported under ``bash``, but not + under a standard Bourne shell. + + New functions: ``startSkipping()``, ``endSkipping``, ``isSkipping`` + + +Changes and Enhancements +------------------------ + +Moving to the use of `reStructured Text +`_ for documentation. It is easy to +read and edit in textual form, but converts nicely to HTML. + +The report format has changed. Rather than including a simple "success" +percentage at the end, a percentage is given for each type of test. + + +Bug Fixes +--------- + +The ``fail()`` function did not output the optional failure message. + +Fixed the ``Makefile`` so that the DocBook XML and XSLT files would be +downloaded before documentation parsing will continue. + + +Deprecated Features +------------------- + +None. + + +Known Bugs and Issues +--------------------- + +None. + + +.. $Revision: 273 $ +.. vim:fileencoding=latin1:spell:syntax=rst:textwidth=80 diff --git a/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.1.txt b/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.1.txt new file mode 100644 index 0000000..4c61005 --- /dev/null +++ b/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.1.txt @@ -0,0 +1,88 @@ +Release Notes for shUnit2 2.1.1 +=============================== + +This is mainly a bug fix release, but it also incorporates a realignment with +the JUnit 4 code. Asserts now provide better failure messages, and the failure +functions no longer perform tests. + +See the ``CHANGES-2.1.txt`` file for a full list of changes. + + +Tested Platforms +---------------- + +This list of platforms comes from the latest version of log4sh as shUnit2 is +used in the testing of log4sh on each of these platforms. + +Cygwin + +- bash 3.2.15(13) +- pdksh 5.2.14 + +Linux + +- bash 3.1.17(1), 3.2.10(1) +- dash 0.5.3 +- ksh 1993-12-28 +- pdksh 5.2.14 +- zsh 4.3.2 (does not work) + +Mac OS X 10.4.9 (Darwin 8.9.1) + +- bash 2.05b.0(1) +- ksh 1993-12-28 + +Solaris 8 U3 (x86) + +- /bin/sh +- bash 2.03.0(1) +- ksh M-11/16/88i + +Solaris 10 U2 (sparc, x86) + +- /bin/sh +- bash 3.00.16(1) +- ksh M-11/16/88i + + +New Features +------------ + +None. + + +Changes and Enhancements +------------------------ + +The internal test in ``assertFalse()`` now accepts any non-zero value as false. + +The ``assertTrue()`` and ``assertFalse()`` functions now accept an integer value +for a conditional test. A value of '0' is considered 'true', while any non-zero +value is considered 'false'. + +Self-testing unit tests were added. + + +Bug Fixes +--------- + +The ``fail()`` assert now honors skipping. + +The ``docs-docbook-prep`` target now works properly. + +All asserts now properly unset their variables. + + +Deprecated Features +------------------- + +None. + + +Known Bugs and Issues +--------------------- + +Functions do not properly test for an invalid number of arguments. + + +.. vim:fileencoding=latin1:ft=rst:spell:textwidth=80 diff --git a/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.2.txt b/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.2.txt new file mode 100644 index 0000000..5492984 --- /dev/null +++ b/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.2.txt @@ -0,0 +1,83 @@ +Release Notes for shUnit2 2.1.2 +=============================== + +This release adds initial support for the zsh shell. Due to some differences +with this shell as compared with others, some special checks have been added, +and there are some extra requirements necessary when this shell is to be used. + +To use zsh with shUnit2, the following two requirements must be met: +* The ``shwordsplit`` option must be set. +* The ``function_argzero`` option must be unset. + +Please read the Shell Errata section of the documentation for guidance on how +to meet these requirements. + + +See the ``CHANGES-2.1.txt`` file for a full list of changes. + + +Tested Platforms +---------------- + +This list of platforms comes from the latest version of log4sh as shUnit2 is +used in the testing of log4sh on each of these platforms. + +Linux + +- bash 3.1.17(1), 3.2.25(1) +- dash 0.5.4 +- ksh 1993-12-28 +- pdksh 5.2.14 +- zsh 4.2.5, 4.3.4 + +Mac OS X 10.4.11 (Darwin 8.11.1) + +- bash 2.05b.0(1) +- ksh 1993-12-28 +- zsh 4.2.3 + +Solaris 10 U3 (x86) + +- /bin/sh +- bash 3.00.16(1) +- ksh M-11/16/88i +- zsh 4.2.1 + + +New Features +------------ + +Support for the zsh shell. + + +Changes and Enhancements +------------------------ + +Added some argument count checks. + + +Bug Fixes +--------- + +None. + + +Deprecated Features +------------------- + +None. + + +Known Bugs and Issues +--------------------- + +Functions do not properly test for an invalid number of arguments. + +ksh and pdksh do not pass null arguments (i.e. empty strings as '') properly, +and as such checks do not work properly. + +zsh requires the ``shwordsplit`` option to be set, and the ``function_argzero`` +option to be unset for proper operation. + + +.. vim:fileencoding=latin1:ft=rst:spell:textwidth=80 diff --git a/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.3.txt b/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.3.txt new file mode 100644 index 0000000..7d1c9f6 --- /dev/null +++ b/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.3.txt @@ -0,0 +1,84 @@ +Release Notes for shUnit2 2.1.3 +=============================== + +This release is minor feature release. It improves support for zsh (although it +still isn't what it could be) and adds automated testing framework support by +returning a non-zero exit when tests fail. + +To use zsh with shUnit2, the following two requirements must be met: +* The ``shwordsplit`` option must be set. +* The ``function_argzero`` option must be unset. + +Please read the Shell Errata section of the documentation for guidance on how +to meet these requirements. + +See the ``CHANGES-2.1.txt`` file for a full list of changes. + + +Tested Platforms +---------------- + +Cygwin + +- bash 3.2.33(18) +- pdksh 5.2.14 + +Linux + +- bash 3.2.33(1) +- dash 0.5.4 +- ksh 1993-12-28 +- pdksh 5.2.14 +- zsh 4.3.4 + +Mac OS X 10.5.2 (Darwin 9.2.2) + +- bash 3.2.17(1) +- ksh 1993-12-28 +- zsh 4.3.4 + +Solaris 11 x86 (Nevada 77) + +- /bin/sh +- bash 3.2.25(1) +- ksh M-11/16/88i +- zsh 4.3.4 + + +New Features +------------ + +None. + + +Changes and Enhancements +------------------------ + +Support for automated testing frameworks. + + +Bug Fixes +--------- + +Fixed some issues with zsh support. + + +Deprecated Features +------------------- + +None. + + +Known Bugs and Issues +--------------------- + +Functions do not properly test for an invalid number of arguments. + +ksh and pdksh do not pass null arguments (i.e. empty strings as '') properly, +and as such checks do not work properly. + +zsh requires the ``shwordsplit`` option to be set, and the ``function_argzero`` +option to be unset for proper operation. + + +.. vim:fileencoding=latin1:ft=rst:spell:textwidth=80 diff --git a/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.4.txt b/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.4.txt new file mode 100644 index 0000000..007b5c3 --- /dev/null +++ b/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.4.txt @@ -0,0 +1,100 @@ +Release Notes for shUnit2 2.1.4 +=============================== + +This release contains lots of bug fixes and changes. Mostly, it fixes zsh +support in zsh 3.0, and the handling of null values in ksh. + +To use zsh with shUnit2, the following requirement must be met: + +- The ``shwordsplit`` option must be set. + +Please read the Shell Errata section of the documentation for guidance on how +to meet these requirements. + +See the ``CHANGES-2.1.txt`` file for a full list of changes. + + +Tested Platforms +---------------- + +Cygwin + +- bash 3.2.39(19) +- pdksh 5.2.14 +- zsh 4.3.4 + +Linux (Ubuntu Dapper 6.06) + +- bash 3.1.17(1) +- pdksh 5.2.14 +- zsh 4.2.5 + +Linux (Ubuntu Hardy 8.04) + +- bash 3.2.39(1) +- dash 0.5.4 +- ksh 1993-12-28 +- pdksh 5.2.14 +- zsh 4.3.4 + +Mac OS X 10.5.4 (Darwin 9.4.0) + +- bash 3.2.17(1) +- ksh 1993-12-28 +- zsh 4.3.4 + +Solaris 9 U6 x86 + +- /bin/sh +- bash 2.05.0(1) +- ksh M-11/16/88i +- zsh 3.0.8 + +Solaris 11 x86 (Nevada 77) + +- /bin/sh +- bash 3.2.25(1) +- ksh M-11/16/88i +- zsh 4.3.4 + + +New Features +------------ + +Support added to output assert source line number as part of assert messages. + + +Changes and Enhancements +------------------------ + +Support for automated testing frameworks. + +Added argument count error checking to all functions. + + +Bug Fixes +--------- + +Fixed some issues with ksh and zsh support. + +Fixed off-by-one of exit value in trap handler. + +Fixed handling of null values under ksh. + +Fixed bug in last resort temporary directory creation. + + +Deprecated Features +------------------- + +None. + + +Known Bugs and Issues +--------------------- + +zsh requires the ``shwordsplit`` option to be set. + +Line numbers in assert messages do not work properly with Bash 2.x. + +.. vim:fileencoding=latin1:ft=rst:spell:tw=80 diff --git a/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.5.txt b/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.5.txt new file mode 100644 index 0000000..d9f26ce --- /dev/null +++ b/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.5.txt @@ -0,0 +1,128 @@ +Release Notes for shUnit2 2.1.5 +=============================== + +This release contains several bug fixes and changes. Additionally, it includes +a rewrite of the test output to better match JUnit and PyUnit. + +This version also includes a slightly expanded set of coding standards by which +shUnit2 is coded. It should help anyone reading the code to better understand +it. + + + +Please read the Shell Errata section of the documentation for guidance on how +to meet these requirements. + +See the ``CHANGES-2.1.txt`` file for a full list of changes. + + +Tested Platforms +---------------- + +Cygwin + +- bash 3.2.39(20) +- ksh (sym-link to pdksh) +- pdksh 5.2.14 +- zsh 4.3.4 + +Linux (Ubuntu Dapper 6.06) + +- bash 3.1.17(1) +- ksh M-1993-12-28 +- pdksh 5.2.14-99/07/13.2 +- zsh 4.2.5 + +Linux (Ubuntu Hardy 8.04) + +- bash 3.2.39(1) +- dash 0.5.4 +- ksh M-1993-12-28 +- pdksh 5.2.14-99/07/13.2 +- zsh 4.3.4 + +Mac OS X 10.5.4 (Darwin 9.4.0) + +- bash 3.2.17(1) +- ksh M-1993-12-28 +- zsh 4.3.4 + +Solaris 9 U6 x86 + +- /bin/sh +- bash 2.05.0(1) +- ksh M-11/16/88i +- zsh 3.0.8 + +Solaris 11 x86 (Nevada 77) + +- /bin/sh +- bash 3.2.25(1) +- ksh M-11/16/88i +- zsh 4.3.4 + + +New Features +------------ + +Support added for output assert source line number as part of assert messages. + +Issue #2: Added assertNotEquals() assert. + +Provided a public ``shunit_tmpDir`` variable that can be used by unit test +scripts that need automated and guaranteed cleanup. + + +Changes and Enhancements +------------------------ + +Issue #3: Removed the check for unset variables as shUnit2 should not expect +scripts being tested to be clean. + +Issue #4: Rewrote the test summary. It is now greatly simplified and much more +script friendly. + +Issue #5: Fixed the documentation around the usage of failures. + +Issue #9: Added unit tests and improved documentation around the use of macros. + +Code updated to meet documented coding standards. + +Improved code reuse of ``_shunit_exit()`` and ``_shunit_fatal()`` functions. + +All output except shUnit2 error messages now goes to STDOUT. + +Converted DocBook documentation to reStructuredText for easier maintenance. + + +Bug Fixes +--------- + +Issue #1: Fixed bug in rap code where certain types of exit conditions did not +generate the ending report. + +Issue #7: Fixed duplicated printing of messages passed to asserts. + +Fixed bugs in ``shlib_relToAbsPath()`` in ``shlib``. + + +Deprecated Features +------------------- + +None. + + +Known Bugs and Issues +--------------------- + +Zsh requires the ``shwordsplit`` option to be set. See the documentation for +examples of how to do this. + +Line numbers in assert messages do not work properly with BASH 2.x. + +The Bourne shell of Solaris, BASH 2.x, and Zsh 3.0.x do not properly catch the +SIGTERM signal. As such, shell interpreter failures due to such things as +unbound variables cannot be caught. (See ``shunit_test_misc.sh``) + + +.. vim:fileencoding=latin1:ft=rst:spell:tw=80 diff --git a/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.6.txt b/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.6.txt new file mode 100644 index 0000000..50087fe --- /dev/null +++ b/test/shunit2-2.1.6/doc/RELEASE_NOTES-2.1.6.txt @@ -0,0 +1,112 @@ +Release Notes for shUnit2 2.1.6 +=============================== + +This release contains bug fixes and changes. It is also the first release to +support running shunit2 as a standalone program. + +Please read the Shell Errata section of the documentation for guidance on how +to meet these requirements. + +See the ``CHANGES-2.1.txt`` file for a full list of changes. + +New Features +------------ + +Support for running shUnit2 as a standalone program. This makes it possible for +users to execute their unit tests in a manner that is not dependent on the +location an OS distribution maintainer chose to place shUnit2 in the file +system. + +Added support for functions defined like 'function someFunction()'. + +Changes and Enhancements +------------------------ + +Renamed the public ``shunit_tmpDir`` variable to ``SHUNIT_TMPDIR`` to be more +consistent with the ``TMPDIR`` variable. + +Bug Fixes +--------- + +Fixed issue where shunit2 would fail on some distributions when creating a +temporary directory because the **od** command was not present. + +Deprecated Features +------------------- + +None. + +Known Bugs and Issues +--------------------- + +Zsh requires the ``shwordsplit`` option to be set. See the documentation for +examples of how to do this. + +Line numbers in assert messages do not work properly with BASH 2.x. + +The Bourne shell of Solaris, BASH 2.x, and Zsh 3.0.x do not properly catch the +SIGTERM signal. As such, shell interpreter failures due to such things as +unbound variables cannot be caught. (See ``shunit_test_misc.sh``) + +Tested Platforms +---------------- + +Cygwin 1.7.9 (Windows XP SP2) + +- bash 4.1.10(4) +- dash 0.5.6.1 +- ksh (sym-link to pdksh) +- pdksh 5.2.14 +- zsh 4.3.11 + +Linux (Ubuntu Dapper 6.06.2 LTS) + +- bash 3.1.17(1) +- dash 0.5.3 +- ksh (sym-link to pdksh) +- pdksh 5.2.14-99/07/13.2 +- zsh 4.2.5 + +Linux (Ubuntu Hardy 8.04.4 LTS) + +- bash 3.2.39(1) +- dash 0.5.4 +- ksh M-1993-12-28 +- pdksh 5.2.14-99/07/13.2 +- zsh 4.3.4 + +Linux (Ubuntu Lucid 10.04.2 LTS) + +- bash 4.1.5(1) +- dash 0.5.5.1 +- ksh JM-93t+-2009-05-01 +- pdksh 5.2.14-99/07/13.2 +- zsh 4.3.10 + +Mac OS X 10.6.7 + +- bash 3.2.48(1) +- ksh M-1993-12-28 +- zsh 4.3.9 + +Solaris 8 U7 x86 + +- /bin/sh +- bash 2.03.0(1) +- ksh M-11/16/88i +- zsh 3.0.6 + +Solaris 9 U6 x86 + +- /bin/sh +- bash 2.05.0(1) +- ksh M-11/16/88i +- zsh 3.0.8 + +OpenSolaris 2009.06(snv_111b) x86 + +- /bin/sh +- bash 3.2.25(1) +- ksh 2008-11-04 + +.. vim:fileencoding=latin1:ft=rst:spell:tw=80 diff --git a/test/shunit2-2.1.6/doc/TODO.txt b/test/shunit2-2.1.6/doc/TODO.txt new file mode 100644 index 0000000..f917cee --- /dev/null +++ b/test/shunit2-2.1.6/doc/TODO.txt @@ -0,0 +1,13 @@ +Make it possible to execute a single test by passing the name of the test on +the command line + +Add support for '--randomize-order' so that the test order is randomized to +check for dependencies (which shouldn't be there) between tests. + +--debug option to display point in source code (line number and such) where the +problem showed up. + +assertTrue() just gives 'ASSERT:', nothing else :-(. others too? +upd: assertNull() will give message passed, but nothing else useful :-( + +$Revision: 228 $ diff --git a/test/shunit2-2.1.6/doc/coding_standards.txt b/test/shunit2-2.1.6/doc/coding_standards.txt new file mode 100644 index 0000000..651f40d --- /dev/null +++ b/test/shunit2-2.1.6/doc/coding_standards.txt @@ -0,0 +1,74 @@ +Coding Standards +================ + +Variable and Function Names +--------------------------- + +All shUnit2 specific constants, variables, and functions will be prefixed +appropriately with 'shunit'. This is to distinguish usage in the shUnit2 code +from users own scripts so that the shell name space remains predictable to +users. The exceptions here are the standard ``assertEquals``, etc. functions. + +All non-builtin constants and variables will be surrouned with squiggle +brackets, e.g. '${shunit_someVariable}' to improve code readability. + +Due to some shells not supporting local variables in functions, care in the +naming and use of variables, both public and private, is very important. +Accidental overriding of the variables can occur easily if care is not taken as +all variables are technically global variables in some shells. + ++----------------------------------+---------------------------+ +| *type* | *sample* | ++==================================+===========================+ +| global public constant | ``SHUNIT_TRUE`` | ++----------------------------------+---------------------------+ +| global private constant | ``__SHUNIT_SHELL_FLAGS`` | ++----------------------------------+---------------------------+ +| global public variable | not used | ++----------------------------------+---------------------------+ +| global private variable | ``__shunit_someVariable`` | ++----------------------------------+---------------------------+ +| global macro | ``_SHUNIT_SOME_MACRO_`` | ++----------------------------------+---------------------------+ +| public function | ``assertEquals`` | ++----------------------------------+---------------------------+ +| public function, local variable | ``shunit_someVariable_`` | ++----------------------------------+---------------------------+ +| private function | ``_shunit_someFunction`` | ++----------------------------------+---------------------------+ +| private function, local variable | ``_shunit_someVariable_`` | ++----------------------------------+---------------------------+ + +Where it makes sense, variables can have the first letter of the second and +later words capitalized. For example, the local variable name for the total +number of test cases seen might be ``shunit_totalTestsSeen_``. + +Local Variable Cleanup +---------------------- + +As many shells do not support local variables, no support for cleanup of +variables is present either. As such, all variables local to a function must be +cleared up with the ``unset`` command at the end of each function. + +Indentation +----------- + +Code block indentation is two (2) spaces, and tabs may not be used. :: + + if [ -z 'some string' ]; then + someFunction + fi + +Lines of code should be no longer than 80 characters unless absolutely +necessary. When lines are wrapped using the backslash character '\', subsequent +lines should be indented with four (4) spaces so as to differentiate from the +standard spacing of two characters. Tabs may *not* be used. :: + + for x in some set of very long set of arguments that make for a very long \ + that extends much too long for one line + do + echo ${x} + done + +.. vim:fileencoding=latin1:ft=rst:spell:tw=80 +.. $Revision: 301 $ diff --git a/test/shunit2-2.1.6/doc/contributors.txt b/test/shunit2-2.1.6/doc/contributors.txt new file mode 100644 index 0000000..97c7d09 --- /dev/null +++ b/test/shunit2-2.1.6/doc/contributors.txt @@ -0,0 +1,14 @@ +The original author of shunit2 is Kate Ward. The following people have +contributed in some way or another to shunit2. + +Bryan Larsen +Kevin Van Horn +Maciej Bliziński +Mario Sparada +Mathias Goldau +Richard Jensen +Rob Holland +Rocky Bernstein +wood4321 (of code.google.com) + +$Revision: 313 $ diff --git a/test/shunit2-2.1.6/doc/design_doc.txt b/test/shunit2-2.1.6/doc/design_doc.txt new file mode 100644 index 0000000..7ac8002 --- /dev/null +++ b/test/shunit2-2.1.6/doc/design_doc.txt @@ -0,0 +1,34 @@ +Design Doc for shUnit + +shUnit is based upon JUnit. The initial ideas for the script came from the book +"Pragmatic Unit Testing - In Java with JUnit" by Andrew Hunt and David Thomas. + +The script was written to perform unit testing for log4sh. log4sh had grown +enough that it was becoming difficult to easily test and and verify that the +tests passed for the many different operating systems on which it was being +used. + +The functions in shUnit are meant to match those in JUnit as much as possible +where shell allows. In the initial version, there will be no concept of +exceptions (as normal POSIX shell has no concept of them) but attempts to trap +problems will be done. + +Programatic Standards: + +* SHUNIT_TRUE - public global constant +* __SHUNIT_SHELL_FLAGS - private global constant +* __shunit_oldShellFlags - private global variable + +* assertEquals - public unit test function +* shunit_publicFunc - public shUnit function; can be called from parent unit + test script +* _shunit_privateFunc - private shUnit function; should not be called from + parent script. meant for internal use by shUnit + +* _su_myVar - variable inside a public function. prefixing with '_su_' to + reduce the chances that a variable outside of shUnit will be overridden. +* _su__myVar - variable inside a private function. prefixing with '_su__' to + reduce the chances that a variable in a shUnit public function, or a variable + outside of shUnit will be overridden. + +$Revision: 4 $ diff --git a/test/shunit2-2.1.6/doc/rst2html.css b/test/shunit2-2.1.6/doc/rst2html.css new file mode 100644 index 0000000..01983a5 --- /dev/null +++ b/test/shunit2-2.1.6/doc/rst2html.css @@ -0,0 +1,292 @@ +/* +:Author: David Goodger +:Contact: goodger@users.sourceforge.net +:Date: $Date: 2007-04-11 11:48:16 +0100 (Wed, 11 Apr 2007) $ +:Revision: $Revision: 2791 $ +:Copyright: This stylesheet has been placed in the public domain. +:Modified by: Kate Ward + +Default cascading style sheet for the HTML output of Docutils. + +See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to +customize this style sheet. +*/ + +/* used to remove borders from tables and images */ +.borderless, table.borderless td, table.borderless th { + border: 0 } + +table.borderless td, table.borderless th { + /* Override padding for "table.docutils td" with "! important". + The right padding separates the table cells. */ + padding: 0 0.5em 0 0 ! important } + +.first { + /* Override more specific margin styles with "! important". */ + margin-top: 0 ! important } + +.last, .with-subtitle { + margin-bottom: 0 ! important } + +.hidden { + display: none } + +a.toc-backref { + text-decoration: none ; + color: black } + +blockquote.epigraph { + margin: 2em 5em ; } + +dl.docutils dd { + margin-bottom: 0.5em } + +/* Uncomment (and remove this text!) to get bold-faced definition list terms +dl.docutils dt { + font-weight: bold } +*/ + +div.abstract { + margin: 2em 5em } + +div.abstract p.topic-title { + font-weight: bold ; + text-align: center } + +div.admonition, div.attention, div.caution, div.danger, div.error, +div.hint, div.important, div.note, div.tip, div.warning { + margin: 2em ; + border: medium outset ; + padding: 1em } + +div.admonition p.admonition-title, div.hint p.admonition-title, +div.important p.admonition-title, div.note p.admonition-title, +div.tip p.admonition-title { + font-weight: bold ; + font-family: sans-serif } + +div.attention p.admonition-title, div.caution p.admonition-title, +div.danger p.admonition-title, div.error p.admonition-title, +div.warning p.admonition-title { + color: red ; + font-weight: bold ; + font-family: sans-serif } + +/* Uncomment (and remove this text!) to get reduced vertical space in + compound paragraphs. +div.compound .compound-first, div.compound .compound-middle { + margin-bottom: 0.5em } + +div.compound .compound-last, div.compound .compound-middle { + margin-top: 0.5em } +*/ + +div.dedication { + margin: 2em 5em ; + text-align: center ; + font-style: italic } + +div.dedication p.topic-title { + font-weight: bold ; + font-style: normal } + +div.figure { + margin-left: 2em ; + margin-right: 2em } + +div.footer, div.header { + clear: both; + font-size: smaller } + +div.line-block { + display: block ; + margin-top: 1em ; + margin-bottom: 1em } + +div.line-block div.line-block { + margin-top: 0 ; + margin-bottom: 0 ; + margin-left: 1.5em } + +div.sidebar { + margin-left: 1em ; + border: medium outset ; + padding: 1em ; + background-color: #ffffee ; + width: 40% ; + float: right ; + clear: right } + +div.sidebar p.rubric { + font-family: sans-serif ; + font-size: medium } + +div.system-messages { + margin: 5em } + +div.system-messages h1 { + color: red } + +div.system-message { + border: medium outset ; + padding: 1em } + +div.system-message p.system-message-title { + color: red ; + font-weight: bold } + +div.topic { + margin: 2em } + +h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, +h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { + margin-top: 0.4em } + +h1.title { + text-align: center } + +h2.subtitle { + text-align: center } + +hr.docutils { + width: 75% } + +img.align-left { + clear: left } + +img.align-right { + clear: right } + +ol.simple, ul.simple { + margin-bottom: 1em } + +ol.arabic { + list-style: decimal } + +ol.loweralpha { + list-style: lower-alpha } + +ol.upperalpha { + list-style: upper-alpha } + +ol.lowerroman { + list-style: lower-roman } + +ol.upperroman { + list-style: upper-roman } + +p.attribution { + text-align: right ; + margin-left: 50% } + +p.caption { + font-style: italic } + +p.credits { + font-style: italic ; + font-size: smaller } + +p.label { + white-space: nowrap } + +p.rubric { + font-weight: bold ; + font-size: larger ; + color: maroon ; + text-align: center } + +p.sidebar-title { + font-family: sans-serif ; + font-weight: bold ; + font-size: larger } + +p.sidebar-subtitle { + font-family: sans-serif ; + font-weight: bold } + +p.topic-title { + font-weight: bold } + +pre.address { + margin-bottom: 0 ; + margin-top: 0 ; + font-family: serif ; + font-size: 100% } + +pre.literal-block, pre.doctest-block { + margin-left: 2em ; + margin-right: 2em ; + background-color: #eeeeee } + +span.classifier { + font-family: sans-serif ; + font-style: oblique } + +span.classifier-delimiter { + font-family: sans-serif ; + font-weight: bold } + +span.interpreted { + font-family: sans-serif } + +span.option { + white-space: nowrap } + +span.pre { + white-space: pre } + +span.problematic { + color: red } + +span.section-subtitle { + /* font-size relative to parent (h1..h6 element) */ + font-size: 80% } + +table.citation { + border-left: solid 1px gray; + margin-left: 1px } + +table.docinfo { + margin: 2em 4em } + +/* +table.docutils { + margin-top: 0.5em ; + margin-bottom: 0.5em } +*/ + +table.footnote { + border-left: solid 1px black; + margin-left: 1px ; + font-size: 80% } + } + +table.docutils td, table.docutils th, +table.docinfo td, table.docinfo th { + padding-left: 0.5em ; + padding-right: 0.5em ; + vertical-align: top } + +table.docutils th.field-name, table.docinfo th.docinfo-name { + font-weight: bold ; + text-align: left ; + white-space: nowrap ; + padding-left: 0 } + +h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, +h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { + font-size: 100% } + +/* +tt.docutils { + background-color: #eeeeee } +*/ + +ul.auto-toc { + list-style-type: none } + +/* customizations by kward */ + +h1 { font-size: 133%; border-top:1px solid #CCCCFF; } +h1.title { font-size: 150%; border-top:0px; padding-top: 1em; } +/* div.document { font-size: 90% } */ diff --git a/test/shunit2-2.1.6/doc/shunit2.html b/test/shunit2-2.1.6/doc/shunit2.html new file mode 100644 index 0000000..c5aa336 --- /dev/null +++ b/test/shunit2-2.1.6/doc/shunit2.html @@ -0,0 +1,880 @@ + + + + + + +shUnit2 2.1.x Documentation + + + +
+

shUnit2 2.1.x Documentation

+ +
+

Abstract

+

shUnit2 is a xUnit unit test framework for Bourne based shell scripts, and it +is designed to work in a similar manner to JUnit, PyUnit, etc.. If you have +ever had the desire to write a unit test for a shell script, shUnit2 can do the +job.

+ +
+
+

Introduction

+

shUnit2 was originally developed to provide a consistent testing solution for +log4sh, a shell based logging framework similar to log4j. During the +development of that product, a repeated problem of having things work just fine +under one shell (/bin/bash on Linux to be specific), and then not working +under another shell (/bin/sh on Solaris) kept coming up. Although several +simple tests were run, they were not adequate and did not catch some corner +cases. The decision was finally made to write a proper unit test framework after +multiple brown-bag releases were made. Research was done to look for an +existing product that met the testing requirements, but no adequate product was +found.

+

Tested Operating Systems (varies over time)

+
    +
  • Cygwin
  • +
  • FreeBSD (user supported)
  • +
  • Linux (Gentoo, Ubuntu)
  • +
  • Mac OS X
  • +
  • Solaris 8, 9, 10 (inc. OpenSolaris)
  • +
+

Tested Shells

+
    +
  • Bourne Shell (sh)
  • +
  • BASH - GNU Bourne Again SHell (bash)
  • +
  • DASH (dash)
  • +
  • Korn Shell (ksh)
  • +
  • pdksh - Public Domain Korn Shell (pdksh)
  • +
  • zsh - Zsh (zsh) (since 2.1.2) please see the Zsh shell errata for more +information
  • +
+

See the appropriate Release Notes for this release +(doc/RELEASE_NOTES-X.X.X.txt) for the list of actual versions tested.

+
+

Credits / Contributors

+

A list of contributors to shUnit2 can be found in the source archive in +doc/contributors.txt. Many thanks go out to all those who have contributed +to make this a better tool.

+

shUnit2 is the original product of many hours of work by Kate Ward, the primary +author of the code. For other products by her, look up log4sh or shFlags, or +visit her website at http://forestent.com/.

+
+
+

Feedback

+

Feedback is most certainly welcome for this document. Send your additions, +comments and criticisms to the shunit2-users@google.com mailing list.

+
+
+
+

Quickstart

+

This section will give a very quick start to running unit tests with shUnit2. +More information is located in later sections.

+

Here is a quick sample script to show how easy it is to write a unit test in +shell. Note: the script as it stands expects that you are running it from the +``examples`` directory.

+
+#! /bin/sh
+# file: examples/equality_test.sh
+
+testEquality()
+{
+  assertEquals 1 1
+}
+
+# load shunit2
+. ../src/shell/shunit2
+
+

Running the unit test should give results similar to the following.

+
+testEquality
+
+Ran 1 test.
+
+OK
+
+

W00t! You've just run your first successful unit test. So, what just happened? +Quite a bit really, and it all happened simply by sourcing the shunit2 +library. The basic functionality for the script above goes like this:

+
    +
  • When shUnit2 is sourced, it will walk through any functions defined whose +namestart with the string test and add those to an internal list of tests +to execute. Once a list of test functions to be run has been determined, +shunit2 will go to work.
  • +
  • Before any tests are executed, shUnit2 again looks for a function, this time +one named oneTimeSetUp(). If it exists, it will be run. This function is +normally used to setup the environment for all tests to be run. Things like +creating directories for output or setting environment variables are good to +place here. Just so you know, you can also declare a corresponding function +named oneTimeTearDown() function that does the same thing, but once all +the tests have been completed. It is good for removing temporary directories, +etc.
  • +
  • shUnit2 is now ready to run tests. Before doing so though, it again looks for +another function that might be declared, one named setUp(). If the +function exists, it will be run before each test. It is good for resetting the +environment so that each test starts with a clean slate. At this stage, the +first test is finally run. The success of the test is recorded for a report +that will be generated later. After the test is run, shUnit2 looks for a final +function that might be declared, one named tearDown(). If it exists, it +will be run after each test. It is a good place for cleaning up after each +test, maybe doing things like removing files that were created, or removing +directories. This set of steps, setUp() > test() > tearDown(), is +repeated for all of the available tests.
  • +
  • Once all the work is done, shUnit2 will generate the nice report you saw +above. A summary of all the successes and failures will be given so that you +know how well your code is doing.
  • +
+

We should now try adding a test that fails. Change your unit test to look like +this.

+
+#! /bin/sh
+# file: examples/party_test.sh
+
+testEquality()
+{
+  assertEquals 1 1
+}
+
+testPartyLikeItIs1999()
+{
+  year=`date '+%Y'`
+  assertEquals "It's not 1999 :-(" \
+      '1999' "${year}"
+}
+
+# load shunit2
+. ../src/shell/shunit2
+
+

So, what did you get? I guess it told you that this isn't 1999. Bummer, eh? +Hopefully, you noticed a couple of things that were different about the second +test. First, we added an optional message that the user will see if the assert +fails. Second, we did comparisons of strings instead of integers as in the first +test. It doesn't matter whether you are testing for equality of strings or +integers. Both work equally well with shUnit2.

+

Hopefully, this is enough to get you started with unit testing. If you want a +ton more examples, take a look at the tests provided with log4sh or shFlags. +Both provide excellent examples of more advanced usage. shUnit2 was after all +written to help with the unit testing problems that log4sh had.

+
+
+

Function Reference

+
+

General Info

+

Any string values passed should be properly quoted -- they should must be +surrounded by single-quote (') or double-quote (") characters -- so that the +shell will properly parse them.

+
+
+

Asserts

+
+
assertEquals [message] expected actual
+
Asserts that expected and actual are equal to one another. The expected +and actual values can be either strings or integer values as both will be +treated as strings. The message is optional, and must be quoted.
+
assertNotEquals [message] expected actual
+
Asserts that unexpected and actual are not equal to one another. The +unexpected and actual values can be either strings or integer values as +both will be treaded as strings. The message is optional, and must be +quoted.
+
assertSame [message] expected actual
+
This function is functionally equivalent to assertEquals.
+
assertNotSame [message] unexpected actual
+
This function is functionally equivalent to assertNotEquals.
+
assertNull [message] value
+
Asserts that value is null, or in shell terms, a zero-length string. The +value must be a string as an integer value does not translate into a +zero-length string. The message is optional, and must be quoted.
+
assertNotNull [message] value
+
Asserts that value is not null, or in shell terms, a non-empty string. The +value may be a string or an integer as the later will be parsed as a +non-empty string value. The message is optional, and must be quoted.
+
assertTrue [message] condition
+

Asserts that a given shell test condition is true. The condition can be as +simple as a shell true value (the value 0 -- equivalent to +${SHUNIT_TRUE}), or a more sophisticated shell conditional expression. The +message is optional, and must be quoted.

+

A sophisticated shell conditional expression is equivalent to what the if +or while shell built-ins would use (more specifically, what the test +command would use). Testing for example whether some value is greater than +another value can be done this way.

+
+assertTrue "[ 34 -gt 23 ]"
+
+

Testing for the ability to read a file can also be done. This particular test +will fail.

+
+assertTrue 'test failed' "[ -r /some/non-existant/file' ]"
+
+

As the expressions are standard shell test expressions, it is possible to +string multiple expressions together with -a and -o in the standard +fashion. This test will succeed as the entire expression evaluates to true.

+
+assertTrue 'test failed' '[ 1 -eq 1 -a 2 -eq 2 ]'
+
+

One word of warning: be very careful with your quoting as shell is not the +most forgiving of bad quoting, and things will fail in strange ways.

+
+
assertFalse [message] condition
+

Asserts that a given shell test condition is false. The condition can be +as simple as a shell false value (the value 1 -- equivalent to +${SHUNIT_FALSE}), or a more sophisticated shell conditional expression. +The message is optional, and must be quoted.

+

For examples of more sophisticated expressions, see ``assertTrue``.

+
+
+
+
+

Failures

+

Just to clarify, failures do not test the various arguments against one +another. Failures simply fail, optionally with a message, and that is all they +do. If you need to test arguments against one another, use asserts.

+

If all failures do is fail, why might one use them? There are times when you may +have some very complicated logic that you need to test, and the simple asserts +provided are simply not adequate. You can do your own validation of the code, +use an assertTrue ${SHUNIT_TRUE} if your own tests succeeded, and use a +failure to record a failure.

+
+
fail [message]
+
Fails the test immediately. The message is optional, and must be quoted.
+
failNotEquals [message] unexpected actual
+

Fails the test immediately, reporting that the unexpected and actual +values are not equal to one another. The message is optional, and must be +quoted.

+

Note: no actual comparison of unexpected and actual is done.

+
+
failSame [message] expected actual
+

Fails the test immediately, reporting that the expected and actual values +are the same. The message is optional, and must be quoted.

+

Note: no actual comparison of expected and actual is done.

+
+
failNotSame [message] expected actual
+

Fails the test immediately, reporting that the expected and actual values +are not the same. The message is optional, and must be quoted.

+

Note: no actual comparison of expected and actual is done.

+
+
+
+
+

Setup/Teardown

+
+
oneTimeSetUp
+

This function can be be optionally overridden by the user in their test suite.

+

If this function exists, it will be called once before any tests are run. It +is useful to prepare a common environment for all tests.

+
+
oneTimeTearDown
+

This function can be be optionally overridden by the user in their test suite.

+

If this function exists, it will be called once after all tests are completed. +It is useful to clean up the environment after all tests.

+
+
setUp
+

This function can be be optionally overridden by the user in their test suite.

+

If this function exists, it will be called before each test is run. It is +useful to reset the environment before each test.

+
+
tearDown
+

This function can be be optionally overridden by the user in their test suite.

+

If this function exists, it will be called after each test completes. It is +useful to clean up the environment after each test.

+
+
+
+
+

Skipping

+
+
startSkipping
+
This function forces the remaining assert and fail functions to be +"skipped", i.e. they will have no effect. Each function skipped will be +recorded so that the total of asserts and fails will not be altered.
+
endSkipping
+
This function returns calls to the assert and fail functions to their +default behavior, i.e. they will be called.
+
isSkipping
+
This function returns the current state of skipping. It can be compared +against ${SHUNIT_TRUE} or ${SHUNIT_FALSE} if desired.
+
+
+
+

Suites

+

The default behavior of shUnit2 is that all tests will be found dynamically. If +you have a specific set of tests you want to run, or you don't want to use the +standard naming scheme of prefixing your tests with test, these functions +are for you. Most users will never use them though.

+
+
suite
+

This function can be optionally overridden by the user in their test suite.

+

If this function exists, it will be called when shunit2 is sourced. If it +does not exist, shUnit2 will search the parent script for all functions +beginning with the word test, and they will be added dynamically to the +test suite.

+
+
suite_addTest name
+
This function adds a function named name to the list of tests scheduled for +execution as part of this test suite. This function should only be called from +within the suite() function.
+
+
+
+
+

Advanced Usage

+

This section covers several advanced usage topics.

+
+

Some constants you can use

+

There are several constants provided by shUnit2 as variables that might be of +use to you.

+

Predefined

+ ++++ + + + + + + + + + + + + + + + + + +
SHUNIT_VERSIONThe version of shUnit2 you are running.
SHUNIT_TRUEStandard shell true value (the integer value 0).
SHUNIT_FALSEStandard shell false value (the integer value 1).
SHUNIT_ERRORThe integer value 2.
SHUNIT_TMPDIRPath to temporary directory that will be automatically +cleaned up upon exit of shUnit2.
+

User defined

+ ++++ + + + + + +
SHUNIT_PARENTThe filename of the shell script containing the tests. This +is needed specifically for Zsh support.
+
+
+

Error handling

+

The constants values SHUNIT_TRUE, SHUNIT_FALSE, and SHUNIT_ERROR are +returned from nearly every function to indicate the success or failure of the +function. Additionally the variable flags_error is filled with a detailed +error message if any function returns with a SHUNIT_ERROR value.

+
+
+

Including Line Numbers in Asserts (Macros)

+

If you include lots of assert statements in an individual test function, it can +become difficult to determine exactly which assert was thrown unless your +messages are unique. To help somewhat, line numbers can be included in the +assert messages. To enable this, a special shell "macro" must be used rather +than the standard assert calls. Shell doesn't actually have macros; the name is +used here as the operation is similar to a standard macro.

+

For example, to include line numbers for a assertEquals() function call, +replace the assertEquals() with ${_ASSERT_EQUALS_}.

+

Example -- Asserts with and without line numbers

+
+#! /bin/sh
+# file: examples/lineno_test.sh
+
+testLineNo()
+{
+  # this assert will have line numbers included (e.g. "ASSERT:[123] ...")
+  echo "ae: ${_ASSERT_EQUALS_}"
+  ${_ASSERT_EQUALS_} 'not equal' 1 2
+
+  # this assert will not have line numbers included (e.g. "ASSERT: ...")
+  assertEquals 'not equal' 1 2
+}
+
+# load shunit2
+. ../src/shell/shunit2
+
+

Notes:

+
    +
  1. Due to how shell parses command-line arguments, all strings used with macros +should be quoted twice. Namely, single-quotes must be converted to +single-double-quotes, and vice-versa. If the string being passed is +absolutely for sure not empty, the extra quoting is not necessary.

    +

    Normal assertEquals call.

    +
    +assertEquals 'some message' 'x' ''
    +
    +

    Macro _ASSERT_EQUALS_ call. Note the extra quoting around the message +and the null value.

    +
    +_ASSERT_EQUALS_ '"some message"' 'x' '""'
    +
    +
  2. +
  3. Line numbers are not supported in all shells. If a shell does not support +them, no errors will be thrown. Supported shells include: bash (>=3.0), +ksh, pdksh, and zsh.

    +
  4. +
+
+
+

Test Skipping

+

There are times where the test code you have written is just not applicable to +the system you are running on. This section describes how to skip these tests +but maintain the total test count.

+

Probably the easiest example would be shell code that is meant to run under the +bash shell, but the unit test is running under the Bourne shell. There are +things that just won't work. The following test code demonstrates two sample +functions, one that will be run under any shell, and the another that will run +only under the bash shell.

+

Example -- math include

+
+# available as examples/math.inc
+
+add_generic()
+{
+  num_a=$1
+  num_b=$2
+
+  expr $1 + $2
+}
+
+add_bash()
+{
+  num_a=$1
+  num_b=$2
+
+  echo $(($1 + $2))
+}
+
+

And here is a corresponding unit test that correctly skips the add_bash() +function when the unit test is not running under the bash shell.

+

Example -- math unit test

+
+#! /bin/sh
+# available as examples/math_test.sh
+
+testAdding()
+{
+  result=`add_generic 1 2`
+  assertEquals \
+      "the result of '${result}' was wrong" \
+      3 "${result}"
+
+  # disable non-generic tests
+  [ -z "${BASH_VERSION:-}" ] && startSkipping
+
+  result=`add_bash 1 2`
+  assertEquals \
+      "the result of '${result}' was wrong" \
+      3 "${result}"
+}
+
+oneTimeSetUp()
+{
+  # load include to test
+  . ./math.inc
+}
+
+# load and run shUnit2
+. ../src/shell/shunit2
+
+

Running the above test under the bash shell will result in the following +output.

+
+$ /bin/bash math_test.sh
+testAdding
+
+Ran 1 test.
+
+OK
+
+

But, running the test under any other Unix shell will result in the following +output.

+
+$ /bin/ksh math_test.sh
+testAdding
+
+Ran 1 test.
+
+OK (skipped=1)
+
+

As you can see, the total number of tests has not changed, but the report +indicates that some tests were skipped.

+

Skipping can be controlled with the following functions: startSkipping(), +stopSkipping(), and isSkipping(). Once skipping is enabled, it will +remain enabled until the end of the current test function call, after which +skipping is disabled.

+
+
+
+

Appendix

+
+

Getting help

+

For help, please send requests to either the shunit2-users@googlegroups.com +mailing list (archives available on the web at +http://groups.google.com/group/shunit2-users) or directly to +Kate Ward <kate dot ward at forestent dot com>.

+
+
+

Zsh

+

For compatibility with Zsh, there is one requirement that must be met -- the +shwordsplit option must be set. There are three ways to accomplish this.

+
    +
  1. In the unit-test script, add the following shell code snippet before sourcing +the shunit2 library.

    +
    +setopt shwordsplit
    +
    +
  2. +
  3. When invoking zsh from either the command-line or as a script with +#!, add the -y parameter.

    +
    +#! /bin/zsh -y
    +
    +
  4. +
  5. When invoking zsh from the command-line, add -o shwordsplit -- as +parameters before the script name.

    +
    +$ zsh -o shwordsplit -- some_script
    +
    +
  6. +
+ + + + + +
+
+
+ + diff --git a/test/shunit2-2.1.6/doc/shunit2.txt b/test/shunit2-2.1.6/doc/shunit2.txt new file mode 100644 index 0000000..fec1b53 --- /dev/null +++ b/test/shunit2-2.1.6/doc/shunit2.txt @@ -0,0 +1,562 @@ +=========================== +shUnit2 2.1.x Documentation +=========================== + +Abstract +======== + +shUnit2_ is a xUnit_ unit test framework for Bourne based shell scripts, and it +is designed to work in a similar manner to JUnit_, PyUnit_, etc.. If you have +ever had the desire to write a unit test for a shell script, shUnit2 can do the +job. + +.. contents:: Table of Contents + :depth: 2 + +Introduction +============ + +shUnit2 was originally developed to provide a consistent testing solution for +log4sh_, a shell based logging framework similar to log4j_. During the +development of that product, a repeated problem of having things work just fine +under one shell (``/bin/bash`` on Linux to be specific), and then not working +under another shell (``/bin/sh`` on Solaris) kept coming up. Although several +simple tests were run, they were not adequate and did not catch some corner +cases. The decision was finally made to write a proper unit test framework after +multiple brown-bag releases were made. *Research was done to look for an +existing product that met the testing requirements, but no adequate product was +found.* + +Tested Operating Systems (varies over time) + +- Cygwin +- FreeBSD (user supported) +- Linux (Gentoo, Ubuntu) +- Mac OS X +- Solaris 8, 9, 10 (inc. OpenSolaris) + +Tested Shells + +- Bourne Shell (**sh**) +- BASH - GNU Bourne Again SHell (**bash**) +- DASH (**dash**) +- Korn Shell (**ksh**) +- pdksh - Public Domain Korn Shell (**pdksh**) +- zsh - Zsh (**zsh**) (since 2.1.2) *please see the Zsh shell errata for more + information* + +See the appropriate Release Notes for this release +(``doc/RELEASE_NOTES-X.X.X.txt``) for the list of actual versions tested. + +Credits / Contributors +---------------------- + +A list of contributors to shUnit2 can be found in the source archive in +``doc/contributors.txt``. Many thanks go out to all those who have contributed +to make this a better tool. + +shUnit2 is the original product of many hours of work by Kate Ward, the primary +author of the code. For other products by her, look up log4sh_ or shFlags_, or +visit her website at http://forestent.com/. + +Feedback +-------- + +Feedback is most certainly welcome for this document. Send your additions, +comments and criticisms to the shunit2-users@google.com mailing list. + +Quickstart +========== + +This section will give a very quick start to running unit tests with shUnit2. +More information is located in later sections. + +Here is a quick sample script to show how easy it is to write a unit test in +shell. *Note: the script as it stands expects that you are running it from the +``examples`` directory.* :: + + #! /bin/sh + # file: examples/equality_test.sh + + testEquality() + { + assertEquals 1 1 + } + + # load shunit2 + . ../src/shell/shunit2 + +Running the unit test should give results similar to the following. :: + + testEquality + + Ran 1 test. + + OK + +W00t! You've just run your first successful unit test. So, what just happened? +Quite a bit really, and it all happened simply by sourcing the ``shunit2`` +library. The basic functionality for the script above goes like this: + +- When shUnit2 is sourced, it will walk through any functions defined whose + namestart with the string ``test`` and add those to an internal list of tests + to execute. Once a list of test functions to be run has been determined, + shunit2 will go to work. +- Before any tests are executed, shUnit2 again looks for a function, this time + one named ``oneTimeSetUp()``. If it exists, it will be run. This function is + normally used to setup the environment for all tests to be run. Things like + creating directories for output or setting environment variables are good to + place here. Just so you know, you can also declare a corresponding function + named ``oneTimeTearDown()`` function that does the same thing, but once all + the tests have been completed. It is good for removing temporary directories, + etc. +- shUnit2 is now ready to run tests. Before doing so though, it again looks for + another function that might be declared, one named ``setUp()``. If the + function exists, it will be run before each test. It is good for resetting the + environment so that each test starts with a clean slate. At this stage, the + first test is finally run. The success of the test is recorded for a report + that will be generated later. After the test is run, shUnit2 looks for a final + function that might be declared, one named ``tearDown()``. If it exists, it + will be run after each test. It is a good place for cleaning up after each + test, maybe doing things like removing files that were created, or removing + directories. This set of steps, ``setUp()`` > ``test()`` > ``tearDown()``, is + repeated for all of the available tests. +- Once all the work is done, shUnit2 will generate the nice report you saw + above. A summary of all the successes and failures will be given so that you + know how well your code is doing. + +We should now try adding a test that fails. Change your unit test to look like +this. :: + + #! /bin/sh + # file: examples/party_test.sh + + testEquality() + { + assertEquals 1 1 + } + + testPartyLikeItIs1999() + { + year=`date '+%Y'` + assertEquals "It's not 1999 :-(" \ + '1999' "${year}" + } + + # load shunit2 + . ../src/shell/shunit2 + +So, what did you get? I guess it told you that this isn't 1999. Bummer, eh? +Hopefully, you noticed a couple of things that were different about the second +test. First, we added an optional message that the user will see if the assert +fails. Second, we did comparisons of strings instead of integers as in the first +test. It doesn't matter whether you are testing for equality of strings or +integers. Both work equally well with shUnit2. + +Hopefully, this is enough to get you started with unit testing. If you want a +ton more examples, take a look at the tests provided with log4sh_ or shFlags_. +Both provide excellent examples of more advanced usage. shUnit2 was after all +written to help with the unit testing problems that log4sh_ had. + +Function Reference +================== + +General Info +------------ + +Any string values passed should be properly quoted -- they should must be +surrounded by single-quote (') or double-quote (") characters -- so that the +shell will properly parse them. + +Asserts +------- + +``assertEquals [message] expected actual`` + Asserts that *expected* and *actual* are equal to one another. The *expected* + and *actual* values can be either strings or integer values as both will be + treated as strings. The *message* is optional, and must be quoted. + +``assertNotEquals [message] expected actual`` + Asserts that *unexpected* and *actual* are not equal to one another. The + *unexpected* and *actual* values can be either strings or integer values as + both will be treaded as strings. The *message* is optional, and must be + quoted. + +``assertSame [message] expected actual`` + This function is functionally equivalent to ``assertEquals``. + +``assertNotSame [message] unexpected actual`` + This function is functionally equivalent to ``assertNotEquals``. + +``assertNull [message] value`` + Asserts that *value* is *null*, or in shell terms, a zero-length string. The + *value* must be a string as an integer value does not translate into a + zero-length string. The *message* is optional, and must be quoted. + +``assertNotNull [message] value`` + Asserts that *value* is *not null*, or in shell terms, a non-empty string. The + *value* may be a string or an integer as the later will be parsed as a + non-empty string value. The *message* is optional, and must be quoted. + +``assertTrue [message] condition`` + Asserts that a given shell test *condition* is *true*. The condition can be as + simple as a shell *true* value (the value ``0`` -- equivalent to + ``${SHUNIT_TRUE}``), or a more sophisticated shell conditional expression. The + *message* is optional, and must be quoted. + + A sophisticated shell conditional expression is equivalent to what the **if** + or **while** shell built-ins would use (more specifically, what the **test** + command would use). Testing for example whether some value is greater than + another value can be done this way. :: + + assertTrue "[ 34 -gt 23 ]" + + Testing for the ability to read a file can also be done. This particular test + will fail. :: + + assertTrue 'test failed' "[ -r /some/non-existant/file' ]" + + As the expressions are standard shell **test** expressions, it is possible to + string multiple expressions together with ``-a`` and ``-o`` in the standard + fashion. This test will succeed as the entire expression evaluates to *true*. + :: + + assertTrue 'test failed' '[ 1 -eq 1 -a 2 -eq 2 ]' + + *One word of warning: be very careful with your quoting as shell is not the + most forgiving of bad quoting, and things will fail in strange ways.* + +``assertFalse [message] condition`` + Asserts that a given shell test *condition* is *false*. The condition can be + as simple as a shell *false* value (the value ``1`` -- equivalent to + ``${SHUNIT_FALSE}``), or a more sophisticated shell conditional expression. + The *message* is optional, and must be quoted. + + *For examples of more sophisticated expressions, see ``assertTrue``.* + +Failures +-------- + +Just to clarify, failures **do not** test the various arguments against one +another. Failures simply fail, optionally with a message, and that is all they +do. If you need to test arguments against one another, use asserts. + +If all failures do is fail, why might one use them? There are times when you may +have some very complicated logic that you need to test, and the simple asserts +provided are simply not adequate. You can do your own validation of the code, +use an ``assertTrue ${SHUNIT_TRUE}`` if your own tests succeeded, and use a +failure to record a failure. + +``fail [message]`` + Fails the test immediately. The *message* is optional, and must be quoted. + +``failNotEquals [message] unexpected actual`` + Fails the test immediately, reporting that the *unexpected* and *actual* + values are not equal to one another. The *message* is optional, and must be + quoted. + + *Note: no actual comparison of unexpected and actual is done.* + +``failSame [message] expected actual`` + Fails the test immediately, reporting that the *expected* and *actual* values + are the same. The *message* is optional, and must be quoted. + + *Note: no actual comparison of expected and actual is done.* + +``failNotSame [message] expected actual`` + Fails the test immediately, reporting that the *expected* and *actual* values + are not the same. The *message* is optional, and must be quoted. + + *Note: no actual comparison of expected and actual is done.* + +Setup/Teardown +-------------- + +``oneTimeSetUp`` + This function can be be optionally overridden by the user in their test suite. + + If this function exists, it will be called once before any tests are run. It + is useful to prepare a common environment for all tests. + +``oneTimeTearDown`` + This function can be be optionally overridden by the user in their test suite. + + If this function exists, it will be called once after all tests are completed. + It is useful to clean up the environment after all tests. + +``setUp`` + This function can be be optionally overridden by the user in their test suite. + + If this function exists, it will be called before each test is run. It is + useful to reset the environment before each test. + +``tearDown`` + This function can be be optionally overridden by the user in their test suite. + + If this function exists, it will be called after each test completes. It is + useful to clean up the environment after each test. + +Skipping +-------- + +``startSkipping`` + This function forces the remaining *assert* and *fail* functions to be + "skipped", i.e. they will have no effect. Each function skipped will be + recorded so that the total of asserts and fails will not be altered. + +``endSkipping`` + This function returns calls to the *assert* and *fail* functions to their + default behavior, i.e. they will be called. + +``isSkipping`` + This function returns the current state of skipping. It can be compared + against ``${SHUNIT_TRUE}`` or ``${SHUNIT_FALSE}`` if desired. + +Suites +------ + +The default behavior of shUnit2 is that all tests will be found dynamically. If +you have a specific set of tests you want to run, or you don't want to use the +standard naming scheme of prefixing your tests with ``test``, these functions +are for you. Most users will never use them though. + +``suite`` + This function can be optionally overridden by the user in their test suite. + + If this function exists, it will be called when ``shunit2`` is sourced. If it + does not exist, shUnit2 will search the parent script for all functions + beginning with the word ``test``, and they will be added dynamically to the + test suite. + +``suite_addTest name`` + This function adds a function named *name* to the list of tests scheduled for + execution as part of this test suite. This function should only be called from + within the ``suite()`` function. + +Advanced Usage +============== + +This section covers several advanced usage topics. + +Some constants you can use +-------------------------- + +There are several constants provided by shUnit2 as variables that might be of +use to you. + +Predefined + +================== =========================================================== +``SHUNIT_VERSION`` The version of shUnit2 you are running. +``SHUNIT_TRUE`` Standard shell *true* value (the integer value 0). +``SHUNIT_FALSE`` Standard shell *false* value (the integer value 1). +``SHUNIT_ERROR`` The integer value 2. +``SHUNIT_TMPDIR`` Path to temporary directory that will be automatically + cleaned up upon exit of shUnit2. +================== =========================================================== + +User defined + +================== =========================================================== +``SHUNIT_PARENT`` The filename of the shell script containing the tests. This + is needed specifically for Zsh support. +================== =========================================================== + +Error handling +-------------- + +The constants values ``SHUNIT_TRUE``, ``SHUNIT_FALSE``, and ``SHUNIT_ERROR`` are +returned from nearly every function to indicate the success or failure of the +function. Additionally the variable ``flags_error`` is filled with a detailed +error message if any function returns with a ``SHUNIT_ERROR`` value. + +Including Line Numbers in Asserts (Macros) +------------------------------------------ + +If you include lots of assert statements in an individual test function, it can +become difficult to determine exactly which assert was thrown unless your +messages are unique. To help somewhat, line numbers can be included in the +assert messages. To enable this, a special shell "macro" must be used rather +than the standard assert calls. *Shell doesn't actually have macros; the name is +used here as the operation is similar to a standard macro.* + +For example, to include line numbers for a ``assertEquals()`` function call, +replace the ``assertEquals()`` with ``${_ASSERT_EQUALS_}``. + +Example -- Asserts with and without line numbers :: + + #! /bin/sh + # file: examples/lineno_test.sh + + testLineNo() + { + # this assert will have line numbers included (e.g. "ASSERT:[123] ...") + echo "ae: ${_ASSERT_EQUALS_}" + ${_ASSERT_EQUALS_} 'not equal' 1 2 + + # this assert will not have line numbers included (e.g. "ASSERT: ...") + assertEquals 'not equal' 1 2 + } + + # load shunit2 + . ../src/shell/shunit2 + +Notes: + +#. Due to how shell parses command-line arguments, all strings used with macros + should be quoted twice. Namely, single-quotes must be converted to + single-double-quotes, and vice-versa. If the string being passed is + absolutely for sure not empty, the extra quoting is not necessary. + + Normal ``assertEquals`` call. :: + + assertEquals 'some message' 'x' '' + + Macro ``_ASSERT_EQUALS_`` call. Note the extra quoting around the *message* + and the *null* value. :: + + _ASSERT_EQUALS_ '"some message"' 'x' '""' + +#. Line numbers are not supported in all shells. If a shell does not support + them, no errors will be thrown. Supported shells include: **bash** (>=3.0), + **ksh**, **pdksh**, and **zsh**. + +Test Skipping +------------- + +There are times where the test code you have written is just not applicable to +the system you are running on. This section describes how to skip these tests +but maintain the total test count. + +Probably the easiest example would be shell code that is meant to run under the +**bash** shell, but the unit test is running under the Bourne shell. There are +things that just won't work. The following test code demonstrates two sample +functions, one that will be run under any shell, and the another that will run +only under the **bash** shell. + +Example -- math include :: + + # available as examples/math.inc + + add_generic() + { + num_a=$1 + num_b=$2 + + expr $1 + $2 + } + + add_bash() + { + num_a=$1 + num_b=$2 + + echo $(($1 + $2)) + } + +And here is a corresponding unit test that correctly skips the ``add_bash()`` +function when the unit test is not running under the **bash** shell. + +Example -- math unit test :: + + #! /bin/sh + # available as examples/math_test.sh + + testAdding() + { + result=`add_generic 1 2` + assertEquals \ + "the result of '${result}' was wrong" \ + 3 "${result}" + + # disable non-generic tests + [ -z "${BASH_VERSION:-}" ] && startSkipping + + result=`add_bash 1 2` + assertEquals \ + "the result of '${result}' was wrong" \ + 3 "${result}" + } + + oneTimeSetUp() + { + # load include to test + . ./math.inc + } + + # load and run shUnit2 + . ../src/shell/shunit2 + +Running the above test under the **bash** shell will result in the following +output. :: + + $ /bin/bash math_test.sh + testAdding + + Ran 1 test. + + OK + +But, running the test under any other Unix shell will result in the following +output. :: + + $ /bin/ksh math_test.sh + testAdding + + Ran 1 test. + + OK (skipped=1) + +As you can see, the total number of tests has not changed, but the report +indicates that some tests were skipped. + +Skipping can be controlled with the following functions: ``startSkipping()``, +``stopSkipping()``, and ``isSkipping()``. Once skipping is enabled, it will +remain enabled until the end of the current test function call, after which +skipping is disabled. + +Appendix +======== + +Getting help +------------ + +For help, please send requests to either the shunit2-users@googlegroups.com +mailing list (archives available on the web at +http://groups.google.com/group/shunit2-users) or directly to +Kate Ward . + +Zsh +--- + +For compatibility with Zsh, there is one requirement that must be met -- the +``shwordsplit`` option must be set. There are three ways to accomplish this. + +#. In the unit-test script, add the following shell code snippet before sourcing + the ``shunit2`` library. :: + + setopt shwordsplit + +#. When invoking **zsh** from either the command-line or as a script with + ``#!``, add the ``-y`` parameter. :: + + #! /bin/zsh -y + +#. When invoking **zsh** from the command-line, add ``-o shwordsplit --`` as + parameters before the script name. :: + + $ zsh -o shwordsplit -- some_script + +.. _log4sh: http://log4sh.sourceforge.net/ +.. _log4j: http://logging.apache.org/ +.. _JUnit: http://www.junit.org/ +.. _PyUnit: http://pyunit.sourceforge.net/ +.. _shFlags: http://shflags.googlecode.com/ +.. _shUnit2: http://shunit2.googlecode.com/ +.. _xUnit: http://en.wikipedia.org/wiki/XUnit + +.. generate HTML using rst2html from Docutils of +.. http://docutils.sourceforge.net/ +.. +.. vim:fileencoding=latin1:ft=rst:spell:sts=2:sw=2:tw=80 +.. $Revision: 233 $ diff --git a/test/shunit2-2.1.6/examples/equality_test.sh b/test/shunit2-2.1.6/examples/equality_test.sh new file mode 100755 index 0000000..e0d68a5 --- /dev/null +++ b/test/shunit2-2.1.6/examples/equality_test.sh @@ -0,0 +1,10 @@ +#! /bin/sh +# file: examples/equality_test.sh + +testEquality() +{ + assertEquals 1 1 +} + +# load shunit2 +. ../src/shunit2 diff --git a/test/shunit2-2.1.6/examples/lineno_test.sh b/test/shunit2-2.1.6/examples/lineno_test.sh new file mode 100755 index 0000000..9c05f1e --- /dev/null +++ b/test/shunit2-2.1.6/examples/lineno_test.sh @@ -0,0 +1,16 @@ +#! /bin/sh +# file: examples/lineno_test.sh + +testLineNo() +{ + # this assert will have line numbers included (e.g. "ASSERT:[123] ...") if + # they are supported. + echo "_ASSERT_EQUALS_ macro value: ${_ASSERT_EQUALS_}" + ${_ASSERT_EQUALS_} 'not equal' 1 2 + + # this assert will not have line numbers included (e.g. "ASSERT: ...") + assertEquals 'not equal' 1 2 +} + +# load shunit2 +. ../src/shunit2 diff --git a/test/shunit2-2.1.6/examples/math.inc b/test/shunit2-2.1.6/examples/math.inc new file mode 100644 index 0000000..4097106 --- /dev/null +++ b/test/shunit2-2.1.6/examples/math.inc @@ -0,0 +1,17 @@ +# available as examples/math.inc + +add_generic() +{ + num_a=$1 + num_b=$2 + + expr $1 + $2 +} + +add_bash() +{ + num_a=$1 + num_b=$2 + + echo $(($1 + $2)) +} diff --git a/test/shunit2-2.1.6/examples/math_test.sh b/test/shunit2-2.1.6/examples/math_test.sh new file mode 100755 index 0000000..41be5ff --- /dev/null +++ b/test/shunit2-2.1.6/examples/math_test.sh @@ -0,0 +1,27 @@ +#! /bin/sh +# available as examples/math_test.sh + +testAdding() +{ + result=`add_generic 1 2` + assertEquals \ + "the result of '${result}' was wrong" \ + 3 "${result}" + + # disable non-generic tests + [ -z "${BASH_VERSION:-}" ] && startSkipping + + result=`add_bash 1 2` + assertEquals \ + "the result of '${result}' was wrong" \ + 3 "${result}" +} + +oneTimeSetUp() +{ + # load include to test + . ./math.inc +} + +# load and run shUnit2 +. ../src/shunit2 diff --git a/test/shunit2-2.1.6/examples/mkdir_test.sh b/test/shunit2-2.1.6/examples/mkdir_test.sh new file mode 100755 index 0000000..28d8d94 --- /dev/null +++ b/test/shunit2-2.1.6/examples/mkdir_test.sh @@ -0,0 +1,89 @@ +#!/bin/sh +# vim:et:ft=sh:sts=2:sw=2 +# +# Copyright 2008 Kate Ward. All Rights Reserved. +# Released under the LGPL (GNU Lesser General Public License) +# +# Author: kate.ward@forestent.com (Kate Ward) +# +# Example unit test for the mkdir command. +# +# There are times when an existing shell script needs to be tested. In this +# example, we will test several aspects of the the mkdir command, but the +# techniques could be used for any existing shell script. + +#----------------------------------------------------------------------------- +# suite tests +# + +testMissingDirectoryCreation() +{ + ${mkdirCmd} "${testDir}" >${stdoutF} 2>${stderrF} + rtrn=$? + th_assertTrueWithNoOutput ${rtrn} "${stdoutF}" "${stderrF}" + + assertTrue 'directory missing' "[ -d '${testDir}' ]" +} + +testExistingDirectoryCreationFails() +{ + # create a directory to test against + ${mkdirCmd} "${testDir}" + + # test for expected failure while trying to create directory that exists + ${mkdirCmd} "${testDir}" >${stdoutF} 2>${stderrF} + rtrn=$? + assertFalse 'expecting return code of 1 (false)' ${rtrn} + assertNull 'unexpected output to stdout' "`cat ${stdoutF}`" + assertNotNull 'expected error message to stderr' "`cat ${stderrF}`" + + assertTrue 'directory missing' "[ -d '${testDir}' ]" +} + +testRecursiveDirectoryCreation() +{ + testDir2="${testDir}/test2" + + ${mkdirCmd} -p "${testDir2}" >${stdoutF} 2>${stderrF} + rtrn=$? + th_assertTrueWithNoOutput ${rtrn} "${stdoutF}" "${stderrF}" + + assertTrue 'first directory missing' "[ -d '${testDir}' ]" + assertTrue 'second directory missing' "[ -d '${testDir2}' ]" +} + +#----------------------------------------------------------------------------- +# suite functions +# + +th_assertTrueWithNoOutput() +{ + th_return_=$1 + th_stdout_=$2 + th_stderr_=$3 + + assertFalse 'unexpected output to STDOUT' "[ -s '${th_stdout_}' ]" + assertFalse 'unexpected output to STDERR' "[ -s '${th_stderr_}' ]" + + unset th_return_ th_stdout_ th_stderr_ +} + +oneTimeSetUp() +{ + outputDir="${SHUNIT_TMPDIR}/output" + mkdir "${outputDir}" + stdoutF="${outputDir}/stdout" + stderrF="${outputDir}/stderr" + + mkdirCmd='mkdir' # save command name in variable to make future changes easy + testDir="${SHUNIT_TMPDIR}/some_test_dir" +} + +tearDown() +{ + rm -fr "${testDir}" +} + +# load and run shUnit2 +[ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0 +. ../src/shunit2 diff --git a/test/shunit2-2.1.6/examples/party_test.sh b/test/shunit2-2.1.6/examples/party_test.sh new file mode 100755 index 0000000..5ca2583 --- /dev/null +++ b/test/shunit2-2.1.6/examples/party_test.sh @@ -0,0 +1,17 @@ +#! /bin/sh +# file: examples/party_test.sh + +testEquality() +{ + assertEquals 1 1 +} + +testPartyLikeItIs1999() +{ + year=`date '+%Y'` + assertEquals "It's not 1999 :-(" \ + '1999' "${year}" +} + +# load shunit2 +. ../src/shunit2 diff --git a/test/shunit2-2.1.6/lib/shflags b/test/shunit2-2.1.6/lib/shflags new file mode 100644 index 0000000..d09867e --- /dev/null +++ b/test/shunit2-2.1.6/lib/shflags @@ -0,0 +1,1011 @@ +# $Id: shflags 138 2010-03-18 00:25:34Z kate.ward@forestent.com $ +# vim:et:ft=sh:sts=2:sw=2 +# +# Copyright 2008 Kate Ward. All Rights Reserved. +# Released under the LGPL (GNU Lesser General Public License) +# +# shFlags -- Advanced command-line flag library for Unix shell scripts. +# http://code.google.com/p/shflags/ +# +# Author: kate.ward@forestent.com (Kate Ward) +# +# This module implements something like the google-gflags library available +# from http://code.google.com/p/google-gflags/. +# +# FLAG TYPES: This is a list of the DEFINE_*'s that you can do. All flags take +# a name, default value, help-string, and optional 'short' name (one-letter +# name). Some flags have other arguments, which are described with the flag. +# +# DEFINE_string: takes any input, and intreprets it as a string. +# +# DEFINE_boolean: typically does not take any argument: say --myflag to set +# FLAGS_myflag to true, or --nomyflag to set FLAGS_myflag to false. +# Alternately, you can say +# --myflag=true or --myflag=t or --myflag=0 or +# --myflag=false or --myflag=f or --myflag=1 +# Passing an option has the same affect as passing the option once. +# +# DEFINE_float: takes an input and intreprets it as a floating point number. As +# shell does not support floats per-se, the input is merely validated as +# being a valid floating point value. +# +# DEFINE_integer: takes an input and intreprets it as an integer. +# +# SPECIAL FLAGS: There are a few flags that have special meaning: +# --help (or -?) prints a list of all the flags in a human-readable fashion +# --flagfile=foo read flags from foo. (not implemented yet) +# -- as in getopt(), terminates flag-processing +# +# EXAMPLE USAGE: +# +# -- begin hello.sh -- +# #! /bin/sh +# . ./shflags +# DEFINE_string name 'world' "somebody's name" n +# FLAGS "$@" || exit $? +# eval set -- "${FLAGS_ARGV}" +# echo "Hello, ${FLAGS_name}." +# -- end hello.sh -- +# +# $ ./hello.sh -n Kate +# Hello, Kate. +# +# NOTE: Not all systems include a getopt version that supports long flags. On +# these systems, only short flags are recognized. + +#============================================================================== +# shFlags +# +# Shared attributes: +# flags_error: last error message +# flags_return: last return value +# +# __flags_longNames: list of long names for all flags +# __flags_shortNames: list of short names for all flags +# __flags_boolNames: list of boolean flag names +# +# __flags_opts: options parsed by getopt +# +# Per-flag attributes: +# FLAGS_: contains value of flag named 'flag_name' +# __flags__default: the default flag value +# __flags__help: the flag help string +# __flags__short: the flag short name +# __flags__type: the flag type +# +# Notes: +# - lists of strings are space separated, and a null value is the '~' char. + +# return if FLAGS already loaded +[ -n "${FLAGS_VERSION:-}" ] && return 0 +FLAGS_VERSION='1.0.4pre' + +# return values +FLAGS_TRUE=0 +FLAGS_FALSE=1 +FLAGS_ERROR=2 + +# reserved flag names +FLAGS_RESERVED='ARGC ARGV ERROR FALSE HELP PARENT RESERVED TRUE VERSION' + +_flags_debug() { echo "flags:DEBUG $@" >&2; } +_flags_warn() { echo "flags:WARN $@" >&2; } +_flags_error() { echo "flags:ERROR $@" >&2; } +_flags_fatal() { echo "flags:FATAL $@" >&2; } + +# specific shell checks +if [ -n "${ZSH_VERSION:-}" ]; then + setopt |grep "^shwordsplit$" >/dev/null + if [ $? -ne ${FLAGS_TRUE} ]; then + _flags_fatal 'zsh shwordsplit option is required for proper zsh operation' + exit ${FLAGS_ERROR} + fi + if [ -z "${FLAGS_PARENT:-}" ]; then + _flags_fatal "zsh does not pass \$0 through properly. please declare' \ +\"FLAGS_PARENT=\$0\" before calling shFlags" + exit ${FLAGS_ERROR} + fi +fi + +# +# constants +# + +# getopt version +__FLAGS_GETOPT_VERS_STD=0 +__FLAGS_GETOPT_VERS_ENH=1 +__FLAGS_GETOPT_VERS_BSD=2 + +getopt >/dev/null 2>&1 +case $? in + 0) __FLAGS_GETOPT_VERS=${__FLAGS_GETOPT_VERS_STD} ;; # bsd getopt + 2) + # TODO(kward): look into '-T' option to test the internal getopt() version + if [ "`getopt --version`" = '-- ' ]; then + __FLAGS_GETOPT_VERS=${__FLAGS_GETOPT_VERS_STD} + else + __FLAGS_GETOPT_VERS=${__FLAGS_GETOPT_VERS_ENH} + fi + ;; + *) + _flags_fatal 'unable to determine getopt version' + exit ${FLAGS_ERROR} + ;; +esac + +# getopt optstring lengths +__FLAGS_OPTSTR_SHORT=0 +__FLAGS_OPTSTR_LONG=1 + +__FLAGS_NULL='~' + +# flag info strings +__FLAGS_INFO_DEFAULT='default' +__FLAGS_INFO_HELP='help' +__FLAGS_INFO_SHORT='short' +__FLAGS_INFO_TYPE='type' + +# flag lengths +__FLAGS_LEN_SHORT=0 +__FLAGS_LEN_LONG=1 + +# flag types +__FLAGS_TYPE_NONE=0 +__FLAGS_TYPE_BOOLEAN=1 +__FLAGS_TYPE_FLOAT=2 +__FLAGS_TYPE_INTEGER=3 +__FLAGS_TYPE_STRING=4 + +# set the constants readonly +__flags_constants=`set |awk -F= '/^FLAGS_/ || /^__FLAGS_/ {print $1}'` +for __flags_const in ${__flags_constants}; do + # skip certain flags + case ${__flags_const} in + FLAGS_HELP) continue ;; + FLAGS_PARENT) continue ;; + esac + # set flag readonly + if [ -z "${ZSH_VERSION:-}" ]; then + readonly ${__flags_const} + else # handle zsh + case ${ZSH_VERSION} in + [123].*) readonly ${__flags_const} ;; + *) readonly -g ${__flags_const} ;; # declare readonly constants globally + esac + fi +done +unset __flags_const __flags_constants + +# +# internal variables +# + +__flags_boolNames=' ' # space separated list of boolean flag names +__flags_longNames=' ' # space separated list of long flag names +__flags_shortNames=' ' # space separated list of short flag names + +__flags_columns='' # screen width in columns +__flags_opts='' # temporary storage for parsed getopt flags + +#------------------------------------------------------------------------------ +# private functions +# + +# Define a flag. +# +# Calling this function will define the following info variables for the +# specified flag: +# FLAGS_flagname - the name for this flag (based upon the long flag name) +# __flags__default - the default value +# __flags_flagname_help - the help string +# __flags_flagname_short - the single letter alias +# __flags_flagname_type - the type of flag (one of __FLAGS_TYPE_*) +# +# Args: +# _flags__type: integer: internal type of flag (__FLAGS_TYPE_*) +# _flags__name: string: long flag name +# _flags__default: default flag value +# _flags__help: string: help string +# _flags__short: string: (optional) short flag name +# Returns: +# integer: success of operation, or error +_flags_define() +{ + if [ $# -lt 4 ]; then + flags_error='DEFINE error: too few arguments' + flags_return=${FLAGS_ERROR} + _flags_error "${flags_error}" + return ${flags_return} + fi + + _flags_type_=$1 + _flags_name_=$2 + _flags_default_=$3 + _flags_help_=$4 + _flags_short_=${5:-${__FLAGS_NULL}} + + _flags_return_=${FLAGS_TRUE} + + # TODO(kward): check for validity of the flag name (e.g. dashes) + + # check whether the flag name is reserved + echo " ${FLAGS_RESERVED} " |grep " ${_flags_name_} " >/dev/null + if [ $? -eq 0 ]; then + flags_error="flag name (${_flags_name_}) is reserved" + _flags_return_=${FLAGS_ERROR} + fi + + # require short option for getopt that don't support long options + if [ ${_flags_return_} -eq ${FLAGS_TRUE} \ + -a ${__FLAGS_GETOPT_VERS} -ne ${__FLAGS_GETOPT_VERS_ENH} \ + -a "${_flags_short_}" = "${__FLAGS_NULL}" ] + then + flags_error="short flag required for (${_flags_name_}) on this platform" + _flags_return_=${FLAGS_ERROR} + fi + + # check for existing long name definition + if [ ${_flags_return_} -eq ${FLAGS_TRUE} ]; then + if _flags_itemInList "${_flags_name_}" \ + ${__flags_longNames} ${__flags_boolNames} + then + flags_error="flag name ([no]${_flags_name_}) already defined" + _flags_warn "${flags_error}" + _flags_return_=${FLAGS_FALSE} + fi + fi + + # check for existing short name definition + if [ ${_flags_return_} -eq ${FLAGS_TRUE} \ + -a "${_flags_short_}" != "${__FLAGS_NULL}" ] + then + if _flags_itemInList "${_flags_short_}" ${__flags_shortNames}; then + flags_error="flag short name (${_flags_short_}) already defined" + _flags_warn "${flags_error}" + _flags_return_=${FLAGS_FALSE} + fi + fi + + # handle default value. note, on several occasions the 'if' portion of an + # if/then/else contains just a ':' which does nothing. a binary reversal via + # '!' is not done because it does not work on all shells. + if [ ${_flags_return_} -eq ${FLAGS_TRUE} ]; then + case ${_flags_type_} in + ${__FLAGS_TYPE_BOOLEAN}) + if _flags_validateBoolean "${_flags_default_}"; then + case ${_flags_default_} in + true|t|0) _flags_default_=${FLAGS_TRUE} ;; + false|f|1) _flags_default_=${FLAGS_FALSE} ;; + esac + else + flags_error="invalid default flag value '${_flags_default_}'" + _flags_return_=${FLAGS_ERROR} + fi + ;; + + ${__FLAGS_TYPE_FLOAT}) + if _flags_validateFloat "${_flags_default_}"; then + : + else + flags_error="invalid default flag value '${_flags_default_}'" + _flags_return_=${FLAGS_ERROR} + fi + ;; + + ${__FLAGS_TYPE_INTEGER}) + if _flags_validateInteger "${_flags_default_}"; then + : + else + flags_error="invalid default flag value '${_flags_default_}'" + _flags_return_=${FLAGS_ERROR} + fi + ;; + + ${__FLAGS_TYPE_STRING}) ;; # everything in shell is a valid string + + *) + flags_error="unrecognized flag type '${_flags_type_}'" + _flags_return_=${FLAGS_ERROR} + ;; + esac + fi + + if [ ${_flags_return_} -eq ${FLAGS_TRUE} ]; then + # store flag information + eval "FLAGS_${_flags_name_}='${_flags_default_}'" + eval "__flags_${_flags_name_}_${__FLAGS_INFO_TYPE}=${_flags_type_}" + eval "__flags_${_flags_name_}_${__FLAGS_INFO_DEFAULT}=\ +\"${_flags_default_}\"" + eval "__flags_${_flags_name_}_${__FLAGS_INFO_HELP}=\"${_flags_help_}\"" + eval "__flags_${_flags_name_}_${__FLAGS_INFO_SHORT}='${_flags_short_}'" + + # append flag name(s) to list of names + __flags_longNames="${__flags_longNames}${_flags_name_} " + __flags_shortNames="${__flags_shortNames}${_flags_short_} " + [ ${_flags_type_} -eq ${__FLAGS_TYPE_BOOLEAN} ] && \ + __flags_boolNames="${__flags_boolNames}no${_flags_name_} " + fi + + flags_return=${_flags_return_} + unset _flags_default_ _flags_help_ _flags_name_ _flags_return_ _flags_short_ \ + _flags_type_ + [ ${flags_return} -eq ${FLAGS_ERROR} ] && _flags_error "${flags_error}" + return ${flags_return} +} + +# Return valid getopt options using currently defined list of long options. +# +# This function builds a proper getopt option string for short (and long) +# options, using the current list of long options for reference. +# +# Args: +# _flags_optStr: integer: option string type (__FLAGS_OPTSTR_*) +# Output: +# string: generated option string for getopt +# Returns: +# boolean: success of operation (always returns True) +_flags_genOptStr() +{ + _flags_optStrType_=$1 + + _flags_opts_='' + + for _flags_flag_ in ${__flags_longNames}; do + _flags_type_=`_flags_getFlagInfo ${_flags_flag_} ${__FLAGS_INFO_TYPE}` + case ${_flags_optStrType_} in + ${__FLAGS_OPTSTR_SHORT}) + _flags_shortName_=`_flags_getFlagInfo \ + ${_flags_flag_} ${__FLAGS_INFO_SHORT}` + if [ "${_flags_shortName_}" != "${__FLAGS_NULL}" ]; then + _flags_opts_="${_flags_opts_}${_flags_shortName_}" + # getopt needs a trailing ':' to indicate a required argument + [ ${_flags_type_} -ne ${__FLAGS_TYPE_BOOLEAN} ] && \ + _flags_opts_="${_flags_opts_}:" + fi + ;; + + ${__FLAGS_OPTSTR_LONG}) + _flags_opts_="${_flags_opts_:+${_flags_opts_},}${_flags_flag_}" + # getopt needs a trailing ':' to indicate a required argument + [ ${_flags_type_} -ne ${__FLAGS_TYPE_BOOLEAN} ] && \ + _flags_opts_="${_flags_opts_}:" + ;; + esac + done + + echo "${_flags_opts_}" + unset _flags_flag_ _flags_opts_ _flags_optStrType_ _flags_shortName_ \ + _flags_type_ + return ${FLAGS_TRUE} +} + +# Returns flag details based on a flag name and flag info. +# +# Args: +# string: long flag name +# string: flag info (see the _flags_define function for valid info types) +# Output: +# string: value of dereferenced flag variable +# Returns: +# integer: one of FLAGS_{TRUE|FALSE|ERROR} +_flags_getFlagInfo() +{ + _flags_name_=$1 + _flags_info_=$2 + + _flags_nameVar_="__flags_${_flags_name_}_${_flags_info_}" + _flags_strToEval_="_flags_value_=\"\${${_flags_nameVar_}:-}\"" + eval "${_flags_strToEval_}" + if [ -n "${_flags_value_}" ]; then + flags_return=${FLAGS_TRUE} + else + # see if the _flags_name_ variable is a string as strings can be empty... + # note: the DRY principle would say to have this function call itself for + # the next three lines, but doing so results in an infinite loop as an + # invalid _flags_name_ will also not have the associated _type variable. + # Because it doesn't (it will evaluate to an empty string) the logic will + # try to find the _type variable of the _type variable, and so on. Not so + # good ;-) + _flags_typeVar_="__flags_${_flags_name_}_${__FLAGS_INFO_TYPE}" + _flags_strToEval_="_flags_type_=\"\${${_flags_typeVar_}:-}\"" + eval "${_flags_strToEval_}" + if [ "${_flags_type_}" = "${__FLAGS_TYPE_STRING}" ]; then + flags_return=${FLAGS_TRUE} + else + flags_return=${FLAGS_ERROR} + flags_error="invalid flag name (${_flags_nameVar_})" + fi + fi + + echo "${_flags_value_}" + unset _flags_info_ _flags_name_ _flags_strToEval_ _flags_type_ _flags_value_ \ + _flags_nameVar_ _flags_typeVar_ + [ ${flags_return} -eq ${FLAGS_ERROR} ] && _flags_error "${flags_error}" + return ${flags_return} +} + +# check for presense of item in a list. passed a string (e.g. 'abc'), this +# function will determine if the string is present in the list of strings (e.g. +# ' foo bar abc '). +# +# Args: +# _flags__str: string: string to search for in a list of strings +# unnamed: list: list of strings +# Returns: +# boolean: true if item is in the list +_flags_itemInList() +{ + _flags_str_=$1 + shift + + echo " ${*:-} " |grep " ${_flags_str_} " >/dev/null + if [ $? -eq 0 ]; then + flags_return=${FLAGS_TRUE} + else + flags_return=${FLAGS_FALSE} + fi + + unset _flags_str_ + return ${flags_return} +} + +# Returns the width of the current screen. +# +# Output: +# integer: width in columns of the current screen. +_flags_columns() +{ + if [ -z "${__flags_columns}" ]; then + # determine the value and store it + if eval stty size >/dev/null 2>&1; then + # stty size worked :-) + set -- `stty size` + __flags_columns=$2 + elif eval tput cols >/dev/null 2>&1; then + set -- `tput cols` + __flags_columns=$1 + else + __flags_columns=80 # default terminal width + fi + fi + echo ${__flags_columns} +} + +# Validate a boolean. +# +# Args: +# _flags__bool: boolean: value to validate +# Returns: +# bool: true if the value is a valid boolean +_flags_validateBoolean() +{ + _flags_bool_=$1 + + flags_return=${FLAGS_TRUE} + case "${_flags_bool_}" in + true|t|0) ;; + false|f|1) ;; + *) flags_return=${FLAGS_FALSE} ;; + esac + + unset _flags_bool_ + return ${flags_return} +} + +# Validate a float. +# +# Args: +# _flags__float: float: value to validate +# Returns: +# bool: true if the value is a valid float +_flags_validateFloat() +{ + _flags_float_=$1 + + if _flags_validateInteger ${_flags_float_}; then + flags_return=${FLAGS_TRUE} + else + flags_return=${FLAGS_TRUE} + case ${_flags_float_} in + -*) # negative floats + _flags_test_=`expr -- "${_flags_float_}" :\ + '\(-[0-9][0-9]*\.[0-9][0-9]*\)'` + ;; + *) # positive floats + _flags_test_=`expr -- "${_flags_float_}" :\ + '\([0-9][0-9]*\.[0-9][0-9]*\)'` + ;; + esac + [ "${_flags_test_}" != "${_flags_float_}" ] && flags_return=${FLAGS_FALSE} + fi + + unset _flags_float_ _flags_test_ + return ${flags_return} +} + +# Validate an integer. +# +# Args: +# _flags__integer: interger: value to validate +# Returns: +# bool: true if the value is a valid integer +_flags_validateInteger() +{ + _flags_int_=$1 + + flags_return=${FLAGS_TRUE} + case ${_flags_int_} in + -*) # negative ints + _flags_test_=`expr -- "${_flags_int_}" : '\(-[0-9][0-9]*\)'` + ;; + *) # positive ints + _flags_test_=`expr -- "${_flags_int_}" : '\([0-9][0-9]*\)'` + ;; + esac + [ "${_flags_test_}" != "${_flags_int_}" ] && flags_return=${FLAGS_FALSE} + + unset _flags_int_ _flags_test_ + return ${flags_return} +} + +# Parse command-line options using the standard getopt. +# +# Note: the flag options are passed around in the global __flags_opts so that +# the formatting is not lost due to shell parsing and such. +# +# Args: +# @: varies: command-line options to parse +# Returns: +# integer: a FLAGS success condition +_flags_getoptStandard() +{ + flags_return=${FLAGS_TRUE} + _flags_shortOpts_=`_flags_genOptStr ${__FLAGS_OPTSTR_SHORT}` + + # check for spaces in passed options + for _flags_opt_ in "$@"; do + # note: the silliness with the x's is purely for ksh93 on Ubuntu 6.06 + _flags_match_=`echo "x${_flags_opt_}x" |sed 's/ //g'` + if [ "${_flags_match_}" != "x${_flags_opt_}x" ]; then + flags_error='the available getopt does not support spaces in options' + flags_return=${FLAGS_ERROR} + break + fi + done + + if [ ${flags_return} -eq ${FLAGS_TRUE} ]; then + __flags_opts=`getopt ${_flags_shortOpts_} $@ 2>&1` + _flags_rtrn_=$? + if [ ${_flags_rtrn_} -ne ${FLAGS_TRUE} ]; then + _flags_warn "${__flags_opts}" + flags_error='unable to parse provided options with getopt.' + flags_return=${FLAGS_ERROR} + fi + fi + + unset _flags_match_ _flags_opt_ _flags_rtrn_ _flags_shortOpts_ + return ${flags_return} +} + +# Parse command-line options using the enhanced getopt. +# +# Note: the flag options are passed around in the global __flags_opts so that +# the formatting is not lost due to shell parsing and such. +# +# Args: +# @: varies: command-line options to parse +# Returns: +# integer: a FLAGS success condition +_flags_getoptEnhanced() +{ + flags_return=${FLAGS_TRUE} + _flags_shortOpts_=`_flags_genOptStr ${__FLAGS_OPTSTR_SHORT}` + _flags_boolOpts_=`echo "${__flags_boolNames}" \ + |sed 's/^ *//;s/ *$//;s/ /,/g'` + _flags_longOpts_=`_flags_genOptStr ${__FLAGS_OPTSTR_LONG}` + + __flags_opts=`getopt \ + -o ${_flags_shortOpts_} \ + -l "${_flags_longOpts_},${_flags_boolOpts_}" \ + -- "$@" 2>&1` + _flags_rtrn_=$? + if [ ${_flags_rtrn_} -ne ${FLAGS_TRUE} ]; then + _flags_warn "${__flags_opts}" + flags_error='unable to parse provided options with getopt.' + flags_return=${FLAGS_ERROR} + fi + + unset _flags_boolOpts_ _flags_longOpts_ _flags_rtrn_ _flags_shortOpts_ + return ${flags_return} +} + +# Dynamically parse a getopt result and set appropriate variables. +# +# This function does the actual conversion of getopt output and runs it through +# the standard case structure for parsing. The case structure is actually quite +# dynamic to support any number of flags. +# +# Args: +# argc: int: original command-line argument count +# @: varies: output from getopt parsing +# Returns: +# integer: a FLAGS success condition +_flags_parseGetopt() +{ + _flags_argc_=$1 + shift + + flags_return=${FLAGS_TRUE} + + if [ ${__FLAGS_GETOPT_VERS} -ne ${__FLAGS_GETOPT_VERS_ENH} ]; then + set -- $@ + else + # note the quotes around the `$@' -- they are essential! + eval set -- "$@" + fi + + # provide user with number of arguments to shift by later + # NOTE: the FLAGS_ARGC variable is obsolete as of 1.0.3 because it does not + # properly give user access to non-flag arguments mixed in between flag + # arguments. Its usage was replaced by FLAGS_ARGV, and it is being kept only + # for backwards compatibility reasons. + FLAGS_ARGC=`expr $# - 1 - ${_flags_argc_}` + + # handle options. note options with values must do an additional shift + while true; do + _flags_opt_=$1 + _flags_arg_=${2:-} + _flags_type_=${__FLAGS_TYPE_NONE} + _flags_name_='' + + # determine long flag name + case "${_flags_opt_}" in + --) shift; break ;; # discontinue option parsing + + --*) # long option + _flags_opt_=`expr -- "${_flags_opt_}" : '--\(.*\)'` + _flags_len_=${__FLAGS_LEN_LONG} + if _flags_itemInList "${_flags_opt_}" ${__flags_longNames}; then + _flags_name_=${_flags_opt_} + else + # check for negated long boolean version + if _flags_itemInList "${_flags_opt_}" ${__flags_boolNames}; then + _flags_name_=`expr -- "${_flags_opt_}" : 'no\(.*\)'` + _flags_type_=${__FLAGS_TYPE_BOOLEAN} + _flags_arg_=${__FLAGS_NULL} + fi + fi + ;; + + -*) # short option + _flags_opt_=`expr -- "${_flags_opt_}" : '-\(.*\)'` + _flags_len_=${__FLAGS_LEN_SHORT} + if _flags_itemInList "${_flags_opt_}" ${__flags_shortNames}; then + # yes. match short name to long name. note purposeful off-by-one + # (too high) with awk calculations. + _flags_pos_=`echo "${__flags_shortNames}" \ + |awk 'BEGIN{RS=" ";rn=0}$0==e{rn=NR}END{print rn}' \ + e=${_flags_opt_}` + _flags_name_=`echo "${__flags_longNames}" \ + |awk 'BEGIN{RS=" "}rn==NR{print $0}' rn="${_flags_pos_}"` + fi + ;; + esac + + # die if the flag was unrecognized + if [ -z "${_flags_name_}" ]; then + flags_error="unrecognized option (${_flags_opt_})" + flags_return=${FLAGS_ERROR} + break + fi + + # set new flag value + [ ${_flags_type_} -eq ${__FLAGS_TYPE_NONE} ] && \ + _flags_type_=`_flags_getFlagInfo \ + "${_flags_name_}" ${__FLAGS_INFO_TYPE}` + case ${_flags_type_} in + ${__FLAGS_TYPE_BOOLEAN}) + if [ ${_flags_len_} -eq ${__FLAGS_LEN_LONG} ]; then + if [ "${_flags_arg_}" != "${__FLAGS_NULL}" ]; then + eval "FLAGS_${_flags_name_}=${FLAGS_TRUE}" + else + eval "FLAGS_${_flags_name_}=${FLAGS_FALSE}" + fi + else + _flags_strToEval_="_flags_val_=\ +\${__flags_${_flags_name_}_${__FLAGS_INFO_DEFAULT}}" + eval "${_flags_strToEval_}" + if [ ${_flags_val_} -eq ${FLAGS_FALSE} ]; then + eval "FLAGS_${_flags_name_}=${FLAGS_TRUE}" + else + eval "FLAGS_${_flags_name_}=${FLAGS_FALSE}" + fi + fi + ;; + + ${__FLAGS_TYPE_FLOAT}) + if _flags_validateFloat "${_flags_arg_}"; then + eval "FLAGS_${_flags_name_}='${_flags_arg_}'" + else + flags_error="invalid float value (${_flags_arg_})" + flags_return=${FLAGS_ERROR} + break + fi + ;; + + ${__FLAGS_TYPE_INTEGER}) + if _flags_validateInteger "${_flags_arg_}"; then + eval "FLAGS_${_flags_name_}='${_flags_arg_}'" + else + flags_error="invalid integer value (${_flags_arg_})" + flags_return=${FLAGS_ERROR} + break + fi + ;; + + ${__FLAGS_TYPE_STRING}) + eval "FLAGS_${_flags_name_}='${_flags_arg_}'" + ;; + esac + + # handle special case help flag + if [ "${_flags_name_}" = 'help' ]; then + if [ ${FLAGS_help} -eq ${FLAGS_TRUE} ]; then + flags_help + flags_error='help requested' + flags_return=${FLAGS_FALSE} + break + fi + fi + + # shift the option and non-boolean arguements out. + shift + [ ${_flags_type_} != ${__FLAGS_TYPE_BOOLEAN} ] && shift + done + + # give user back non-flag arguments + FLAGS_ARGV='' + while [ $# -gt 0 ]; do + FLAGS_ARGV="${FLAGS_ARGV:+${FLAGS_ARGV} }'$1'" + shift + done + + unset _flags_arg_ _flags_len_ _flags_name_ _flags_opt_ _flags_pos_ \ + _flags_strToEval_ _flags_type_ _flags_val_ + return ${flags_return} +} + +#------------------------------------------------------------------------------ +# public functions +# + +# A basic boolean flag. Boolean flags do not take any arguments, and their +# value is either 1 (false) or 0 (true). For long flags, the false value is +# specified on the command line by prepending the word 'no'. With short flags, +# the presense of the flag toggles the current value between true and false. +# Specifying a short boolean flag twice on the command results in returning the +# value back to the default value. +# +# A default value is required for boolean flags. +# +# For example, lets say a Boolean flag was created whose long name was 'update' +# and whose short name was 'x', and the default value was 'false'. This flag +# could be explicitly set to 'true' with '--update' or by '-x', and it could be +# explicitly set to 'false' with '--noupdate'. +DEFINE_boolean() { _flags_define ${__FLAGS_TYPE_BOOLEAN} "$@"; } + +# Other basic flags. +DEFINE_float() { _flags_define ${__FLAGS_TYPE_FLOAT} "$@"; } +DEFINE_integer() { _flags_define ${__FLAGS_TYPE_INTEGER} "$@"; } +DEFINE_string() { _flags_define ${__FLAGS_TYPE_STRING} "$@"; } + +# Parse the flags. +# +# Args: +# unnamed: list: command-line flags to parse +# Returns: +# integer: success of operation, or error +FLAGS() +{ + # define a standard 'help' flag if one isn't already defined + [ -z "${__flags_help_type:-}" ] && \ + DEFINE_boolean 'help' false 'show this help' 'h' + + # parse options + if [ $# -gt 0 ]; then + if [ ${__FLAGS_GETOPT_VERS} -ne ${__FLAGS_GETOPT_VERS_ENH} ]; then + _flags_getoptStandard "$@" + else + _flags_getoptEnhanced "$@" + fi + flags_return=$? + else + # nothing passed; won't bother running getopt + __flags_opts='--' + flags_return=${FLAGS_TRUE} + fi + + if [ ${flags_return} -eq ${FLAGS_TRUE} ]; then + _flags_parseGetopt $# "${__flags_opts}" + flags_return=$? + fi + + [ ${flags_return} -eq ${FLAGS_ERROR} ] && _flags_fatal "${flags_error}" + return ${flags_return} +} + +# This is a helper function for determining the `getopt` version for platforms +# where the detection isn't working. It simply outputs debug information that +# can be included in a bug report. +# +# Args: +# none +# Output: +# debug info that can be included in a bug report +# Returns: +# nothing +flags_getoptInfo() +{ + # platform info + _flags_debug "uname -a: `uname -a`" + _flags_debug "PATH: ${PATH}" + + # shell info + if [ -n "${BASH_VERSION:-}" ]; then + _flags_debug 'shell: bash' + _flags_debug "BASH_VERSION: ${BASH_VERSION}" + elif [ -n "${ZSH_VERSION:-}" ]; then + _flags_debug 'shell: zsh' + _flags_debug "ZSH_VERSION: ${ZSH_VERSION}" + fi + + # getopt info + getopt >/dev/null + _flags_getoptReturn=$? + _flags_debug "getopt return: ${_flags_getoptReturn}" + _flags_debug "getopt --version: `getopt --version 2>&1`" + + unset _flags_getoptReturn +} + +# Returns whether the detected getopt version is the enhanced version. +# +# Args: +# none +# Output: +# none +# Returns: +# bool: true if getopt is the enhanced version +flags_getoptIsEnh() +{ + test ${__FLAGS_GETOPT_VERS} -eq ${__FLAGS_GETOPT_VERS_ENH} +} + +# Returns whether the detected getopt version is the standard version. +# +# Args: +# none +# Returns: +# bool: true if getopt is the standard version +flags_getoptIsStd() +{ + test ${__FLAGS_GETOPT_VERS} -eq ${__FLAGS_GETOPT_VERS_STD} +} + +# This is effectively a 'usage()' function. It prints usage information and +# exits the program with ${FLAGS_FALSE} if it is ever found in the command line +# arguments. Note this function can be overridden so other apps can define +# their own --help flag, replacing this one, if they want. +# +# Args: +# none +# Returns: +# integer: success of operation (always returns true) +flags_help() +{ + if [ -n "${FLAGS_HELP:-}" ]; then + echo "${FLAGS_HELP}" >&2 + else + echo "USAGE: ${FLAGS_PARENT:-$0} [flags] args" >&2 + fi + if [ -n "${__flags_longNames}" ]; then + echo 'flags:' >&2 + for flags_name_ in ${__flags_longNames}; do + flags_flagStr_='' + flags_boolStr_='' + + flags_default_=`_flags_getFlagInfo \ + "${flags_name_}" ${__FLAGS_INFO_DEFAULT}` + flags_help_=`_flags_getFlagInfo \ + "${flags_name_}" ${__FLAGS_INFO_HELP}` + flags_short_=`_flags_getFlagInfo \ + "${flags_name_}" ${__FLAGS_INFO_SHORT}` + flags_type_=`_flags_getFlagInfo \ + "${flags_name_}" ${__FLAGS_INFO_TYPE}` + + [ "${flags_short_}" != "${__FLAGS_NULL}" ] \ + && flags_flagStr_="-${flags_short_}" + + if [ ${__FLAGS_GETOPT_VERS} -eq ${__FLAGS_GETOPT_VERS_ENH} ]; then + [ "${flags_short_}" != "${__FLAGS_NULL}" ] \ + && flags_flagStr_="${flags_flagStr_}," + [ ${flags_type_} -eq ${__FLAGS_TYPE_BOOLEAN} ] \ + && flags_boolStr_='[no]' + flags_flagStr_="${flags_flagStr_}--${flags_boolStr_}${flags_name_}:" + fi + + case ${flags_type_} in + ${__FLAGS_TYPE_BOOLEAN}) + if [ ${flags_default_} -eq ${FLAGS_TRUE} ]; then + flags_defaultStr_='true' + else + flags_defaultStr_='false' + fi + ;; + ${__FLAGS_TYPE_FLOAT}|${__FLAGS_TYPE_INTEGER}) + flags_defaultStr_=${flags_default_} ;; + ${__FLAGS_TYPE_STRING}) flags_defaultStr_="'${flags_default_}'" ;; + esac + flags_defaultStr_="(default: ${flags_defaultStr_})" + + flags_helpStr_=" ${flags_flagStr_} ${flags_help_} ${flags_defaultStr_}" + flags_helpStrLen_=`expr -- "${flags_helpStr_}" : '.*'` + flags_columns_=`_flags_columns` + if [ ${flags_helpStrLen_} -lt ${flags_columns_} ]; then + echo "${flags_helpStr_}" >&2 + else + echo " ${flags_flagStr_} ${flags_help_}" >&2 + # note: the silliness with the x's is purely for ksh93 on Ubuntu 6.06 + # because it doesn't like empty strings when used in this manner. + flags_emptyStr_="`echo \"x${flags_flagStr_}x\" \ + |awk '{printf "%"length($0)-2"s", ""}'`" + flags_helpStr_=" ${flags_emptyStr_} ${flags_defaultStr_}" + flags_helpStrLen_=`expr -- "${flags_helpStr_}" : '.*'` + if [ ${__FLAGS_GETOPT_VERS} -eq ${__FLAGS_GETOPT_VERS_STD} \ + -o ${flags_helpStrLen_} -lt ${flags_columns_} ]; then + # indented to match help string + echo "${flags_helpStr_}" >&2 + else + # indented four from left to allow for longer defaults as long flag + # names might be used too, making things too long + echo " ${flags_defaultStr_}" >&2 + fi + fi + done + fi + + unset flags_boolStr_ flags_default_ flags_defaultStr_ flags_emptyStr_ \ + flags_flagStr_ flags_help_ flags_helpStr flags_helpStrLen flags_name_ \ + flags_columns_ flags_short_ flags_type_ + return ${FLAGS_TRUE} +} + +# Reset shflags back to an uninitialized state. +# +# Args: +# none +# Returns: +# nothing +flags_reset() +{ + for flags_name_ in ${__flags_longNames}; do + flags_strToEval_="unset FLAGS_${flags_name_}" + for flags_type_ in \ + ${__FLAGS_INFO_DEFAULT} \ + ${__FLAGS_INFO_HELP} \ + ${__FLAGS_INFO_SHORT} \ + ${__FLAGS_INFO_TYPE} + do + flags_strToEval_=\ +"${flags_strToEval_} __flags_${flags_name_}_${flags_type_}" + done + eval ${flags_strToEval_} + done + + # reset internal variables + __flags_boolNames=' ' + __flags_longNames=' ' + __flags_shortNames=' ' + + unset flags_name_ flags_type_ flags_strToEval_ +} diff --git a/test/shunit2-2.1.6/lib/shlib b/test/shunit2-2.1.6/lib/shlib new file mode 100644 index 0000000..b5d5779 --- /dev/null +++ b/test/shunit2-2.1.6/lib/shlib @@ -0,0 +1,39 @@ +# $Id: shlib 14 2007-02-18 19:43:41Z sfsetse $ +# vim:et:ft=sh:sts=2:sw=2 +# +# Copyright 2008 Kate Ward. All Rights Reserved. +# Released under the LGPL (GNU Lesser General Public License). +# Author: kate.ward@forestent.com (Kate Ward) +# +# Library of shell functions. + +# Convert a relative path into it's absolute equivalent. +# +# This function will automatically prepend the current working directory if the +# path is not already absolute. It then removes all parent references (../) to +# reconstruct the proper absolute path. +# +# Args: +# shlib_path_: string: relative path +# Outputs: +# string: absolute path +shlib_relToAbsPath() +{ + shlib_path_=$1 + + # prepend current directory to relative paths + echo "${shlib_path_}" |grep '^/' >/dev/null 2>&1 \ + || shlib_path_="`pwd`/${shlib_path_}" + + # clean up the path. if all seds supported true regular expressions, then + # this is what it would be: + shlib_old_=${shlib_path_} + while true; do + shlib_new_=`echo "${shlib_old_}" |sed 's/[^/]*\/\.\.\/*//g;s/\/\.\//\//'` + [ "${shlib_old_}" = "${shlib_new_}" ] && break + shlib_old_=${shlib_new_} + done + echo "${shlib_new_}" + + unset shlib_path_ shlib_old_ shlib_new_ +} diff --git a/test/shunit2-2.1.6/lib/versions b/test/shunit2-2.1.6/lib/versions new file mode 100755 index 0000000..b874d80 --- /dev/null +++ b/test/shunit2-2.1.6/lib/versions @@ -0,0 +1,227 @@ +#! /bin/sh +# $Id: versions 100 2008-11-15 20:24:03Z kate.ward@forestent.com $ +# vim:et:ft=sh:sts=2:sw=2 +# +# Copyright 2008 Kate Ward. All Rights Reserved. +# Released under the LGPL (GNU Lesser General Public License) +# +# Author: kate.ward@forestent.com (Kate Ward) +# +# This library provides reusable functions that determine actual names and +# versions of installed shells and the OS. The library can also be run as a +# script if set execuatable. + +ARGV0=`basename "$0"` +LSB_RELEASE='/etc/lsb-release' +VERSIONS_SHELLS="/bin/bash /bin/dash /bin/ksh /bin/pdksh /bin/sh /bin/zsh" + +TRUE=0 +FALSE=1 +ERROR=2 + +__versions_haveStrings=${ERROR} + +#------------------------------------------------------------------------------ +# functions +# + +versions_osName() +{ + os_name_='unrecognized' + os_system_=`uname -s` + case ${os_system_} in + CYGWIN_NT-*) os_name_='Cygwin' ;; + Darwin) os_name_='Mac OS X' ;; + FreeBSD) os_name_='FreeBSD' ;; + Linux) os_name_='Linux' ;; + SunOS) + if grep 'OpenSolaris' /etc/release >/dev/null; then + os_name_='OpenSolaris' + else + os_name_='Solaris' + fi + ;; + esac + echo ${os_name_} + unset os_name_ os_system_ +} + +versions_osVersion() +{ + os_version_='unrecognized' + os_system_=`uname -s` + os_release_=`uname -r` + case ${os_system_} in + CYGWIN_NT-*) + os_version_=`expr "${os_release_}" : '\([0-9]*\.[0-9]\.[0-9]*\).*'` + ;; + Darwin) + major_='10' + sub_=`echo ${os_release_} |sed 's/^[0-9]*\.\([0-9]*\)\.[0-9]*$/\1/'` + case ${os_release_} in + 8.*) minor_='4' ;; + 9.*) minor_='5' ;; + 10.*) minor_='6' ;; + *) minor_='X'; sub_='X' ;; + esac + os_version_="${major_}.${minor_}.${sub_}" + ;; + FreeBSD) + os_version_=`expr "${os_release_}" : '\([0-9]*\.[0-9]*\)-.*'` + ;; + Linux) + if [ -r "${LSB_RELEASE}" ]; then + if grep -q 'DISTRIB_ID=Ubuntu' "${LSB_RELEASE}"; then + os_version_=`cat "${LSB_RELEASE}" \ + |awk -F= '$1~/DISTRIB_DESCRIPTION/{print $2}' \ + |sed 's/"//g;s/ /-/g'` + fi + elif [ -r '/etc/redhat-release' ]; then + os_version_=`cat /etc/redhat-release` + fi + ;; + SunOS) + if grep 'OpenSolaris' /etc/release >/dev/null; then + os_version_=`grep 'OpenSolaris' /etc/release |awk '{print $2"("$3")"}'` + else + major_=`echo ${os_release_} |sed 's/[0-9]*\.\([0-9]*\)/\1/'` + minor_=`grep Solaris /etc/release |sed 's/[^u]*\(u[0-9]*\).*/\1/'` + os_version_="${major_}${minor_}" + fi + ;; + esac + echo ${os_version_} + unset os_name_ os_release_ os_version_ major_ minor_ sub_ +} + +versions_shellVersion() +{ + shell_=$1 + + if [ ! -x "${shell_}" ]; then + echo 'not installed' + return + fi + + version_='' + case ${shell_} in + */sh) + # TODO(kward): fix this + ## this could be one of any number of shells. try until one fits. + #version_=`versions_shell_bash ${shell_}` + ## dash cannot be self determined yet + #[ -z "${version_}" ] && version_=`versions_shell_ksh ${shell_}` + ## pdksh is covered in versions_shell_ksh() + #[ -z "${version_}" ] && version_=`versions_shell_zsh ${shell_}` + ;; + */bash) version_=`versions_shell_bash ${shell_}` ;; + */dash) + # simply assuming Ubuntu Linux until somebody comes up with a better + # test. the following test will return an empty string if dash is not + # installed. + version_=`versions_shell_dash` + ;; + */ksh) version_=`versions_shell_ksh ${shell_}` ;; + */pdksh) version_=`versions_shell_pdksh ${shell_}` ;; + */zsh) version_=`versions_shell_zsh ${shell_}` ;; + *) version_='invalid' + esac + + echo ${version_:-unknown} + unset shell_ version_ +} + +versions_shell_bash() +{ + $1 --version 2>&1 |grep 'GNU bash' |sed 's/.*version \([^ ]*\).*/\1/' +} + +versions_shell_dash() +{ + eval dpkg >/dev/null 2>&1 + [ $? -eq 127 ] && return # return if dpkg not found + + dpkg -l |grep ' dash ' |awk '{print $3}' +} + +versions_shell_ksh() +{ + versions_shell_=$1 + + # see if --version gives a result + versions_version_=`${versions_shell_} --version 2>&1 \ + |sed 's/.*\([0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\).*/\1/'` + + # --version didn't work... look into the binary + if [ $? -ne ${TRUE} ]; then + _versions_have_strings + versions_version_=`strings ${versions_shell_} 2>&1 \ + |grep Version \ + |sed 's/^.*Version \(.*\)$/\1/;s/ s+ \$$//;s/ /-/g'` + [ -z "${versions_version_}" ] \ + && versions_version_=`versions_shell_pdksh ${versions_shell_}` + fi + + echo ${versions_version_} + + unset versions_shell_ versions_version_ +} + +versions_shell_pdksh() +{ + _versions_have_strings + strings $1 2>&1 \ + |grep 'PD KSH' \ + |sed -e 's/.*PD KSH \(.*\)/\1/;s/ /-/g' +} + +versions_shell_zsh() +{ + versions_shell_=$1 + + versions_version_=`${versions_shell_} --version 2>&1 |awk '{print $2}'` + + if [ $? -ne ${TRUE} ]; then + versions_version_=`echo 'echo ${ZSH_VERSION}' |${versions_shell_}` + fi + + echo ${versions_version_} + + unset versions_shell_ versions_version_ +} + +# Determine if the 'strings' binary installed. +_versions_have_strings() +{ + [ ${__versions_haveStrings} -ne ${ERROR} ] && return + eval strings /dev/null >/dev/null 2>&1 + if [ $? -eq 0 ]; then + __versions_haveStrings=${TRUE} + else + echo 'WARN: strings not installed. try installing binutils?' >&2 + __versions_haveStrings=${FALSE} + fi +} + +#------------------------------------------------------------------------------ +# main +# + +versions_main() +{ + # treat unset variables as an error + set -u + + os_name=`versions_osName` + os_version=`versions_osVersion` + echo "os: ${os_name} version: ${os_version}" + + for shell in ${VERSIONS_SHELLS}; do + shell_version=`versions_shellVersion ${shell}` + echo "shell: ${shell} version: ${shell_version}" + done +} + +if [ "${ARGV0}" = 'versions' ]; then + versions_main "$@" +fi diff --git a/test/shunit2-2.1.6/src/shunit2 b/test/shunit2-2.1.6/src/shunit2 new file mode 100755 index 0000000..8862ffd --- /dev/null +++ b/test/shunit2-2.1.6/src/shunit2 @@ -0,0 +1,1048 @@ +#! /bin/sh +# $Id: shunit2 335 2011-05-01 20:10:33Z kate.ward@forestent.com $ +# vim:et:ft=sh:sts=2:sw=2 +# +# Copyright 2008 Kate Ward. All Rights Reserved. +# Released under the LGPL (GNU Lesser General Public License) +# +# shUnit2 -- Unit testing framework for Unix shell scripts. +# http://code.google.com/p/shunit2/ +# +# Author: kate.ward@forestent.com (Kate Ward) +# +# shUnit2 is a xUnit based unit test framework for Bourne shell scripts. It is +# based on the popular JUnit unit testing framework for Java. + +# return if shunit already loaded +[ -n "${SHUNIT_VERSION:-}" ] && exit 0 + +SHUNIT_VERSION='2.1.6' + +SHUNIT_TRUE=0 +SHUNIT_FALSE=1 +SHUNIT_ERROR=2 + +# enable strict mode by default +SHUNIT_STRICT=${SHUNIT_STRICT:-${SHUNIT_TRUE}} + +_shunit_warn() { echo "shunit2:WARN $@" >&2; } +_shunit_error() { echo "shunit2:ERROR $@" >&2; } +_shunit_fatal() { echo "shunit2:FATAL $@" >&2; exit ${SHUNIT_ERROR}; } + +# specific shell checks +if [ -n "${ZSH_VERSION:-}" ]; then + setopt |grep "^shwordsplit$" >/dev/null + if [ $? -ne ${SHUNIT_TRUE} ]; then + _shunit_fatal 'zsh shwordsplit option is required for proper operation' + fi + if [ -z "${SHUNIT_PARENT:-}" ]; then + _shunit_fatal "zsh does not pass \$0 through properly. please declare \ +\"SHUNIT_PARENT=\$0\" before calling shUnit2" + fi +fi + +# +# constants +# + +__SHUNIT_ASSERT_MSG_PREFIX='ASSERT:' +__SHUNIT_MODE_SOURCED='sourced' +__SHUNIT_MODE_STANDALONE='standalone' +__SHUNIT_PARENT=${SHUNIT_PARENT:-$0} + +# set the constants readonly +shunit_constants_=`set |grep '^__SHUNIT_' |cut -d= -f1` +echo "${shunit_constants_}" |grep '^Binary file' >/dev/null && \ + shunit_constants_=`set |grep -a '^__SHUNIT_' |cut -d= -f1` +for shunit_constant_ in ${shunit_constants_}; do + shunit_ro_opts_='' + case ${ZSH_VERSION:-} in + '') ;; # this isn't zsh + [123].*) ;; # early versions (1.x, 2.x, 3.x) + *) shunit_ro_opts_='-g' ;; # all later versions. declare readonly globally + esac + readonly ${shunit_ro_opts_} ${shunit_constant_} +done +unset shunit_constant_ shunit_constants_ shunit_ro_opts_ + +# variables +__shunit_lineno='' # line number of executed test +__shunit_mode=${__SHUNIT_MODE_SOURCED} # operating mode +__shunit_reportGenerated=${SHUNIT_FALSE} # is report generated +__shunit_script='' # filename of unittest script (standalone mode) +__shunit_skip=${SHUNIT_FALSE} # is skipping enabled +__shunit_suite='' # suite of tests to execute + +# counts of tests +__shunit_testSuccess=${SHUNIT_TRUE} +__shunit_testsTotal=0 +__shunit_testsPassed=0 +__shunit_testsFailed=0 + +# counts of asserts +__shunit_assertsTotal=0 +__shunit_assertsPassed=0 +__shunit_assertsFailed=0 +__shunit_assertsSkipped=0 + +# macros +_SHUNIT_LINENO_='eval __shunit_lineno=""; if [ "${1:-}" = "--lineno" ]; then [ -n "$2" ] && __shunit_lineno="[$2] "; shift 2; fi' + +#----------------------------------------------------------------------------- +# assert functions +# + +# Assert that two values are equal to one another. +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertEquals() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "assertEquals() requires two or three arguments; $# given" + _shunit_error "1: ${1:+$1} 2: ${2:+$2} 3: ${3:+$3}${4:+ 4: $4}" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_expected_=$1 + shunit_actual_=$2 + + shunit_return=${SHUNIT_TRUE} + if [ "${shunit_expected_}" = "${shunit_actual_}" ]; then + _shunit_assertPass + else + failNotEquals "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}" + shunit_return=${SHUNIT_FALSE} + fi + + unset shunit_message_ shunit_expected_ shunit_actual_ + return ${shunit_return} +} +_ASSERT_EQUALS_='eval assertEquals --lineno "${LINENO:-}"' + +# Assert that two values are not equal to one another. +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertNotEquals() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "assertNotEquals() requires two or three arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_expected_=$1 + shunit_actual_=$2 + + shunit_return=${SHUNIT_TRUE} + if [ "${shunit_expected_}" != "${shunit_actual_}" ]; then + _shunit_assertPass + else + failSame "${shunit_message_}" "$@" + shunit_return=${SHUNIT_FALSE} + fi + + unset shunit_message_ shunit_expected_ shunit_actual_ + return ${shunit_return} +} +_ASSERT_NOT_EQUALS_='eval assertNotEquals --lineno "${LINENO:-}"' + +# Assert that a value is null (i.e. an empty string) +# +# Args: +# message: string: failure message [optional] +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertNull() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 1 -o $# -gt 2 ]; then + _shunit_error "assertNull() requires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 2 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + assertTrue "${shunit_message_}" "[ -z '$1' ]" + shunit_return=$? + + unset shunit_message_ + return ${shunit_return} +} +_ASSERT_NULL_='eval assertNull --lineno "${LINENO:-}"' + +# Assert that a value is not null (i.e. a non-empty string) +# +# Args: +# message: string: failure message [optional] +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertNotNull() +{ + ${_SHUNIT_LINENO_} + if [ $# -gt 2 ]; then # allowing 0 arguments as $1 might actually be null + _shunit_error "assertNotNull() requires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 2 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_actual_=`_shunit_escapeCharactersInString "${1:-}"` + test -n "${shunit_actual_}" + assertTrue "${shunit_message_}" $? + shunit_return=$? + + unset shunit_actual_ shunit_message_ + return ${shunit_return} +} +_ASSERT_NOT_NULL_='eval assertNotNull --lineno "${LINENO:-}"' + +# Assert that two values are the same (i.e. equal to one another). +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertSame() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "assertSame() requires two or three arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + assertEquals "${shunit_message_}" "$1" "$2" + shunit_return=$? + + unset shunit_message_ + return ${shunit_return} +} +_ASSERT_SAME_='eval assertSame --lineno "${LINENO:-}"' + +# Assert that two values are not the same (i.e. not equal to one another). +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertNotSame() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "assertNotSame() requires two or three arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_:-}$1" + shift + fi + assertNotEquals "${shunit_message_}" "$1" "$2" + shunit_return=$? + + unset shunit_message_ + return ${shunit_return} +} +_ASSERT_NOT_SAME_='eval assertNotSame --lineno "${LINENO:-}"' + +# Assert that a value or shell test condition is true. +# +# In shell, a value of 0 is true and a non-zero value is false. Any integer +# value passed can thereby be tested. +# +# Shell supports much more complicated tests though, and a means to support +# them was needed. As such, this function tests that conditions are true or +# false through evaluation rather than just looking for a true or false. +# +# The following test will succeed: +# assertTrue 0 +# assertTrue "[ 34 -gt 23 ]" +# The folloing test will fail with a message: +# assertTrue 123 +# assertTrue "test failed" "[ -r '/non/existant/file' ]" +# +# Args: +# message: string: failure message [optional] +# condition: string: integer value or shell conditional statement +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertTrue() +{ + ${_SHUNIT_LINENO_} + if [ $# -gt 2 ]; then + _shunit_error "assertTrue() takes one two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 2 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_condition_=$1 + + # see if condition is an integer, i.e. a return value + shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'` + shunit_return=${SHUNIT_TRUE} + if [ -z "${shunit_condition_}" ]; then + # null condition + shunit_return=${SHUNIT_FALSE} + elif [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ] + then + # possible return value. treating 0 as true, and non-zero as false. + [ ${shunit_condition_} -ne 0 ] && shunit_return=${SHUNIT_FALSE} + else + # (hopefully) a condition + ( eval ${shunit_condition_} ) >/dev/null 2>&1 + [ $? -ne 0 ] && shunit_return=${SHUNIT_FALSE} + fi + + # record the test + if [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then + _shunit_assertPass + else + _shunit_assertFail "${shunit_message_}" + fi + + unset shunit_message_ shunit_condition_ shunit_match_ + return ${shunit_return} +} +_ASSERT_TRUE_='eval assertTrue --lineno "${LINENO:-}"' + +# Assert that a value or shell test condition is false. +# +# In shell, a value of 0 is true and a non-zero value is false. Any integer +# value passed can thereby be tested. +# +# Shell supports much more complicated tests though, and a means to support +# them was needed. As such, this function tests that conditions are true or +# false through evaluation rather than just looking for a true or false. +# +# The following test will succeed: +# assertFalse 1 +# assertFalse "[ 'apples' = 'oranges' ]" +# The folloing test will fail with a message: +# assertFalse 0 +# assertFalse "test failed" "[ 1 -eq 1 -a 2 -eq 2 ]" +# +# Args: +# message: string: failure message [optional] +# condition: string: integer value or shell conditional statement +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertFalse() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 1 -o $# -gt 2 ]; then + _shunit_error "assertFalse() quires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 2 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_condition_=$1 + + # see if condition is an integer, i.e. a return value + shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'` + shunit_return=${SHUNIT_TRUE} + if [ -z "${shunit_condition_}" ]; then + # null condition + shunit_return=${SHUNIT_FALSE} + elif [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ] + then + # possible return value. treating 0 as true, and non-zero as false. + [ ${shunit_condition_} -eq 0 ] && shunit_return=${SHUNIT_FALSE} + else + # (hopefully) a condition + ( eval ${shunit_condition_} ) >/dev/null 2>&1 + [ $? -eq 0 ] && shunit_return=${SHUNIT_FALSE} + fi + + # record the test + if [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then + _shunit_assertPass + else + _shunit_assertFail "${shunit_message_}" + fi + + unset shunit_message_ shunit_condition_ shunit_match_ + return ${shunit_return} +} +_ASSERT_FALSE_='eval assertFalse --lineno "${LINENO:-}"' + +#----------------------------------------------------------------------------- +# failure functions +# + +# Records a test failure. +# +# Args: +# message: string: failure message [optional] +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +fail() +{ + ${_SHUNIT_LINENO_} + if [ $# -gt 1 ]; then + _shunit_error "fail() requires zero or one arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 1 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + + _shunit_assertFail "${shunit_message_}" + + unset shunit_message_ + return ${SHUNIT_FALSE} +} +_FAIL_='eval fail --lineno "${LINENO:-}"' + +# Records a test failure, stating two values were not equal. +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +failNotEquals() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "failNotEquals() requires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_expected_=$1 + shunit_actual_=$2 + + _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected:<${shunit_expected_}> but was:<${shunit_actual_}>" + + unset shunit_message_ shunit_expected_ shunit_actual_ + return ${SHUNIT_FALSE} +} +_FAIL_NOT_EQUALS_='eval failNotEquals --lineno "${LINENO:-}"' + +# Records a test failure, stating two values should have been the same. +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +failSame() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "failSame() requires two or three arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + + _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected not same" + + unset shunit_message_ + return ${SHUNIT_FALSE} +} +_FAIL_SAME_='eval failSame --lineno "${LINENO:-}"' + +# Records a test failure, stating two values were not equal. +# +# This is functionally equivalent to calling failNotEquals(). +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +failNotSame() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "failNotEquals() requires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + failNotEquals "${shunit_message_}" "$1" "$2" + shunit_return=$? + + unset shunit_message_ + return ${shunit_return} +} +_FAIL_NOT_SAME_='eval failNotSame --lineno "${LINENO:-}"' + +#----------------------------------------------------------------------------- +# skipping functions +# + +# Force remaining assert and fail functions to be "skipped". +# +# This function forces the remaining assert and fail functions to be "skipped", +# i.e. they will have no effect. Each function skipped will be recorded so that +# the total of asserts and fails will not be altered. +# +# Args: +# None +startSkipping() +{ + __shunit_skip=${SHUNIT_TRUE} +} + +# Resume the normal recording behavior of assert and fail calls. +# +# Args: +# None +endSkipping() +{ + __shunit_skip=${SHUNIT_FALSE} +} + +# Returns the state of assert and fail call skipping. +# +# Args: +# None +# Returns: +# boolean: (TRUE/FALSE constant) +isSkipping() +{ + return ${__shunit_skip} +} + +#----------------------------------------------------------------------------- +# suite functions +# + +# Stub. This function should contains all unit test calls to be made. +# +# DEPRECATED (as of 2.1.0) +# +# This function can be optionally overridden by the user in their test suite. +# +# If this function exists, it will be called when shunit2 is sourced. If it +# does not exist, shunit2 will search the parent script for all functions +# beginning with the word 'test', and they will be added dynamically to the +# test suite. +# +# This function should be overridden by the user in their unit test suite. +# Note: see _shunit_mktempFunc() for actual implementation +# +# Args: +# None +#suite() { :; } # DO NOT UNCOMMENT THIS FUNCTION + +# Adds a function name to the list of tests schedule for execution. +# +# This function should only be called from within the suite() function. +# +# Args: +# function: string: name of a function to add to current unit test suite +suite_addTest() +{ + shunit_func_=${1:-} + + __shunit_suite="${__shunit_suite:+${__shunit_suite} }${shunit_func_}" + __shunit_testsTotal=`expr ${__shunit_testsTotal} + 1` + + unset shunit_func_ +} + +# Stub. This function will be called once before any tests are run. +# +# Common one-time environment preparation tasks shared by all tests can be +# defined here. +# +# This function should be overridden by the user in their unit test suite. +# Note: see _shunit_mktempFunc() for actual implementation +# +# Args: +# None +#oneTimeSetUp() { :; } # DO NOT UNCOMMENT THIS FUNCTION + +# Stub. This function will be called once after all tests are finished. +# +# Common one-time environment cleanup tasks shared by all tests can be defined +# here. +# +# This function should be overridden by the user in their unit test suite. +# Note: see _shunit_mktempFunc() for actual implementation +# +# Args: +# None +#oneTimeTearDown() { :; } # DO NOT UNCOMMENT THIS FUNCTION + +# Stub. This function will be called before each test is run. +# +# Common environment preparation tasks shared by all tests can be defined here. +# +# This function should be overridden by the user in their unit test suite. +# Note: see _shunit_mktempFunc() for actual implementation +# +# Args: +# None +#setUp() { :; } + +# Note: see _shunit_mktempFunc() for actual implementation +# Stub. This function will be called after each test is run. +# +# Common environment cleanup tasks shared by all tests can be defined here. +# +# This function should be overridden by the user in their unit test suite. +# Note: see _shunit_mktempFunc() for actual implementation +# +# Args: +# None +#tearDown() { :; } # DO NOT UNCOMMENT THIS FUNCTION + +#------------------------------------------------------------------------------ +# internal shUnit2 functions +# + +# Create a temporary directory to store various run-time files in. +# +# This function is a cross-platform temporary directory creation tool. Not all +# OSes have the mktemp function, so one is included here. +# +# Args: +# None +# Outputs: +# string: the temporary directory that was created +_shunit_mktempDir() +{ + # try the standard mktemp function + ( exec mktemp -dqt shunit.XXXXXX 2>/dev/null ) && return + + # the standard mktemp didn't work. doing our own. + if [ -r '/dev/urandom' -a -x '/usr/bin/od' ]; then + _shunit_random_=`/usr/bin/od -vAn -N4 -tx4 "${_shunit_file_}" +#! /bin/sh +exit ${SHUNIT_TRUE} +EOF + chmod +x "${_shunit_file_}" + done + + unset _shunit_file_ +} + +# Final cleanup function to leave things as we found them. +# +# Besides removing the temporary directory, this function is in charge of the +# final exit code of the unit test. The exit code is based on how the script +# was ended (e.g. normal exit, or via Ctrl-C). +# +# Args: +# name: string: name of the trap called (specified when trap defined) +_shunit_cleanup() +{ + _shunit_name_=$1 + + case ${_shunit_name_} in + EXIT) _shunit_signal_=0 ;; + INT) _shunit_signal_=2 ;; + TERM) _shunit_signal_=15 ;; + *) + _shunit_warn "unrecognized trap value (${_shunit_name_})" + _shunit_signal_=0 + ;; + esac + + # do our work + rm -fr "${__shunit_tmpDir}" + + # exit for all non-EXIT signals + if [ ${_shunit_name_} != 'EXIT' ]; then + _shunit_warn "trapped and now handling the (${_shunit_name_}) signal" + # disable EXIT trap + trap 0 + # add 128 to signal and exit + exit `expr ${_shunit_signal_} + 128` + elif [ ${__shunit_reportGenerated} -eq ${SHUNIT_FALSE} ] ; then + _shunit_assertFail 'Unknown failure encountered running a test' + _shunit_generateReport + exit ${SHUNIT_ERROR} + fi + + unset _shunit_name_ _shunit_signal_ +} + +# The actual running of the tests happens here. +# +# Args: +# None +_shunit_execSuite() +{ + for _shunit_test_ in ${__shunit_suite}; do + __shunit_testSuccess=${SHUNIT_TRUE} + + # disable skipping + endSkipping + + # execute the per-test setup function + setUp + + # execute the test + echo "${_shunit_test_}" + eval ${_shunit_test_} + + # execute the per-test tear-down function + tearDown + + # update stats + if [ ${__shunit_testSuccess} -eq ${SHUNIT_TRUE} ]; then + __shunit_testsPassed=`expr ${__shunit_testsPassed} + 1` + else + __shunit_testsFailed=`expr ${__shunit_testsFailed} + 1` + fi + done + + unset _shunit_test_ +} + +# Generates the user friendly report with appropriate OK/FAILED message. +# +# Args: +# None +# Output: +# string: the report of successful and failed tests, as well as totals. +_shunit_generateReport() +{ + _shunit_ok_=${SHUNIT_TRUE} + + # if no exit code was provided one, determine an appropriate one + [ ${__shunit_testsFailed} -gt 0 \ + -o ${__shunit_testSuccess} -eq ${SHUNIT_FALSE} ] \ + && _shunit_ok_=${SHUNIT_FALSE} + + echo + if [ ${__shunit_testsTotal} -eq 1 ]; then + echo "Ran ${__shunit_testsTotal} test." + else + echo "Ran ${__shunit_testsTotal} tests." + fi + + _shunit_failures_='' + _shunit_skipped_='' + [ ${__shunit_assertsFailed} -gt 0 ] \ + && _shunit_failures_="failures=${__shunit_assertsFailed}" + [ ${__shunit_assertsSkipped} -gt 0 ] \ + && _shunit_skipped_="skipped=${__shunit_assertsSkipped}" + + if [ ${_shunit_ok_} -eq ${SHUNIT_TRUE} ]; then + _shunit_msg_='OK' + [ -n "${_shunit_skipped_}" ] \ + && _shunit_msg_="${_shunit_msg_} (${_shunit_skipped_})" + else + _shunit_msg_="FAILED (${_shunit_failures_}" + [ -n "${_shunit_skipped_}" ] \ + && _shunit_msg_="${_shunit_msg_},${_shunit_skipped_}" + _shunit_msg_="${_shunit_msg_})" + fi + + echo + echo ${_shunit_msg_} + __shunit_reportGenerated=${SHUNIT_TRUE} + + unset _shunit_failures_ _shunit_msg_ _shunit_ok_ _shunit_skipped_ +} + +# Test for whether a function should be skipped. +# +# Args: +# None +# Returns: +# boolean: whether the test should be skipped (TRUE/FALSE constant) +_shunit_shouldSkip() +{ + [ ${__shunit_skip} -eq ${SHUNIT_FALSE} ] && return ${SHUNIT_FALSE} + _shunit_assertSkip +} + +# Records a successful test. +# +# Args: +# None +_shunit_assertPass() +{ + __shunit_assertsPassed=`expr ${__shunit_assertsPassed} + 1` + __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1` +} + +# Records a test failure. +# +# Args: +# message: string: failure message to provide user +_shunit_assertFail() +{ + _shunit_msg_=$1 + + __shunit_testSuccess=${SHUNIT_FALSE} + __shunit_assertsFailed=`expr ${__shunit_assertsFailed} + 1` + __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1` + echo "${__SHUNIT_ASSERT_MSG_PREFIX}${_shunit_msg_}" + + unset _shunit_msg_ +} + +# Records a skipped test. +# +# Args: +# None +_shunit_assertSkip() +{ + __shunit_assertsSkipped=`expr ${__shunit_assertsSkipped} + 1` + __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1` +} + +# Prepare a script filename for sourcing. +# +# Args: +# script: string: path to a script to source +# Returns: +# string: filename prefixed with ./ (if necessary) +_shunit_prepForSourcing() +{ + _shunit_script_=$1 + case "${_shunit_script_}" in + /*|./*) echo "${_shunit_script_}" ;; + *) echo "./${_shunit_script_}" ;; + esac + unset _shunit_script_ +} + +# Escape a character in a string. +# +# Args: +# c: string: unescaped character +# s: string: to escape character in +# Returns: +# string: with escaped character(s) +_shunit_escapeCharInStr() +{ + [ -n "$2" ] || return # no point in doing work on an empty string + + # Note: using shorter variable names to prevent conflicts with + # _shunit_escapeCharactersInString(). + _shunit_c_=$1 + _shunit_s_=$2 + + + # escape the character + echo ''${_shunit_s_}'' |sed 's/\'${_shunit_c_}'/\\\'${_shunit_c_}'/g' + + unset _shunit_c_ _shunit_s_ +} + +# Escape a character in a string. +# +# Args: +# str: string: to escape characters in +# Returns: +# string: with escaped character(s) +_shunit_escapeCharactersInString() +{ + [ -n "$1" ] || return # no point in doing work on an empty string + + _shunit_str_=$1 + + # Note: using longer variable names to prevent conflicts with + # _shunit_escapeCharInStr(). + for _shunit_char_ in '"' '$' "'" '`'; do + _shunit_str_=`_shunit_escapeCharInStr "${_shunit_char_}" "${_shunit_str_}"` + done + + echo "${_shunit_str_}" + unset _shunit_char_ _shunit_str_ +} + +# Extract list of functions to run tests against. +# +# Args: +# script: string: name of script to extract functions from +# Returns: +# string: of function names +_shunit_extractTestFunctions() +{ + _shunit_script_=$1 + + # extract the lines with test function names, strip of anything besides the + # function name, and output everything on a single line. + _shunit_regex_='^[ ]*(function )*test[A-Za-z0-9_]* *\(\)' + egrep "${_shunit_regex_}" "${_shunit_script_}" \ + |sed 's/^[^A-Za-z0-9_]*//;s/^function //;s/\([A-Za-z0-9_]*\).*/\1/g' \ + |xargs + + unset _shunit_regex_ _shunit_script_ +} + +#------------------------------------------------------------------------------ +# main +# + +# determine the operating mode +if [ $# -eq 0 ]; then + __shunit_script=${__SHUNIT_PARENT} + __shunit_mode=${__SHUNIT_MODE_SOURCED} +else + __shunit_script=$1 + [ -r "${__shunit_script}" ] || \ + _shunit_fatal "unable to read from ${__shunit_script}" + __shunit_mode=${__SHUNIT_MODE_STANDALONE} +fi + +# create a temporary storage location +__shunit_tmpDir=`_shunit_mktempDir` + +# provide a public temporary directory for unit test scripts +# TODO(kward): document this +SHUNIT_TMPDIR="${__shunit_tmpDir}/tmp" +mkdir "${SHUNIT_TMPDIR}" + +# setup traps to clean up after ourselves +trap '_shunit_cleanup EXIT' 0 +trap '_shunit_cleanup INT' 2 +trap '_shunit_cleanup TERM' 15 + +# create phantom functions to work around issues with Cygwin +_shunit_mktempFunc +PATH="${__shunit_tmpDir}:${PATH}" + +# make sure phantom functions are executable. this will bite if /tmp (or the +# current $TMPDIR) points to a path on a partition that was mounted with the +# 'noexec' option. the noexec command was created with _shunit_mktempFunc(). +noexec 2>/dev/null || _shunit_fatal \ + 'please declare TMPDIR with path on partition with exec permission' + +# we must manually source the tests in standalone mode +if [ "${__shunit_mode}" = "${__SHUNIT_MODE_STANDALONE}" ]; then + . "`_shunit_prepForSourcing \"${__shunit_script}\"`" +fi + +# execute the oneTimeSetUp function (if it exists) +oneTimeSetUp + +# execute the suite function defined in the parent test script +# deprecated as of 2.1.0 +suite + +# if no suite function was defined, dynamically build a list of functions +if [ -z "${__shunit_suite}" ]; then + shunit_funcs_=`_shunit_extractTestFunctions "${__shunit_script}"` + for shunit_func_ in ${shunit_funcs_}; do + suite_addTest ${shunit_func_} + done +fi +unset shunit_func_ shunit_funcs_ + +# execute the tests +_shunit_execSuite + +# execute the oneTimeTearDown function (if it exists) +oneTimeTearDown + +# generate the report +_shunit_generateReport + +# that's it folks +[ ${__shunit_testsFailed} -eq 0 ] +exit $? diff --git a/test/shunit2-2.1.6/src/shunit2_test.sh b/test/shunit2-2.1.6/src/shunit2_test.sh new file mode 100755 index 0000000..f5a0ff8 --- /dev/null +++ b/test/shunit2-2.1.6/src/shunit2_test.sh @@ -0,0 +1,124 @@ +#! /bin/sh +# $Id: shunit2_test.sh 322 2011-04-24 00:09:45Z kate.ward@forestent.com $ +# vim:et:ft=sh:sts=2:sw=2 +# +# Copyright 2008 Kate Ward. All Rights Reserved. +# Released under the LGPL (GNU Lesser General Public License) +# Author: kate.ward@forestent.com (Kate Ward) +# +# shUnit2 unit test suite runner. +# +# This script runs all the unit tests that can be found, and generates a nice +# report of the tests. + +MY_NAME=`basename $0` +MY_PATH=`dirname $0` + +PREFIX='shunit2_test_' +SHELLS='/bin/sh /bin/bash /bin/dash /bin/ksh /bin/pdksh /bin/zsh' +TESTS='' +for test in ${PREFIX}[a-z]*.sh; do + TESTS="${TESTS} ${test}" +done + +# load common unit test functions +. ../lib/versions +. ./shunit2_test_helpers + +usage() +{ + echo "usage: ${MY_NAME} [-e key=val ...] [-s shell(s)] [-t test(s)]" +} + +env='' + +# process command line flags +while getopts 'e:hs:t:' opt; do + case ${opt} in + e) # set an environment variable + key=`expr "${OPTARG}" : '\([^=]*\)='` + val=`expr "${OPTARG}" : '[^=]*=\(.*\)'` + if [ -z "${key}" -o -z "${val}" ]; then + usage + exit 1 + fi + eval "${key}='${val}'" + export ${key} + env="${env:+${env} }${key}" + ;; + h) usage; exit 0 ;; # output help + s) shells=${OPTARG} ;; # list of shells to run + t) tests=${OPTARG} ;; # list of tests to run + *) usage; exit 1 ;; + esac +done +shift `expr ${OPTIND} - 1` + +# fill shells and/or tests +shells=${shells:-${SHELLS}} +tests=${tests:-${TESTS}} + +# error checking +if [ -z "${tests}" ]; then + th_error 'no tests found to run; exiting' + exit 1 +fi + +cat <&1; ) + done +done diff --git a/test/shunit2-2.1.6/src/shunit2_test_asserts.sh b/test/shunit2-2.1.6/src/shunit2_test_asserts.sh new file mode 100755 index 0000000..1f8040d --- /dev/null +++ b/test/shunit2-2.1.6/src/shunit2_test_asserts.sh @@ -0,0 +1,209 @@ +#! /bin/sh +# $Id: shunit2_test_asserts.sh 312 2011-03-14 22:41:29Z kate.ward@forestent.com $ +# vim:et:ft=sh:sts=2:sw=2 +# +# Copyright 2008 Kate Ward. All Rights Reserved. +# Released under the LGPL (GNU Lesser General Public License) +# +# Author: kate.ward@forestent.com (Kate Ward) +# +# shUnit2 unit test for assert functions + +# load test helpers +. ./shunit2_test_helpers + +#------------------------------------------------------------------------------ +# suite tests +# + +commonEqualsSame() +{ + fn=$1 + + ( ${fn} 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) + th_assertTrueWithNoOutput 'equal' $? "${stdoutF}" "${stderrF}" + + ( ${fn} "${MSG}" 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) + th_assertTrueWithNoOutput 'equal; with msg' $? "${stdoutF}" "${stderrF}" + + ( ${fn} 'abc def' 'abc def' >"${stdoutF}" 2>"${stderrF}" ) + th_assertTrueWithNoOutput 'equal with spaces' $? "${stdoutF}" "${stderrF}" + + ( ${fn} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'not equal' $? "${stdoutF}" "${stderrF}" + + ( ${fn} '' '' >"${stdoutF}" 2>"${stderrF}" ) + th_assertTrueWithNoOutput 'null values' $? "${stdoutF}" "${stderrF}" + + ( ${fn} arg1 >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" + + ( ${fn} arg1 arg2 arg3 arg4 >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" +} + +commonNotEqualsSame() +{ + fn=$1 + + ( ${fn} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) + th_assertTrueWithNoOutput 'not same' $? "${stdoutF}" "${stderrF}" + + ( ${fn} "${MSG}" 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) + th_assertTrueWithNoOutput 'not same, with msg' $? "${stdoutF}" "${stderrF}" + + ( ${fn} 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'same' $? "${stdoutF}" "${stderrF}" + + ( ${fn} '' '' >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'null values' $? "${stdoutF}" "${stderrF}" + + ( ${fn} arg1 >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" + + ( ${fn} arg1 arg2 arg3 arg4 >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" +} + +testAssertEquals() +{ + commonEqualsSame 'assertEquals' +} + +testAssertNotEquals() +{ + commonNotEqualsSame 'assertNotEquals' +} + +testAssertSame() +{ + commonEqualsSame 'assertSame' +} + +testAssertNotSame() +{ + commonNotEqualsSame 'assertNotSame' +} + +testAssertNull() +{ + ( assertNull '' >"${stdoutF}" 2>"${stderrF}" ) + th_assertTrueWithNoOutput 'null' $? "${stdoutF}" "${stderrF}" + + ( assertNull "${MSG}" '' >"${stdoutF}" 2>"${stderrF}" ) + th_assertTrueWithNoOutput 'null, with msg' $? "${stdoutF}" "${stderrF}" + + ( assertNull 'x' >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'not null' $? "${stdoutF}" "${stderrF}" + + ( assertNull >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" + + ( assertNull arg1 arg2 arg3 >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" +} + +testAssertNotNull() +{ + ( assertNotNull 'x' >"${stdoutF}" 2>"${stderrF}" ) + th_assertTrueWithNoOutput 'not null' $? "${stdoutF}" "${stderrF}" + + ( assertNotNull "${MSG}" 'x' >"${stdoutF}" 2>"${stderrF}" ) + th_assertTrueWithNoOutput 'not null, with msg' $? "${stdoutF}" "${stderrF}" + + ( assertNotNull 'x"b' >"${stdoutF}" 2>"${stderrF}" ) + th_assertTrueWithNoOutput 'not null, with double-quote' $? \ + "${stdoutF}" "${stderrF}" + + ( assertNotNull "x'b" >"${stdoutF}" 2>"${stderrF}" ) + th_assertTrueWithNoOutput 'not null, with single-quote' $? \ + "${stdoutF}" "${stderrF}" + + ( assertNotNull 'x$b' >"${stdoutF}" 2>"${stderrF}" ) + th_assertTrueWithNoOutput 'not null, with dollar' $? \ + "${stdoutF}" "${stderrF}" + + ( assertNotNull 'x`b' >"${stdoutF}" 2>"${stderrF}" ) + th_assertTrueWithNoOutput 'not null, with backtick' $? \ + "${stdoutF}" "${stderrF}" + + ( assertNotNull '' >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'null' $? "${stdoutF}" "${stderrF}" + + # there is no test for too few arguments as $1 might actually be null + + ( assertNotNull arg1 arg2 arg3 >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" +} + +testAssertTrue() +{ + ( assertTrue 0 >"${stdoutF}" 2>"${stderrF}" ) + th_assertTrueWithNoOutput 'true' $? "${stdoutF}" "${stderrF}" + + ( assertTrue "${MSG}" 0 >"${stdoutF}" 2>"${stderrF}" ) + th_assertTrueWithNoOutput 'true, with msg' $? "${stdoutF}" "${stderrF}" + + ( assertTrue '[ 0 -eq 0 ]' >"${stdoutF}" 2>"${stderrF}" ) + th_assertTrueWithNoOutput 'true condition' $? "${stdoutF}" "${stderrF}" + + ( assertTrue 1 >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'false' $? "${stdoutF}" "${stderrF}" + + ( assertTrue '[ 0 -eq 1 ]' >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'false condition' $? "${stdoutF}" "${stderrF}" + + ( assertTrue '' >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'null' $? "${stdoutF}" "${stderrF}" + + ( assertTrue >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" + + ( assertTrue arg1 arg2 arg3 >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" +} + +testAssertFalse() +{ + ( assertFalse 1 >"${stdoutF}" 2>"${stderrF}" ) + th_assertTrueWithNoOutput 'false' $? "${stdoutF}" "${stderrF}" + + ( assertFalse "${MSG}" 1 >"${stdoutF}" 2>"${stderrF}" ) + th_assertTrueWithNoOutput 'false, with msg' $? "${stdoutF}" "${stderrF}" + + ( assertFalse '[ 0 -eq 1 ]' >"${stdoutF}" 2>"${stderrF}" ) + th_assertTrueWithNoOutput 'false condition' $? "${stdoutF}" "${stderrF}" + + ( assertFalse 0 >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'true' $? "${stdoutF}" "${stderrF}" + + ( assertFalse '[ 0 -eq 0 ]' >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'true condition' $? "${stdoutF}" "${stderrF}" + + ( assertFalse '' >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'true condition' $? "${stdoutF}" "${stderrF}" + + ( assertFalse >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" + + ( assertFalse arg1 arg2 arg3 >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" +} + +#------------------------------------------------------------------------------ +# suite functions +# + +oneTimeSetUp() +{ + tmpDir="${__shunit_tmpDir}/output" + mkdir "${tmpDir}" + stdoutF="${tmpDir}/stdout" + stderrF="${tmpDir}/stderr" + + MSG='This is a test message' +} + +# load and run shUnit2 +[ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0 +. ${TH_SHUNIT} diff --git a/test/shunit2-2.1.6/src/shunit2_test_failures.sh b/test/shunit2-2.1.6/src/shunit2_test_failures.sh new file mode 100755 index 0000000..4aec943 --- /dev/null +++ b/test/shunit2-2.1.6/src/shunit2_test_failures.sh @@ -0,0 +1,89 @@ +#! /bin/sh +# $Id: shunit2_test_failures.sh 286 2008-11-24 21:42:34Z kate.ward@forestent.com $ +# vim:et:ft=sh:sts=2:sw=2 +# +# Copyright 2008 Kate Ward. All Rights Reserved. +# Released under the LGPL (GNU Lesser General Public License) +# +# Author: kate.ward@forestent.com (Kate Ward) +# +# shUnit2 unit test for failure functions + +# load common unit-test functions +. ./shunit2_test_helpers + +#----------------------------------------------------------------------------- +# suite tests +# + +testFail() +{ + ( fail >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'fail' $? "${stdoutF}" "${stderrF}" + + ( fail "${MSG}" >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'fail with msg' $? "${stdoutF}" "${stderrF}" + + ( fail arg1 >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'too many arguments' $? "${stdoutF}" "${stderrF}" +} + +testFailNotEquals() +{ + ( failNotEquals 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'same' $? "${stdoutF}" "${stderrF}" + + ( failNotEquals "${MSG}" 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'same with msg' $? "${stdoutF}" "${stderrF}" + + ( failNotEquals 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'not same' $? "${stdoutF}" "${stderrF}" + + ( failNotEquals '' '' >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'null values' $? "${stdoutF}" "${stderrF}" + + ( failNotEquals >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" + + ( failNotEquals arg1 arg2 arg3 arg4 >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" +} + +testFailSame() +{ + ( failSame 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'same' $? "${stdoutF}" "${stderrF}" + + ( failSame "${MSG}" 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'same with msg' $? "${stdoutF}" "${stderrF}" + + ( failSame 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'not same' $? "${stdoutF}" "${stderrF}" + + ( failSame '' '' >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithOutput 'null values' $? "${stdoutF}" "${stderrF}" + + ( failSame >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" + + ( failSame arg1 arg2 arg3 arg4 >"${stdoutF}" 2>"${stderrF}" ) + th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" +} + +#----------------------------------------------------------------------------- +# suite functions +# + +oneTimeSetUp() +{ + tmpDir="${__shunit_tmpDir}/output" + mkdir "${tmpDir}" + stdoutF="${tmpDir}/stdout" + stderrF="${tmpDir}/stderr" + + MSG='This is a test message' +} + +# load and run shUnit2 +[ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0 +. ${TH_SHUNIT} diff --git a/test/shunit2-2.1.6/src/shunit2_test_helpers b/test/shunit2-2.1.6/src/shunit2_test_helpers new file mode 100644 index 0000000..82a0eef --- /dev/null +++ b/test/shunit2-2.1.6/src/shunit2_test_helpers @@ -0,0 +1,177 @@ +# $Id: shunit2_test_helpers 286 2008-11-24 21:42:34Z kate.ward@forestent.com $ +# vim:et:ft=sh:sts=2:sw=2 +# +# Copyright 2008 Kate Ward. All Rights Reserved. +# Released under the LGPL (GNU Lesser General Public License) +# +# Author: kate.ward@forestent.com (Kate Ward) +# +# shUnit2 unit test common functions + +# treat unset variables as an error when performing parameter expansion +set -u + +# set shwordsplit for zsh +[ -n "${ZSH_VERSION:-}" ] && setopt shwordsplit + +# +# constants +# + +# path to shUnit2 library. can be overridden by setting SHUNIT_INC +TH_SHUNIT=${SHUNIT_INC:-./shunit2} + +# configure debugging. set the DEBUG environment variable to any +# non-empty value to enable debug output, or TRACE to enable trace +# output. +TRACE=${TRACE:+'th_trace '} +[ -n "${TRACE}" ] && DEBUG=1 +[ -z "${TRACE}" ] && TRACE=':' + +DEBUG=${DEBUG:+'th_debug '} +[ -z "${DEBUG}" ] && DEBUG=':' + +# +# variables +# + +th_RANDOM=0 + +# +# functions +# + +# message functions +th_trace() { echo "${MY_NAME}:TRACE $@" >&2; } +th_debug() { echo "${MY_NAME}:DEBUG $@" >&2; } +th_info() { echo "${MY_NAME}:INFO $@" >&2; } +th_warn() { echo "${MY_NAME}:WARN $@" >&2; } +th_error() { echo "${MY_NAME}:ERROR $@" >&2; } +th_fatal() { echo "${MY_NAME}:FATAL $@" >&2; } + +# output subtest name +th_subtest() { echo " $@" >&2; } + +# generate a random number +th_generateRandom() +{ + tfgr_random=${th_RANDOM} + + while [ "${tfgr_random}" = "${th_RANDOM}" ]; do + if [ -n "${RANDOM:-}" ]; then + # $RANDOM works + tfgr_random=${RANDOM}${RANDOM}${RANDOM}$$ + elif [ -r '/dev/urandom' ]; then + tfgr_random=`od -vAn -N4 -tu4 "${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_ASSERT_EQUALS_ failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 + + ( ${_ASSERT_EQUALS_} '"some msg"' 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_ASSERT_EQUALS_ w/ msg failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 +} + +testAssertNotEquals() +{ + # start skipping if LINENO not available + [ -z "${LINENO:-}" ] && startSkipping + + ( ${_ASSERT_NOT_EQUALS_} 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_ASSERT_NOT_EQUALS_ failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 + + ( ${_ASSERT_NOT_EQUALS_} '"some msg"' 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_ASSERT_NOT_EQUALS_ w/ msg failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 +} + +testSame() +{ + # start skipping if LINENO not available + [ -z "${LINENO:-}" ] && startSkipping + + ( ${_ASSERT_SAME_} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_ASSERT_SAME_ failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 + + ( ${_ASSERT_SAME_} '"some msg"' 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_ASSERT_SAME_ w/ msg failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 +} + +testNotSame() +{ + # start skipping if LINENO not available + [ -z "${LINENO:-}" ] && startSkipping + + ( ${_ASSERT_NOT_SAME_} 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_ASSERT_NOT_SAME_ failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 + + ( ${_ASSERT_NOT_SAME_} '"some msg"' 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_ASSERT_NOT_SAME_ w/ msg failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 +} + +testNull() +{ + # start skipping if LINENO not available + [ -z "${LINENO:-}" ] && startSkipping + + ( ${_ASSERT_NULL_} 'x' >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_ASSERT_NULL_ failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 + + ( ${_ASSERT_NULL_} '"some msg"' 'x' >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_ASSERT_NULL_ w/ msg failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 +} + +testNotNull() +{ + # start skipping if LINENO not available + [ -z "${LINENO:-}" ] && startSkipping + + ( ${_ASSERT_NOT_NULL_} '' >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_ASSERT_NOT_NULL_ failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 + + ( ${_ASSERT_NOT_NULL_} '"some msg"' '""' >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_ASSERT_NOT_NULL_ w/ msg failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stdoutF}" "${stderrF}" >&2 +} + +testAssertTrue() +{ + # start skipping if LINENO not available + [ -z "${LINENO:-}" ] && startSkipping + + ( ${_ASSERT_TRUE_} ${SHUNIT_FALSE} >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_ASSERT_TRUE_ failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 + + + ( ${_ASSERT_TRUE_} '"some msg"' ${SHUNIT_FALSE} >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_ASSERT_TRUE_ w/ msg failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 +} + +testAssertFalse() +{ + # start skipping if LINENO not available + [ -z "${LINENO:-}" ] && startSkipping + + ( ${_ASSERT_FALSE_} ${SHUNIT_TRUE} >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_ASSERT_FALSE_ failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 + + ( ${_ASSERT_FALSE_} '"some msg"' ${SHUNIT_TRUE} >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_ASSERT_FALSE_ w/ msg failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 +} + +testFail() +{ + # start skipping if LINENO not available + [ -z "${LINENO:-}" ] && startSkipping + + ( ${_FAIL_} >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_FAIL_ failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 + + ( ${_FAIL_} '"some msg"' >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_FAIL_ w/ msg failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 +} + +testFailNotEquals() +{ + # start skipping if LINENO not available + [ -z "${LINENO:-}" ] && startSkipping + + ( ${_FAIL_NOT_EQUALS_} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_FAIL_NOT_EQUALS_ failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 + + ( ${_FAIL_NOT_EQUALS_} '"some msg"' 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_FAIL_NOT_EQUALS_ w/ msg failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 +} + +testFailSame() +{ + # start skipping if LINENO not available + [ -z "${LINENO:-}" ] && startSkipping + + ( ${_FAIL_SAME_} 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_FAIL_SAME_ failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 + + ( ${_FAIL_SAME_} '"some msg"' 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_FAIL_SAME_ w/ msg failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 +} + +testFailNotSame() +{ + # start skipping if LINENO not available + [ -z "${LINENO:-}" ] && startSkipping + + ( ${_FAIL_NOT_SAME_} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_FAIL_NOT_SAME_ failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 + + ( ${_FAIL_NOT_SAME_} '"some msg"' 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) + grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null + rtrn=$? + assertTrue '_FAIL_NOT_SAME_ w/ msg failure' ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 +} + +#------------------------------------------------------------------------------ +# suite functions +# + +oneTimeSetUp() +{ + tmpDir="${__shunit_tmpDir}/output" + mkdir "${tmpDir}" + stdoutF="${tmpDir}/stdout" + stderrF="${tmpDir}/stderr" +} + +# load and run shUnit2 +[ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0 +. ${TH_SHUNIT} diff --git a/test/shunit2-2.1.6/src/shunit2_test_misc.sh b/test/shunit2-2.1.6/src/shunit2_test_misc.sh new file mode 100755 index 0000000..e3be229 --- /dev/null +++ b/test/shunit2-2.1.6/src/shunit2_test_misc.sh @@ -0,0 +1,165 @@ +#! /bin/sh +# $Id: shunit2_test_misc.sh 322 2011-04-24 00:09:45Z kate.ward@forestent.com $ +# vim:et:ft=sh:sts=2:sw=2 +# +# Copyright 2008 Kate Ward. All Rights Reserved. +# Released under the LGPL (GNU Lesser General Public License) +# +# Author: kate.ward@forestent.com (Kate Ward) +# +# shUnit2 unit tests of miscellaneous things + +# load test helpers +. ./shunit2_test_helpers + +#------------------------------------------------------------------------------ +# suite tests +# + +# Note: the test script is prefixed with '#' chars so that shUnit2 does not +# incorrectly interpret the embedded functions as real functions. +testUnboundVariable() +{ + sed 's/^#//' >"${unittestF}" <"${stdoutF}" 2>"${stderrF}" ) + assertFalse 'expected a non-zero exit value' $? + grep '^ASSERT:Unknown failure' "${stdoutF}" >/dev/null + assertTrue 'assert message was not generated' $? + grep '^Ran [0-9]* test' "${stdoutF}" >/dev/null + assertTrue 'test count message was not generated' $? + grep '^FAILED' "${stdoutF}" >/dev/null + assertTrue 'failure message was not generated' $? +} + +testIssue7() +{ + ( assertEquals 'Some message.' 1 2 >"${stdoutF}" 2>"${stderrF}" ) + diff "${stdoutF}" - >/dev/null < but was:<2> +EOF + rtrn=$? + assertEquals ${SHUNIT_TRUE} ${rtrn} + [ ${rtrn} -ne ${SHUNIT_TRUE} ] && cat "${stderrF}" >&2 +} + +testPrepForSourcing() +{ + assertEquals '/abc' `_shunit_prepForSourcing '/abc'` + assertEquals './abc' `_shunit_prepForSourcing './abc'` + assertEquals './abc' `_shunit_prepForSourcing 'abc'` +} + +testEscapeCharInStr() +{ + actual=`_shunit_escapeCharInStr '\' ''` + assertEquals '' "${actual}" + assertEquals 'abc\\' `_shunit_escapeCharInStr '\' 'abc\'` + assertEquals 'abc\\def' `_shunit_escapeCharInStr '\' 'abc\def'` + assertEquals '\\def' `_shunit_escapeCharInStr '\' '\def'` + + actual=`_shunit_escapeCharInStr '"' ''` + assertEquals '' "${actual}" + assertEquals 'abc\"' `_shunit_escapeCharInStr '"' 'abc"'` + assertEquals 'abc\"def' `_shunit_escapeCharInStr '"' 'abc"def'` + assertEquals '\"def' `_shunit_escapeCharInStr '"' '"def'` + + actual=`_shunit_escapeCharInStr '$' ''` + assertEquals '' "${actual}" + assertEquals 'abc\$' `_shunit_escapeCharInStr '$' 'abc$'` + assertEquals 'abc\$def' `_shunit_escapeCharInStr '$' 'abc$def'` + assertEquals '\$def' `_shunit_escapeCharInStr '$' '$def'` + +# actual=`_shunit_escapeCharInStr "'" ''` +# assertEquals '' "${actual}" +# assertEquals "abc\\'" `_shunit_escapeCharInStr "'" "abc'"` +# assertEquals "abc\\'def" `_shunit_escapeCharInStr "'" "abc'def"` +# assertEquals "\\'def" `_shunit_escapeCharInStr "'" "'def"` + +# # must put the backtick in a variable so the shell doesn't misinterpret it +# # while inside a backticked sequence (e.g. `echo '`'` would fail). +# backtick='`' +# actual=`_shunit_escapeCharInStr ${backtick} ''` +# assertEquals '' "${actual}" +# assertEquals '\`abc' \ +# `_shunit_escapeCharInStr "${backtick}" ${backtick}'abc'` +# assertEquals 'abc\`' \ +# `_shunit_escapeCharInStr "${backtick}" 'abc'${backtick}` +# assertEquals 'abc\`def' \ +# `_shunit_escapeCharInStr "${backtick}" 'abc'${backtick}'def'` +} + +testEscapeCharInStr_specialChars() +{ + # make sure our forward slash doesn't upset sed + assertEquals '/' `_shunit_escapeCharInStr '\' '/'` + + # some shells escape these differently + #assertEquals '\\a' `_shunit_escapeCharInStr '\' '\a'` + #assertEquals '\\b' `_shunit_escapeCharInStr '\' '\b'` +} + +# Test the various ways of declaring functions. +# +# Prefixing (then stripping) with comment symbol so these functions aren't +# treated as real functions by shUnit2. +testExtractTestFunctions() +{ + f="${tmpD}/extract_test_functions" + sed 's/^#//' <"${f}" +#testABC() { echo 'ABC'; } +#test_def() { +# echo 'def' +#} +#testG3 () +#{ +# echo 'G3' +#} +#function test4() { echo '4'; } +# test5() { echo '5'; } +#some_test_function() { echo 'some func'; } +#func_with_test_vars() { +# testVariable=1234 +#} +EOF + + actual=`_shunit_extractTestFunctions "${f}"` + assertEquals 'testABC test_def testG3 test4 test5' "${actual}" +} + +#------------------------------------------------------------------------------ +# suite functions +# + +setUp() +{ + for f in ${expectedF} ${stdoutF} ${stderrF}; do + cp /dev/null ${f} + done + rm -fr "${tmpD}" + mkdir "${tmpD}" +} + +oneTimeSetUp() +{ + tmpD="${SHUNIT_TMPDIR}/tmp" + expectedF="${SHUNIT_TMPDIR}/expected" + stdoutF="${SHUNIT_TMPDIR}/stdout" + stderrF="${SHUNIT_TMPDIR}/stderr" + unittestF="${SHUNIT_TMPDIR}/unittest" +} + +# load and run shUnit2 +[ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0 +. ${TH_SHUNIT} diff --git a/test/shunit2-2.1.6/src/shunit2_test_standalone.sh b/test/shunit2-2.1.6/src/shunit2_test_standalone.sh new file mode 100755 index 0000000..6df64b3 --- /dev/null +++ b/test/shunit2-2.1.6/src/shunit2_test_standalone.sh @@ -0,0 +1,41 @@ +#! /bin/sh +# $Id: shunit2_test_standalone.sh 303 2010-05-03 13:11:27Z kate.ward@forestent.com $ +# vim:et:ft=sh:sts=2:sw=2 +# +# Copyright 2010 Kate Ward. All Rights Reserved. +# Released under the LGPL (GNU Lesser General Public License) +# Author: kate.ward@forestent.com (Kate Ward) +# +# shUnit2 unit test for standalone operation. +# +# This unit test is purely to test that calling shunit2 directly, while passing +# the name of a unit test script, works. When run, this script determines if it +# is running as a standalone program, and calls main() if it is. + +ARGV0=`basename "$0"` + +# load test helpers +. ./shunit2_test_helpers + +#------------------------------------------------------------------------------ +# suite tests +# + +testStandalone() +{ + assertTrue ${SHUNIT_TRUE} +} + +#------------------------------------------------------------------------------ +# main +# + +main() +{ + ${TH_SHUNIT} "${ARGV0}" +} + +# are we running as a standalone? +if [ "${ARGV0}" = 'shunit2_test_standalone.sh' ]; then + if [ $# -gt 0 ]; then main "$@"; else main; fi +fi diff --git a/test/stub-1.0.2.sh b/test/stub-1.0.2.sh new file mode 100644 index 0000000..9f21a39 --- /dev/null +++ b/test/stub-1.0.2.sh @@ -0,0 +1,419 @@ +# !/usr/bin/env bash +# +# stub.sh 1.0.2 - stubbing helpers for simplifying bash script tests. +# https://github.com/jimeh/stub.sh +# +# (The MIT License) +# +# Copyright (c) 2014 Jim Myhrberg. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# + + +# Public: Stub given command. +# +# Arguments: +# - $1: Name of command to stub. +# - $2: (optional) When set to "STDOUT", echo a default message to STDOUT. +# When set to "STDERR", echo default message to STDERR. +# +# Echoes nothing. +# Returns nothing. +stub() { + local redirect="null" + if [ "$2" = "stdout" ] || [ "$2" = "STDOUT" ]; then redirect=""; fi + if [ "$2" = "stderr" ] || [ "$2" = "STDERR" ]; then redirect="stderr"; fi + + stub_and_echo "$1" "$1 stub: \$@" "$redirect" +} + + +# Public: Stub given command, and echo given string. +# +# Arguments: +# - $1: Name of command to stub. +# - $2: String to echo when stub is called. +# - $3: (optional) When set to "STDERR", echo to STDERR instead of STDOUT. +# When set to "null", all output is redirected to /dev/null. +# +# Echoes nothing. +# Returns nothing. +stub_and_echo() { + local redirect="" + if [ "$3" = "stderr" ] || [ "$3" = "STDERR" ]; then redirect=" 1>&2"; fi + if [ "$3" = "null" ]; then redirect=" &>/dev/null"; fi + + stub_and_eval "$1" "echo \"$2\"$redirect" +} + + +# Public: Stub given command, and execute given string with eval. +# +# Arguments: +# - $1: Name of command to stub. +# - $2: String to eval when stub is called. +# +# Echoes nothing. +# Returns nothing. +stub_and_eval() { + local cmd="$1" + + # Setup empty list of active stubs. + if [ -z "$STUB_ACTIVE_STUBS" ]; then STUB_ACTIVE_STUBS=(); fi + + # If stubbing a function, store non-stubbed copy of it required for restore. + if [ -n "$(command -v "$cmd")" ]; then + if [ -z "$(command -v "non_stubbed_${cmd}")" ]; then + if [[ "$(type "$cmd" | head -1)" == *"is a function" ]]; then + local source="$(type "$cmd" | tail -n +2)" + source="${source/$cmd/non_stubbed_${cmd}}" + eval "$source" + fi + fi + fi + + # Prepare stub index and call list for this stub. + __stub_register "$cmd" + + # Keep track of what is currently stubbed to ensure restore only acts on + # actual stubs. + if [[ " ${STUB_ACTIVE_STUBS[@]} " != *" $cmd "* ]]; then + STUB_ACTIVE_STUBS+=("$cmd") + fi + + # Create the stub. + eval "$( printf "%s" "${cmd}() { __stub_call \"${cmd}\" \$@; $2;}")" +} + + +# Public: Find out if stub has been called. Returns 0 if yes, 1 if no. +# +# Arguments: +# - $1: Name of stubbed command. +# +# Echoes nothing. +# Returns 0 (success) is stub has been called, 1 (error) otherwise. +stub_called() { + if [ "$(stub_called_times "$1")" -lt 1 ]; then + return 1 + fi +} + + +# Public: Find out if stub has been called with specific arguments. +# +# Arguments: +# - $1: Name of stubbed command. +# - $@: Any/all additional arguments are used to specify what stub was +# called with. +# +# Examples: +# stub uname +# uname +# uname -r -a +# stub_called_with uname # Returns 0 (success). +# stub_called_with uname -r # Returns 1 (error). +# stub_called_with uname -r -a # Returns 0 (success). +# +# Echoes nothing. +# Returns 0 (success) if specified stub has been called with given arguments, +# otherwise returns 1 (error). +stub_called_with() { + local cmd="$1" + shift 1 + + if [ "$(stub_called_with_times "$cmd" $@)" -lt 1 ]; then + return 1 + fi +} + + +# Public: Find out how many times a stub has been called. +# +# Arguments: +# - $1: Name of stubbed command. +# +# Echoes number of times stub has been called if $2 is not given, otherwise +# echoes nothing. +# Returns 0 (success) if $2 is not given, or if it is given and it matches the +# number of times the stub has been called. Otherwise 1 (error) is returned if +# it doesn't match.. +stub_called_times() { + local cmd="$1" + + local index="$(__stub_index "$1")" + local count=0 + + if [ -n "$index" ]; then + eval "count=\"\${#STUB_${index}_CALLS[@]}\"" + fi + + echo $count +} + + +# Public: Find out if stub has been called exactly the given number of times +# with specified arguments. +# +# Arguments: +# - $1: Name of stubbed command. +# - $2: Exact number of times stub has been called. +# +# Echoes nothing. +# Returns 0 (success) if stub has been called at least the given number of +# times with specified arguments, otherwise 1 (error) is returned. +stub_called_exactly_times() { + if [ "$(stub_called_times "$1")" != "$2" ]; then + return 1 + fi +} + + +# Public: Find out if stub has been called at least the given number of times. +# +# Arguments: +# - $1: Name of stubbed command. +# - $2: Minimum required number of times stub has been called. +# +# Echoes nothing. +# Returns 0 (success) if stub has been called at least the given number of +# times, otherwise 1 (error) is returned. +stub_called_at_least_times() { + if [ "$(stub_called_times "$1")" -lt "$2" ]; then + return 1 + fi +} + + +# Public: Find out if stub has been called no more than the given number of +# times. +# +# Arguments: +# - $1: Name of stubbed command. +# - $2: Maximum allowed number of times stub has been called. +# +# Echoes nothing. +# Returns 0 (success) if stub has been called no more than the given number of +# times, otherwise 1 (error) is returned. +stub_called_at_most_times() { + if [ "$(stub_called_times "$1")" -gt "$2" ]; then + return 1 + fi +} + + +# Public: Find out how many times a stub has been called with specific +# arguments. +# +# Arguments: +# - $1: Name of stubbed command. +# - $@: Any/all additional arguments are used to specify what stub was +# called with. +# +# Echoes number of times stub has been called with given arguments. +# Return 0 (success). +stub_called_with_times() { + local cmd="$1" + + shift 1 + local args="$@" + if [ "$args" = "" ]; then args=""; fi + + local count=0 + local index="$(__stub_index "$cmd")" + if [ -n "$index" ]; then + eval "local calls=(\"\${STUB_${index}_CALLS[@]}\")" + for call in "${calls[@]}"; do + if [ "$call" = "$args" ]; then ((count++)); fi + done + fi + + echo $count +} + + +# Public: Find out if stub has been called exactly the given number of times +# with specified arguments. +# +# Arguments: +# - $1: Name of stubbed command. +# - $2: Exact number of times stub has been called. +# - $@: Any/all additional arguments are used to specify what stub was +# called with. +# +# Echoes nothing. +# Returns 0 (success) if stub has been called at least the given number of +# times with specified arguments, otherwise 1 (error) is returned. +stub_called_with_exactly_times() { + local cmd="$1" + local count="$2" + shift 2 + + if [ "$(stub_called_with_times "$cmd" $@)" != "$count" ]; then + return 1 + fi +} + + +# Public: Find out if stub has been called at least the given number of times +# with specified arguments. +# +# Arguments: +# - $1: Name of stubbed command. +# - $2: Minimum required number of times stub has been called. +# - $@: Any/all additional arguments are used to specify what stub was +# called with. +# +# Echoes nothing. +# Returns 0 (success) if stub has been called at least the given number of +# times with specified arguments, otherwise 1 (error) is returned. +stub_called_with_at_least_times() { + local cmd="$1" + local count="$2" + shift 2 + + if [ "$(stub_called_with_times "$cmd" $@)" -lt "$count" ]; then + return 1 + fi +} + + +# Public: Find out if stub has been called no more than the given number of +# times. +# +# Arguments: +# - $1: Name of stubbed command. +# - $2: Maximum allowed number of times stub has been called. +# - $@: Any/all additional arguments are used to specify what stub was +# called with. +# +# Echoes nothing. +# Returns 0 (success) if stub has been called no more than the given number of +# times with specified arguments, otherwise 1 (error) is returned. +stub_called_with_at_most_times() { + local cmd="$1" + local count="$2" + shift 2 + + if [ "$(stub_called_with_times "$cmd" $@)" -gt "$count" ]; then + return 1 + fi +} + + +# Public: Restore the original command/function that was stubbed. +# +# Arguments: +# - $1: Name of command to restore. +# +# Echoes nothing. +# Returns nothing. +restore() { + local cmd="$1" + + # Don't do anything if the command isn't currently stubbed. + if [[ " ${STUB_ACTIVE_STUBS[@]} " != *" $1 "* ]]; then + return 0 + fi + + # Remove stub functions. + unset -f "$cmd" + + # Remove stub from list of active stubs. + STUB_ACTIVE_STUBS=(${STUB_ACTIVE_STUBS[@]/$cmd/}) + + # If stub was for a function, restore the original function. + if type "non_stubbed_${cmd}" &>/dev/null; then + local original_type="$(type "non_stubbed_${cmd}" | head -1)" + if [[ "$original_type" == *"is a function" ]]; then + local source="$(type "non_stubbed_$cmd" | tail -n +2)" + source="${source/non_stubbed_${cmd}/$cmd}" + eval "$source" + unset -f "non_stubbed_${cmd}" + fi + fi +} + + +# +# Internal functions +# + +# Private: Used to keep track of which stubs have been called and how many +# times. +__stub_call() { + local cmd="$1" + shift 1 + local args="$@" + if [ "$args" = "" ]; then args=""; fi + + local index="$(__stub_index "$cmd")" + if [ -n "$index" ]; then + eval "STUB_${index}_CALLS+=(\"\$args\")" + fi +} + + +# Private: Get index value of stub. Required to access list of stub calls. +__stub_index() { + local cmd="$1" + + for item in ${STUB_INDEX[@]}; do + if [[ "$item" = "${cmd}="* ]]; then + local index="$item" + index="${index/${cmd}=/}" + echo "$index" + fi + done +} + + +# Private: Prepare for the creation of a new stub. Adds stub to index and +# sets up an empty call list. +__stub_register() { + local cmd="$1" + + if [ -z "$STUB_NEXT_INDEX" ]; then STUB_NEXT_INDEX=0; fi + if [ -z "$STUB_INDEX" ]; then STUB_INDEX=(); fi + + # Clean up after any previous stub for the same command. + __stub_clean "$cmd" + + # Add stub to index. + STUB_INDEX+=("${cmd}=${STUB_NEXT_INDEX}") + eval "STUB_${STUB_NEXT_INDEX}_CALLS=()" + + # Increment stub count. + ((STUB_NEXT_INDEX++)) +} + +# Private: Cleans out and removes a stub's call list, and removes stub from +# index. +__stub_clean() { + local cmd="$1" + local index="$(__stub_index "$cmd")" + + # Remove all relevant details from any previously existing stub for the same + # command. + if [ -n "$index" ]; then + eval "unset STUB_${index}_CALLS" + STUB_INDEX=(${STUB_INDEX[@]/${cmd}=*/}) + fi +} diff --git a/zsh-autosuggestions.plugin.zsh b/zsh-autosuggestions.plugin.zsh deleted file mode 100644 index 16c2256..0000000 --- a/zsh-autosuggestions.plugin.zsh +++ /dev/null @@ -1 +0,0 @@ -source ${0:A:h}/zsh-autosuggestions.zsh diff --git a/zsh-autosuggestions.plugin.zsh b/zsh-autosuggestions.plugin.zsh new file mode 120000 index 0000000..e41b4f7 --- /dev/null +++ b/zsh-autosuggestions.plugin.zsh @@ -0,0 +1 @@ +zsh-autosuggestions.zsh \ No newline at end of file diff --git a/zsh-autosuggestions.zsh b/zsh-autosuggestions.zsh index e780225..f5b0f6f 100644 --- a/zsh-autosuggestions.zsh +++ b/zsh-autosuggestions.zsh @@ -1,8 +1,8 @@ # Fish-like fast/unobtrusive autosuggestions for zsh. -# https://github.com/zsh-users/zsh-autosuggestions -# v0.7.1 +# https://github.com/tarruda/zsh-autosuggestions +# v0.2.5 # Copyright (c) 2013 Thiago de Arruda -# Copyright (c) 2016-2021 Eric Freese +# Copyright (c) 2016 Eric Freese # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation @@ -32,189 +32,122 @@ # Color to use when highlighting suggestion # Uses format of `region_highlight` # More info: http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Widgets -(( ! ${+ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE} )) && -typeset -g ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8' +ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8' # Prefix to use when saving original versions of bound widgets -(( ! ${+ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX} )) && -typeset -g ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig- - -# Strategies to use to fetch a suggestion -# Will try each strategy in order until a suggestion is returned -(( ! ${+ZSH_AUTOSUGGEST_STRATEGY} )) && { - typeset -ga ZSH_AUTOSUGGEST_STRATEGY - ZSH_AUTOSUGGEST_STRATEGY=(history) -} +ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX=autosuggest-orig- # Widgets that clear the suggestion -(( ! ${+ZSH_AUTOSUGGEST_CLEAR_WIDGETS} )) && { - typeset -ga ZSH_AUTOSUGGEST_CLEAR_WIDGETS - ZSH_AUTOSUGGEST_CLEAR_WIDGETS=( - history-search-forward - history-search-backward - history-beginning-search-forward - history-beginning-search-backward - history-beginning-search-forward-end - history-beginning-search-backward-end - history-substring-search-up - history-substring-search-down - up-line-or-beginning-search - down-line-or-beginning-search - up-line-or-history - down-line-or-history - accept-line - copy-earlier-word - ) -} +ZSH_AUTOSUGGEST_CLEAR_WIDGETS=( + history-search-forward + history-search-backward + history-beginning-search-forward + history-beginning-search-backward + up-line-or-history + down-line-or-history + accept-line +) # Widgets that accept the entire suggestion -(( ! ${+ZSH_AUTOSUGGEST_ACCEPT_WIDGETS} )) && { - typeset -ga ZSH_AUTOSUGGEST_ACCEPT_WIDGETS - ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=( - forward-char - end-of-line - vi-forward-char - vi-end-of-line - vi-add-eol - ) -} - -# Widgets that accept the entire suggestion and execute it -(( ! ${+ZSH_AUTOSUGGEST_EXECUTE_WIDGETS} )) && { - typeset -ga ZSH_AUTOSUGGEST_EXECUTE_WIDGETS - ZSH_AUTOSUGGEST_EXECUTE_WIDGETS=( - ) -} +ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=( + forward-char + end-of-line + vi-forward-char + vi-end-of-line +) # Widgets that accept the suggestion as far as the cursor moves -(( ! ${+ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS} )) && { - typeset -ga ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS - ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=( - forward-word - emacs-forward-word - vi-forward-word - vi-forward-word-end - vi-forward-blank-word - vi-forward-blank-word-end - vi-find-next-char - vi-find-next-char-skip - ) -} - -# Widgets that should be ignored (globbing supported but must be escaped) -(( ! ${+ZSH_AUTOSUGGEST_IGNORE_WIDGETS} )) && { - typeset -ga ZSH_AUTOSUGGEST_IGNORE_WIDGETS - ZSH_AUTOSUGGEST_IGNORE_WIDGETS=( - orig-\* - beep - run-help - set-local-history - which-command - yank - yank-pop - zle-\* - ) -} - -# Pty name for capturing completions for completion suggestion strategy -(( ! ${+ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME} )) && -typeset -g ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME=zsh_autosuggest_completion_pty +ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS=( + forward-word + vi-forward-word + vi-forward-word-end + vi-forward-blank-word + vi-forward-blank-word-end +) #--------------------------------------------------------------------# -# Utility Functions # +# Handle Deprecated Variables/Widgets # #--------------------------------------------------------------------# -_zsh_autosuggest_escape_command() { - setopt localoptions EXTENDED_GLOB - - # Escape special chars in the string (requires EXTENDED_GLOB) - echo -E "${1//(#m)[\"\'\\()\[\]|*?~]/\\$MATCH}" +_zsh_autosuggest_deprecated_warning() { + >&2 echo "zsh-autosuggestions: $@" } +_zsh_autosuggest_check_deprecated_config() { + if [ -n "$AUTOSUGGESTION_HIGHLIGHT_COLOR" ]; then + _zsh_autosuggest_deprecated_warning "AUTOSUGGESTION_HIGHLIGHT_COLOR is deprecated. Use ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE instead." + [ -z "$ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE" ] && ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE=$AUTOSUGGESTION_HIGHLIGHT_STYLE + unset AUTOSUGGESTION_HIGHLIGHT_STYLE + fi + + if [ -n "$AUTOSUGGESTION_HIGHLIGHT_CURSOR" ]; then + _zsh_autosuggest_deprecated_warning "AUTOSUGGESTION_HIGHLIGHT_CURSOR is deprecated." + unset AUTOSUGGESTION_HIGHLIGHT_CURSOR + fi + + if [ -n "$AUTOSUGGESTION_ACCEPT_RIGHT_ARROW" ]; then + _zsh_autosuggest_deprecated_warning "AUTOSUGGESTION_ACCEPT_RIGHT_ARROW is deprecated. The right arrow now accepts the suggestion by default." + unset AUTOSUGGESTION_ACCEPT_RIGHT_ARROW + fi +} + +_zsh_autosuggest_deprecated_start_widget() { + _zsh_autosuggest_deprecated_warning "The autosuggest-start widget is deprecated. For more info, see the README at https://github.com/tarruda/zsh-autosuggestions." + zle -D autosuggest-start + eval "zle-line-init() { + $(echo $functions[${widgets[zle-line-init]#*:}] | sed -e 's/zle autosuggest-start//g') + }" +} + +zle -N autosuggest-start _zsh_autosuggest_deprecated_start_widget + #--------------------------------------------------------------------# # Widget Helpers # #--------------------------------------------------------------------# -_zsh_autosuggest_incr_bind_count() { - typeset -gi bind_count=$((_ZSH_AUTOSUGGEST_BIND_COUNTS[$1]+1)) - _ZSH_AUTOSUGGEST_BIND_COUNTS[$1]=$bind_count -} - # Bind a single widget to an autosuggest widget, saving a reference to the original widget _zsh_autosuggest_bind_widget() { - typeset -gA _ZSH_AUTOSUGGEST_BIND_COUNTS - local widget=$1 local autosuggest_action=$2 local prefix=$ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX - local -i bind_count - # Save a reference to the original widget case $widgets[$widget] in # Already bound - user:_zsh_autosuggest_(bound|orig)_*) - bind_count=$((_ZSH_AUTOSUGGEST_BIND_COUNTS[$widget])) - ;; + user:_zsh_autosuggest_(widget|orig)_*);; # User-defined widget user:*) - _zsh_autosuggest_incr_bind_count $widget - zle -N $prefix$bind_count-$widget ${widgets[$widget]#*:} + zle -N $prefix$widget ${widgets[$widget]#*:} ;; # Built-in widget builtin) - _zsh_autosuggest_incr_bind_count $widget - eval "_zsh_autosuggest_orig_${(q)widget}() { zle .${(q)widget} }" - zle -N $prefix$bind_count-$widget _zsh_autosuggest_orig_$widget + eval "_zsh_autosuggest_orig_$widget() { zle .$widget }" + zle -N $prefix$widget _zsh_autosuggest_orig_$widget ;; # Completion widget completion:*) - _zsh_autosuggest_incr_bind_count $widget - eval "zle -C $prefix$bind_count-${(q)widget} ${${(s.:.)widgets[$widget]}[2,3]}" + eval "zle -C $prefix$widget ${${widgets[$widget]#*:}/:/ }" ;; esac - # Pass the original widget's name explicitly into the autosuggest - # function. Use this passed in widget name to call the original - # widget instead of relying on the $WIDGET variable being set - # correctly. $WIDGET cannot be trusted because other plugins call - # zle without the `-w` flag (e.g. `zle self-insert` instead of - # `zle self-insert -w`). - eval "_zsh_autosuggest_bound_${bind_count}_${(q)widget}() { - _zsh_autosuggest_widget_$autosuggest_action $prefix$bind_count-${(q)widget} \$@ - }" - # Create the bound widget - zle -N -- $widget _zsh_autosuggest_bound_${bind_count}_$widget + zle -N $widget _zsh_autosuggest_widget_$autosuggest_action } # Map all configured widgets to the right autosuggest widgets _zsh_autosuggest_bind_widgets() { - emulate -L zsh - - local widget - local ignore_widgets - - ignore_widgets=( - .\* - _\* - ${_ZSH_AUTOSUGGEST_BUILTIN_ACTIONS/#/autosuggest-} - $ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX\* - $ZSH_AUTOSUGGEST_IGNORE_WIDGETS - ) + local widget; # Find every widget we might want to bind and bind it appropriately - for widget in ${${(f)"$(builtin zle -la)"}:#${(j:|:)~ignore_widgets}}; do - if [[ -n ${ZSH_AUTOSUGGEST_CLEAR_WIDGETS[(r)$widget]} ]]; then + for widget in ${${(f)"$(builtin zle -la)"}:#(.*|_*|orig-*|autosuggest-*|$ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX*|zle-line-*|run-help|which-command|beep|set-local-history|yank)}; do + if [ ${ZSH_AUTOSUGGEST_CLEAR_WIDGETS[(r)$widget]} ]; then _zsh_autosuggest_bind_widget $widget clear - elif [[ -n ${ZSH_AUTOSUGGEST_ACCEPT_WIDGETS[(r)$widget]} ]]; then + elif [ ${ZSH_AUTOSUGGEST_ACCEPT_WIDGETS[(r)$widget]} ]; then _zsh_autosuggest_bind_widget $widget accept - elif [[ -n ${ZSH_AUTOSUGGEST_EXECUTE_WIDGETS[(r)$widget]} ]]; then - _zsh_autosuggest_bind_widget $widget execute - elif [[ -n ${ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS[(r)$widget]} ]]; then + elif [ ${ZSH_AUTOSUGGEST_PARTIAL_ACCEPT_WIDGETS[(r)$widget]} ]; then _zsh_autosuggest_bind_widget $widget partial_accept else # Assume any unspecified widget might modify the buffer @@ -223,16 +156,11 @@ _zsh_autosuggest_bind_widgets() { done } -# Given the name of an original widget and args, invoke it, if it exists +# Given the name of a widget, invoke the original we saved, if it exists _zsh_autosuggest_invoke_original_widget() { - # Do nothing unless called with at least one arg - (( $# )) || return 0 + local original_widget_name="$ZSH_AUTOSUGGEST_ORIGINAL_WIDGET_PREFIX$WIDGET" - local original_widget_name="$1" - - shift - - if (( ${+widgets[$original_widget_name]} )); then + if [ $widgets[$original_widget_name] ]; then zle $original_widget_name -- $@ fi } @@ -243,9 +171,7 @@ _zsh_autosuggest_invoke_original_widget() { # If there was a highlight, remove it _zsh_autosuggest_highlight_reset() { - typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT - - if [[ -n "$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT" ]]; then + if [ -n "$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT" ]; then region_highlight=("${(@)region_highlight:#$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT}") unset _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT fi @@ -253,11 +179,9 @@ _zsh_autosuggest_highlight_reset() { # If there's a suggestion, highlight it _zsh_autosuggest_highlight_apply() { - typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT - - if (( $#POSTDISPLAY )); then - typeset -g _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT="$#BUFFER $(($#BUFFER + $#POSTDISPLAY)) $ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE" - region_highlight+=("$_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT") + if [ $#POSTDISPLAY -gt 0 ]; then + _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT="$#BUFFER $(($#BUFFER + $#POSTDISPLAY)) $ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE" + region_highlight+=($_ZSH_AUTOSUGGEST_LAST_HIGHLIGHT) else unset _ZSH_AUTOSUGGEST_LAST_HIGHLIGHT fi @@ -267,570 +191,102 @@ _zsh_autosuggest_highlight_apply() { # Autosuggest Widget Implementations # #--------------------------------------------------------------------# -# Disable suggestions -_zsh_autosuggest_disable() { - typeset -g _ZSH_AUTOSUGGEST_DISABLED - _zsh_autosuggest_clear -} - -# Enable suggestions -_zsh_autosuggest_enable() { - unset _ZSH_AUTOSUGGEST_DISABLED - - if (( $#BUFFER )); then - _zsh_autosuggest_fetch - fi -} - -# Toggle suggestions (enable/disable) -_zsh_autosuggest_toggle() { - if (( ${+_ZSH_AUTOSUGGEST_DISABLED} )); then - _zsh_autosuggest_enable - else - _zsh_autosuggest_disable - fi -} - # Clear the suggestion _zsh_autosuggest_clear() { # Remove the suggestion - POSTDISPLAY= + unset POSTDISPLAY _zsh_autosuggest_invoke_original_widget $@ } # Modify the buffer and get a new suggestion _zsh_autosuggest_modify() { - local -i retval - - # Only available in zsh >= 5.4 - local -i KEYS_QUEUED_COUNT - - # Save the contents of the buffer/postdisplay - local orig_buffer="$BUFFER" - local orig_postdisplay="$POSTDISPLAY" - - # Clear suggestion while waiting for next one - POSTDISPLAY= - - # Original widget may modify the buffer + # Original widget modifies the buffer _zsh_autosuggest_invoke_original_widget $@ - retval=$? - - emulate -L zsh - - # Don't fetch a new suggestion if there's more input to be read immediately - if (( $PENDING > 0 || $KEYS_QUEUED_COUNT > 0 )); then - POSTDISPLAY="$orig_postdisplay" - return $retval - fi - - # Optimize if manually typing in the suggestion or if buffer hasn't changed - if [[ "$BUFFER" = "$orig_buffer"* && "$orig_postdisplay" = "${BUFFER:$#orig_buffer}"* ]]; then - POSTDISPLAY="${orig_postdisplay:$(($#BUFFER - $#orig_buffer))}" - return $retval - fi - - # Bail out if suggestions are disabled - if (( ${+_ZSH_AUTOSUGGEST_DISABLED} )); then - return $? - fi # Get a new suggestion if the buffer is not empty after modification - if (( $#BUFFER > 0 )); then - if [[ -z "$ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" ]] || (( $#BUFFER <= $ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE )); then - _zsh_autosuggest_fetch - fi + local suggestion + if [ $#BUFFER -gt 0 ]; then + suggestion=$(_zsh_autosuggest_suggestion $BUFFER) fi - return $retval -} - -# Fetch a new suggestion based on what's currently in the buffer -_zsh_autosuggest_fetch() { - if (( ${+ZSH_AUTOSUGGEST_USE_ASYNC} )); then - _zsh_autosuggest_async_request "$BUFFER" + # Add the suggestion to the POSTDISPLAY + if [ -n "$suggestion" ]; then + POSTDISPLAY=${suggestion#$BUFFER} else - local suggestion - _zsh_autosuggest_fetch_suggestion "$BUFFER" - _zsh_autosuggest_suggest "$suggestion" - fi -} - -# Offer a suggestion -_zsh_autosuggest_suggest() { - emulate -L zsh - - local suggestion="$1" - - if [[ -n "$suggestion" ]] && (( $#BUFFER )); then - POSTDISPLAY="${suggestion#$BUFFER}" - else - POSTDISPLAY= + unset POSTDISPLAY fi } # Accept the entire suggestion _zsh_autosuggest_accept() { - local -i retval max_cursor_pos=$#BUFFER - - # When vicmd keymap is active, the cursor can't move all the way - # to the end of the buffer - if [[ "$KEYMAP" = "vicmd" ]]; then - max_cursor_pos=$((max_cursor_pos - 1)) - fi - - # If we're not in a valid state to accept a suggestion, just run the - # original widget and bail out - if (( $CURSOR != $max_cursor_pos || !$#POSTDISPLAY )); then - _zsh_autosuggest_invoke_original_widget $@ - return - fi - # Only accept if the cursor is at the end of the buffer - # Add the suggestion to the buffer - BUFFER="$BUFFER$POSTDISPLAY" + if [ $CURSOR -eq $#BUFFER ]; then + # Add the suggestion to the buffer + BUFFER="$BUFFER$POSTDISPLAY" - # Remove the suggestion - POSTDISPLAY= + # Remove the suggestion + unset POSTDISPLAY - # Run the original widget before manually moving the cursor so that the - # cursor movement doesn't make the widget do something unexpected - _zsh_autosuggest_invoke_original_widget $@ - retval=$? - - # Move the cursor to the end of the buffer - if [[ "$KEYMAP" = "vicmd" ]]; then - CURSOR=$(($#BUFFER - 1)) - else - CURSOR=$#BUFFER + # Move the cursor to the end of the buffer + CURSOR=${#BUFFER} fi - return $retval -} - -# Accept the entire suggestion and execute it -_zsh_autosuggest_execute() { - # Add the suggestion to the buffer - BUFFER="$BUFFER$POSTDISPLAY" - - # Remove the suggestion - POSTDISPLAY= - - # Call the original `accept-line` to handle syntax highlighting or - # other potential custom behavior - _zsh_autosuggest_invoke_original_widget "accept-line" + _zsh_autosuggest_invoke_original_widget $@ } # Partially accept the suggestion _zsh_autosuggest_partial_accept() { - local -i retval cursor_loc - # Save the contents of the buffer so we can restore later if needed - local original_buffer="$BUFFER" + local original_buffer=$BUFFER # Temporarily accept the suggestion. BUFFER="$BUFFER$POSTDISPLAY" # Original widget moves the cursor _zsh_autosuggest_invoke_original_widget $@ - retval=$? - - # Normalize cursor location across vi/emacs modes - cursor_loc=$CURSOR - if [[ "$KEYMAP" = "vicmd" ]]; then - cursor_loc=$((cursor_loc + 1)) - fi # If we've moved past the end of the original buffer - if (( $cursor_loc > $#original_buffer )); then + if [ $CURSOR -gt $#original_buffer ]; then # Set POSTDISPLAY to text right of the cursor - POSTDISPLAY="${BUFFER[$(($cursor_loc + 1)),$#BUFFER]}" + POSTDISPLAY=$RBUFFER # Clip the buffer at the cursor - BUFFER="${BUFFER[1,$cursor_loc]}" + BUFFER=$LBUFFER else # Restore the original buffer - BUFFER="$original_buffer" + BUFFER=$original_buffer fi - - return $retval } -() { - typeset -ga _ZSH_AUTOSUGGEST_BUILTIN_ACTIONS +for action in clear modify accept partial_accept; do + eval "_zsh_autosuggest_widget_$action() { + _zsh_autosuggest_highlight_reset + _zsh_autosuggest_$action \$@ + _zsh_autosuggest_highlight_apply + }" +done - _ZSH_AUTOSUGGEST_BUILTIN_ACTIONS=( - clear - fetch - suggest - accept - execute - enable - disable - toggle - ) - - local action - for action in $_ZSH_AUTOSUGGEST_BUILTIN_ACTIONS modify partial_accept; do - eval "_zsh_autosuggest_widget_$action() { - local -i retval - - _zsh_autosuggest_highlight_reset - - _zsh_autosuggest_$action \$@ - retval=\$? - - _zsh_autosuggest_highlight_apply - - zle -R - - return \$retval - }" - done - - for action in $_ZSH_AUTOSUGGEST_BUILTIN_ACTIONS; do - zle -N autosuggest-$action _zsh_autosuggest_widget_$action - done -} +zle -N autosuggest-accept _zsh_autosuggest_widget_accept +zle -N autosuggest-clear _zsh_autosuggest_widget_clear #--------------------------------------------------------------------# -# Completion Suggestion Strategy # -#--------------------------------------------------------------------# -# Fetches a suggestion from the completion engine -# - -_zsh_autosuggest_capture_postcompletion() { - # Always insert the first completion into the buffer - compstate[insert]=1 - - # Don't list completions - unset 'compstate[list]' -} - -_zsh_autosuggest_capture_completion_widget() { - # Add a post-completion hook to be called after all completions have been - # gathered. The hook can modify compstate to affect what is done with the - # gathered completions. - local -a +h comppostfuncs - comppostfuncs=(_zsh_autosuggest_capture_postcompletion) - - # Only capture completions at the end of the buffer - CURSOR=$#BUFFER - - # Run the original widget wrapping `.complete-word` so we don't - # recursively try to fetch suggestions, since our pty is forked - # after autosuggestions is initialized. - zle -- ${(k)widgets[(r)completion:.complete-word:_main_complete]} - - if is-at-least 5.0.3; then - # Don't do any cr/lf transformations. We need to do this immediately before - # output because if we do it in setup, onlcr will be re-enabled when we enter - # vared in the async code path. There is a bug in zpty module in older versions - # where the tty is not properly attached to the pty slave, resulting in stty - # getting stopped with a SIGTTOU. See zsh-workers thread 31660 and upstream - # commit f75904a38 - stty -onlcr -ocrnl -F /dev/tty - fi - - # The completion has been added, print the buffer as the suggestion - echo -nE - $'\0'$BUFFER$'\0' -} - -zle -N autosuggest-capture-completion _zsh_autosuggest_capture_completion_widget - -_zsh_autosuggest_capture_setup() { - # There is a bug in zpty module in older zsh versions by which a - # zpty that exits will kill all zpty processes that were forked - # before it. Here we set up a zsh exit hook to SIGKILL the zpty - # process immediately, before it has a chance to kill any other - # zpty processes. - if ! is-at-least 5.4; then - zshexit() { - # The zsh builtin `kill` fails sometimes in older versions - # https://unix.stackexchange.com/a/477647/156673 - kill -KILL $$ 2>&- || command kill -KILL $$ - - # Block for long enough for the signal to come through - sleep 1 - } - fi - - # Try to avoid any suggestions that wouldn't match the prefix - zstyle ':completion:*' matcher-list '' - zstyle ':completion:*' path-completion false - zstyle ':completion:*' max-errors 0 not-numeric - - bindkey '^I' autosuggest-capture-completion -} - -_zsh_autosuggest_capture_completion_sync() { - _zsh_autosuggest_capture_setup - - zle autosuggest-capture-completion -} - -_zsh_autosuggest_capture_completion_async() { - _zsh_autosuggest_capture_setup - - zmodload zsh/parameter 2>/dev/null || return # For `$functions` - - # Make vared completion work as if for a normal command line - # https://stackoverflow.com/a/7057118/154703 - autoload +X _complete - functions[_original_complete]=$functions[_complete] - function _complete() { - unset 'compstate[vared]' - _original_complete "$@" - } - - # Open zle with buffer set so we can capture completions for it - vared 1 -} - -_zsh_autosuggest_strategy_completion() { - # Reset options to defaults and enable LOCAL_OPTIONS - emulate -L zsh - - # Enable extended glob for completion ignore pattern - setopt EXTENDED_GLOB - - typeset -g suggestion - local line REPLY - - # Exit if we don't have completions - whence compdef >/dev/null || return - - # Exit if we don't have zpty - zmodload zsh/zpty 2>/dev/null || return - - # Exit if our search string matches the ignore pattern - [[ -n "$ZSH_AUTOSUGGEST_COMPLETION_IGNORE" ]] && [[ "$1" == $~ZSH_AUTOSUGGEST_COMPLETION_IGNORE ]] && return - - # Zle will be inactive if we are in async mode - if zle; then - zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_completion_sync - else - zpty $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME _zsh_autosuggest_capture_completion_async "\$1" - zpty -w $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME $'\t' - fi - - { - # The completion result is surrounded by null bytes, so read the - # content between the first two null bytes. - zpty -r $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME line '*'$'\0''*'$'\0' - - # Extract the suggestion from between the null bytes. On older - # versions of zsh (older than 5.3), we sometimes get extra bytes after - # the second null byte, so trim those off the end. - # See http://www.zsh.org/mla/workers/2015/msg03290.html - suggestion="${${(@0)line}[2]}" - } always { - # Destroy the pty - zpty -d $ZSH_AUTOSUGGEST_COMPLETIONS_PTY_NAME - } -} - -#--------------------------------------------------------------------# -# History Suggestion Strategy # -#--------------------------------------------------------------------# -# Suggests the most recent history item that matches the given -# prefix. -# - -_zsh_autosuggest_strategy_history() { - # Reset options to defaults and enable LOCAL_OPTIONS - emulate -L zsh - - # Enable globbing flags so that we can use (#m) and (x~y) glob operator - setopt EXTENDED_GLOB - - # Escape backslashes and all of the glob operators so we can use - # this string as a pattern to search the $history associative array. - # - (#m) globbing flag enables setting references for match data - # TODO: Use (b) flag when we can drop support for zsh older than v5.0.8 - local prefix="${1//(#m)[\\*?[\]<>()|^~#]/\\$MATCH}" - - # Get the history items that match the prefix, excluding those that match - # the ignore pattern - local pattern="$prefix*" - if [[ -n $ZSH_AUTOSUGGEST_HISTORY_IGNORE ]]; then - pattern="($pattern)~($ZSH_AUTOSUGGEST_HISTORY_IGNORE)" - fi - - # Give the first history item matching the pattern as the suggestion - # - (r) subscript flag makes the pattern match on values - typeset -g suggestion="${history[(r)$pattern]}" -} - -#--------------------------------------------------------------------# -# Match Previous Command Suggestion Strategy # -#--------------------------------------------------------------------# -# Suggests the most recent history item that matches the given -# prefix and whose preceding history item also matches the most -# recently executed command. -# -# For example, suppose your history has the following entries: -# - pwd -# - ls foo -# - ls bar -# - pwd -# -# Given the history list above, when you type 'ls', the suggestion -# will be 'ls foo' rather than 'ls bar' because your most recently -# executed command (pwd) was previously followed by 'ls foo'. -# -# Note that this strategy won't work as expected with ZSH options that don't -# preserve the history order such as `HIST_IGNORE_ALL_DUPS` or -# `HIST_EXPIRE_DUPS_FIRST`. - -_zsh_autosuggest_strategy_match_prev_cmd() { - # Reset options to defaults and enable LOCAL_OPTIONS - emulate -L zsh - - # Enable globbing flags so that we can use (#m) and (x~y) glob operator - setopt EXTENDED_GLOB - - # TODO: Use (b) flag when we can drop support for zsh older than v5.0.8 - local prefix="${1//(#m)[\\*?[\]<>()|^~#]/\\$MATCH}" - - # Get the history items that match the prefix, excluding those that match - # the ignore pattern - local pattern="$prefix*" - if [[ -n $ZSH_AUTOSUGGEST_HISTORY_IGNORE ]]; then - pattern="($pattern)~($ZSH_AUTOSUGGEST_HISTORY_IGNORE)" - fi - - # Get all history event numbers that correspond to history - # entries that match the pattern - local history_match_keys - history_match_keys=(${(k)history[(R)$~pattern]}) - - # By default we use the first history number (most recent history entry) - local histkey="${history_match_keys[1]}" - - # Get the previously executed command - local prev_cmd="$(_zsh_autosuggest_escape_command "${history[$((HISTCMD-1))]}")" - - # Iterate up to the first 200 history event numbers that match $prefix - for key in "${(@)history_match_keys[1,200]}"; do - # Stop if we ran out of history - [[ $key -gt 1 ]] || break - - # See if the history entry preceding the suggestion matches the - # previous command, and use it if it does - if [[ "${history[$((key - 1))]}" == "$prev_cmd" ]]; then - histkey="$key" - break - fi - done - - # Give back the matched history entry - typeset -g suggestion="$history[$histkey]" -} - -#--------------------------------------------------------------------# -# Fetch Suggestion # -#--------------------------------------------------------------------# -# Loops through all specified strategies and returns a suggestion -# from the first strategy to provide one. -# - -_zsh_autosuggest_fetch_suggestion() { - typeset -g suggestion - local -a strategies - local strategy - - # Ensure we are working with an array - strategies=(${=ZSH_AUTOSUGGEST_STRATEGY}) - - for strategy in $strategies; do - # Try to get a suggestion from this strategy - _zsh_autosuggest_strategy_$strategy "$1" - - # Ensure the suggestion matches the prefix - [[ "$suggestion" != "$1"* ]] && unset suggestion - - # Break once we've found a valid suggestion - [[ -n "$suggestion" ]] && break - done -} - -#--------------------------------------------------------------------# -# Async # +# Suggestion # #--------------------------------------------------------------------# -_zsh_autosuggest_async_request() { - zmodload zsh/system 2>/dev/null # For `$sysparams` +# Get a suggestion from history that matches a given prefix +_zsh_autosuggest_suggestion() { + setopt localoptions extendedglob - typeset -g _ZSH_AUTOSUGGEST_ASYNC_FD _ZSH_AUTOSUGGEST_CHILD_PID + # Escape the prefix (requires EXTENDED_GLOB) + local prefix=${1//(#m)[\][()|\\*?#<>~^]/\\$MATCH} - # If we've got a pending request, cancel it - if [[ -n "$_ZSH_AUTOSUGGEST_ASYNC_FD" ]] && { true <&$_ZSH_AUTOSUGGEST_ASYNC_FD } 2>/dev/null; then - # Close the file descriptor and remove the handler - builtin exec {_ZSH_AUTOSUGGEST_ASYNC_FD}<&- - zle -F $_ZSH_AUTOSUGGEST_ASYNC_FD + # Get all history items (reversed) that match pattern $prefix* + local history_matches + history_matches=(${history[(R)$prefix*]}) - # We won't know the pid unless the user has zsh/system module installed - if [[ -n "$_ZSH_AUTOSUGGEST_CHILD_PID" ]]; then - # Zsh will make a new process group for the child process only if job - # control is enabled (MONITOR option) - if [[ -o MONITOR ]]; then - # Send the signal to the process group to kill any processes that may - # have been forked by the suggestion strategy - kill -TERM -$_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null - else - # Kill just the child process since it wasn't placed in a new process - # group. If the suggestion strategy forked any child processes they may - # be orphaned and left behind. - kill -TERM $_ZSH_AUTOSUGGEST_CHILD_PID 2>/dev/null - fi - fi - fi - - # Fork a process to fetch a suggestion and open a pipe to read from it - builtin exec {_ZSH_AUTOSUGGEST_ASYNC_FD}< <( - # Tell parent process our pid - echo $sysparams[pid] - - # Fetch and print the suggestion - local suggestion - _zsh_autosuggest_fetch_suggestion "$1" - echo -nE "$suggestion" - ) - - # There's a weird bug here where ^C stops working unless we force a fork - # See https://github.com/zsh-users/zsh-autosuggestions/issues/364 - autoload -Uz is-at-least - is-at-least 5.8 || command true - - # Read the pid from the child process - read _ZSH_AUTOSUGGEST_CHILD_PID <&$_ZSH_AUTOSUGGEST_ASYNC_FD - - # When the fd is readable, call the response handler - zle -F "$_ZSH_AUTOSUGGEST_ASYNC_FD" _zsh_autosuggest_async_response -} - -# Called when new data is ready to be read from the pipe -# First arg will be fd ready for reading -# Second arg will be passed in case of error -_zsh_autosuggest_async_response() { - emulate -L zsh - - local suggestion - - if [[ -z "$2" || "$2" == "hup" ]]; then - # Read everything from the fd and give it as a suggestion - IFS='' read -rd '' -u $1 suggestion - zle autosuggest-suggest -- "$suggestion" - - # Close the fd - builtin exec {1}<&- - fi - - # Always remove the handler - zle -F "$1" - _ZSH_AUTOSUGGEST_ASYNC_FD= + # Echo the first item that matches + echo ${history_matches[1]} } #--------------------------------------------------------------------# @@ -839,29 +295,9 @@ _zsh_autosuggest_async_response() { # Start the autosuggestion widgets _zsh_autosuggest_start() { - # By default we re-bind widgets on every precmd to ensure we wrap other - # wrappers. Specifically, highlighting breaks if our widgets are wrapped by - # zsh-syntax-highlighting widgets. This also allows modifications to the - # widget list variables to take effect on the next precmd. However this has - # a decent performance hit, so users can set ZSH_AUTOSUGGEST_MANUAL_REBIND - # to disable the automatic re-binding. - if (( ${+ZSH_AUTOSUGGEST_MANUAL_REBIND} )); then - add-zsh-hook -d precmd _zsh_autosuggest_start - fi - + _zsh_autosuggest_check_deprecated_config _zsh_autosuggest_bind_widgets } -# Mark for auto-loading the functions that we use -autoload -Uz add-zsh-hook is-at-least - -# Automatically enable asynchronous mode in newer versions of zsh. Disable for -# older versions because there is a bug when using async mode where ^C does not -# work immediately after fetching a suggestion. -# See https://github.com/zsh-users/zsh-autosuggestions/issues/364 -if is-at-least 5.0.8; then - typeset -g ZSH_AUTOSUGGEST_USE_ASYNC= -fi - -# Start the autosuggestion widgets on the next precmd +autoload -Uz add-zsh-hook add-zsh-hook precmd _zsh_autosuggest_start