├── .editorconfig ├── .travis.yml ├── LICENSE ├── README.md ├── Vagrantfile ├── appveyor.yml ├── bin ├── answer ├── depur ├── posit └── trix ├── doc ├── depur.md ├── dispatch.md ├── platforms.md ├── posit.md └── trix.md ├── lib └── workshop │ ├── answer │ ├── answer.sh │ └── cli.sh │ ├── common.sh │ ├── depur │ ├── cli.sh │ └── depur.sh │ ├── dispatch.sh │ ├── posit │ ├── cli.sh │ ├── cov.sh │ ├── posit.sh │ ├── spec.sh │ └── tiny.sh │ └── trix │ ├── cli.sh │ └── trix.sh └── test ├── README.md ├── depur └── unit.test.sh ├── dispatch └── unit.test.sh ├── matrix.sh ├── posit ├── resources │ ├── posit_postcov.fixture.sh │ ├── posit_postcov2.fixture.sh │ └── posit_postcov3.fixture.sh └── unit.test.sh └── trix └── unit.test.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | # Mosai Workshop Editor Configuration 2 | # 3 | # These are settings required for a nice and cozy 4 | # formatting of our source. 5 | 6 | # Unix-style newlines 7 | # Removes trailing spaces from lines 8 | # Adds a newline to the end of file if not present 9 | # Suggests 78 width line lenght (RFC 5322 Section 2.1.1) 10 | [*] 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | rulers = 78 15 | 16 | # Use tabs for .sh files (required for shell heredocs to 17 | # work properly). 18 | # Tabs have 8 width, because GitHub only uses that 19 | # width. Damn it. 20 | [*.sh] 21 | indent_style = tab 22 | tab_width = 8 23 | [bin/*] 24 | indent_style = tab 25 | tab_width = 8 26 | 27 | # Travis and AppVeyor files have classical YAML formatting 28 | [{.travis.yml, appveyor.yml}] 29 | indent_style = space 30 | indent_size = 2 31 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # A courtesy of trix, a Mosai Workshop tool. 2 | # Generated from the matrix_travis on test/matrix.sh 3 | 4 | install: 5 | - bin/trix --steps="setup" --matrix matrix_travis --env "$TRIX_ENV" run test/matrix.sh 6 | script: 7 | - bin/trix --steps="script" --matrix matrix_travis --env "$TRIX_ENV" run test/matrix.sh 8 | after_script: 9 | - bin/trix --steps="clean" --matrix matrix_travis --env "$TRIX_ENV" run test/matrix.sh 10 | matrix: 11 | include: 12 | # Result environment: TARGET_SHELL="bash" SHELL_PKG="bash" 13 | - env: TRIX_ENV="env_travis_linux env_common_bash" 14 | os: linux 15 | 16 | # Result environment: TARGET_SHELL="busybox sh" SHELL_PKG="busybox" 17 | - env: TRIX_ENV="env_travis_linux env_common_busybox" 18 | os: linux 19 | 20 | # Result environment: TARGET_SHELL="dash" SHELL_PKG="dash" 21 | - env: TRIX_ENV="env_travis_linux env_common_dash" 22 | os: linux 23 | 24 | # Result environment: TARGET_SHELL="bash2.05b" SHELL_PKG="bash2.05b" PPA_REQUIRED="agriffis/bashes precise F5751EC8" 25 | - env: TRIX_ENV="env_travis_linux env_extras_bash2_0" 26 | os: linux 27 | 28 | # Result environment: TARGET_SHELL="bash3.0.16" SHELL_PKG="bash3.0.16" PPA_REQUIRED="agriffis/bashes precise F5751EC8" 29 | - env: TRIX_ENV="env_travis_linux env_extras_bash3_0" 30 | os: linux 31 | 32 | # Result environment: TARGET_SHELL="bash3.2.48" SHELL_PKG="bash3.2.48" PPA_REQUIRED="agriffis/bashes precise F5751EC8" 33 | - env: TRIX_ENV="env_travis_linux env_extras_bash3_2" 34 | os: linux 35 | 36 | # Result environment: TARGET_SHELL="bash4.2.45" SHELL_PKG="bash4.2.45" PPA_REQUIRED="agriffis/bashes precise F5751EC8" 37 | - env: TRIX_ENV="env_travis_linux env_extras_bash4_2" 38 | os: linux 39 | 40 | # Result environment: TARGET_SHELL="zsh-beta" SHELL_PKG="zsh-beta" 41 | - env: TRIX_ENV="env_travis_linux env_extras_zsh_beta" 42 | os: linux 43 | 44 | # Result environment: TARGET_SHELL="ksh" SHELL_PKG="ksh" 45 | - env: TRIX_ENV="env_travis_linux env_shell_ksh" 46 | os: linux 47 | 48 | # Result environment: TARGET_SHELL="mksh" SHELL_PKG="mksh" 49 | - env: TRIX_ENV="env_travis_linux env_shell_mksh" 50 | os: linux 51 | 52 | # Result environment: TARGET_SHELL="pdksh" SHELL_PKG="pdksh" 53 | - env: TRIX_ENV="env_travis_linux env_shell_pdksh" 54 | os: linux 55 | 56 | # Result environment: TARGET_SHELL="posh" SHELL_PKG="posh" 57 | - env: TRIX_ENV="env_travis_linux env_shell_posh" 58 | os: linux 59 | 60 | # Result environment: TARGET_SHELL="yash" SHELL_PKG="yash" 61 | - env: TRIX_ENV="env_travis_linux env_shell_yash" 62 | os: linux 63 | 64 | # Result environment: TARGET_SHELL="zsh" SHELL_PKG="zsh" 65 | - env: TRIX_ENV="env_travis_linux env_shell_zsh" 66 | os: linux 67 | 68 | # Result environment: TARGET_SHELL="bash" SHELL_PKG="bash" 69 | - env: TRIX_ENV="env_travis_osx env_common_bash" 70 | os: osx 71 | 72 | # Result environment: TARGET_SHELL="ksh" SHELL_PKG="ksh" 73 | - env: TRIX_ENV="env_travis_osx env_shell_ksh" 74 | os: osx 75 | 76 | # Result environment: TARGET_SHELL="mksh" SHELL_PKG="mksh" 77 | - env: TRIX_ENV="env_travis_osx env_shell_mksh" 78 | os: osx 79 | 80 | # Result environment: TARGET_SHELL="zsh" SHELL_PKG="zsh" 81 | - env: TRIX_ENV="env_travis_osx env_shell_zsh" 82 | os: osx 83 | 84 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014 Alexandre Gaigalas 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Mosai Workshop 2 | ============== 3 | 4 | Tools that [works on a lot of platforms](doc/platforms.md) and **don't require any dependencies** in most of them. 5 | 6 | Workshop is designed to be the ideal lighweight toolkit for software automation, provisioning and testing. 7 | 8 | [![Linux and OS X Build Status](https://travis-ci.org/Mosai/workshop.svg?branch=master)](https://travis-ci.org/Mosai/workshop) 9 | [![Windows Build status](https://ci.appveyor.com/api/projects/status/k560be2nhnu245i9/branch/master)](https://ci.appveyor.com/project/alganet/workshop/branch/master) 10 | 11 | The Tools 12 | --------- 13 | 14 | *This is a work in progress. Tools may be incomplete and lacking comprehensive documentation.* 15 | 16 | - [posit](doc/posit.md) - Unit test framework and runner. 17 | - [depur](doc/depur.md) - Shell script stack trace utilities. 18 | - [dispatch](doc/dispatch.md) - Lightweight microframework and command line argument parser. 19 | - [trix](doc/trix.md) - Language-independent test matrix runner. 20 | 21 | Instructions 22 | ------------ 23 | 24 | 1. Clone or [download and unzip](https://github.com/Mosai/workshop/archive/master.zip) the project. 25 | 2. `$ cd /path/to/project/workshop` (use your real path) 26 | 27 | If you're on Windows, [download git](http://git-scm.com/download/win). Workshop 28 | is compatible with the git bash. 29 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # Testing machines for the entire Workshop build 2 | 3 | Vagrant.configure("2") do |config| 4 | 5 | config.vm.define "precise64", autostart: false do |machine| 6 | machine.vm.box = "hashicorp/precise64" 7 | end 8 | 9 | config.vm.define "trusty64", autostart: false do |machine| 10 | machine.vm.box = "ubuntu/trusty64" 11 | end 12 | 13 | config.vm.define "debian7464", autostart: false do |machine| 14 | machine.vm.box = "chef/debian-7.4" 15 | end 16 | 17 | config.vm.define "debian6064", autostart: false do |machine| 18 | machine.vm.box = "puppetlabs/debian-6.0.9-64-nocm" 19 | machine.vm.synced_folder './', '/vagrant', type: 'rsync' 20 | end 21 | 22 | config.vm.define "fedora2064", autostart: false do |machine| 23 | machine.vm.box = "chef/fedora-20" 24 | machine.ssh.pty = true 25 | machine.vm.synced_folder './', '/vagrant', type: 'rsync' 26 | end 27 | 28 | config.vm.define "fedora1964", autostart: false do |machine| 29 | machine.vm.box = "chef/fedora-19" 30 | machine.ssh.pty = true 31 | machine.vm.synced_folder './', '/vagrant', type: 'rsync' 32 | end 33 | 34 | config.vm.define "centos6464", autostart: false do |machine| 35 | machine.vm.box = "chef/centos-6.5" 36 | machine.ssh.pty = true 37 | machine.vm.synced_folder './', '/vagrant', type: 'rsync' 38 | end 39 | 40 | 41 | end 42 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # No version yet. 2 | version: "0.0.{build}" 3 | os: Windows Server 2012 R2 4 | 5 | # No build, no deploy 6 | build: off 7 | deploy: off 8 | 9 | # Git from chocolatey has the git bash we need 10 | install: 11 | - cinst git 12 | 13 | test_script: 14 | - cd %APPVEYOR_BUILD_FOLDER% 15 | - cmd: bash -lc "bin/posit --timeout=0 --report=spec run ./test/" 16 | -------------------------------------------------------------------------------- /bin/answer: -------------------------------------------------------------------------------- 1 | #/usr/bin/env sh 2 | 3 | # Gets the lib folder relative to this script location 4 | # If this script is in /foo/bin, points to /foo/lib/workshop 5 | lib_path="$(cd "$(dirname "$0")/../lib/workshop";pwd)" 6 | 7 | . "$lib_path/common.sh" || exit 1 8 | . "$lib_path/dispatch.sh" || exit 1 9 | . "$lib_path/answer/cli.sh" || exit 1 10 | . "$lib_path/answer/answer.sh" || exit 1 11 | 12 | # This variable should not be used anymore 13 | unset lib_path 14 | 15 | answer "${@:-}" 16 | -------------------------------------------------------------------------------- /bin/depur: -------------------------------------------------------------------------------- 1 | #/usr/bin/env sh 2 | 3 | # Gets the lib folder relative to this script location 4 | # If this script is in /foo/bin, points to /foo/lib/workshop 5 | lib_path="$(cd "$(dirname "$0")/../lib/workshop";pwd)" 6 | 7 | . "$lib_path/common.sh" || exit 1 8 | . "$lib_path/dispatch.sh" || exit 1 9 | . "$lib_path/depur/cli.sh" || exit 1 10 | . "$lib_path/depur/depur.sh" || exit 1 11 | 12 | # This variable should not be used anymore 13 | unset lib_path 14 | 15 | depur "${@:-}" 16 | -------------------------------------------------------------------------------- /bin/posit: -------------------------------------------------------------------------------- 1 | #/usr/bin/env sh 2 | 3 | # Gets the lib folder relative to this script location 4 | # If this script is in /foo/bin, points to /foo/lib/workshop 5 | lib_path="$(cd "$(dirname "$0")/../lib/workshop";pwd)" 6 | 7 | . "$lib_path/common.sh" || exit 1 8 | . "$lib_path/dispatch.sh" || exit 1 9 | . "$lib_path/depur/depur.sh" || exit 1 10 | . "$lib_path/posit/tiny.sh" || exit 1 11 | . "$lib_path/posit/cov.sh" || exit 1 12 | . "$lib_path/posit/spec.sh" || exit 1 13 | . "$lib_path/posit/cli.sh" || exit 1 14 | . "$lib_path/posit/posit.sh" || exit 1 15 | 16 | # This variable should not be used anymore 17 | unset lib_path 18 | 19 | posit "${@:-}" 20 | -------------------------------------------------------------------------------- /bin/trix: -------------------------------------------------------------------------------- 1 | #/usr/bin/env sh 2 | 3 | # Gets the lib folder relative to this script location 4 | # If this script is in /foo/bin, points to /foo/lib/workshop 5 | lib_path="$(cd "$(dirname "$0")/../lib/workshop";pwd)" 6 | 7 | . "$lib_path/common.sh" || exit 1 8 | . "$lib_path/dispatch.sh" || exit 1 9 | . "$lib_path/trix/cli.sh" || exit 1 10 | . "$lib_path/trix/trix.sh" || exit 1 11 | 12 | # This variable should not be used anymore 13 | unset lib_path 14 | 15 | trix "${@:-}" 16 | -------------------------------------------------------------------------------- /doc/depur.md: -------------------------------------------------------------------------------- 1 | depur 2 | ===== 3 | 4 | depur is a toolkit for extracting stack trace information 5 | from shell scripts. 6 | 7 | Although it can be used as a standalone tool, depur is probably 8 | more useful as a dependency providing automatic stack traces 9 | and code coverage for [posit](posit.md). -------------------------------------------------------------------------------- /doc/dispatch.md: -------------------------------------------------------------------------------- 1 | dispatch 2 | ======== 3 | 4 | A full command line argument dispatcher in 50 lines of portable shell script. 5 | 6 | Download the [standalone version](https://github.com/Mosai/workshop/blob/master/lib/workshop/dispatch.sh) or use the full [Mosai Workshop](https://github.com/Mosai/workshop). 7 | 8 | Usage 9 | ----- 10 | 11 | Unlike many argument parsers, **dispatch** is not designed to be used inside case/esac control structures. It behaves like a router for shell script functions. 12 | 13 | Let's create a command line tool called `dexample` to explore the dispatch features: 14 | 15 | ### Dispatching Commands 16 | 17 | Our first command will be `dsample hello`, a command to print the string "Hello World". 18 | 19 | Create a file called `dsample` with the following contents: 20 | 21 | ```sh 22 | # Loads the dispatch library 23 | . "/path/to/dispatch.sh" 24 | 25 | # The hello command 26 | dsample_command_hello () ( echo "Hello World" ) 27 | 28 | # Dispatch the arguments 29 | dispatch dsample "$@" 30 | ``` 31 | 32 | You should replace `/path/to/dispatch.sh` with the proper path to the library and make the file executable by running `$ chmod +x dsample`. 33 | 34 | Now, when you run it you should see the string "Hello World": 35 | 36 | ```sh 37 | $ ./dsample hello 38 | Hello World 39 | ``` 40 | 41 | ### Command Arguments 42 | 43 | dispatch will repass remaining arguments to your command: 44 | 45 | ```sh 46 | dsample_command_greet () ( echo "Hello $1" ) 47 | 48 | dispatch dsample "$@" 49 | ``` 50 | 51 | ```sh 52 | $ ./dsample greet "Alexandre" 53 | Hello Alexandre 54 | ``` 55 | 56 | ### Empty Call Placeholder 57 | 58 | You might have experimented with the sample and tried to run it without any arguments. Whenever dispatch is called without arguments, it routes to a placeholder function: 59 | 60 | ```sh 61 | dsample_ () ( echo "No arguments provided." ) 62 | 63 | dispatch dsample "$@" 64 | ``` 65 | 66 | You can execute whatever you want on the empty call placeholder, but in this sample we will just print a message saying that no arguments were provided: 67 | 68 | ```sh 69 | $ ./dsample 70 | No arguments provided. 71 | ``` 72 | 73 | ### Not Found Calls 74 | 75 | If dispatch can't find any route, it will fall back to a default one passing the entire command line called: 76 | 77 | ```sh 78 | dsample_call_ () ( echo "Invalid call '$@'" ) 79 | 80 | dispatch dsample "$@" 81 | ``` 82 | 83 | Calling any invalid command or option will now display an error message: 84 | 85 | ```sh 86 | $ ./dsample foobarbaz 87 | Invalid call 'dsample foobarbaz'. 88 | ``` 89 | 90 | ### Simple Options 91 | 92 | Support for short and long options is available. 93 | 94 | ```sh 95 | dsample_option_v () ( echo "Version: 0.0" ) 96 | dsample_option_help () ( echo "Usage: dsample [options] [command]" ) 97 | 98 | dispatch dsample "$@" 99 | ``` 100 | 101 | The result should be: 102 | 103 | ```sh 104 | $ ./dsample --help 105 | Usage: dsample [options] [command] 106 | $ ./dsample -v 107 | Version: 0.0 108 | ``` 109 | 110 | These options will end the dispatch process, but if you want you can 111 | dispatch the arguments again. The following sample introduces the `--short` 112 | option which changes the _Hello_ to _Hi_ if passed. 113 | 114 | ```sh 115 | dsample_short=0 116 | dsample_command_hello () 117 | { 118 | if [ $dsample_short = 0 ]; then 119 | echo "Hello World" 120 | else 121 | echo "Hi World" 122 | fi 123 | } 124 | dsample_option_short () ( dsample_short=1; dispatch dsample "$@" ) 125 | 126 | dispatch dsample "$@" 127 | ``` 128 | 129 | And now it is possible to call commands with options: 130 | 131 | ```sh 132 | $ ./dsample hello 133 | Hello World 134 | $ ./dsample --short hello 135 | Hi World 136 | ``` 137 | 138 | ### Options and Values 139 | 140 | If you need options with values, you'll need to shift them: 141 | 142 | ```sh 143 | dsample_message="Hello" 144 | dsample_command_greet () ( echo "$dsample_message $1" ) 145 | dsample_option_message () ( dsample_message="$1"; shift; dispatch dsample "$@" ) 146 | 147 | dispatch dsample "$@" 148 | ``` 149 | 150 | Long option values can be quoted and the equal sign `=` is optional: 151 | 152 | ```sh 153 | $ ./dsample --message Welcome greet Alexandre 154 | Welcome Alexandre 155 | $ ./dsample --message=Welcome greet Alexandre 156 | Welcome Alexandre 157 | $ ./dsample --message="Welcome to dispatch, " greet Alexandre 158 | Welcome to dispatch, Alexandre 159 | ``` 160 | 161 | ### Full Sample 162 | 163 | Here is our full sample in a single file: 164 | 165 | ```sh 166 | # Loads the dispatch library 167 | . "/path/to/dispatch.sh" 168 | 169 | # Variables and flags 170 | dsample_short=0 171 | dsample_message="Hello" 172 | 173 | # Placeholder calls 174 | dsample_ () ( echo "No arguments provided." ) 175 | dsample_call_ () ( echo "Invalid call '$@'." ) 176 | 177 | # Options 178 | dsample_option_v () ( echo "Version: 0.0" ) 179 | dsample_option_help () ( echo "Usage: dsample [options] [command]" ) 180 | dsample_option_short () ( dsample_short=1; dispatch dsample "$@" ) 181 | dsample_option_message () ( dsample_message="$1"; shift; dispatch dsample "$@" ) 182 | 183 | # Commands 184 | dsample_command_greet () ( echo "$dsample_message $1" ) 185 | dsample_command_hello () 186 | { 187 | if [ $dsample_short = 0 ]; then 188 | echo "Hello World" 189 | else 190 | echo "Hi World" 191 | fi 192 | } 193 | 194 | # Dispatch the arguments 195 | dispatch dsample "$@" 196 | ``` 197 | -------------------------------------------------------------------------------- /doc/platforms.md: -------------------------------------------------------------------------------- 1 | Supported Platforms 2 | =================== 3 | 4 | Workshop is tested in a large number of distros and shells. It uses only POSIX Shell Scripts, so it is also architecture independent. For a quick overview of supported environments check our [Travis](https://travis-ci.org/Mosai/workshop) and [AppVeyor](https://ci.appveyor.com/project/alganet/workshop/branch/master) builds. 5 | 6 | Distros Tested 7 | -------------- 8 | 9 | - **Unknown OS X** from Travis CI. 10 | - **Ubuntu 12.04** from Travis CI and test matrix. 11 | - **Ubuntu 14.04** from test matrix. 12 | - **Debian 7.4** from test matrix. 13 | - **Debian 6.0.9** from test matrix. 14 | - **Fedora 20** from test matrix. 15 | - **Fedora 19** from test matrix. 16 | - **CentOS 6.5** from test matrix. 17 | - **Windows Server 2012** from AppVeyor. 18 | 19 | Previous versions were tested on OpenSuse, Arch Linux, FreeBSD and older versions of Ubuntu and CentOS. Although they're not in our matrix, Workshop should work just fine on them. 20 | 21 | Shells Tested 22 | ------------- 23 | 24 | Mosai Workshop has been proved portable by real testing. These are the shells and versions we targeted on our test matrix: 25 | 26 | - **bash** - _Bourne Again Shell_. Probably the most popular shell around. 27 | - 2.05b 28 | - 3.0.16 29 | - 3.2.48 30 | - 4.1.5 31 | - 4.2.24 32 | - 4.2.37 33 | - 4.2.45 34 | - 4.3.11 35 | - **busybox sh** - Busybox uses the _Alquemist Shell_ (ash) which does not have a way to report its own version, these are busybox versions: 36 | - 1.15.1 37 | - 1.17.1 38 | - 1.18.5 39 | - 1.19.4 40 | - 1.20.2 41 | - 1.21.1 42 | - **dash** - The default non-user shell on Ubuntu. 43 | - (dash does not have a way to find its own version, we tested in all distros available) 44 | - **ksh** - _The Korn Shell_. A classic. 45 | - JM 93u 2011-02-08 46 | - JM 93u+ 2012-02-29 47 | - AJM 93u+ 2012-08-01 48 | - **mksh** - _MirOS Korn Shell_. A clone based on ksh88. 49 | - R39 2009/08/01 50 | - R39 2010/07/25 51 | - R40 2012/07/20 52 | - R40 2012/02/11 53 | - R46 2013/05/02 54 | - R50 2014/06/29 55 | - **pdksh** - _Public Domain Korn Shell_. A clone based on ksh88. 56 | - 5.2.14 99/07/13.2 57 | - R40 2012/07/20 58 | - R46 2013/05/02 59 | - **posh** - _Policy-Compliant Ordinary Shell_. Lightweight, very POSIX compliant shell. 60 | - (unknown version on Debian 6 before `$POSH_VERSION` was available) 61 | - 0.10.2 62 | - 0.12.3 63 | - **yash** - _Yet Another Shell_, a very lighweight one. 64 | - 2.30 65 | - 2.29 66 | - 2.35 67 | - 2.36 68 | - **zsh** - _The Z Shell_. Another popular shell. 69 | - 4.3.10 70 | - 4.3.10-dev-1-cvs0720 71 | - 4.3.17-dev-0-cvs0621 72 | - 4.3.12-dev-1-cvs 73 | - 5.0.2 74 | - 5.0.6 75 | 76 | Check for Yourself 77 | ------------------ 78 | 79 | If you want to run tests in your own machine, you can run our test matrix: 80 | 81 | ```sh 82 | bin/trix --matrix=local run test/matrix.sh 83 | ``` 84 | 85 | You can also run these tests in virtual machines. The following command 86 | runs all shells available one machine per time: 87 | 88 | ```sh 89 | bin/trix --matrix=remote run test/matrix.sh 90 | ``` 91 | 92 | And if you want, you can run one shell per time, which is really slow but far more isolated: 93 | 94 | ```sh 95 | bin/trix --matrix=virtual run test/matrix.sh 96 | ``` 97 | -------------------------------------------------------------------------------- /doc/posit.md: -------------------------------------------------------------------------------- 1 | posit 2 | ===== 3 | 4 | posit can run tests written as shell functions. A test should be named "*.test.sh" and 5 | look likes this: 6 | 7 | ```sh 8 | test_tr_can_replace_fancy_chars () 9 | { 10 | translated="$(echo 'Foo' | tr 'o' 'a')" 11 | 12 | [ "$translated" = "Faa" ] 13 | } 14 | ``` 15 | 16 | Tests on posit are isolated, so you can mock other commands and functions: 17 | 18 | ```sh 19 | 20 | test_posit_list_using_files () 21 | { 22 | # Mocks the posit_listfile function that should be called 23 | posit_listfile () ( echo "$1 OK" ) 24 | 25 | # Run the test 26 | expected_list="$(posit list /usr/bin/env)" 27 | 28 | # Checks if the mock command was called 29 | [ "$expected_list" = "/usr/bin/env OK" ] 30 | } 31 | ``` 32 | 33 | Each one of these functions runs in its own process to prevent contamination. A test 34 | passes when its function returns a successful code. 35 | 36 | The following variables are available for each test: 37 | 38 | 39 | - `$POSIT_FILE` has the relative path to the current test. 40 | - `$POSIT_DIR` has the relative directory to the current test. 41 | - `$POSIT_FUNCTION` has the function name for the test 42 | - `$POSIT_CMD` has the command line to invoke the current shell 43 | 44 | 45 | Test Runner 46 | ----------- 47 | 48 | You can run tests using the `$ bin/posit` tool from this package. This is 49 | how the runner output is presented: 50 | 51 | ``` 52 | $ bin/posit run test/ 53 | ............................ 28/28 passed. 54 | ``` 55 | 56 | More report modes are available: 57 | 58 | ``` 59 | $ bin/posit --report=spec run test/dispatch/ 60 | 61 | ### test/dispatch/unit.test.sh 62 | 63 | - pass: dispatch with empty placeholder 64 | - pass: dispatch with call placeholder 65 | - pass: dispatch command 66 | - pass: dispatch option short 67 | - pass: dispatch option short repassing 68 | - pass: dispatch option long 69 | - pass: dispatch option long repassing 70 | - pass: dispatch option long value and repassing 71 | 72 | Totals: 8/8 passed. 73 | 74 | ``` 75 | 76 | Flags 77 | ----- 78 | 79 | Output from test functions is not displayed on the test runner unless any errors occour, 80 | so you don't need to redirect it to `/dev/null` by yourself. You can ommit this behavior 81 | by setting `$ posit -s` or `$ posit --silent` before any command. 82 | 83 | Even if a test fails, posit will continue running the others. You can instruct posit to 84 | fail fast using `$ posit -f` or `$posit --fast`. 85 | 86 | You can tell posit to use any shell for the tests by using `$ posit --shell=ksh`, for 87 | example. 88 | 89 | See more flags on `$ posit help`. 90 | 91 | Stack Traces 92 | ------------ 93 | 94 | When an error is returned from a test function, posit displays a handy stack trace 95 | for the test (available on zsh, bash and ksh): 96 | 97 | ``` 98 | ### test/posit/unit.test.sh 99 | - fail: posit empty call 100 | + unit.test.sh:12 dispatched=+ 101 | + posit.sh:18 posit_demo 1 2 3 102 | + :3 echo 'OK 1' 2 3 103 | + unit.test.sh:12 dispatched='OK 1 2 3' 104 | + unit.test.sh:14 [ 'OK 1 2 3' '=' 'OK 1 2 ' ']' 105 | + zsh:9 has_passed=1 106 | - pass: posit help 107 | - pass: posit list using files 108 | - pass: posit list using directories 109 | - pass: posit list without parameters 110 | - pass: posit run 111 | - pass: posit spec 112 | - pass: posit cov 113 | - pass: posit postcov counts lines properly 114 | 115 | 8 tests out of 9 passed. 116 | 117 | ``` 118 | 119 | You can ommit the stack by setting `$ posit -s` or `$ posit --silent` before the 120 | run command 121 | 122 | For dash and busybox, a simpler trace is still displayed without the files and 123 | line numbers. 124 | 125 | Code Coverage 126 | ------------- 127 | 128 | Experimental code coverage reports are available for shells 129 | that support the rich stack traces. 130 | 131 | This is an excerpt from the `$ bash bin/posit --shell=bash --report=cov run test/dispatch/` output: 132 | 133 | ``` 134 | ### /home/alganet/Projects/mosai/workshop/lib/dispatch.sh 135 | 136 | > `-` `# Changes zsh globbing patterns` 137 | > `11` `unsetopt NO_MATCH >/dev/null 2>&1 || :` 138 | > `-` 139 | > `-` `# Dispatches calls of commands and arguments` 140 | > `-` `dispatch ()` 141 | > `-` `{` 142 | > `14` ` namespace="$1" # Namespace to be dispatched` 143 | > `14` ` arg="$2" # First argument` 144 | > `14` ` short="${arg#*-}" # First argument without trailing -` 145 | > `14` ` long="${short#*-}" # First argument without trailing --` 146 | > `-` 147 | > `-` ` # Exit and warn if no first argument is found` 148 | > `14` ` if [ -z "$arg" ]; then` 149 | > `1` ` "${namespace}_" # Call empty call placeholder` 150 | > `1` ` return 1` 151 | > `-` ` fi` 152 | ``` 153 | 154 | The number on the left is the number of passes that each specific line had. 155 | Coverage information is subject to shell support: 156 | 157 | - **bash** presents the most accurate count. 158 | - **zsh** and **ksh** may miss some lines. 159 | - others have only support for stack traces without files/lines. 160 | 161 | The standard coverage output is Markdown. 162 | -------------------------------------------------------------------------------- /doc/trix.md: -------------------------------------------------------------------------------- 1 | trix 2 | ==== 3 | 4 | trix handles test matrixes to perform tests on different environments. -------------------------------------------------------------------------------- /lib/workshop/answer/answer.sh: -------------------------------------------------------------------------------- 1 | # Global Options 2 | answer_cr=$(printf '\r') # A plain carriage return 3 | answer_esc=$(printf '\33') # A plain ESC character 4 | answer_oldterm='' # Contains the previous terminal state when changed 5 | answer_tryfocus=-1 # Current widget trying to be focused 6 | answer_total=0 # Total number of widgets 7 | answer_chosen='' # Text for the chosen widget 8 | answer_length=50 # Line length 9 | 10 | # Gets an answer from a input specification list 11 | answer_command_get () 12 | { 13 | answer_tryfocus=-1 14 | answer_total=0 15 | answer_chosen='' 16 | question="$1" 17 | shift 18 | 19 | # Gets each widget on a line 20 | widgets="$(answer_widgets "$question")" 21 | # Total number of elements 22 | answer_total="$(echo "$widgets" | wc -l)" 23 | 24 | # Saves old terminal settings and start receiving raw keyboard input 25 | answer_oldterm="$(stty -g)" 26 | stty raw -echo || exit 27 | 28 | # Restore old terminal settings if the script ends 29 | trap "answer_end; exit" 1 2 15 30 | 31 | # Starts the focus process 32 | answer_focus "$widgets" 0 33 | 34 | k="K[" # Marker used to identify escape sequences 35 | keybuffer='' # This buffer holds escape sequences for special keys 36 | while true; do 37 | # Gets a single byte from stdin, probably the keyboard 38 | byte="$(dd bs=1 count=1 2>/dev/null)" 39 | keypressed="$byte" 40 | 41 | case "$keypressed" in 42 | # Starts to buffer an escape sequence 43 | $answer_esc ) 44 | keybuffer="K" # Stores the marker prefix 45 | keypressed='' 46 | ;; 47 | # Ends the escape sequence buffer using common 48 | # characters 49 | "" | [a-zA-NP-Z~^\$@$answer_esc] ) 50 | if [ ! -z "$keybuffer" ]; then 51 | keypressed="${keybuffer}${keypressed}" 52 | keybuffer='' 53 | fi 54 | ;; 55 | # Buffers additional characters in the escape sequence 56 | * ) 57 | if [ ! -z "$keybuffer" ]; then 58 | keybuffer="${keybuffer}${keypressed}" 59 | keypressed='' 60 | fi 61 | ;; 62 | esac 63 | 64 | # Chooses what to do with the key pressed 65 | case "$keypressed" in 66 | "${k}C" | "${k}OC" ) # RIGHT 67 | answer_focus "$widgets" $((answer_tryfocus+1)) 68 | ;; 69 | "${k}D" | "${k}OD" ) # LEFT 70 | answer_focus "$widgets" $((answer_tryfocus-1)) 71 | ;; 72 | "$answer_cr" ) # ENTER 73 | answer_choose "$widgets" "$answer_tryfocus" 74 | return 75 | ;; 76 | "$byte" ) # CHAR 77 | 78 | ;; 79 | * ) # UNKNOWN 80 | 81 | ;; 82 | esac 83 | 84 | done 85 | 86 | # Clears the raw terminal settings and return 87 | answer_end 88 | } 89 | 90 | # Gets widgets from a spec one in each line 91 | answer_widgets () 92 | { 93 | answer="$1" 94 | # Needs an unindented line in order to work on OS X sed :( 95 | echo "$answer" | 96 | sed 's/|/\ 97 | '$answer_cr'/g' 98 | } 99 | 100 | # Restores old terminal settings and prints a new line 101 | answer_end () ( stty "$answer_oldterm" ) 102 | 103 | # Prints a line of widgets and focus one of them by its id. 104 | answer_focus () 105 | { 106 | widgets="$1" 107 | chosen_focus="$2" 108 | answer_length="$(stty size <&2 | cut -d" " -f2)" 109 | 110 | # Loops until a widget is focusable 111 | is_focusable='' 112 | while :; do 113 | 114 | # Widgets start on 1, so we skip 0 115 | if [ "$chosen_focus" -gt 0 ]; then 116 | is_focusable="$(echo "$widgets" | 117 | sed -n "${chosen_focus}p" | 118 | tr -d "$answer_cr" | 119 | sed -n ' 120 | /^ *\[/p 121 | ')" 122 | fi 123 | 124 | # Stops if the widget found is focusable 125 | if [ ! -z "$is_focusable" ]; then 126 | break 127 | fi 128 | 129 | # Finds if this widget is before or after the last focused 130 | # in order to properly skip unfocusable widgets in the 131 | # right order 132 | if [ "$chosen_focus" -gt "$answer_tryfocus" ]; then 133 | chosen_focus=$((chosen_focus+1)) 134 | elif [ "$chosen_focus" -lt "$answer_tryfocus" ]; then 135 | chosen_focus=$((chosen_focus-1)) 136 | fi 137 | 138 | # Finds if we reached the end of the widget list 139 | if [ "$chosen_focus" -gt $((answer_total+1)) ] || 140 | [ "$chosen_focus" -lt 1 ]; then 141 | break 142 | fi 143 | done 144 | 145 | # Ends if no focusable widget is found 146 | if [ "$chosen_focus" -gt $((answer_total)) ] && 147 | [ "$answer_tryfocus" = -1 ];then 148 | answer_end; return 149 | fi 150 | 151 | # If the focus is the same, does nothing 152 | if [ "$chosen_focus" = "$answer_tryfocus" ] || 153 | [ -z "$is_focusable" ]; then 154 | return 155 | fi 156 | 157 | answer_tryfocus="$chosen_focus" 158 | 159 | # Erases the entire line 160 | printf '\033[2K\r' 161 | # Prints the new line 162 | echo "$widgets" | answer_focus_render "$chosen_focus" 163 | 164 | 165 | } 166 | 167 | # Renders the actual widgets from answer_focus 168 | answer_focus_render () 169 | { 170 | current_focus="$1" 171 | rendering=0 # Current widget id 172 | widgets_length=0 173 | 174 | # Loops through widgets from stdin 175 | while read widget; do 176 | widget="$(printf %s "${widget}" | tr -d "$answer_cr")" 177 | widget_length="${#widget}" 178 | widgets_length=$((widgets_length+widget_length+1)) 179 | 180 | if [ $widgets_length -gt $((answer_length)) ]; then 181 | if [ "$current_focus" -gt $((rendering)) ]; then 182 | widgets_length=$((widget_length+2)) 183 | printf '\033[2K\r' 184 | printf %s "< " 185 | else 186 | widgets_length=$((widgets_length-widget_length+2)) 187 | printf %s " >" 188 | break 189 | fi 190 | fi 191 | 192 | rendering=$((rendering+1)) 193 | 194 | # Focused widget 195 | if [ "$current_focus" = "$rendering" ]; then 196 | printf "\033[7m${widget}\033[0m " 197 | # Unfocused 198 | else 199 | printf "${widget} " 200 | fi 201 | done 202 | } 203 | 204 | # Prints the output answer for a widget and ends 205 | answer_choose () 206 | { 207 | widgets="$1" 208 | answer_tryfocus="$2" 209 | 210 | printf '\033[2K\r' 211 | 212 | export answer_chosen="$(printf "$widgets" | 213 | sed -n "${answer_tryfocus}p" | # Finds current widget 214 | sed 's/\[ \(.*\) \]/\1/g' | # Removes decorations 215 | tr -d "$answer_cr" 216 | )" 217 | 218 | printf %s\\r\\n "$answer_chosen" 219 | 220 | answer_end; return # Resets terminal and ends 221 | } 222 | 223 | answer_command_menu () 224 | { 225 | menu_answer="$1:" 226 | menu_entries="Exit exit" 227 | nl=" 228 | " 229 | 230 | while read menu_entry; do 231 | menu_button="$(echo "$menu_entry" | cut -d " " -f1)" 232 | menu_answer="${menu_answer}|[ $menu_button ]" 233 | menu_entries="${menu_entries}${nl}${menu_entry}" 234 | done 235 | 236 | while :; do 237 | answer_command_get "$menu_answer|[ Exit ]" < /dev/tty 238 | answer_match="$(echo "$menu_entries" | sed -n "/^${answer_chosen}/p" | cut -d" " -f2)" 239 | if [ "$answer_match" = "exit" ]; then 240 | break 241 | fi 242 | 243 | if [ ! -z "$answer_match" ]; then 244 | $answer_match 245 | break 246 | fi 247 | done 248 | } 249 | -------------------------------------------------------------------------------- /lib/workshop/answer/cli.sh: -------------------------------------------------------------------------------- 1 | 2 | answer () ( dispatch answer "$@" ) 3 | 4 | # Provides help 5 | answer_command_help () 6 | { 7 | cat <<-HELP 8 | Usage: answer [option_list...] [command] 9 | answer help, -h, --help [command] Displays help for command. 10 | 11 | Commands: get [spec] Gets an answer from a specification list 12 | 13 | Spec: A spec is a string containing elements separated by a pipe "|". 14 | Buttons are enclosed by square brackets '[ A Button ]' 15 | Labels are just plain text. 16 | 17 | Examples: answer get 'Continue?|[ yes ]|[ no ]' 18 | 19 | HELP 20 | } 21 | 22 | # Options 23 | answer_option_help () ( answer_command_help ) 24 | answer_option_h () ( answer_command_help ) 25 | 26 | -------------------------------------------------------------------------------- /lib/workshop/common.sh: -------------------------------------------------------------------------------- 1 | # Common settings for all Mosai Workshop scripts 2 | 3 | # Enables word split on zsh 4 | setopt SH_WORD_SPLIT >/dev/null 2>&1 || : 5 | 6 | # -e exits on any untreated error 7 | # -u exits on any undeclared variable 8 | # -f disables pathname expansion 9 | set -euf 10 | -------------------------------------------------------------------------------- /lib/workshop/depur/cli.sh: -------------------------------------------------------------------------------- 1 | 2 | # Dispatches commands to other depur_ functions 3 | depur () ( dispatch depur "${@:-}" ) 4 | 5 | # Provides help 6 | depur_command_help () 7 | { 8 | cat <<-HELP 9 | Usage: depur [option_list...] [command] 10 | depur help, -h, --help [command] Displays help for command. 11 | 12 | Commands: run [script] Runs and traces the given script. 13 | tracer Gets a tracer command for a shell. 14 | format Formats a stack trace from stdin. 15 | coverage Formats a trace from stdin into code coverage. 16 | profile Formats a trace from stdin into profile info. 17 | 18 | Options: --shell [shell] Changes the shell used for debugging. 19 | --ignore [pattern] Don't report files matching this pattern. 20 | --short, -s Displays only the basename for paths. 21 | --full, -f Displays complete paths for the trace. 22 | HELP 23 | } 24 | 25 | # Options 26 | depur_option_help () ( depur_command_help ) 27 | depur_option_h () ( depur_command_help ) 28 | depur_option_f () ( depur_filter="echo"; dispatch depur "${@:-}" ) 29 | depur_option_full () ( depur_filter="echo"; dispatch depur "${@:-}" ) 30 | depur_option_s () ( depur_filter="basename"; dispatch depur "${@:-}" ) 31 | depur_option_short () ( depur_filter="basename"; dispatch depur "${@:-}" ) 32 | depur_option_shell () ( depur_shell="$1"; shift; dispatch depur "${@:-}" ) 33 | depur_option_ignore () ( depur_ignore="$1"; shift; dispatch depur "${@:-}" ) 34 | 35 | depur_ () ( echo "No command provided. Try 'depur --help'"; return 1 ) 36 | depur_call_ () ( echo "Call '$*' invalid. Try 'depur --help'"; return 1) 37 | 38 | -------------------------------------------------------------------------------- /lib/workshop/depur/depur.sh: -------------------------------------------------------------------------------- 1 | # Global option defaults 2 | depur_trace_command="" # Command to extract file/line info on stacks 3 | depur_filter="basename" # Filter used on file names when showing a stack trace 4 | depur_shell="sh" # Shell used as interpreter 5 | depur_ignore='*' 6 | 7 | # Runs a command and displays its stack trace 8 | depur_command_run () 9 | { 10 | # Sets a tracer and run the command on a shell with -x 11 | PS4="$(depur_command_tracer "$depur_shell")" $depur_shell -x $@ 2>&1 12 | } 13 | 14 | # Sets and returns the tracer command to be used on PS4 prompts 15 | depur_command_tracer () 16 | { 17 | if [ -z "$depur_trace_command" ]; then 18 | depur_trace_command="$(depur_get_tracer "$1")" 19 | fi 20 | 21 | echo "$depur_trace_command" 22 | } 23 | 24 | # Parses a stack from the stdin and outputs its code coverage report 25 | depur_command_coverage () 26 | { 27 | # Should contain a list of files and lines covered 28 | unsorted="$(depur_clean)" 29 | # Gets an unique list of files 30 | filelist="$(echo "$unsorted" | 31 | cut -d" " -f1 | 32 | sort | 33 | uniq | 34 | grep -v "$depur_ignore")" 35 | 36 | 37 | # Loop all files listed in the stack 38 | for file in $filelist; do 39 | if [ ! -z "$file" ] && [ -f "$file" ]; then 40 | cat "$file" | depur_covfile "$file" "$unsorted" 41 | echo "" 42 | fi 43 | done 44 | } 45 | 46 | # Parses a stack from the stdin and outputs its profiling report 47 | depur_command_profile () 48 | { 49 | cat | 50 | depur_clean | 51 | sort -k 1,1 -k 2,2 | 52 | uniq -c | 53 | sort -n -r | 54 | sed 's/^ *//g' | 55 | tr ' ' ' ' | 56 | while read profile_info; do 57 | profile_count="$(echo "$profile_info" | cut -d ' ' -f1)" 58 | profile_file="$(echo "$profile_info" | cut -d ' ' -f2)" 59 | profile_line="$(echo "$profile_info" | cut -d ' ' -f3)" 60 | profile_code="$(cat "$profile_file" | sed -n "${profile_line}p")" 61 | printf "%s %s:%s %s\n"\ 62 | "$profile_count" "$(basename "$profile_file")"\ 63 | "$profile_line" "$profile_code" 64 | done 65 | } 66 | 67 | # Formats a stack into columns 68 | depur_command_format () 69 | { 70 | # Displays the stack in aligned columns 71 | awk 'BEGIN {FS=OFS="\t"} 72 | { printf " %-4s %-20s %-30s\n", $1, $2, $3}' 73 | } 74 | 75 | # Processes the code coverage for one file 76 | depur_covfile () 77 | { 78 | file="$1" 79 | unsorted="$2" 80 | total_lines=0 81 | covered_lines=0 82 | traced_lines=0 83 | 84 | cat <<-FILEHEADER 85 | 86 | ### $file 87 | 88 | FILEHEADER 89 | 90 | # Gets lines that were covered only for this file 91 | thisfile="$(echo "$unsorted" | grep "^$file")" 92 | 93 | while IFS='' read -r file_line; do 94 | total_lines=$((total_lines+1)) 95 | # Full line text 96 | line="$(printf "%s\n" "$file_line" | tr '`' ' ')" 97 | # Number of matches on this line 98 | matched="$(echo "$thisfile" | 99 | sed -n "/ $total_lines$/p" | 100 | wc -l | 101 | sed "s/[ ]*//")" 102 | 103 | if [ "$matched" -gt 0 ]; then 104 | covered_lines="$((covered_lines+1))" 105 | fi 106 | 107 | # Formatted number of matched lines the file line 108 | covline="$(depur_covline "$total_lines" "$line" "$matched")" 109 | traced="$(echo "$covline" | 110 | grep "^> \`-\`" | 111 | wc -l | 112 | sed "s/[ ]*//")" 113 | 114 | if [ "$traced" -gt 0 ]; then 115 | traced_lines=$((traced_lines+1)) 116 | fi 117 | echo "$covline" 118 | done 119 | 120 | valid_lines=$(( total_lines - traced_lines )) 121 | 122 | if [ $valid_lines -gt 0 ]; then 123 | percent=$((100*covered_lines/valid_lines)) 124 | else 125 | percent=0 126 | fi 127 | 128 | filename="$(basename "$file")" 129 | totals="$covered_lines/$valid_lines" 130 | 131 | echo "" 132 | echo "Total: $filename has $totals lines covered (${percent}%)." 133 | IFS= # Restore separator 134 | } 135 | 136 | 137 | # Cleans up a coverage line before displaying it 138 | depur_covline () 139 | { 140 | # $1 has the current line number, it is unused. 141 | line="$2" # Full line text 142 | matched="$3" # How many cover matches 143 | ws="[ ]*" # Pattern to look for whitespace 144 | alnum="[a-zA-Z0-9_]" # Pattern to look for alnum 145 | excludes_pattern=" 146 | # Ignore comment lines 147 | /^${ws}#/d 148 | # Ignore lines with only a '{' or '}' 149 | /^${ws}{${ws}$/d 150 | /^${ws}}${ws}$/d 151 | # Ignore lines with only a 'fi' 152 | /^${ws}fi${ws}$/d 153 | # Ignore lines with only a 'done' 154 | /^${ws}done${ws}$/d 155 | # Ignore lines with only a 'else' 156 | /^${ws}else${ws}$/d 157 | # Ignore lines with only a 'elif' 158 | /^${ws}else${ws}$/d 159 | # Ignore lines with only a 'continue' 160 | /^${ws}continue${ws}$/d 161 | # Ignore lines with only a 'break' 162 | /^${ws}break${ws}$/d 163 | # Ignore lines with only a function declaration 164 | /^${ws}${alnum}*${ws}()${ws}$/d 165 | # Ignore blank lines 166 | /^${ws}$/d 167 | " 168 | 169 | # Ignore comment lines 170 | if [ -z "$(echo "$line" | sed "$excludes_pattern")" ]; then 171 | 172 | if [ -z "$line" ]; then 173 | echo "> \`-\` " 174 | else 175 | echo "> \`-\` \`$line\` " 176 | fi 177 | 178 | return 179 | fi 180 | 181 | echo "> \`$matched\` \`${line}\`" 182 | 183 | } 184 | 185 | # Cleans up a stack 186 | depur_clean () 187 | { 188 | # Remove non-stack lines (stack lines start with +) 189 | sed '/^[^+]/d' | 190 | # Gets only the file:lineno column 191 | cut -d" " -f2 | 192 | # Removes empty lines and lines without file names, 193 | # change the : into a tab. 194 | sed '/^:/d; /^[ ]*$/d; s/:/ /' | 195 | while read covered_line; do 196 | dirtypath="$(echo "$covered_line" | cut -d " " -f1)" 197 | fileline="$(echo "$covered_line" | cut -d " " -f2)" 198 | filepath="$(depur_realpath "$dirtypath")" 199 | echo "$filepath $fileline" 200 | done 201 | } 202 | 203 | depur_realpath () 204 | { 205 | cd -P "$(dirname "$1")" && 206 | echo "$(pwd -P)/$(basename "$1")" | 207 | sed 's/\/\//\//g' 208 | } 209 | 210 | # Returns the command tracer without caching it 211 | depur_get_tracer () 212 | { 213 | shell="$1" 214 | filter="${depur_filter}" 215 | theline=':\${LINENO:-0} ' 216 | 217 | $shell <<-EXTERNAL 2>/dev/null 218 | if [ "\${BASH_VERSION:-}" != "" ]; then 219 | echo "+ \\\$($filter \"\\\${BASH_SOURCE:-}\")$theline" 220 | elif [ "\$(echo "\$KSH_VERSION" | sed -n /93/p)" != "" ]; then 221 | echo "+ \\\$($filter \"\\\${.sh.file:-}\")$theline" 222 | elif [ "\${ZSH_VERSION:-}" != "" ]; then 223 | echo "+ \\\$($filter \\\${(%):-%x:%I}) " 224 | elif [ "\${POSH_VERSION:-}" != "" ]; then 225 | echo "+ :0 " # Fallback 226 | else 227 | echo "+ $theline" # Fallback 228 | fi 229 | EXTERNAL 230 | } 231 | -------------------------------------------------------------------------------- /lib/workshop/dispatch.sh: -------------------------------------------------------------------------------- 1 | # Changes zsh globbing patterns 2 | unsetopt NO_MATCH >/dev/null 2>&1 || : 3 | 4 | # Dispatches calls of commands and arguments 5 | dispatch () 6 | { 7 | namespace="$1" # Namespace to be dispatched 8 | arg="${2:-}" # First argument 9 | short="${arg#*-}" # First argument without trailing - 10 | long="${short#*-}" # First argument without trailing -- 11 | 12 | # Exit and warn if no first argument is found 13 | if [ -z "$arg" ]; then 14 | "${namespace}_" # Call empty call placeholder 15 | return 1 16 | fi 17 | 18 | shift 2 # Remove namespace and first argument from $@ 19 | 20 | # Detects if a command, --long or -short option was called 21 | if [ "$arg" = "--$long" ];then 22 | longname="${long%%=*}" # Long argument before the first = sign 23 | 24 | # Detects if the --long=option has = sign 25 | if [ "$long" != "$longname" ]; then 26 | longval="${long#*=}" 27 | long="$longname" 28 | set -- "$longval" "${@:-}" 29 | fi 30 | 31 | main_call=${namespace}_option_${long} 32 | 33 | 34 | elif [ "$arg" = "-$short" ];then 35 | main_call=${namespace}_option_${short} 36 | else 37 | main_call=${namespace}_command_${long} 38 | fi 39 | 40 | $main_call "${@:-}" && dispatch_returned=$? || dispatch_returned=$? 41 | 42 | if [ $dispatch_returned = 127 ]; then 43 | "${namespace}_call_" "$namespace" "$arg" # Empty placeholder 44 | return 1 45 | fi 46 | 47 | return $dispatch_returned 48 | } 49 | -------------------------------------------------------------------------------- /lib/workshop/posit/cli.sh: -------------------------------------------------------------------------------- 1 | # Dispatches commands to other posit_(command|option) functions 2 | posit () ( dispatch posit "${@:-}" ) 3 | 4 | # Displays help 5 | posit_command_help () 6 | { 7 | cat <<-HELP 8 | Usage: posit [option_list...] [command] 9 | posit help, -h, --help [command] Displays help for command. 10 | 11 | Commands: run [path] Runs tests on the specified path. 12 | list [path] Lists the tests on the specified path. 13 | 14 | Options: --report [mode] Changes the output mode. 15 | --shell [shell] Changes the shell used for tests. 16 | --files [pattern] Inclusion pattern for test file lookup 17 | --funcs [pattern] Inclusion pattern for test function lookup 18 | --timeout [seconds] Timeout for each single test 19 | --fast, -f Stops on the first failed test. 20 | --silent, -s Don't collect stacks, just run them. 21 | 22 | Modes: tiny Uses a single line for the results. 23 | spec A complete report with the test names and statuses. 24 | trace The spec report including stack staces of failures. 25 | cov A code coverage report for the tests. 26 | 27 | HELP 28 | } 29 | 30 | # Option handlers 31 | posit_option_help () ( posit_command_help ) 32 | posit_option_h () ( posit_command_help ) 33 | posit_option_f () ( posit_fast="1"; dispatch posit "${@:-}" ) 34 | posit_option_fast () ( posit_fast="1"; dispatch posit "${@:-}" ) 35 | posit_option_s () ( posit_silent="1"; dispatch posit "${@:-}" ) 36 | posit_option_silent () ( posit_silent="1"; dispatch posit "${@:-}" ) 37 | posit_option_shell () ( posit_shell="$1"; shift; dispatch posit "${@:-}" ) 38 | posit_option_files () ( posit_files="$1"; shift; dispatch posit "${@:-}" ) 39 | posit_option_timeout () ( posit_timeout="$1"; shift; dispatch posit "${@:-}" ) 40 | posit_option_report () ( posit_mode="$1"; shift; dispatch posit "${@:-}" ) 41 | posit_option_funcs () 42 | { 43 | posit_functions="$1" 44 | shift 45 | dispatch posit "${@:-}" 46 | } 47 | 48 | posit_ () ( echo "No command provided. Try 'posit --help'";return 1 ) 49 | posit_call_ () ( echo "Call '$*' invalid. Try 'posit --help'"; return 1) 50 | -------------------------------------------------------------------------------- /lib/workshop/posit/cov.sh: -------------------------------------------------------------------------------- 1 | posit_filter_cov () ( echo "echo" ) 2 | posit_exec_cov () ( posit_external "$1" "$2" 2>&1 ) 3 | posit_all_cov () 4 | { 5 | posit_process "$1" | 6 | depur_ignore="$posit_files" depur_command_coverage 7 | } 8 | posit_head_cov () ( : ) 9 | posit_unit_cov () ( echo "$4" ) 10 | posit_count_cov () ( posit_count_spec "$@" ) 11 | -------------------------------------------------------------------------------- /lib/workshop/posit/posit.sh: -------------------------------------------------------------------------------- 1 | # Global option defaults 2 | posit_files=".test.sh" # File name pattern for test files 3 | posit_functions="test_" 4 | posit_mode="tiny" # Reporting mode to be used 5 | posit_shell="sh" # Shell used to tiny the isolated tests 6 | posit_fast="-1" # Fails fast. Use -1 to turn off 7 | posit_silent="-1" # Displays full stacks. Use -1 to turn off 8 | posit_timeout=0 # Timeout for each test 9 | posit_timer='' 10 | posit_tracer='' 11 | 12 | # Lists tests in the specified target path 13 | posit_command_list () 14 | { 15 | ([ -f "$1" ] && posit_listfile "$1") || 16 | ([ -d "$1" ] && posit_listdir "$1") 17 | } 18 | 19 | # Run tests for the specified target path 20 | posit_command_run () 21 | { 22 | posit_command_list "$1" | # Lists all tests for the path 23 | "posit_all_${posit_mode}" "$1" # Processes the tests 24 | } 25 | 26 | # Run tests from a STDIN list 27 | posit_process () 28 | { 29 | mode="$posit_mode" 30 | passed_count=0 31 | total_count=0 32 | last_file="" 33 | skipped_count="0" 34 | # Stores the previous error mode 35 | previous_errmode="$(set +o | grep errexit)" 36 | filter="$(posit_filter_$mode)" 37 | 38 | # If not silent 39 | if [ "$posit_silent" = "-1" ]; then 40 | posit_tracer="$(depur_filter="$filter" "depur_command_tracer"\ 41 | "$posit_shell")" 42 | fi 43 | # If timeout command is present, use it 44 | if [ "$posit_timeout" != 0 ]; then 45 | posit_timer="$(posit_get_timer)" 46 | fi 47 | 48 | # Each line should have a file and a test function on that file 49 | while read test_parameters; do 50 | test_file="$(echo "$test_parameters" | cut -d " " -f1)" 51 | test_func="$(echo "$test_parameters" | cut -d " " -f2)" 52 | total_count=$((total_count+1)) 53 | results='' # Resets the results variable 54 | skipped_return=3 55 | 56 | # Detects when tests should skip 57 | if [ "$skipped_count" -gt "0" ]; then 58 | skipped_count=$((skipped_count+1)) 59 | "posit_unit_$mode" "$test_file" "$test_func"\ 60 | "$skipped_return" "$results" 61 | continue 62 | fi 63 | 64 | # Don't exit on errors 65 | set +e 66 | # Runs a test and stores results 67 | results="$(: | "posit_exec_$mode" "$test_file" "$test_func")" 68 | # Stores the returned code 69 | returned=$? 70 | # Restore previous error mode 71 | $previous_errmode 72 | 73 | # Displays a header when the file changes 74 | [ "$test_file" != "$last_file" ] && 75 | "posit_head_$mode" "$test_file" 76 | 77 | # Run the customized report 78 | "posit_unit_$mode" "$test_file" "$test_func"\ 79 | "$returned" "$results" 80 | 81 | if [ $returned = 0 ]; then 82 | passed_count=$((passed_count+1)) 83 | elif [ "$posit_fast" = "1" ];then 84 | # Starts skipping if fail fast was enabled 85 | skipped_count=1 86 | fi 87 | 88 | last_file="$test_file" 89 | done 90 | 91 | # Display results counter 92 | "posit_count_$mode" $passed_count $total_count $skipped_count 93 | 94 | if [ "$passed_count" != "$total_count" ]; then 95 | return 1 96 | fi 97 | } 98 | 99 | posit_get_timer () 100 | { 101 | if [ ! -z "$posit_timer" ]; then 102 | echo "$posit_timer" 103 | return 104 | fi 105 | 106 | if command -v timeout >/dev/null 2>&1; then 107 | posit_timer="timeout $posit_timeout" 108 | fi 109 | 110 | echo "$posit_timer" 111 | } 112 | 113 | # Executes a file on a function using an external shell process 114 | posit_external () 115 | { 116 | test_file="$1" 117 | test_func="$2" 118 | test_dir="$(dirname "$1")" 119 | shell="$posit_shell" 120 | test_command="$posit_timer $posit_shell" 121 | 122 | # If not silent 123 | if [ "$posit_silent" = "-1" ]; then 124 | test_command="$test_command -x" 125 | fi 126 | 127 | # Declares env variables and executes the test in 128 | # another environment. 129 | PS4="$posit_tracer" \ 130 | POSIT_CMD="$shell" \ 131 | POSIT_FILE="$test_file" \ 132 | POSIT_DIR="$test_dir" \ 133 | POSIT_FUNCTION="$test_func" \ 134 | $test_command <<-EXTERNAL 135 | # Compat options for zsh 136 | setopt PROMPT_SUBST SH_WORD_SPLIT >/dev/null 2>&1 || : 137 | 138 | setup () ( : ) # Placeholder setup function 139 | teardown () ( : ) # Placeholder teardown function 140 | . "\$POSIT_FILE" # Loads the tested file 141 | setup # Calls the setup function 142 | \$POSIT_FUNCTION && # Calls the tested function 143 | has_passed=\$? || # Stores the result from the test 144 | has_passed=\$? 145 | teardown # Calls the teardown function 146 | exit \$has_passed # Exits with the test status 147 | EXTERNAL 148 | } 149 | 150 | # Lists test functions for a specified dir 151 | posit_listdir () 152 | { 153 | target_dir="$1" 154 | 155 | find "$target_dir" -type f | 156 | grep "$posit_files" | 157 | while read test_file; do 158 | posit_listfile "$test_file" 159 | done 160 | } 161 | 162 | # Lists test functions in a single file 163 | posit_listfile () 164 | { 165 | target_file="$1" 166 | signature="/^\(${posit_functions}[a-zA-Z0-9_]*\)[ ]*/p" 167 | 168 | cat "$target_file" | 169 | sed -n "$signature" | 170 | cut -d" " -f1 | 171 | while read line; do 172 | echo "$target_file $line" 173 | done 174 | } 175 | 176 | -------------------------------------------------------------------------------- /lib/workshop/posit/spec.sh: -------------------------------------------------------------------------------- 1 | posit_filter_spec () ( echo "basename" ) 2 | # Executes a single test 3 | posit_exec_spec () ( posit_external "$1" "$2" 2>&1 || false ) 4 | posit_count_spec () ( echo ""; printf %s "Totals:"; posit_count_tiny "$@" ) 5 | posit_all_spec () ( posit_process "$1" ) 6 | # Reports a test file 7 | posit_head_spec () 8 | { 9 | cat <<-FILEHEADER 10 | 11 | ### $1 12 | 13 | FILEHEADER 14 | } 15 | # Reports a single unit 16 | posit_unit_spec () 17 | { 18 | test_function="$2" 19 | test_returned="$3" 20 | results="$4" 21 | test_status="fail:" 22 | 23 | if [ "$test_returned" = "0" ]; then 24 | test_status="pass:" 25 | elif [ "$test_returned" = "3" ]; then 26 | test_status="skip:" 27 | else 28 | test_returned=1 29 | fi 30 | 31 | # Removes the 'test_' from the start of the name 32 | test_function="${test_function#$posit_functions}" 33 | 34 | # Displays the test status and humanized test name 35 | # replacing _ to spaces 36 | echo " - $test_status $test_function" | tr '_' ' ' 37 | 38 | # Formats a stack trace with the test results 39 | if [ $test_returned = 1 ] && [ "$posit_silent" = "-1" ]; then 40 | echo "$results" | depur_command_format 41 | fi 42 | } 43 | 44 | -------------------------------------------------------------------------------- /lib/workshop/posit/tiny.sh: -------------------------------------------------------------------------------- 1 | posit_filter_tiny () ( echo "basename" ) 2 | # Single execution for the "tiny" report 3 | posit_exec_tiny () ( posit_external "$1" "$2" 2>/dev/null ) 4 | # Filter for the overall test output on mode "tiny" 5 | posit_all_tiny () ( posit_process "$1" ) 6 | # Header for each file report on mode "tiny" 7 | posit_head_tiny () ( : ) 8 | # Report for each unit on mode "tiny" 9 | posit_unit_tiny () 10 | { 11 | returned_code="$3" 12 | 13 | ([ "$returned_code" = 0 ] && printf %s "." ) || # . for pass 14 | ([ "$returned_code" = 3 ] && printf %s "S" ) || # S for skip 15 | printf %s "F" # F for failure 16 | } 17 | # Count report for the "tiny" mode 18 | posit_count_tiny () 19 | { 20 | passed="$1" 21 | total="$2" 22 | skipped="$3" 23 | 24 | if [ "$total" -gt 0 ]; then 25 | printf %s " $passed/$total passed." 26 | else 27 | printf %s "No tests found." 28 | fi 29 | 30 | if [ "$skipped" -gt 0 ]; then 31 | printf %s " $skipped/$total skipped." 32 | fi 33 | 34 | echo "" 35 | } 36 | 37 | -------------------------------------------------------------------------------- /lib/workshop/trix/cli.sh: -------------------------------------------------------------------------------- 1 | 2 | # Dispatches commands to other trix_ functions 3 | trix () ( dispatch trix "${@:-}" ) 4 | 5 | # Provides help 6 | trix_command_help () 7 | { 8 | cat <<-HELP 9 | Usage: trix [option_list...] [command] 10 | trix help, -h, --help [command] Displays help for command. 11 | 12 | Commands: run [file] Runs the target matrix file 13 | list [file] Lists all tested environments 14 | 15 | Options: --env [name] Runs only the selected environment 16 | --matrix [name] Runs only the selected matrix 17 | --steps [name] Runs only the selected steps 18 | 19 | Steps: setup Runs before the actual environment starts 20 | script The actual script for the env 21 | clean Runs after the environment has completed its job 22 | 23 | HELP 24 | } 25 | 26 | trix_option_help () ( trix_command_help ) 27 | trix_option_h () ( trix_command_help ) 28 | trix_option_env () ( trix_env_filter="$1"; shift; dispatch trix "${@:-}" ) 29 | trix_option_steps () ( trix_steps="$1"; shift; dispatch trix "${@:-}" ) 30 | trix_option_matrix () 31 | { 32 | trix_matrix_filter="$1" 33 | shift 34 | dispatch trix "${@:-}" 35 | } 36 | 37 | trix_ () ( echo "No command provided. Try 'trix --help'"; return 1 ) 38 | trix_call_ () ( echo "Call '$*' invalid. Try 'trix --help'"; return 1 ) 39 | 40 | 41 | -------------------------------------------------------------------------------- /lib/workshop/trix/trix.sh: -------------------------------------------------------------------------------- 1 | # Global Options 2 | trix_matrix_functions="matrix_" 3 | trix_env_functions="env_" 4 | trix_env_filter=".*" 5 | trix_matrix_filter=".*" 6 | trix_steps='setup script clean' 7 | 8 | trix_command_run () 9 | { 10 | target_file="$1" 11 | environments="$(trix_probe_env "$target_file")" 12 | 13 | trix_probe_matrix "$target_file" | trix_iterate_matrix 14 | } 15 | 16 | trix_iterate_matrix () 17 | { 18 | run_passed=0 19 | 20 | while read matrix_entry; do 21 | trix_process "$target_file" "$matrix_entry" 22 | 23 | if [ $? != 0 ]; then 24 | run_passed=1 25 | fi 26 | done 27 | 28 | return $run_passed 29 | } 30 | 31 | trix_command_list () 32 | { 33 | target_file="$1" 34 | environments="$(trix_probe_env "$target_file")" 35 | 36 | trix_probe_matrix "$target_file" | 37 | while read matrix_entry; do 38 | 39 | . "$target_file" 40 | 41 | include () ( trix_spawn "run" "$environments" "$@" ) 42 | exclude () ( trix_spawn "skip" "$environments" "$@" ) 43 | 44 | $matrix_entry | sort | uniq 45 | 46 | done 47 | } 48 | 49 | trix_command_travis () 50 | { 51 | target_file="$1" 52 | environments="$(trix_probe_env "$target_file")" 53 | 54 | trix_probe_matrix "$target_file" | 55 | while read matrix_entry; do 56 | command_options="--matrix $matrix_entry --env \"\$TRIX_ENV\"" 57 | 58 | cat <<-TRAVISYML 59 | # A courtesy of trix, a Mosai Workshop tool. 60 | # Generated from the $matrix_entry on $target_file 61 | 62 | install: 63 | - $0 --steps="setup" $command_options run $target_file 64 | script: 65 | - $0 --steps="script" $command_options run $target_file 66 | after_script: 67 | - $0 --steps="clean" $command_options run $target_file 68 | matrix: 69 | include: 70 | TRAVISYML 71 | 72 | . "$target_file" 73 | 74 | include () ( trix_spawn "" "$environments" "$@" ) 75 | exclude () ( trix_spawn "" "$environments" "$@" ) 76 | 77 | $matrix_entry | sort | uniq | 78 | while read entry; do 79 | trix_travis_entry "$entry" 80 | done 81 | 82 | break 83 | done 84 | } 85 | 86 | trix_travis_entry () 87 | { 88 | entry="$1" 89 | os="" 90 | vars_line="" 91 | 92 | 93 | var () ( trix_parsevar "" "$@" ) 94 | 95 | for env_entry in $entry; do 96 | vars="$(${env_entry})" 97 | is_os_var="$(echo "$vars" | sed -n /TRAVIS_/p | wc -l)" 98 | 99 | if [ $is_os_var != 0 ]; then 100 | os="$(echo "$vars" | sed 's/TRAVIS_OS=//;s/"//g')" 101 | else 102 | vars_line="${vars_line}${vars}" 103 | fi 104 | 105 | done 106 | 107 | echo " # Result environment:$vars_line" 108 | echo " - env: TRIX_ENV=\"$entry\"" 109 | echo " os: $os" 110 | echo "" 111 | } 112 | 113 | trix_spawn () 114 | { 115 | mode="$1" 116 | environments="$2" 117 | spawned="" 118 | shift 2 119 | 120 | for spawn_entry in $@; do 121 | grouped="$(echo "$environments" | grep "$spawn_entry")" 122 | if [ -z "$spawned" ]; then 123 | spawned="$grouped" 124 | else 125 | spawned="$(trix_spawn_merge "$spawned" $grouped)" 126 | fi 127 | done 128 | 129 | echo "$spawned" | 130 | while read full_entry; do 131 | echo "$mode $full_entry" 132 | done | 133 | sed -n "/$trix_env_filter/p" 134 | } 135 | 136 | trix_spawn_merge () 137 | { 138 | spawned="$1" 139 | shift 140 | 141 | echo "$spawned" | 142 | while read spawned_line; do 143 | for grouped_entry in $@; do 144 | echo "$spawned_line $grouped_entry" 145 | done 146 | done 147 | } 148 | 149 | trix_process () 150 | { 151 | target_file="$1" 152 | matrix_entry="$2" 153 | 154 | . $target_file 155 | 156 | include () ( trix_spawn "+" "$environments" "$@" ) 157 | exclude () ( trix_spawn "-" "$environments" "$@" ) 158 | 159 | entries="$($matrix_entry | sort | uniq)" 160 | excluded="$(echo "$entries" | grep "^- " | sed "s/^[-] //")" 161 | all_entries="$(echo "$entries" | sed "s/^[-+] //")" 162 | 163 | echo "$all_entries" | 164 | trix_iterate_entries "$matrix_entry" "$excluded" 165 | } 166 | 167 | trix_iterate_entries () 168 | { 169 | matrix_entry="$1" 170 | excluded="$2" 171 | success_count=0 172 | total_count=0 173 | 174 | while read entry; do 175 | total_count=$((total_count+1)) 176 | is_excluded="$(echo "$excluded" | sed -n "/$entry/p" | wc -l)" 177 | 178 | if [ $is_excluded -gt 0 ]; then 179 | continue 180 | fi 181 | 182 | previous_errmode="$(set +o | grep errexit)" 183 | set +e 184 | 185 | trix_process_entry "$matrix_entry" "$entry" 186 | 187 | if [ $? = 0 ]; then 188 | success_count=$((success_count+1)) 189 | fi 190 | 191 | # Restore previous error mode 192 | $previous_errmode 193 | done 194 | 195 | printf \\n%s\\n "Totals: $success_count/$total_count envs passed." 196 | 197 | if [ $total_count != $success_count ]; then 198 | return 1 199 | fi 200 | } 201 | 202 | trix_process_entry () 203 | { 204 | matrix_entry="$1" 205 | TRIX_ENV="$2" 206 | script_passed=0 207 | 208 | include () ( : ) 209 | exclude () ( : ) 210 | setup () ( : ) 211 | script () ( : ) 212 | clean () ( : ) 213 | var () ( trix_parsevar "export " "$@" ) 214 | 215 | for env_setting in $TRIX_ENV; do 216 | eval "$($env_setting)" 217 | done 218 | 219 | $matrix_entry 220 | 221 | case $trix_steps in *"setup"* ) : | setup 1>&2 ;; esac 222 | 223 | case $trix_steps in *"script"* ) 224 | : | script 225 | script_passed=$? 226 | ;; 227 | esac 228 | 229 | case $trix_steps in *"clean"* ) : | clean 1>&2 ;; esac 230 | 231 | unset -f include 232 | unset -f exclude 233 | unset -f setup 234 | unset -f script 235 | unset -f clean 236 | 237 | return $script_passed 238 | } 239 | 240 | trix_parsevar () 241 | { 242 | prefix="$1" 243 | shift 244 | parsedvar="${prefix} " 245 | add_quotes="s/\(^[a-zA-Z0-9_]*=\)\(.*\)$/\1\"\2\" /" 246 | 247 | if [ $# = 0 ]; then 248 | return 249 | fi 250 | 251 | while [ $# -gt 0 ]; do 252 | piece="$(printf %s "$1" | sed "$add_quotes")" 253 | parsedvar="${parsedvar}${piece}" 254 | shift 255 | done 256 | 257 | echo "$parsedvar" 258 | } 259 | 260 | trix_probe () 261 | { 262 | identifier="$1" 263 | target_file="$2" 264 | signature="/^\(\(${identifier}\)[a-zA-Z0-9_]*\)[ ]*/p" 265 | 266 | cat "$target_file" | 267 | sed -n "$signature" | 268 | cut -d" " -f1 | 269 | while read line; do 270 | echo "$line" 271 | done 272 | } 273 | 274 | trix_probe_env () 275 | { 276 | trix_probe "$trix_env_functions" "$@" 277 | } 278 | 279 | trix_probe_matrix () 280 | { 281 | trix_probe "$trix_matrix_functions" "$@" | 282 | sed -n "/$trix_matrix_filter/p" 283 | } 284 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | Testing Mosai Workshop 2 | ---------------------- 3 | 4 | Workshop is tested by its own testing tool `posit`. Although 5 | the tool itself doesn't force any particular directory structure, 6 | we keep an in-house one: 7 | 8 | ```sh 9 | test/ # Main test directory 10 | └── matrix.sh # Test matrix used by `trix` 11 | └── [tool folder]/ # Each tool has its own subdir 12 | └── resources/ # Resources needed for the tests 13 | └── unit.test.sh # The main library unit tests 14 | └── [...].test.sh # Other tests 15 | 16 | └── [tool folder...]/ # Same for each tool 17 | └── ... 18 | ``` 19 | -------------------------------------------------------------------------------- /test/depur/unit.test.sh: -------------------------------------------------------------------------------- 1 | setup () 2 | { 3 | lib_path="$POSIT_DIR/../../lib/workshop" 4 | 5 | . "$lib_path/common.sh" 6 | . "$lib_path/depur/depur.sh" 7 | } 8 | 9 | test_depur_realpath_should_solve_relative_file_paths_to_absolute_ones () 10 | { 11 | real_file="/usr/bin/env" 12 | relative_file="/usr/bin/../bin/env" 13 | 14 | output="$(depur_realpath "$relative_file")" 15 | 16 | [ "$output" = "$real_file" ] 17 | } 18 | 19 | test_depur_clean_should_extract_files_and_lines_from_a_stack () 20 | { 21 | # Mocks the depur_realpath function to prevent filesystem interaction 22 | depur_realpath () ( echo "$1" ) 23 | 24 | # Stubs a stack trace 25 | stack_stub () 26 | { 27 | cat <<-STUBBED_STACK_TRACE 28 | This line should be removed. All Stack lines start 29 | with a +[TAB]. 30 | + /usr/bin/env:4 some command 31 | + /usr/bin/env:9 some command 32 | 33 | Misc non-stack lines can appear at any point 34 | 35 | The line below is a stack line without a file, it 36 | should also be removed: 37 | + :0 some_command 38 | 39 | + /usr/bin/env:9 some command 40 | + /etc/passwd:9 some command 41 | STUBBED_STACK_TRACE 42 | } 43 | 44 | # Prints out the expected stack trace used for assertion 45 | expected_stack () 46 | { 47 | cat <<-EXPECTED_CLEAN_STACK 48 | /usr/bin/env 4 49 | /usr/bin/env 9 50 | /usr/bin/env 9 51 | /etc/passwd 9 52 | EXPECTED_CLEAN_STACK 53 | } 54 | 55 | expected="$(expected_stack | cat)" 56 | output="$(stack_stub | depur_clean)" 57 | 58 | [ "$output" = "$expected" ] 59 | } 60 | 61 | test_depur_profile_should_count_and_order_line_executions () 62 | { 63 | resource_prefix="+ $POSIT_DIR/../posit/resources/posit_post" 64 | # Mocks the depur_realpath function to prevent filesystem interaction 65 | depur_realpath () ( echo "$1" ) 66 | 67 | # Stubs a stack trace 68 | stack_stub () 69 | { 70 | cat <<-STUBBED_STACK_TRACE 71 | ${resource_prefix}cov.fixture.sh:4 some command 4cov 72 | ${resource_prefix}cov.fixture.sh:4 some command 4cov 73 | ${resource_prefix}cov.fixture.sh:9 some command 9cov 74 | ${resource_prefix}cov.fixture.sh:9 some command 9cov 75 | ${resource_prefix}cov.fixture.sh:9 some command 9cov 76 | ${resource_prefix}cov2.fixture.sh:9 some command 9cov2 77 | STUBBED_STACK_TRACE 78 | } 79 | 80 | # Prints out the expected stack trace used for assertion 81 | expected_stack () 82 | { 83 | cat <<-EXPECTED_PROFILE_INFO 84 | 3 posit_postcov.fixture.sh:9 true 85 | 2 posit_postcov.fixture.sh:4 true 86 | 1 posit_postcov2.fixture.sh:9 true 87 | EXPECTED_PROFILE_INFO 88 | } 89 | 90 | expected="$(expected_stack | cat)" 91 | output="$(stack_stub | depur_command_profile)" 92 | 93 | [ "$output" = "$expected" ] 94 | } 95 | 96 | 97 | # This is a sort of functional test, it should be rewritten as unit. 98 | test_depur_coverage_should_count_lines_from_a_clean_stack () 99 | { 100 | # Gets the resources directory for this test suite 101 | resources_dir="$POSIT_DIR/../posit/resources" 102 | 103 | # Mocks the interal call to depur_clean to do nothing but output 104 | depur_clean () ( cat ) 105 | 106 | # Stubs an output that depur_clean should actually do 107 | # using resources from the test folder 108 | clean_stub () 109 | { 110 | cat <<-STUBBED_STACK_TRACE 111 | $resources_dir/posit_postcov.fixture.sh 4 112 | $resources_dir/posit_postcov.fixture.sh 9 113 | $resources_dir/posit_postcov.fixture.sh 9 114 | $resources_dir/posit_postcov2.fixture.sh 9 115 | STUBBED_STACK_TRACE 116 | } 117 | 118 | check () 119 | { 120 | output="$(cat)" 121 | skipped_lines="$(echo "$output" | grep "^> \`-\`" | wc -l)" 122 | zeroed_lines="$(echo "$output" | grep "^> \`0\` " | wc -l)" 123 | covered="$(echo "$output" | grep "^> \`1\` " | wc -l)" 124 | doubled="$(echo "$output" | grep "^> \`2\` " | wc -l)" 125 | 126 | # Variables are unquoted to avoid wc whitespace output 127 | [ $skipped_lines = 16 ] && 128 | [ $zeroed_lines = 1 ] && 129 | [ $covered = 2 ] && 130 | [ $doubled = 1 ] 131 | 132 | exit $? 133 | } 134 | 135 | clean_stub | depur_command_coverage | check 136 | } 137 | 138 | test_depur_run_should_invoke_a_shell_with_stack_settings () 139 | { 140 | # Depur uses this variable to look for the shell 141 | depur_shell="shell_mock" 142 | 143 | # Mocks the shell to just print what its arguments and the 144 | # mocked PS4 145 | shell_mock () ( echo "$@ $PS4" ) 146 | 147 | # Mocks the function tracer with fake PS4 information 148 | depur_command_tracer () ( echo "! PS4_MOCK" ) 149 | 150 | oldPS4="$PS4" 151 | PS4='' # Disables the real posit PS4 152 | set +x # Disables the real posit debugging 153 | output="$(depur_command_run "some_file.sh")" 154 | set -x # Re-enables 155 | PS4="$oldPS4" 156 | 157 | # Now we can expect a clean call without posit interference 158 | expected="-x some_file.sh ! PS4_MOCK" 159 | 160 | [ "$output" = "$expected" ] 161 | } 162 | 163 | test_depur_command_tracer_should_get_a_tracer_if_none_is_set () 164 | { 165 | # Mocks the function that generates the tracer 166 | depur_get_tracer () ( echo "tracer mock" ) 167 | 168 | # Keeps the old trace command (from posit) 169 | old_depur_trace_command="$depur_trace_command" 170 | 171 | # Cleans, so the tracer will be called 172 | export depur_trace_command='' 173 | 174 | # Calls the command_tracer twice 175 | output="$(depur_command_tracer "foo")" 176 | 177 | # Restores the posit tracer 178 | depur_trace_command="$old_depur_trace_command" 179 | 180 | [ "$output" = "tracer mock" ] 181 | } 182 | 183 | 184 | test_depur_command_tracer_should_reuse_a_previously_set_tracer () 185 | { 186 | # Mocks the function that generates the tracer 187 | depur_get_tracer () ( echo "fail! this should not be called" ) 188 | 189 | # Keeps the old trace command (from posit) 190 | old_depur_trace_command="$depur_trace_command" 191 | 192 | # Cleans, so the tracer will be called 193 | export depur_trace_command='tracer mock' 194 | 195 | # Calls the command_tracer twice 196 | output="$(depur_command_tracer "foo")" 197 | 198 | # Restores the posit tracer 199 | depur_trace_command="$old_depur_trace_command" 200 | 201 | [ "$output" = "tracer mock" ] 202 | } 203 | 204 | test_depur_format_should_print_formatted_columns () 205 | { 206 | input="+ /s:4 some command" 207 | expected=" + /s:4 some command " 208 | output="$(echo "$input" | depur_command_format)" 209 | 210 | [ "$output" = "$expected" ] 211 | } 212 | -------------------------------------------------------------------------------- /test/dispatch/unit.test.sh: -------------------------------------------------------------------------------- 1 | setup () 2 | { 3 | lib_path="$POSIT_DIR/../../lib/workshop" 4 | 5 | . "$lib_path/common.sh" 6 | . "$lib_path/dispatch.sh" 7 | } 8 | 9 | test_dispatch_with_empty_placeholder () 10 | { 11 | expected_string="Called empty placeholder" 12 | 13 | example () ( dispatch example "${@:-}" ) 14 | example_ () ( echo "$expected_string" ) 15 | 16 | OLDPS4="$PS4" # Prevent debugger from changing the output on MinGW 17 | set +e # We care only about the output on this test 18 | help_call="$(: | dispatch example)" 19 | set -e 20 | PS4="$OLDPS4" 21 | 22 | [ "$expected_string" = "$help_call" ] 23 | } 24 | 25 | test_dispatch_with_call_placeholder () 26 | { 27 | expected_string="Called empty command placeholder" 28 | 29 | example () ( dispatch example "$@" ) 30 | example_call_ () ( echo "$expected_string $@" ) 31 | 32 | OLDPS4="$PS4" # Prevent debugger from changing the output on MinGW 33 | set +e # We care only about the output on this test 34 | help_call="$(: | dispatch example foaks)" 35 | set -e 36 | PS4="$OLDPS4" 37 | 38 | [ "$expected_string example foaks" = "$help_call" ] 39 | } 40 | 41 | test_dispatch_command () 42 | { 43 | expected_string="Called command" 44 | 45 | example () ( dispatch example "$@" ) 46 | example_command_foo () ( echo "$expected_string $@") 47 | 48 | OLDPS4="$PS4" # Prevent debugger from changing the output on MinGW 49 | set +e # We care only about the output on this test 50 | command_call="$(: | dispatch example foo bar baz)" 51 | set -e 52 | PS4="$OLDPS4" 53 | 54 | [ "$expected_string bar baz" = "$command_call" ] 55 | } 56 | 57 | test_dispatch_option_short () 58 | { 59 | expected_string="Called option" 60 | 61 | example () ( dispatch example "$@" ) 62 | example_option_f () ( echo "$expected_string $@"; shift ) 63 | 64 | short_call="$(: | example -f bar baz)" 65 | 66 | [ "$expected_string bar baz" = "$short_call" ] 67 | } 68 | 69 | test_dispatch_option_short_repassing () 70 | { 71 | expected_string="Called!" 72 | 73 | example () ( dispatch example "$@" ) 74 | example_command_foo () ( printf %s "Command $expected_string" ) 75 | example_option_f () 76 | { 77 | printf %s "Option $expected_string $@" 78 | dispatch example "$@" 79 | } 80 | 81 | short_repassing_call="$(: | example -f foo)" 82 | 83 | [ "Option ${expected_string} fooCommand ${expected_string}" = "$short_repassing_call" ] 84 | } 85 | 86 | test_dispatch_option_long () 87 | { 88 | expected_string="Called option" 89 | 90 | example () ( dispatch example "$@" ) 91 | example_option_fanz () ( echo "$expected_string $@"; shift ) 92 | 93 | long_call="$(: | example --fanz bar baz)" 94 | 95 | [ "$expected_string bar baz" = "$long_call" ] 96 | } 97 | 98 | test_dispatch_option_long_repassing () 99 | { 100 | expected_string="Called!" 101 | 102 | example () ( dispatch example "$@" ) 103 | example_command_foo () ( printf %s "Command $expected_string $@" ) 104 | example_option_fanz () 105 | { 106 | printf %s "Option $expected_string $@" 107 | dispatch example "$@" 108 | } 109 | 110 | short_repassing_call="$(: | example --fanz foo)" 111 | 112 | [ "Option ${expected_string} fooCommand ${expected_string} " = "$short_repassing_call" ] 113 | } 114 | 115 | 116 | test_dispatch_option_long_with_equal_sign () 117 | { 118 | expected_string="Called option" 119 | 120 | example () ( dispatch example "$@" ) 121 | example_option_fanz () ( echo "$expected_string $@"; shift ) 122 | 123 | long_call="$(: | example --fanz=bar baz)" 124 | 125 | [ "$expected_string bar baz" = "$long_call" ] 126 | } 127 | 128 | 129 | test_dispatch_option_long_with_equal_sign_and_quotes () 130 | { 131 | expected_string="Called option" 132 | 133 | example () ( dispatch example "$@" ) 134 | example_option_fanz () ( echo "$expected_string $@"; shift ) 135 | 136 | long_call="$(: | example --fanz="bar baz")" 137 | 138 | [ "$expected_string bar baz " = "$long_call" ] 139 | } 140 | 141 | 142 | test_dispatch_option_long_with_equal_sign_quotes_and_equal_value () 143 | { 144 | expected_string="Called option" 145 | 146 | example () ( dispatch example "$@" ) 147 | example_option_fanz () ( echo "$expected_string $@"; shift ) 148 | 149 | long_call="$(: | example --fanz="bar=baz")" 150 | 151 | [ "$expected_string bar=baz " = "$long_call" ] 152 | } 153 | 154 | test_dispatch_option_long_repassing_with_equal_sign () 155 | { 156 | expected_string="Called!" 157 | 158 | example () ( dispatch example "$@" ) 159 | example_command_foo () ( printf %s "Command $expected_string $@" ) 160 | example_option_fanz () 161 | { 162 | printf %s "Option $expected_string $@" 163 | shift 164 | dispatch example "$@" 165 | } 166 | 167 | long_repassing_call="$(: | example --fanz="bla bar" foo)" 168 | 169 | [ "Option ${expected_string} bla barfooCommand ${expected_string} " = "$long_repassing_call" ] 170 | } 171 | -------------------------------------------------------------------------------- /test/matrix.sh: -------------------------------------------------------------------------------- 1 | # 2 | # Mosai Workshop Test Matrix 3 | # 4 | 5 | # Main tested shells 6 | env_common_busybox () ( var TARGET_SHELL="busybox sh" SHELL_PKG="busybox" ) 7 | env_common_dash () ( var TARGET_SHELL="dash" SHELL_PKG="dash" ) 8 | env_common_bash () ( var TARGET_SHELL="bash" SHELL_PKG="bash" ) 9 | env_shell_zsh () ( var TARGET_SHELL="zsh" SHELL_PKG="zsh" ) 10 | env_shell_ksh () ( var TARGET_SHELL="ksh" SHELL_PKG="ksh" ) 11 | env_shell_pdksh () ( var TARGET_SHELL="pdksh" SHELL_PKG="pdksh" ) 12 | env_shell_mksh () ( var TARGET_SHELL="mksh" SHELL_PKG="mksh" ) 13 | env_shell_yash () ( var TARGET_SHELL="yash" SHELL_PKG="yash" ) 14 | env_shell_posh () ( var TARGET_SHELL="posh" SHELL_PKG="posh" ) 15 | 16 | # Extra shells for Ubuntu versions higher than 11. 17 | env_extras_bash2_0 () ( var TARGET_SHELL="bash2.05b" SHELL_PKG="bash2.05b"\ 18 | PPA_REQUIRED="agriffis/bashes precise F5751EC8" ) 19 | env_extras_bash3_0 () ( var TARGET_SHELL="bash3.0.16" SHELL_PKG="bash3.0.16"\ 20 | PPA_REQUIRED="agriffis/bashes precise F5751EC8" ) 21 | env_extras_bash3_2 () ( var TARGET_SHELL="bash3.2.48" SHELL_PKG="bash3.2.48"\ 22 | PPA_REQUIRED="agriffis/bashes precise F5751EC8" ) 23 | env_extras_bash4_2 () ( var TARGET_SHELL="bash4.2.45" SHELL_PKG="bash4.2.45"\ 24 | PPA_REQUIRED="agriffis/bashes precise F5751EC8" ) 25 | env_extras_zsh_beta () ( var TARGET_SHELL="zsh-beta" SHELL_PKG="zsh-beta" ) 26 | 27 | # Environments for Travis CI 28 | env_travis_linux () ( var TRAVIS_OS="linux" ) 29 | env_travis_osx () ( var TRAVIS_OS="osx" ) 30 | 31 | # deb/rpm environment machines for vagrant 32 | env_deb_precise64 () ( var VAGRANT_MACHINE="precise64" ) 33 | env_deb_trusty64 () ( var VAGRANT_MACHINE="trusty64" ) 34 | env_deb_debian7464 () ( var VAGRANT_MACHINE="debian7464" ) 35 | env_deb_debian6064 () ( var VAGRANT_MACHINE="debian6064" ) 36 | env_rpm_fedora2064 () ( var VAGRANT_MACHINE="fedora2064" ) 37 | env_rpm_fedora1964 () ( var VAGRANT_MACHINE="fedora1964" ) 38 | env_rpm_centos6464 () ( var VAGRANT_MACHINE="centos6464" ) 39 | 40 | 41 | # Local Test Matrix 42 | matrix_local () 43 | { 44 | setup () 45 | { 46 | provision_chooser 47 | shell_version "$TARGET_SHELL" "$TRIX_ENV" 48 | } 49 | 50 | script () 51 | { 52 | $TARGET_SHELL bin/posit --shell "$TARGET_SHELL"\ 53 | --silent --fast\ 54 | --report tiny run "test/" 55 | } 56 | 57 | include "common_*" 58 | include "shell_*" 59 | include "extras_*" 60 | 61 | } 62 | # Runs the local matrix on remote machines 63 | # Faster than matrix_virtual but not as isolated 64 | matrix_remote () 65 | { 66 | setup () ( vagrant up "$VAGRANT_MACHINE" ) 67 | clean () ( vagrant halt "$VAGRANT_MACHINE" ) 68 | script () 69 | { 70 | trix_cmd="bin/trix --matrix=local run test/matrix.sh" 71 | 72 | vagrant ssh "$VAGRANT_MACHINE" -c "cd /vagrant; $trix_cmd" 73 | } 74 | 75 | include "deb_*" 76 | include "rpm_*" 77 | } 78 | 79 | # Virtual Test Matrix 80 | matrix_virtual () 81 | { 82 | setup () 83 | { 84 | vagrant up "$VAGRANT_MACHINE" 85 | provision_chooser 86 | } 87 | clean () ( vagrant halt "$VAGRANT_MACHINE" ) 88 | script () 89 | { 90 | posit_opt=" --shell \"$TARGET_SHELL\" --report spec" 91 | posit_cmd="$TARGET_SHELL bin/posit $posit_opt run test/" 92 | 93 | vagrant ssh "$VAGRANT_MACHINE" -c "cd /vagrant; $posit_cmd" 94 | } 95 | 96 | include "deb_*" "common_*" 97 | include "deb_*" "shell_*" 98 | 99 | include "rpm_*" "common_*" 100 | include "rpm_*" shell_zsh 101 | include "rpm_*" shell_ksh 102 | } 103 | 104 | # Travis Matrix 105 | matrix_travis () 106 | { 107 | setup () 108 | { 109 | echo "Setting up build for '$TARGET_SHELL' on '$TRAVIS_OS'..." 110 | 111 | provision_chooser 112 | shell_version "$TARGET_SHELL" "$TRIX_ENV" 113 | } 114 | 115 | script () 116 | { 117 | $TARGET_SHELL bin/posit --shell "$TARGET_SHELL"\ 118 | --report spec run "test/" 119 | } 120 | 121 | include travis_linux "common_*" 122 | include travis_linux "shell_*" 123 | include travis_linux "extras_*" 124 | 125 | include travis_osx shell_ksh 126 | include travis_osx shell_mksh 127 | include travis_osx shell_zsh 128 | include travis_osx common_bash 129 | } 130 | 131 | # 132 | # Provisioning Helper Functions (not actually part of the matrix) 133 | # 134 | 135 | # Chooses if new packages are needed and sets up them 136 | provision_chooser () 137 | { 138 | main_shell_command="$(echo "$TARGET_SHELL" | cut -d" " -f1)" 139 | # No setup if environment has no packages 140 | [ -z "${SHELL_PKG-}" ] && return 141 | 142 | # No setup if shell already present 143 | command -v "$main_shell_command" 2>/dev/null 1>/dev/null && return 144 | 145 | # Install PPA if required 146 | [ ! -z "${PPA_REQUIRED-}" ] && provision_ppa $PPA_REQUIRED 147 | 148 | provision_package "$SHELL_PKG" 149 | } 150 | 151 | # Sets up a single package for apt, yum or brew 152 | provision_package () 153 | { 154 | package="$1" 155 | 156 | # Install packages for debian-based linuxes 157 | command -v apt-get 2>/dev/null 1>/dev/null && provision_apt "$package" 158 | 159 | # Install packages for debian-based linuxes 160 | command -v yum 2>/dev/null 1>/dev/null && provision_yum "$package" 161 | 162 | # Install packages for OS X 163 | command -v brew 2>/dev/null 1>/dev/null && provision_brew "$package" 164 | } 165 | 166 | # Helper function to add a ppa to the apt sources 167 | provision_ppa () 168 | { 169 | ppa_name="$1" 170 | dist="$2" 171 | keys="$3" 172 | keyserver="keyserver.ubuntu.com" 173 | sources_file="/etc/apt/sources.list" 174 | address="http://ppa.launchpad.net/$ppa_name/ubuntu" 175 | has_ppa="$(grep "^deb.*$ppa_name" "$sources_file" | wc -l)" 176 | 177 | [ $has_ppa -gt 0 ] && return 178 | 179 | echo "A PPA is required for this environment. Installing..." 180 | 181 | sudo apt-key adv --keyserver $keyserver --recv-keys "$keys" | 182 | sed 's/^/ > /' 183 | 184 | echo "deb $address $dist main" | sudo tee -a "$sources_file" 185 | } 186 | 187 | # Helper function to install apt packages 188 | provision_apt () 189 | { 190 | pkgspec="$1" 191 | 192 | echo "Packages from apt are required. Installing..." 193 | 194 | sudo apt-get update -qq -y | sed 's/^/ > /' 195 | sudo apt-get install -y $pkgspec | sed 's/^/ > /' 196 | } 197 | 198 | # Helper function to install yum packages 199 | provision_yum () 200 | { 201 | pkgspec="$1" 202 | 203 | echo "Packages from yum are required. Installing..." 204 | 205 | sudo yum check-update | sed 's/^/ > /' 206 | sudo yum install -y $pkgspec | sed 's/^/ > /' 207 | } 208 | 209 | # Helper function to install brew packages 210 | provision_brew () 211 | { 212 | pkgspec="$1" 213 | 214 | echo "Packages from brew are required. Installing..." 215 | 216 | brew update | sed 's/^/ > /' 217 | brew install $pkgspec | sed 's/^/ > /' 218 | } 219 | 220 | # Finds the version for a given shell 221 | shell_version () 222 | { 223 | printf %s "Env '$TRIX_ENV': " 224 | $1 <<-'WHATSHELL' 225 | 226 | : 'This script aims at recognizing all Bourne compatible shells. 227 | Emphasis is on shells without any version variables. 228 | Comments to mascheck@in-ulm.de' 229 | : '$Id: whatshell.sh,v 1.17 2012/04/23 21:59:02 xmascheck Exp xmascheck $' 230 | : 'fixes are tracked on www.in-ulm.de/~mascheck/various/whatshell/' 231 | 232 | LC_ALL=C export LC_ALL 233 | : 'trivial cases first, yet parseable for historic shells' 234 | case $BASH_VERSION in *.*) { echo "bash $BASH_VERSION";exit;};;esac 235 | case $ZSH_VERSION in *.*) { echo "zsh $ZSH_VERSION";exit;};;esac 236 | case "$VERSION" in *zsh*) { echo "$VERSION";exit;};;esac 237 | case "$SH_VERSION" in *PD*|*MIRBSD*) { echo "$SH_VERSION";exit;};;esac 238 | case "$KSH_VERSION" in *PD*|*MIRBSD*) { echo "$KSH_VERSION";exit;};;esac 239 | case "$POSH_VERSION" in 0.[1234]*) \ 240 | { echo "posh $POSH_VERSION, possibly slightly newer, yet<0.5";exit;} 241 | ;; *.*|*POSH*) { echo "posh $POSH_VERSION";exit;};; esac 242 | case $YASH_VERSION in *.*) { echo "yash $YASH_VERSION";exit;};;esac 243 | 244 | myex(){ echo "$@";exit;} # "exec echo" might call the external command 245 | 246 | # Almquist shell aka ash 247 | (typeset -i var) 2>/dev/null || { 248 | case $SHELLVERS in "ash 0.2") myex 'original ash';;esac 249 | test "$1" = "debug" && debug=1 250 | n=1; case `(! :) 2>&1` in *not*) n=0;;esac 251 | b=1; case `echo \`:\` ` in '`:`') b=0;;esac 252 | g=0; { set -- -x; getopts x: var 253 | case $OPTIND in 2) g=1;;esac;} >/dev/null 2>&1 254 | p=0; (eval ': ${var#value}') 2>/dev/null && p=1 255 | r=0; ( (read/dev/null; case $? in 0|1|2) 256 | var=`(read&1`; case $var in *arg*) r=1;;esac 257 | ;;esac 258 | v=1; set x; case $10 in x0) v=0;;esac 259 | t=0; (PATH=;type :) >/dev/null 2>&1 && t=1 260 | test -z "$debug" || echo debug '$n$b$g$p$r$v$t: ' $n$b$g$p$r$v$t 261 | case $n$b$g$p$r$v$t in 262 | 00*) myex 'early ash (4.3BSD, 386BSD 0.0-p0.2.3/NetBSD 0.8)' 263 | ;; 010*) myex 'early ash (ash-0.2 port, Slackware 2.1-8.0,'\ 264 | '386BSD p0.2.4, NetBSD 0.9)' 265 | ;; 1110100) myex 'early ash (Minix 2.x-3.1.2)' 266 | ;; 1000000) myex 'early ash (4.4BSD Alpha)' 267 | ;; 1100000) myex 'early ash (4.4BSD)' 268 | ;; 11001*) myex 'early ash (4.4BSD Lite, early NetBSD 1.x, BSD/OS 2.x)' 269 | ;; 1101100) myex 'early ash (4.4BSD Lite2, BSD/OS 3 ff)' 270 | ;; 1101101) myex 'ash (FreeBSD, Cygwin pre-1.7, Minix 3.1.3 ff)' 271 | ;; esac 272 | e=0; case `(PATH=;exp 0)2>&1` in 0) e=1;;esac 273 | n=0; case y in [^x]) n=1;;esac 274 | r=1; case `(PATH=;noexist 2>/dev/null) 2>&1` in 275 | *not*) r=0 ;; *file*) r=2 ;;esac 276 | f=0; case `eval 'for i in x;{ echo $i;}' 2>/dev/null` in x) f=1;;esac 277 | test -z "$debug" || echo debug '$e$n$r$a$f: ' $e$n$r$a$f 278 | case $e$n$r$f in 279 | 1100) myex 'ash (dash 0.3.8-30 - 0.4.6)' 280 | ;; 1110) myex 'ash (dash 0.4.7 - 0.4.25)' 281 | ;; 1010) myex 'ash (dash 0.4.26 - 0.5.2)' 282 | ;; 0120|1120|0100) myex 'ash (Busybox 0.x)' 283 | ;; 0110) myex 'ash (Busybox 1.x)' 284 | ;;esac 285 | a=0; case `eval 'x=1;(echo $((x)) )2>/dev/null'` in 1) a=1;;esac 286 | x=0; case `f(){ echo $?;};false;f` in 1) x=1;;esac 287 | c=0; case `echo -e '\x'` in *\\x) c=1;;esac 288 | test -z "$debug" || echo debug '$e$n$r$f$a$x$c: ' $e$n$r$f$a$x$c 289 | case $e$n$r$f$a$x$c in 290 | 1001010) myex 'ash (Slackware 8.1 ff, dash 0.3.7-11 - 0.3.7-14)' 291 | ;; 10010??) myex 'ash (dash 0.3-1 - 0.3.7-10, NetBSD 1.2 - 3.1/4.0)' 292 | ;; 10011*) myex 'ash (NetBSD 3.1/4.0 ff)' 293 | ;; 00101*) myex 'ash (dash 0.5.5.1 ff)' 294 | ;; 00100*) myex 'ash (dash 0.5.3-0.5.5)' 295 | ;; *) myex 'unknown ash' 296 | ;;esac 297 | } 298 | 299 | savedbg=$! # save unused $! for a later check 300 | 301 | # Korn shell ksh93, $KSH_VERSION not implemented before 93t' 302 | # protected: fatal substitution error in non-ksh 303 | ( eval 'test "x${.sh.version}" != x' ) 2>/dev/null & 304 | wait $! && { eval 'myex "ksh93 ${.sh.version}"' ; } 305 | 306 | # Korn shell ksh86/88 307 | _XPG=1;test "`typeset -Z2 x=0; echo $x`" = '00' && { 308 | case `print -- 2>&1` in *"bad option"*) 309 | myex 'ksh86 Version 06/03/86(/a)';; esac 310 | test "$savedbg" = '0'&& myex 'ksh88 Version (..-)11/16/88 (1st release)' 311 | test ${x-"{a}"b} = '{ab}' && myex 'ksh88 Version (..-)11/16/88a' 312 | case "`for i in . .; do echo ${i[@]} ;done 2>&1`" in 313 | "subscript out of range"*) 314 | myex 'ksh88 Version (..-)11/16/88b or c' ;; esac 315 | test "`whence -v true`" = 'true is an exported alias for :' && 316 | myex 'ksh88 Version (..-)11/16/88d' 317 | test "`(cd /dev/null 2>/dev/null; echo $?)`" != '1' && 318 | myex 'ksh88 Version (..-)11/16/88e' 319 | test "`(: $(/dev/null`" = '' && 320 | myex 'ksh88 Version (..-)11/16/88f' 321 | case `([[ "-b" > "-a" ]]) 2>&1` in *"bad number"*) \ 322 | myex 'ksh88 Version (..-)11/16/88g';;esac # fixed in OSR5euc 323 | test "`cd /dev;cd -P ..;pwd 2>&1`" != '/' && 324 | myex 'ksh88 Version (..-)11/16/88g' # fixed in OSR5euc 325 | test "`f(){ typeset REPLY;echo|read;}; echo dummy|read; f; 326 | echo $REPLY`" = "" && myex 'ksh88 Version (..-)11/16/88h' 327 | test $(( 010 )) = 8 && 328 | myex 'ksh88 Version (..-)11/16/88i (posix octal base)' 329 | myex 'ksh88 Version (..-)11/16/88i' 330 | } 331 | 332 | echo 'oh dear, unknown shell' 333 | WHATSHELL 334 | } 335 | -------------------------------------------------------------------------------- /test/posit/resources/posit_postcov.fixture.sh: -------------------------------------------------------------------------------- 1 | 2 | test_should_always_pass () 3 | { 4 | true 5 | } 6 | 7 | test_should_always_pass2 () 8 | { 9 | true 10 | } 11 | -------------------------------------------------------------------------------- /test/posit/resources/posit_postcov2.fixture.sh: -------------------------------------------------------------------------------- 1 | 2 | test_should_always_pass () 3 | { 4 | true 5 | } 6 | 7 | test_should_always_pass2 () 8 | { 9 | true 10 | } 11 | -------------------------------------------------------------------------------- /test/posit/resources/posit_postcov3.fixture.sh: -------------------------------------------------------------------------------- 1 | 2 | test_should_always_fail () 3 | { 4 | false 5 | } 6 | 7 | test_should_always_fail2 () 8 | { 9 | false 10 | } 11 | -------------------------------------------------------------------------------- /test/posit/unit.test.sh: -------------------------------------------------------------------------------- 1 | setup () 2 | { 3 | lib_path="$POSIT_DIR/../../lib/workshop" 4 | 5 | . "$lib_path/common.sh" 6 | . "$lib_path/posit/cov.sh" 7 | . "$lib_path/posit/spec.sh" 8 | . "$lib_path/posit/tiny.sh" 9 | . "$lib_path/posit/posit.sh" 10 | } 11 | 12 | test_posit_list_using_files () 13 | { 14 | posit_listfile () ( echo "$1 OK" ) 15 | 16 | expected_list="$(posit_command_list /usr/bin/env)" 17 | 18 | [ "$expected_list" = "/usr/bin/env OK" ] 19 | } 20 | 21 | test_posit_list_using_directories () 22 | { 23 | posit_listdir () ( echo "$1 OK" ) 24 | 25 | expected_list="$(posit_command_list /usr)" 26 | 27 | [ "$expected_list" = "/usr OK" ] 28 | } 29 | 30 | test_posit_list_without_parameters () 31 | { 32 | posit_listfile () ( echo "should not be called" ) 33 | posit_listdir () ( echo "should not be called" ) 34 | 35 | expected_list="$(posit_command_list)" 36 | 37 | [ "$expected_list" = "" ] 38 | } 39 | 40 | test_posit_process_with_single_test () 41 | { 42 | posit_file_pattern=".fixture.sh" 43 | file_mock_location="$POSIT_DIR/resources/posit_postcov.fixture.sh test_should_always_pass" 44 | posit_count_mock () ( echo "$1...$2" ) 45 | posit_filter_mock () ( : ) 46 | posit_exec_mock () ( echo "exec mock called" ) 47 | posit_head_mock () ( echo "head_mock called" ) 48 | posit_unit_mock () ( echo "unit_mock called" ) 49 | posit_all_mock () ( cat ) 50 | depur_command_tracer () ( : ) 51 | 52 | check () 53 | { 54 | result="$(cat)" 55 | head_results="$(echo "$result" | grep "^head_mock called")" 56 | unit_results="$(echo "$result" | grep "^unit_mock called")" 57 | last_line="$(echo "$result" | tail -n 1)" 58 | 59 | [ "$head_results" = "head_mock called" ] && 60 | [ "$unit_results" = "unit_mock called" ] && 61 | [ "$last_line" = "1...1" ] 62 | 63 | exit $? 64 | } 65 | 66 | 67 | 68 | echo $file_mock_location | 69 | posit_mode="mock" posit_timer="" posit_process "$POSIT_DIR/resources/" | 70 | check 71 | } 72 | 73 | 74 | test_posit_process_with_multiple_tests () 75 | { 76 | posit_file_pattern=".fixture.sh" 77 | posit_filter_mock () ( : ) 78 | posit_exec_mock () ( echo "exec mock called" ) 79 | posit_head_mock () ( echo "head_mock called" ) 80 | posit_unit_mock () ( echo "unit_mock called" ) 81 | posit_count_mock () ( echo "$1...$2" ) 82 | posit_all_mock () ( cat ) 83 | depur_command_tracer () ( : ) 84 | 85 | mocklist () 86 | { 87 | cat <<-LIST 88 | $POSIT_DIR/resources/posit_postcov.fixture.sh test_should_always_pass 89 | $POSIT_DIR/resources/posit_postcov.fixture.sh test_should_always_pass2 90 | $POSIT_DIR/resources/posit_postcov2.fixture.sh test_should_always_pass 91 | $POSIT_DIR/resources/posit_postcov2.fixture.sh test_should_always_pass2 92 | LIST 93 | } 94 | 95 | check () 96 | { 97 | result="$(cat)" 98 | head_results="$(echo "$result" | grep "^head_mock called" | wc -l)" 99 | unit_results="$(echo "$result" | grep "^unit_mock called" | wc -l)" 100 | last_line="$(echo "$result" | tail -n 1)" 101 | 102 | [ $head_results = 2 ] && 103 | [ $unit_results = 4 ] && 104 | [ "$last_line" = "4...4" ] 105 | 106 | exit $? 107 | } 108 | 109 | 110 | mocklist | 111 | posit_timer="" posit_mode="mock" posit_process "$POSIT_DIR/resources/" | 112 | check 113 | } 114 | 115 | test_posit_process_with_no_tests () 116 | { 117 | posit_filter_mock () ( : ) 118 | posit_exec_mock () ( echo "exec_mock called $@" ) 119 | posit_head_mock () ( echo "head_mock called $@" ) 120 | posit_unit_mock () ( echo "unit_mock called $@" ) 121 | posit_count_mock () ( echo "$@" ) 122 | mocklist () ( true ) 123 | depur_command_tracer () ( : ) 124 | 125 | posit_timer="" 126 | posit_mode="mock" 127 | result="$(mocklist | posit_process "$POSIT_DIR/resources/")" 128 | 129 | [ "$result" = "0 0 0" ] 130 | } 131 | 132 | template_posit_unit () 133 | { 134 | expected_mode="$1" 135 | expected_code="$2" 136 | expected_string="$3" 137 | 138 | OLDPS4="$PS4" # Prevent debugger from changing the output on MinGW 139 | set +e # We care only about the output on this test 140 | result="$(posit_unit_$expected_mode "/foo/bar" "test_foo_bar" $expected_code "")" 141 | set -e 142 | PS4="$OLDPS4" 143 | 144 | pass_results="$(echo "$result" | grep "$expected_string" | wc -l)" 145 | 146 | [ $pass_results = 1 ] 147 | } 148 | 149 | test_posit_unit_spec_success () 150 | { 151 | posit_stack_format () ( : ) 152 | template_posit_unit spec 0 "pass:" 153 | } 154 | 155 | test_posit_unit_spec_fail () 156 | { 157 | posit_stack_format () ( : ) 158 | template_posit_unit spec 1 "fail:" 159 | } 160 | 161 | test_posit_unit_spec_skip () 162 | { 163 | posit_stack_format () ( : ) 164 | template_posit_unit spec 3 "skip:" 165 | } 166 | 167 | test_posit_unit_tiny_fail () 168 | { 169 | template_posit_unit tiny 1 "F" 170 | } 171 | 172 | test_posit_unit_tiny_success () 173 | { 174 | template_posit_unit tiny 0 "\." 175 | } 176 | 177 | test_posit_unit_tiny_skip () 178 | { 179 | template_posit_unit tiny 3 "S" 180 | } 181 | 182 | 183 | template_posit_exec () 184 | { 185 | mode="$1" 186 | posit_external () ( echo "external called $@" ) 187 | 188 | check () 189 | { 190 | result="$(cat | tail -n 1)" 191 | 192 | [ "$result" = "external called /foo/bar foo_bar" ] 193 | exit $? 194 | } 195 | 196 | posit_exec_$mode "/foo/bar" "foo_bar" | check 197 | } 198 | 199 | test_posit_exec_spec () 200 | { 201 | template_posit_exec spec 202 | } 203 | 204 | test_posit_exec_tiny () 205 | { 206 | template_posit_exec tiny 207 | } 208 | 209 | test_posit_exec_cov () 210 | { 211 | template_posit_exec cov 212 | } 213 | 214 | test_posit_head_spec () 215 | { 216 | result="$(posit_head_spec "/foo/bar" | sed '1d;$d')" 217 | 218 | [ "$result" = "### /foo/bar" ] 219 | } 220 | -------------------------------------------------------------------------------- /test/trix/unit.test.sh: -------------------------------------------------------------------------------- 1 | setup () 2 | { 3 | lib_path="$POSIT_DIR/../../lib/workshop" 4 | 5 | . "$lib_path/common.sh" 6 | . "$lib_path/trix/trix.sh" 7 | } 8 | 9 | test_trix_parsevar_with_one_variable () 10 | { 11 | parsed="$(trix_parsevar "export" FOO=bar)" 12 | 13 | [ "$parsed" = 'export FOO="bar" ' ] 14 | } 15 | 16 | test_trix_parsevar_with_more_variables () 17 | { 18 | parsed="$(trix_parsevar "export" FOO=bar BAR=baz)" 19 | 20 | [ "$parsed" = 'export FOO="bar" BAR="baz" ' ] 21 | } 22 | 23 | test_trix_parsevar_with_quoted_variables () 24 | { 25 | parsed="$(trix_parsevar "export" FOO="bar zoo" BAR="baz ZAZ")" 26 | 27 | [ "$parsed" = 'export FOO="bar zoo" BAR="baz ZAZ" ' ] 28 | } 29 | 30 | test_trix_parsevar_with_mixed_equal_signs () 31 | { 32 | parsed="$(trix_parsevar "export" FOO="bar=zoo" BAR="baz ZAZ")" 33 | 34 | [ "$parsed" = 'export FOO="bar=zoo" BAR="baz ZAZ" ' ] 35 | } 36 | 37 | test_trix_probe_with_results () 38 | { 39 | real_cat="$(which cat)" 40 | nl=" 41 | " 42 | cat () 43 | { 44 | $real_cat <<-PROBED 45 | sample_foo () 46 | { 47 | : 48 | } 49 | sample_bar () 50 | { 51 | : 52 | } 53 | PROBED 54 | } 55 | 56 | probed="$(trix_probe sample_ /mocked/file)" 57 | 58 | [ "$probed" = "sample_foo${nl}sample_bar" ] 59 | } 60 | 61 | test_trix_probe_with_no_results () 62 | { 63 | real_cat="$(which cat)" 64 | nl=" 65 | " 66 | cat () ( : ) 67 | 68 | probed="$(trix_probe sample_ /mocked/file)" 69 | 70 | [ "$probed" = "" ] 71 | } 72 | 73 | test_trix_spawn () 74 | { 75 | nl=" 76 | " 77 | tab=" " 78 | mock_environments="env_mock_foo${nl}env_mock_bar${nl}env_mock_baz" 79 | spawned="$(trix_spawn "mock_mode" "$mock_environments" "mock_*")" 80 | 81 | [ "$spawned" = "mock_mode${tab}env_mock_foo${nl}mock_mode${tab}env_mock_bar${nl}mock_mode${tab}env_mock_baz" ] 82 | } 83 | 84 | test_trix_spawn_with_env_filter () 85 | { 86 | nl=" 87 | " 88 | tab=" " 89 | mock_environments="env_mock_foo${nl}env_mock_foo_also${nl}env_mock_baz" 90 | trix_env_filter="foo" 91 | spawned="$(trix_spawn "mock_mode" "$mock_environments" "mock_*")" 92 | 93 | [ "$spawned" = "mock_mode${tab}env_mock_foo${nl}mock_mode${tab}env_mock_foo_also" ] 94 | } 95 | 96 | test_trix_probe_matrix_with_filter () 97 | { 98 | 99 | real_cat="$(which cat)" 100 | cat () 101 | { 102 | $real_cat <<-PROBED 103 | matrix_foo () 104 | { 105 | : 106 | } 107 | matrix_bar () 108 | { 109 | : 110 | } 111 | PROBED 112 | } 113 | 114 | trix_matrix_filter="foo" 115 | probed="$(trix_probe_matrix /mocked/file)" 116 | 117 | [ "$probed" = "matrix_foo" ] 118 | } 119 | --------------------------------------------------------------------------------