pax_global_header00006660000000000000000000000064136377041710014523gustar00rootroot0000000000000052 comment=080159b303537888c5d41910b4d47a5002638e30 shunit2-2.1.8/000077500000000000000000000000001363770417100131275ustar00rootroot00000000000000shunit2-2.1.8/.gitignore000066400000000000000000000000611363770417100151140ustar00rootroot00000000000000# Hidden files generated by macOS. .DS_Store ._* shunit2-2.1.8/.travis.yml000066400000000000000000000007301363770417100152400ustar00rootroot00000000000000language: bash env: - SHUNIT_COLOR='always' script: # Execute the unit tests. - ./test_runner os: - linux - osx addons: apt: packages: - ksh - zsh matrix: include: - os: linux script: # Run the source through ShellCheck (http://www.shellcheck.net). - shellcheck shunit2 *_test.sh - shellcheck -s sh shunit2_test_helpers - os: linux # Support Ubuntu Trusty through Apr 2019. dist: trusty shunit2-2.1.8/CODE_OF_CONDUCT.md000066400000000000000000000062241363770417100157320ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at kate.ward@forestent.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ shunit2-2.1.8/LICENSE000066400000000000000000000261351363770417100141430ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. shunit2-2.1.8/README.md000066400000000000000000000546531363770417100144230ustar00rootroot00000000000000# shUnit2 shUnit2 is a [xUnit](http://en.wikipedia.org/wiki/XUnit) unit test framework for Bourne based shell scripts, and it is designed to work in a similar manner to [JUnit](http://www.junit.org), [PyUnit](http://pyunit.sourceforge.net), etc.. If you have ever had the desire to write a unit test for a shell script, shUnit2 can do the job. [![Travis CI](https://img.shields.io/travis/kward/shunit2.svg)](https://travis-ci.org/kward/shunit2) ## Table of Contents * [Introduction](#introduction) * [Credits / Contributors](#credits-contributors) * [Feedback](#feedback) * [Quickstart](#quickstart) * [Function Reference](#function-reference) * [General Info](#general-info) * [Asserts](#asserts) * [Failures](#failures) * [Setup/Teardown](#setup-teardown) * [Skipping](#skipping) * [Suites](#suites) * [Advanced Usage](#advanced-usage) * [Some constants you can use](#some-constants-you-can-use) * [Error Handling](#error-handling) * [Including Line Numbers in Asserts (Macros)](#including-line-numbers-in-asserts-macros) * [Test Skipping](#test-skipping) * [Running specific tests from the command line](#cmd-line-args) * [Appendix](#appendix) * [Getting help](#getting-help) * [Zsh](#zsh) --- ## Introduction shUnit2 was originally developed to provide a consistent testing solution for [log4sh][log4sh], a shell based logging framework similar to [log4j](http://logging.apache.org). During the development of that product, a repeated problem of having things work just fine under one shell (`/bin/bash` on Linux to be specific), and then not working under another shell (`/bin/sh` on Solaris) kept coming up. Although several simple tests were run, they were not adequate and did not catch some corner cases. The decision was finally made to write a proper unit test framework after multiple brown-bag releases were made. _Research was done to look for an existing product that met the testing requirements, but no adequate product was found._ ### Tested software **Tested Operating Systems** (varies over time) OS | Support | Verified ----------------------------------- | --------- | -------- Ubuntu Linux (14.04.05 LTS) | Travis CI | continuous macOS High Sierra (10.13.3) | Travis CI | continuous FreeBSD | user | unknown Solaris 8, 9, 10 (inc. OpenSolaris) | user | unknown Cygwin | user | unknown **Tested Shells** * Bourne Shell (__sh__) * BASH - GNU Bourne Again SHell (__bash__) * DASH (__dash__) * Korn Shell (__ksh__) * pdksh - Public Domain Korn Shell (__pdksh__) * zsh - Zsh (__zsh__) (since 2.1.2) _please see the Zsh shell errata for more information_ See the appropriate Release Notes for this release (`doc/RELEASE_NOTES-X.X.X.txt`) for the list of actual versions tested. ### Credits / Contributors A list of contributors to shUnit2 can be found in `doc/contributors.md`. Many thanks go out to all those who have contributed to make this a better tool. shUnit2 is the original product of many hours of work by Kate Ward, the primary author of the code. For related software, check out https://github.com/kward. ### Feedback Feedback is most certainly welcome for this document. Send your questions, comments, and criticisms via the [shunit2-users](https://groups.google.com/a/forestent.com/forum/#!forum/shunit2-users/new) forum (created 2018-12-09), or file an issue via https://github.com/kward/shunit2/issues. --- ## Quickstart This section will give a very quick start to running unit tests with shUnit2. More information is located in later sections. Here is a quick sample script to show how easy it is to write a unit test in shell. _Note: the script as it stands expects that you are running it from the "examples" directory._ ```sh #! /bin/sh # file: examples/equality_test.sh testEquality() { assertEquals 1 1 } # Load shUnit2. . ./shunit2 ``` Running the unit test should give results similar to the following. ```console $ cd examples $ ./equality_test.sh testEquality Ran 1 test. OK ``` W00t! You've just run your first successful unit test. So, what just happened? Quite a bit really, and it all happened simply by sourcing the `shunit2` library. The basic functionality for the script above goes like this: * When shUnit2 is sourced, it will walk through any functions defined whose name starts with the string `test`, and add those to an internal list of tests to execute. Once a list of test functions to be run has been determined, shunit2 will go to work. * Before any tests are executed, shUnit2 again looks for a function, this time one named `oneTimeSetUp()`. If it exists, it will be run. This function is normally used to setup the environment for all tests to be run. Things like creating directories for output or setting environment variables are good to place here. Just so you know, you can also declare a corresponding function named `oneTimeTearDown()` function that does the same thing, but once all the tests have been completed. It is good for removing temporary directories, etc. * shUnit2 is now ready to run tests. Before doing so though, it again looks for another function that might be declared, one named `setUp()`. If the function exists, it will be run before each test. It is good for resetting the environment so that each test starts with a clean slate. **At this stage, the first test is finally run.** The success of the test is recorded for a report that will be generated later. After the test is run, shUnit2 looks for a final function that might be declared, one named `tearDown()`. If it exists, it will be run after each test. It is a good place for cleaning up after each test, maybe doing things like removing files that were created, or removing directories. This set of steps, `setUp() > test() > tearDown()`, is repeated for all of the available tests. * Once all the work is done, shUnit2 will generate the nice report you saw above. A summary of all the successes and failures will be given so that you know how well your code is doing. We should now try adding a test that fails. Change your unit test to look like this. ```sh #! /bin/sh # file: examples/party_test.sh testEquality() { assertEquals 1 1 } testPartyLikeItIs1999() { year=`date '+%Y'` assertEquals "It's not 1999 :-(" '1999' "${year}" } # Load shUnit2. . ./shunit2 ``` So, what did you get? I guess it told you that this isn't 1999. Bummer, eh? Hopefully, you noticed a couple of things that were different about the second test. First, we added an optional message that the user will see if the assert fails. Second, we did comparisons of strings instead of integers as in the first test. It doesn't matter whether you are testing for equality of strings or integers. Both work equally well with shUnit2. Hopefully, this is enough to get you started with unit testing. If you want a ton more examples, take a look at the tests provided with [log4sh][log4sh] or [shFlags][shflags]. Both provide excellent examples of more advanced usage. shUnit2 was after all written to meet the unit testing need that [log4sh][log4sh] had. --- ## Function Reference ### General Info Any string values passed should be properly quoted -- they should must be surrounded by single-quote (`'`) or double-quote (`"`) characters -- so that the shell will properly parse them. ### Asserts `assertEquals [message] expected actual` Asserts that _expected_ and _actual_ are equal to one another. The _expected_ and _actual_ values can be either strings or integer values as both will be treated as strings. The _message_ is optional, and must be quoted. `assertNotEquals [message] unexpected actual` Asserts that _unexpected_ and _actual_ are not equal to one another. The _unexpected_ and _actual_ values can be either strings or integer values as both will be treaded as strings. The _message_ is optional, and must be quoted. `assertSame [message] expected actual` This function is functionally equivalent to `assertEquals`. `assertNotSame [message] unexpected actual` This function is functionally equivalent to `assertNotEquals`. `assertContains [message] container content` Asserts that _container_ contains _content_. The _container_ and _content_ values can be either strings or integer values as both will be treated as strings. The _message_ is optional, and must be quoted. `assertNotContains [message] container content` Asserts that _container_ does not contain _content_. The _container_ and _content_ values can be either strings or integer values as both will be treaded as strings. The _message_ is optional, and must be quoted. `assertNull [message] value` Asserts that _value_ is _null_, or in shell terms, a zero-length string. The _value_ must be a string as an integer value does not translate into a zero- length string. The _message_ is optional, and must be quoted. `assertNotNull [message] value` Asserts that _value_ is _not null_, or in shell terms, a non-empty string. The _value_ may be a string or an integer as the later will be parsed as a non-empty string value. The _message_ is optional, and must be quoted. `assertTrue [message] condition` Asserts that a given shell test _condition_ is _true_. The condition can be as simple as a shell _true_ value (the value `0` -- equivalent to `${SHUNIT_TRUE}`), or a more sophisticated shell conditional expression. The _message_ is optional, and must be quoted. A sophisticated shell conditional expression is equivalent to what the __if__ or __while__ shell built-ins would use (more specifically, what the __test__ command would use). Testing for example whether some value is greater than another value can be done this way. `assertTrue "[ 34 -gt 23 ]"` Testing for the ability to read a file can also be done. This particular test will fail. `assertTrue 'test failed' "[ -r /some/non-existant/file' ]"` As the expressions are standard shell __test__ expressions, it is possible to string multiple expressions together with `-a` and `-o` in the standard fashion. This test will succeed as the entire expression evaluates to _true_. `assertTrue 'test failed' '[ 1 -eq 1 -a 2 -eq 2 ]'` One word of warning: be very careful with your quoting as shell is not the most forgiving of bad quoting, and things will fail in strange ways. `assertFalse [message] condition` Asserts that a given shell test _condition_ is _false_. The condition can be as simple as a shell _false_ value (the value `1` -- equivalent to `${SHUNIT_FALSE}`), or a more sophisticated shell conditional expression. The _message_ is optional, and must be quoted. _For examples of more sophisticated expressions, see `assertTrue`._ ### Failures Just to clarify, failures __do not__ test the various arguments against one another. Failures simply fail, optionally with a message, and that is all they do. If you need to test arguments against one another, use asserts. If all failures do is fail, why might one use them? There are times when you may have some very complicated logic that you need to test, and the simple asserts provided are simply not adequate. You can do your own validation of the code, use an `assertTrue ${SHUNIT_TRUE}` if your own tests succeeded, and use a failure to record a failure. `fail [message]` Fails the test immediately. The _message_ is optional, and must be quoted. `failNotEquals [message] unexpected actual` Fails the test immediately, reporting that the _unexpected_ and _actual_ values are not equal to one another. The _message_ is optional, and must be quoted. _Note: no actual comparison of unexpected and actual is done._ `failSame [message] expected actual` Fails the test immediately, reporting that the _expected_ and _actual_ values are the same. The _message_ is optional, and must be quoted. _Note: no actual comparison of expected and actual is done._ `failNotSame [message] expected actual` Fails the test immediately, reporting that the _expected_ and _actual_ values are not the same. The _message_ is optional, and must be quoted. _Note: no actual comparison of expected and actual is done._ `failFound [message] content` Fails the test immediately, reporting that the _content_ was found. The _message_ is optional, and must be quoted. _Note: no actual search of content is done._ `failNotFound [message] content` Fails the test immediately, reporting that the _content_ was not found. The _message_ is optional, and must be quoted. _Note: no actual search of content is done._ ### Setup/Teardown `oneTimeSetUp` This function can be be optionally overridden by the user in their test suite. If this function exists, it will be called once before any tests are run. It is useful to prepare a common environment for all tests. `oneTimeTearDown` This function can be be optionally overridden by the user in their test suite. If this function exists, it will be called once after all tests are completed. It is useful to clean up the environment after all tests. `setUp` This function can be be optionally overridden by the user in their test suite. If this function exists, it will be called before each test is run. It is useful to reset the environment before each test. `tearDown` This function can be be optionally overridden by the user in their test suite. If this function exists, it will be called after each test completes. It is useful to clean up the environment after each test. ### Skipping `startSkipping` This function forces the remaining _assert_ and _fail_ functions to be "skipped", i.e. they will have no effect. Each function skipped will be recorded so that the total of asserts and fails will not be altered. `endSkipping` This function returns calls to the _assert_ and _fail_ functions to their default behavior, i.e. they will be called. `isSkipping` This function returns the current state of skipping. It can be compared against `${SHUNIT_TRUE}` or `${SHUNIT_FALSE}` if desired. ### Suites The default behavior of shUnit2 is that all tests will be found dynamically. If you have a specific set of tests you want to run, or you don't want to use the standard naming scheme of prefixing your tests with `test`, these functions are for you. Most users will never use them though. `suite` This function can be optionally overridden by the user in their test suite. If this function exists, it will be called when `shunit2` is sourced. If it does not exist, shUnit2 will search the parent script for all functions beginning with the word `test`, and they will be added dynamically to the test suite. `suite_addTest name` This function adds a function named _name_ to the list of tests scheduled for execution as part of this test suite. This function should only be called from within the `suite()` function. --- ## Advanced Usage ### Some constants you can use There are several constants provided by shUnit2 as variables that might be of use to you. *Predefined* | Constant | Value | | --------------- | ----- | | SHUNIT\_TRUE | Standard shell `true` value (the integer value 0). | | SHUNIT\_FALSE | Standard shell `false` value (the integer value 1). | | SHUNIT\_ERROR | The integer value 2. | | SHUNIT\_TMPDIR | Path to temporary directory that will be automatically cleaned up upon exit of shUnit2. | | SHUNIT\_VERSION | The version of shUnit2 you are running. | *User defined* | Constant | Value | | ----------------- | ----- | | SHUNIT\_CMD\_EXPR | Override which `expr` command is used. By default `expr` is used, except on BSD systems where `gexpr` is used. | | SHUNIT\_COLOR | Enable colorized output. Options are 'auto', 'always', or 'none', with 'auto' being the default. | | SHUNIT\_PARENT | The filename of the shell script containing the tests. This is needed specifically for Zsh support. | | SHUNIT\_TEST\_PREFIX | Define this variable to add a prefix in front of each test name that is output in the test report. | ### Error handling The constants values `SHUNIT_TRUE`, `SHUNIT_FALSE`, and `SHUNIT_ERROR` are returned from nearly every function to indicate the success or failure of the function. Additionally the variable `flags_error` is filled with a detailed error message if any function returns with a `SHUNIT_ERROR` value. ### Including Line Numbers in Asserts (Macros) If you include lots of assert statements in an individual test function, it can become difficult to determine exactly which assert was thrown unless your messages are unique. To help somewhat, line numbers can be included in the assert messages. To enable this, a special shell "macro" must be used rather than the standard assert calls. _Shell doesn't actually have macros; the name is used here as the operation is similar to a standard macro._ For example, to include line numbers for a `assertEquals()` function call, replace the `assertEquals()` with `${_ASSERT_EQUALS_}`. _**Example** -- Asserts with and without line numbers_ ```sh #! /bin/sh # file: examples/lineno_test.sh testLineNo() { # This assert will have line numbers included (e.g. "ASSERT:[123] ..."). echo "ae: ${_ASSERT_EQUALS_}" ${_ASSERT_EQUALS_} 'not equal' 1 2 # This assert will not have line numbers included (e.g. "ASSERT: ..."). assertEquals 'not equal' 1 2 } # Load shUnit2. . ./shunit2 ``` Notes: 1. Due to how shell parses command-line arguments, all strings used with macros should be quoted twice. Namely, single-quotes must be converted to single- double-quotes, and vice-versa. If the string being passed is absolutely for sure not empty, the extra quoting is not necessary.

Normal `assertEquals` call.
`assertEquals 'some message' 'x' ''`

Macro `_ASSERT_EQUALS_` call. Note the extra quoting around the _message_ and the _null_ value.
`_ASSERT_EQUALS_ '"some message"' 'x' '""'` 1. Line numbers are not supported in all shells. If a shell does not support them, no errors will be thrown. Supported shells include: __bash__ (>=3.0), __ksh__, __pdksh__, and __zsh__. ### Test Skipping There are times where the test code you have written is just not applicable to the system you are running on. This section describes how to skip these tests but maintain the total test count. Probably the easiest example would be shell code that is meant to run under the __bash__ shell, but the unit test is running under the Bourne shell. There are things that just won't work. The following test code demonstrates two sample functions, one that will be run under any shell, and the another that will run only under the __bash__ shell. _**Example** -- math include_ ```sh # file: examples/math.inc. add_generic() { num_a=$1 num_b=$2 expr $1 + $2 } add_bash() { num_a=$1 num_b=$2 echo $(($1 + $2)) } ``` And here is a corresponding unit test that correctly skips the `add_bash()` function when the unit test is not running under the __bash__ shell. _**Example** -- math unit test_ ```sh #! /bin/sh # file: examples/math_test.sh testAdding() { result=`add_generic 1 2` assertEquals \ "the result of '${result}' was wrong" \ 3 "${result}" # Disable non-generic tests. [ -z "${BASH_VERSION:-}" ] && startSkipping result=`add_bash 1 2` assertEquals \ "the result of '${result}' was wrong" \ 3 "${result}" } oneTimeSetUp() { # Load include to test. . ./math.inc } # Load and run shUnit2. . ./shunit2 ``` Running the above test under the __bash__ shell will result in the following output. ```console $ /bin/bash math_test.sh testAdding Ran 1 test. OK ``` But, running the test under any other Unix shell will result in the following output. ```console $ /bin/ksh math_test.sh testAdding Ran 1 test. OK (skipped=1) ``` As you can see, the total number of tests has not changed, but the report indicates that some tests were skipped. Skipping can be controlled with the following functions: `startSkipping()`, `endSkipping()`, and `isSkipping()`. Once skipping is enabled, it will remain enabled until the end of the current test function call, after which skipping is disabled. ### Running specific tests from the command line. When running a test script, you may override the default set of tests, or the suite-specified set of tests, by providing additional arguments on the command line. Each additional argument after the `--` marker is assumed to be the name of a test function to be run in the order specified. e.g. ```console test-script.sh -- testOne testTwo otherFunction ``` or ```console shunit2 test-script.sh testOne testTwo otherFunction ``` In either case, three functions will be run as tests, `testOne`, `testTwo`, and `otherFunction`. Note that the function `otherFunction` would not normally be run by `shunit2` as part of the implicit collection of tests as it's function name does not match the test function name pattern `test*`. If a specified test function does not exist, `shunit2` will still attempt to run that function and thereby cause a failure which `shunit2` will catch and mark as a failed test. All other tests will run normally. The specification of tests does not affect how `shunit2` looks for and executes the setup and tear down functions, which will still run as expected. --- ## Appendix ### Getting Help For help, please send requests to either the shunit2-users@forestent.com mailing list (archives available on the web at https://groups.google.com/a/forestent.com/forum/#!forum/shunit2-users) or directly to Kate Ward . ### Zsh For compatibility with Zsh, there is one requirement that must be met -- the `shwordsplit` option must be set. There are three ways to accomplish this. 1. In the unit-test script, add the following shell code snippet before sourcing the `shunit2` library. ```sh setopt shwordsplit ``` 2. When invoking __zsh__ from either the command-line or as a script with `#!`, add the `-y` parameter. ```sh #! /bin/zsh -y ``` 3. When invoking __zsh__ from the command-line, add `-o shwordsplit --` as parameters before the script name. ```console $ zsh -o shwordsplit -- some_script ``` [log4sh]: https://github.com/kward/log4sh [shflags]: https://github.com/kward/shflags shunit2-2.1.8/doc/000077500000000000000000000000001363770417100136745ustar00rootroot00000000000000shunit2-2.1.8/doc/CHANGES-2.1.md000066400000000000000000000205561363770417100155740ustar00rootroot00000000000000# shUnit2 2.1.x Changes ## Changes with 2.1.8 ### New Issue #29. Add support for user defined prefix for test names. A prefix can be added by defining the `SHUNIT_TEST_PREFIX` variable. ### Improvements Issue #78. Added an example for using suite tests. Run continuous integration additionally against Ubuntu Trusty. ### Fixed Issue #94. Removed the `gen_test_report.sh` script as the Travis CI output can be used instead. Reports were used before Travis CI was used. Issue #84. Treat syntax errors in functions as test failures. Issue #77. Fail tests when the environment functions (e.g. `setup()` or `tearDown()`) fail. ## Changes with 2.1.7 ### Bug fixes Issue #69. shUnit2 should not exit with 0 when it has (syntax) errors. ### Enhancements Issue #54. Shell commands prefixed with '\' so that they can be stubbed in tests. Issue #68. Ran all code through [ShellCheck](http://www.shellcheck.net/). Issue #60. Continuous integration tests now run with [Travis CI](https://travis-ci.org/kward/shunit2). Issue #56. Added color support. Color is enabled automatically when supported, but can be disabled by defining the SHUNIT_COLOR environment variable before sourcing shunit2. Accepted values are `always`, `auto` (the default), and `none`. Issue #35. Add colored output. ### Other Moved code to GitHub (https://github.com/kward/shunit2), and restructured to be more GitHub like. Changed to the Apache 2.0 license. ## Changes with 2.1.6 Removed all references to the DocBook documentation. Simplified the 'src' structure. Fixed error message in fail() that stated wrong number of required arguments. Updated lib/versions. Fixed bug in `_shunit_mktempDir()` where a failure occurred when the 'od' command was not present in `/usr/bin`. Renamed `shunit_tmpDir` variable to `SHUNIT_TMPDIR` to closer match the standard `TMPDIR` variable. Added support for calling shunit2 as an executable, in addition to the existing method of sourcing it in as a library. This allows users to keep tests working despite the location of the shunit2 executable being different for each OS distribution. Issue #14: Improved handling of some strange chars (e.g. single and double quotes) in messages. Issue# 27: Fixed error message for `assertSame()`. Issue# 25: Added check and error message to user when phantom functions are written to a partition mounted with `noexec`. Issue# 11: Added support for defining functions like `function someFunction()`. ## Changes with 2.1.5 Issue# 1: Fixed bug pointed out by R Bernstein in the trap code where certain types of exit conditions did not generate the ending report. Issue# 2: Added `assertNotEquals()` assert. Issue# 3: Moved check for unset variables out of shUnit2 into the unit tests. Testing poorly written software blows up if this check is in, but it is only interesting for shUnit2 itself. Added `shunit_test_output.sh` unit test for this. Some shells still do not catch such errors properly (e.g. Bourne shell and BASH 2.x). Added new custom assert in test_helpers to check for output to STDOUT, and none to STDERR. Replaced fatal message in the temp directory creation with a `_shunit_fatal()` function call. Fixed test_output unit test so it works now that the 'set -u' stuff was removed for Issue# 3. Flushed out the coding standards in the `README.txt` a bit more, and brought the shunit2 code up to par with the documented standards. Issue# 4: Completely changed the reporting output to be a closer match for JUnit and PyUnit. As a result, tests are counted separately from assertions. Provide public `shunit_tmpDir` variable that can be used by unit test scripts that need automated and guaranteed cleanup. Issue# 7: Fixed duplicated printing of messages passed to asserts. Per code review, fixed wording of `failSame()` and `failNotSame()` messages. Replaced `version_info.sh` with versions library and made appropriate changes in other scripts to use it. Added `gen_test_results.sh` to make releases easier. Fixed bugs in `shlib_relToAbsPath()` in shlib. Converted DocBook documentation to reStructuredText for easier maintenance. The DocBook documentation is now considered obsolete, and will be removed in a future release. Issue# 5: Fixed the documentation around the usage of failures. Issue# 9: Added unit tests and updated documentation to demonstrate the requirement of quoting values twice when macros are used. This is due to how shell parses arguments. When an invalid number of arguments is passed to a function, the invalid number is returned to the user so they are more aware of what the cause might be. ## Changes with 2.1.4 Removed the `_shunit_functionExists()` function as it was dead code. Fixed zsh version number check in version_info. Fixed bug in last resort temporary directory creation. Fixed off-by-one in exit value for scripts caught by the trap handler. Added argument count error checking to all functions. Added mkdir_test.sh example. Moved src/test into src/shell to better match structure used with shFlags. Fixed problem where null values were not handled properly under ksh. Added support for outputting line numbers as part of assert messages. Started documenting the coding standards, and changed some variable names as a result. Improved zsh version and option checks. Renamed the `__SHUNIT_VERSION` variable to `SHUNIT_VERSION`. ## Changes with 2.1.3 Added some explicit variable defaults, even though the variables are set, as they sometimes behave strange when the script is canceled. Additional workarounds for zsh compatibility. shUnit2 now exits with a non-zero exit code if any of the tests failed. This was done for automated testing frameworks. Tests that were skipped are not considered failures, and do not affect the exit code. Changed detection of STDERR output in unit tests. ## Changes with 2.1.2 Unset additional variables that were missed. Added checks and workarounds to improve zsh compatibility. Added some argument count checks `assertEquals()`, `assertNull()`, and `assertSame()`. ## Changes with 2.1.1 Fixed bug where `fail()` was not honoring skipping. Fixed problem with `docs-docbook-prep` target that prevented it from working. (Thanks to Bryan Larsen for pointing this out.) Changed the test in `assertFalse()` so that any non-zero value registers as false. (Credits to Bryan Larsen) Major fiddling to bring more in line with [JUnit](http://junit.org/). Asserts give better output when no message is given, and failures now just fail. It was pointed out that the simple 'failed' message for a failed assert was not only insufficient, it was nonstandard (when compared to JUnit) and didn't provide the user with an expected vs actual result. The code was revised somewhat to bring closer into alignment with JUnit (v4.3.1 specifically) so that it feels more "normal". (Credits to Richard Jensen) As part of the JUnit realignment, it was noticed that `fail*()` functions in JUnit don't actually do any comparisons themselves. They only generate a failure message. Updated the code to match. Added self-testing unit tests. Kinda horkey, but they did find bugs during the JUnit realignment. Fixed the code for returning from asserts as the return was being called before the unsetting of variables occurred. (Credits to Mathias Goldau) The assert(True|False)() functions now accept an integer value for a conditional test. A value of '0' is considered 'true', while any non-zero value is considered 'false'. All public functions now fill use default values to work properly with the '-x' shell debugging flag. Fixed the method of percent calculation for the report to get achieve better accuracy. ## Changes with 2.1.0 (since 2.0.1) This release is a branch of the 2.0.1 release. Moving to [reStructured Text](http://docutils.sourceforge.net/rst.html) for the documentation. Fixed problem with `fail()`. The failure message was not properly printed. Fixed the `Makefile` so that the DocBook XML and XSLT files would be downloaded before parsing can continue. Renamed the internal `__SHUNIT_TRUE` and `__SHUNIT_FALSE` variables to `SHUNIT_TRUE` and `SHUNIT_FALSE` so that unit tests can "use" them. Added support for test "skipping". If skipping is turned on with the `startSkip()` function, `assert` and `fail` functions will return immediately, and the skip will be recorded. The report output format was changed to include the percentage for each test result, rather than just those successful. [travis_ci]: https://travis-ci.org/kward/shunit2 shunit2-2.1.8/doc/RELEASE_NOTES-2.1.0.txt000066400000000000000000000043071363770417100171250ustar00rootroot00000000000000Release Notes for shUnit2 2.1.0 =============================== This release was branched from shUnit2 2.0.1. It mostly adds new functionality, but there are couple of bugs fixed from the previous release. See the ``CHANGES-2.1.rst`` file for a full list of changes. Tested Platforms ---------------- This list of platforms comes from the latest version of log4sh as shUnit2 is used in the testing of log4sh on each of these platforms. Cygwin - bash 3.2.9(10) - pdksh 5.2.14 Linux - bash 3.1.17(1), 3.2.10(1) - dash 0.5.3 - ksh 1993-12-28 - pdksh 5.2.14 - zsh 4.3.2 (does not work) Mac OS X 10.4.8 (Darwin 8.8) - bash 2.05b.0(1) - ksh 1993-12-28 Solaris 8 U3 (x86) - /bin/sh - bash 2.03.0(1) - ksh M-11/16/88i Solaris 10 U2 (sparc) - /bin/sh - bash 3.00.16(1) - ksh M-11/16/88i Solaris 10 U2 (x86) - /bin/sh - bash 3.00.16(1) - ksh M-11/16/88i New Features ------------ Test skipping Support added for test "skipping". A skip mode can be enabled so that subsequent ``assert`` and ``fail`` functions that are called will be recorded as "skipped" rather than as "passed" or "failed". This functionality can be used such that when a set of tests makes sense on one platform but not on another, they can be effectively disabled without altering the total number of tests. One example might be when something is supported under ``bash``, but not under a standard Bourne shell. New functions: ``startSkipping()``, ``endSkipping``, ``isSkipping`` Changes and Enhancements ------------------------ Moving to the use of `reStructured Text `_ for documentation. It is easy to read and edit in textual form, but converts nicely to HTML. The report format has changed. Rather than including a simple "success" percentage at the end, a percentage is given for each type of test. Bug Fixes --------- The ``fail()`` function did not output the optional failure message. Fixed the ``Makefile`` so that the DocBook XML and XSLT files would be downloaded before documentation parsing will continue. Deprecated Features ------------------- None. Known Bugs and Issues --------------------- None. .. $Revision$ .. vim:fileencoding=latin1:spell:syntax=rst:textwidth=80 shunit2-2.1.8/doc/RELEASE_NOTES-2.1.1.txt000066400000000000000000000031421363770417100171220ustar00rootroot00000000000000Release Notes for shUnit2 2.1.1 =============================== This is mainly a bug fix release, but it also incorporates a realignment with the JUnit 4 code. Asserts now provide better failure messages, and the failure functions no longer perform tests. See the ``CHANGES-2.1.txt`` file for a full list of changes. Tested Platforms ---------------- This list of platforms comes from the latest version of log4sh as shUnit2 is used in the testing of log4sh on each of these platforms. Cygwin - bash 3.2.15(13) - pdksh 5.2.14 Linux - bash 3.1.17(1), 3.2.10(1) - dash 0.5.3 - ksh 1993-12-28 - pdksh 5.2.14 - zsh 4.3.2 (does not work) Mac OS X 10.4.9 (Darwin 8.9.1) - bash 2.05b.0(1) - ksh 1993-12-28 Solaris 8 U3 (x86) - /bin/sh - bash 2.03.0(1) - ksh M-11/16/88i Solaris 10 U2 (sparc, x86) - /bin/sh - bash 3.00.16(1) - ksh M-11/16/88i New Features ------------ None. Changes and Enhancements ------------------------ The internal test in ``assertFalse()`` now accepts any non-zero value as false. The ``assertTrue()`` and ``assertFalse()`` functions now accept an integer value for a conditional test. A value of '0' is considered 'true', while any non-zero value is considered 'false'. Self-testing unit tests were added. Bug Fixes --------- The ``fail()`` assert now honors skipping. The ``docs-docbook-prep`` target now works properly. All asserts now properly unset their variables. Deprecated Features ------------------- None. Known Bugs and Issues --------------------- Functions do not properly test for an invalid number of arguments. .. vim:fileencoding=latin1:ft=rst:spell:textwidth=80 shunit2-2.1.8/doc/RELEASE_NOTES-2.1.2.txt000066400000000000000000000032401363770417100171220ustar00rootroot00000000000000Release Notes for shUnit2 2.1.2 =============================== This release adds initial support for the zsh shell. Due to some differences with this shell as compared with others, some special checks have been added, and there are some extra requirements necessary when this shell is to be used. To use zsh with shUnit2, the following two requirements must be met: * The ``shwordsplit`` option must be set. * The ``function_argzero`` option must be unset. Please read the Shell Errata section of the documentation for guidance on how to meet these requirements. See the ``CHANGES-2.1.txt`` file for a full list of changes. Tested Platforms ---------------- This list of platforms comes from the latest version of log4sh as shUnit2 is used in the testing of log4sh on each of these platforms. Linux - bash 3.1.17(1), 3.2.25(1) - dash 0.5.4 - ksh 1993-12-28 - pdksh 5.2.14 - zsh 4.2.5, 4.3.4 Mac OS X 10.4.11 (Darwin 8.11.1) - bash 2.05b.0(1) - ksh 1993-12-28 - zsh 4.2.3 Solaris 10 U3 (x86) - /bin/sh - bash 3.00.16(1) - ksh M-11/16/88i - zsh 4.2.1 New Features ------------ Support for the zsh shell. Changes and Enhancements ------------------------ Added some argument count checks. Bug Fixes --------- None. Deprecated Features ------------------- None. Known Bugs and Issues --------------------- Functions do not properly test for an invalid number of arguments. ksh and pdksh do not pass null arguments (i.e. empty strings as '') properly, and as such checks do not work properly. zsh requires the ``shwordsplit`` option to be set, and the ``function_argzero`` option to be unset for proper operation. .. vim:fileencoding=latin1:ft=rst:spell:textwidth=80 shunit2-2.1.8/doc/RELEASE_NOTES-2.1.3.txt000066400000000000000000000030421363770417100171230ustar00rootroot00000000000000Release Notes for shUnit2 2.1.3 =============================== This release is minor feature release. It improves support for zsh (although it still isn't what it could be) and adds automated testing framework support by returning a non-zero exit when tests fail. To use zsh with shUnit2, the following two requirements must be met: * The ``shwordsplit`` option must be set. * The ``function_argzero`` option must be unset. Please read the Shell Errata section of the documentation for guidance on how to meet these requirements. See the ``CHANGES-2.1.txt`` file for a full list of changes. Tested Platforms ---------------- Cygwin - bash 3.2.33(18) - pdksh 5.2.14 Linux - bash 3.2.33(1) - dash 0.5.4 - ksh 1993-12-28 - pdksh 5.2.14 - zsh 4.3.4 Mac OS X 10.5.2 (Darwin 9.2.2) - bash 3.2.17(1) - ksh 1993-12-28 - zsh 4.3.4 Solaris 11 x86 (Nevada 77) - /bin/sh - bash 3.2.25(1) - ksh M-11/16/88i - zsh 4.3.4 New Features ------------ None. Changes and Enhancements ------------------------ Support for automated testing frameworks. Bug Fixes --------- Fixed some issues with zsh support. Deprecated Features ------------------- None. Known Bugs and Issues --------------------- Functions do not properly test for an invalid number of arguments. ksh and pdksh do not pass null arguments (i.e. empty strings as '') properly, and as such checks do not work properly. zsh requires the ``shwordsplit`` option to be set, and the ``function_argzero`` option to be unset for proper operation. .. vim:fileencoding=latin1:ft=rst:spell:textwidth=80 shunit2-2.1.8/doc/RELEASE_NOTES-2.1.4.txt000066400000000000000000000032641363770417100171320ustar00rootroot00000000000000Release Notes for shUnit2 2.1.4 =============================== This release contains lots of bug fixes and changes. Mostly, it fixes zsh support in zsh 3.0, and the handling of null values in ksh. To use zsh with shUnit2, the following requirement must be met: - The ``shwordsplit`` option must be set. Please read the Shell Errata section of the documentation for guidance on how to meet these requirements. See the ``CHANGES-2.1.txt`` file for a full list of changes. Tested Platforms ---------------- Cygwin - bash 3.2.39(19) - pdksh 5.2.14 - zsh 4.3.4 Linux (Ubuntu Dapper 6.06) - bash 3.1.17(1) - pdksh 5.2.14 - zsh 4.2.5 Linux (Ubuntu Hardy 8.04) - bash 3.2.39(1) - dash 0.5.4 - ksh 1993-12-28 - pdksh 5.2.14 - zsh 4.3.4 Mac OS X 10.5.4 (Darwin 9.4.0) - bash 3.2.17(1) - ksh 1993-12-28 - zsh 4.3.4 Solaris 9 U6 x86 - /bin/sh - bash 2.05.0(1) - ksh M-11/16/88i - zsh 3.0.8 Solaris 11 x86 (Nevada 77) - /bin/sh - bash 3.2.25(1) - ksh M-11/16/88i - zsh 4.3.4 New Features ------------ Support added to output assert source line number as part of assert messages. Changes and Enhancements ------------------------ Support for automated testing frameworks. Added argument count error checking to all functions. Bug Fixes --------- Fixed some issues with ksh and zsh support. Fixed off-by-one of exit value in trap handler. Fixed handling of null values under ksh. Fixed bug in last resort temporary directory creation. Deprecated Features ------------------- None. Known Bugs and Issues --------------------- zsh requires the ``shwordsplit`` option to be set. Line numbers in assert messages do not work properly with Bash 2.x. .. vim:fileencoding=latin1:ft=rst:spell:tw=80 shunit2-2.1.8/doc/RELEASE_NOTES-2.1.5.txt000066400000000000000000000054561363770417100171400ustar00rootroot00000000000000Release Notes for shUnit2 2.1.5 =============================== This release contains several bug fixes and changes. Additionally, it includes a rewrite of the test output to better match JUnit and PyUnit. This version also includes a slightly expanded set of coding standards by which shUnit2 is coded. It should help anyone reading the code to better understand it. Please read the Shell Errata section of the documentation for guidance on how to meet these requirements. See the ``CHANGES-2.1.txt`` file for a full list of changes. Tested Platforms ---------------- Cygwin - bash 3.2.39(20) - ksh (sym-link to pdksh) - pdksh 5.2.14 - zsh 4.3.4 Linux (Ubuntu Dapper 6.06) - bash 3.1.17(1) - ksh M-1993-12-28 - pdksh 5.2.14-99/07/13.2 - zsh 4.2.5 Linux (Ubuntu Hardy 8.04) - bash 3.2.39(1) - dash 0.5.4 - ksh M-1993-12-28 - pdksh 5.2.14-99/07/13.2 - zsh 4.3.4 Mac OS X 10.5.4 (Darwin 9.4.0) - bash 3.2.17(1) - ksh M-1993-12-28 - zsh 4.3.4 Solaris 9 U6 x86 - /bin/sh - bash 2.05.0(1) - ksh M-11/16/88i - zsh 3.0.8 Solaris 11 x86 (Nevada 77) - /bin/sh - bash 3.2.25(1) - ksh M-11/16/88i - zsh 4.3.4 New Features ------------ Support added for output assert source line number as part of assert messages. Issue #2: Added assertNotEquals() assert. Provided a public ``shunit_tmpDir`` variable that can be used by unit test scripts that need automated and guaranteed cleanup. Changes and Enhancements ------------------------ Issue #3: Removed the check for unset variables as shUnit2 should not expect scripts being tested to be clean. Issue #4: Rewrote the test summary. It is now greatly simplified and much more script friendly. Issue #5: Fixed the documentation around the usage of failures. Issue #9: Added unit tests and improved documentation around the use of macros. Code updated to meet documented coding standards. Improved code reuse of ``_shunit_exit()`` and ``_shunit_fatal()`` functions. All output except shUnit2 error messages now goes to STDOUT. Converted DocBook documentation to reStructuredText for easier maintenance. Bug Fixes --------- Issue #1: Fixed bug in rap code where certain types of exit conditions did not generate the ending report. Issue #7: Fixed duplicated printing of messages passed to asserts. Fixed bugs in ``shlib_relToAbsPath()`` in ``shlib``. Deprecated Features ------------------- None. Known Bugs and Issues --------------------- Zsh requires the ``shwordsplit`` option to be set. See the documentation for examples of how to do this. Line numbers in assert messages do not work properly with BASH 2.x. The Bourne shell of Solaris, BASH 2.x, and Zsh 3.0.x do not properly catch the SIGTERM signal. As such, shell interpreter failures due to such things as unbound variables cannot be caught. (See ``shunit_test_misc.sh``) .. vim:fileencoding=latin1:ft=rst:spell:tw=80 shunit2-2.1.8/doc/RELEASE_NOTES-2.1.6.txt000066400000000000000000000045301363770417100171310ustar00rootroot00000000000000Release Notes for shUnit2 2.1.6 =============================== This release contains bug fixes and changes. It is also the first release to support running shunit2 as a standalone program. Please read the Shell Errata section of the documentation for guidance on how to meet these requirements. See the ``CHANGES-2.1.txt`` file for a full list of changes. New Features ------------ Support for running shUnit2 as a standalone program. This makes it possible for users to execute their unit tests in a manner that is not dependent on the location an OS distribution maintainer chose to place shUnit2 in the file system. Added support for functions defined like 'function someFunction()'. Changes and Enhancements ------------------------ Renamed the public ``shunit_tmpDir`` variable to ``SHUNIT_TMPDIR`` to be more consistent with the ``TMPDIR`` variable. Bug Fixes --------- Fixed issue where shunit2 would fail on some distributions when creating a temporary directory because the **od** command was not present. Deprecated Features ------------------- None. Known Bugs and Issues --------------------- Zsh requires the ``shwordsplit`` option to be set. See the documentation for examples of how to do this. Line numbers in assert messages do not work properly with BASH 2.x. The Bourne shell of Solaris, BASH 2.x, and Zsh 3.0.x do not properly catch the SIGTERM signal. As such, shell interpreter failures due to such things as unbound variables cannot be caught. (See ``shunit_test_misc.sh``) Tested Platforms ---------------- Cygwin 1.7.9 (Windows XP SP2) - bash 4.1.10(4) - dash 0.5.6.1 - ksh (sym-link to pdksh) - pdksh 5.2.14 - zsh 4.3.11 Linux (Ubuntu Dapper 6.06.2 LTS) - bash 3.1.17(1) - dash 0.5.3 - ksh (sym-link to pdksh) - pdksh 5.2.14-99/07/13.2 - zsh 4.2.5 Linux (Ubuntu Hardy 8.04.4 LTS) - bash 3.2.39(1) - dash 0.5.4 - ksh M-1993-12-28 - pdksh 5.2.14-99/07/13.2 - zsh 4.3.4 Linux (Ubuntu Lucid 10.04.2 LTS) - bash 4.1.5(1) - dash 0.5.5.1 - ksh JM-93t+-2009-05-01 - pdksh 5.2.14-99/07/13.2 - zsh 4.3.10 Mac OS X 10.6.7 - bash 3.2.48(1) - ksh M-1993-12-28 - zsh 4.3.9 Solaris 8 U7 x86 - /bin/sh - bash 2.03.0(1) - ksh M-11/16/88i - zsh 3.0.6 Solaris 9 U6 x86 - /bin/sh - bash 2.05.0(1) - ksh M-11/16/88i - zsh 3.0.8 OpenSolaris 2009.06(snv_111b) x86 - /bin/sh - bash 3.2.25(1) - ksh 2008-11-04 .. vim:fileencoding=latin1:ft=rst:spell:tw=80 shunit2-2.1.8/doc/RELEASE_NOTES-2.1.7.md000066400000000000000000000031461363770417100167150ustar00rootroot00000000000000# shUnit2 2.1.7 Release Notes https://github.com/kward/shunit2 This release contains bug fixes and enhancements. It is the first release since moving to GitHub. Users can now clone the latest version at any time. See the `CHANGES-2.1.md` file for a full list of changes. ## New Features Colorized output, based on popular demand. shUnit2 output is now colorized based on the result of the asserts. ## Changes and Enhancements With the move to GitHub, the shUnit2 unit tests are run on every commit using the [Travis CI][TravisCI] continuous integration framework. Additionally, all code is run through [ShellCheck](http:/www.shellcheck.net/) on every commit. [TravisCI]: https://travis-ci.org/kward/shunit2 Shell commands in shUnit2 are prefixed with '\' so that they can be stubbed in tests. ## Bug Fixes shUnit2 no longer exits with an 'OK' result if there were syntax errors due to incorrect usage of the assert commands. ## Deprecated Features None. ## Known Bugs and Issues Zsh requires the `shwordsplit` option to be set. See the documentation for examples of how to do this. Line numbers in assert messages do not work properly with BASH 2.x. The Bourne shell of Solaris, BASH 2.x, and Zsh 3.0.x do not properly catch the SIGTERM signal. As such, shell interpreter failures due to such things as unbound variables cannot be caught. (See `shunit_test_misc.sh`) ## Tested Platforms Continuous integration testing is provided by [Travis CI](https://travis-ci.org/). https://travis-ci.org/github/kward/shunit2 Tested OSes: - Linux - macOS Tested shells: - /bin/sh - ash - bash - dash - ksh - pdksh - zsh shunit2-2.1.8/doc/RELEASE_NOTES-2.1.8.md000066400000000000000000000023271363770417100167160ustar00rootroot00000000000000# shUnit2 2.1.8 Release Notes https://github.com/kward/shunit2 This release contains bug fixes and enhancements. See the `CHANGES-2.1.md` file for a full list of changes. ## New features Users can now define a custom prefix for test function names. The prefix can be configured by defining a `SHUNIT_TEST_PREFIX` variable. ## Bug fixes Syntax errors in functions are now treated as test failures. Test now fail when `setup()` or `tearDown()` fail. ## Deprecated features None. ## Known bugs and issues Zsh requires the `shwordsplit` option to be set. See the documentation for examples of how to do this. Line numbers in assert messages do not work properly with BASH 2.x. The Bourne shell of Solaris, BASH 2.x, and Zsh 3.0.x do not properly catch the SIGTERM signal. As such, shell interpreter failures due to such things as unbound variables cannot be caught. (See `shunit_test_misc.sh`) shUnit2 does not work when the `-e` shell option is set (typically done with `set -e`). ## Tested platforms Continuous integration testing is provided by [Travis CI](https://travis-ci.org/). https://travis-ci.org/github/kward/shunit2 Tested OSes: - Linux - macOS Tested shells: - /bin/sh - ash - bash - dash - ksh - pdksh - zsh shunit2-2.1.8/doc/TODO.txt000066400000000000000000000007461363770417100152110ustar00rootroot00000000000000Make it possible to execute a single test by passing the name of the test on the command line Add support for '--randomize-order' so that the test order is randomized to check for dependencies (which shouldn't be there) between tests. --debug option to display point in source code (line number and such) where the problem showed up. assertTrue() just gives 'ASSERT:', nothing else :-(. others too? upd: assertNull() will give message passed, but nothing else useful :-( $Revision$ shunit2-2.1.8/doc/contributors.md000066400000000000000000000007001363770417100167500ustar00rootroot00000000000000The original author of shunit2 is Kate Ward. The following people have contributed in some way or another to shunit2. - [Alex Harvey](https://github.com/alexharv074) - Bryan Larsen - [David Acacio](https://github.com/dacacioa) - Kevin Van Horn - [Maciej Bliziński](https://github.com/automatthias) - Mario Sparada - Mathias Goldau - Richard Jensen - Rob Holland - Rocky Bernstein - [rugk](https://github.com/rugk) - wood4321 (of code.google.com) shunit2-2.1.8/doc/design_doc.txt000066400000000000000000000026561363770417100165440ustar00rootroot00000000000000Design Doc for shUnit shUnit is based upon JUnit. The initial ideas for the script came from the book "Pragmatic Unit Testing - In Java with JUnit" by Andrew Hunt and David Thomas. The script was written to perform unit testing for log4sh. log4sh had grown enough that it was becoming difficult to easily test and and verify that the tests passed for the many different operating systems on which it was being used. The functions in shUnit are meant to match those in JUnit as much as possible where shell allows. In the initial version, there will be no concept of exceptions (as normal POSIX shell has no concept of them) but attempts to trap problems will be done. Programatic Standards: * SHUNIT_TRUE - public global constant * __SHUNIT_SHELL_FLAGS - private global constant * __shunit_oldShellFlags - private global variable * assertEquals - public unit test function * shunit_publicFunc - public shUnit function; can be called from parent unit test script * _shunit_privateFunc - private shUnit function; should not be called from parent script. meant for internal use by shUnit * _su_myVar - variable inside a public function. prefixing with '_su_' to reduce the chances that a variable outside of shUnit will be overridden. * _su__myVar - variable inside a private function. prefixing with '_su__' to reduce the chances that a variable in a shUnit public function, or a variable outside of shUnit will be overridden. $Revision$ shunit2-2.1.8/examples/000077500000000000000000000000001363770417100147455ustar00rootroot00000000000000shunit2-2.1.8/examples/equality_test.sh000077500000000000000000000001721363770417100202000ustar00rootroot00000000000000#! /bin/sh # file: examples/equality_test.sh testEquality() { assertEquals 1 1 } # Load and run shUnit2. . ../shunit2 shunit2-2.1.8/examples/lineno_test.sh000077500000000000000000000006221363770417100176270ustar00rootroot00000000000000#! /bin/sh # file: examples/lineno_test.sh testLineNo() { # This assert will have line numbers included (e.g. "ASSERT:[123] ...") if # they are supported. echo "_ASSERT_EQUALS_ macro value: ${_ASSERT_EQUALS_}" ${_ASSERT_EQUALS_} '"not equal"' 1 2 # This assert will not have line numbers included (e.g. "ASSERT: ..."). assertEquals 'not equal' 1 2 } # Load and run shUnit2. . ../shunit2 shunit2-2.1.8/examples/math.inc000066400000000000000000000002251363770417100163700ustar00rootroot00000000000000# available as examples/math.inc add_generic() { num_a=$1 num_b=$2 expr $1 + $2 } add_bash() { num_a=$1 num_b=$2 echo $(($1 + $2)) } shunit2-2.1.8/examples/math_test.sh000077500000000000000000000007061363770417100172770ustar00rootroot00000000000000#! /bin/sh # file: examples/math_test.sh testAdding() { result=`add_generic 1 2` assertEquals \ "the result of '${result}' was wrong" \ 3 "${result}" # Disable non-generic tests. [ -z "${BASH_VERSION:-}" ] && startSkipping result=`add_bash 1 2` assertEquals \ "the result of '${result}' was wrong" \ 3 "${result}" } oneTimeSetUp() { # Load include to test. . ./math.inc } # Load and run shUnit2. . ../shunit2 shunit2-2.1.8/examples/mkdir_test.sh000077500000000000000000000043301363770417100174510ustar00rootroot00000000000000#!/bin/sh # vim:et:ft=sh:sts=2:sw=2 # # Copyright 2008-2019 Kate Ward. All Rights Reserved. # Released under the Apache 2.0 license. # http://www.apache.org/licenses/LICENSE-2.0 # # shUnit2 -- Unit testing framework for Unix shell scripts. # https://github.com/kward/shunit2 # # Author: kate.ward@forestent.com (Kate Ward) # # Example unit test for the mkdir command. # # There are times when an existing shell script needs to be tested. In this # example, we will test several aspects of the the mkdir command, but the # techniques could be used for any existing shell script. testMissingDirectoryCreation() { ${mkdirCmd} "${testDir}" >${stdoutF} 2>${stderrF} rtrn=$? th_assertTrueWithNoOutput ${rtrn} "${stdoutF}" "${stderrF}" assertTrue 'directory missing' "[ -d '${testDir}' ]" } testExistingDirectoryCreationFails() { # Create a directory to test against. ${mkdirCmd} "${testDir}" # Test for expected failure while trying to create directory that exists. ${mkdirCmd} "${testDir}" >${stdoutF} 2>${stderrF} rtrn=$? assertFalse 'expecting return code of 1 (false)' ${rtrn} assertNull 'unexpected output to stdout' "`cat ${stdoutF}`" assertNotNull 'expected error message to stderr' "`cat ${stderrF}`" assertTrue 'directory missing' "[ -d '${testDir}' ]" } testRecursiveDirectoryCreation() { testDir2="${testDir}/test2" ${mkdirCmd} -p "${testDir2}" >${stdoutF} 2>${stderrF} rtrn=$? th_assertTrueWithNoOutput ${rtrn} "${stdoutF}" "${stderrF}" assertTrue 'first directory missing' "[ -d '${testDir}' ]" assertTrue 'second directory missing' "[ -d '${testDir2}' ]" } th_assertTrueWithNoOutput() { th_return_=$1 th_stdout_=$2 th_stderr_=$3 assertFalse 'unexpected output to STDOUT' "[ -s '${th_stdout_}' ]" assertFalse 'unexpected output to STDERR' "[ -s '${th_stderr_}' ]" unset th_return_ th_stdout_ th_stderr_ } oneTimeSetUp() { outputDir="${SHUNIT_TMPDIR}/output" mkdir "${outputDir}" stdoutF="${outputDir}/stdout" stderrF="${outputDir}/stderr" mkdirCmd='mkdir' # save command name in variable to make future changes easy testDir="${SHUNIT_TMPDIR}/some_test_dir" } tearDown() { rm -fr "${testDir}" } # Load and run shUnit2. [ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0 . ../shunit2 shunit2-2.1.8/examples/mock_file.sh000077500000000000000000000047021363770417100172370ustar00rootroot00000000000000#!/bin/sh # # shUnit2 example for mocking files. # # This example demonstrates two different mechanisms for mocking files on the # system. The first method is preferred for testing specific aspects of a file, # and the second method is preferred when multiple tests need access to the # same mock data. # # When mocking files, the key thing of importance is providing the code under # test with the correct file to read. The best practice for writing code where # files need to be mocked is either: # - Pass the filename to be tested into a function and test that function, or # - Provide a function that returns the name of the filename to be read. # # The first case is preferred whenever possible as it allows the unit test to # be explicit about what is being tested. The second case is useful when the # first case is not achievable. # # For the second case, there are two common methods to mock the filename # returned by the function: # - Provide a special value (e.g. a mock variable) that is only available # during testing, or # - Override something (e.g. the constant) in the test script. # # The first case is preferred as it doesn't require the unit test to alter code # in any way. Yes, it means that the code itself knows that it is under test, # and it behaves slightly differently than under normal conditions, but a # visual inspection of the code by the developer should be sufficient to # validate proper functionality of such a simple function. # Treat unset variables as an error. set -u PASSWD='/etc/passwd' # Read the root UID from the passwd filename provided as the first argument. root_uid_from_passed_filename() { filename=$1 root_uid "${filename}" unset filename } # Read the root UID from the passwd filename derived by call to the # passwd_filename() function. root_uid_from_derived_filename() { root_uid "$(passwd_filename)" } passwd_filename() { if [ -n "${MOCK_PASSWD:-}" ]; then echo "${MOCK_PASSWD}" # Mock file for testing. return fi echo "${PASSWD}" } # Extract the root UID. root_uid() { awk -F: 'u==$1{print $3}' u=root "$1"; } main() { echo "root_uid_from_passed_filename:" root_uid_from_passed_filename "${PASSWD}" echo echo "root_uid_from_derived_filename:" root_uid_from_derived_filename } # Execute main() if this is run in standalone mode (i.e. not in a unit test). ARGV0="$(basename "$0")" argv0="$(echo "${ARGV0}" |sed 's/_test$//;s/_test\.sh$//')" if [ "${ARGV0}" = "${argv0}" ]; then main "$@" fi shunit2-2.1.8/examples/mock_file_test.sh000077500000000000000000000016011363770417100202710ustar00rootroot00000000000000#!/bin/sh # # shUnit2 example for mocking files. MOCK_PASSWD='' # This will be overridden in oneTimeSetUp(). test_root_uid_from_passed_filename() { result="$(root_uid_from_passed_filename "${MOCK_PASSWD}")" assertEquals 'unexpected root uid' '0' "${result}" } test_root_uid_from_derived_filename() { result="$(root_uid_from_derived_filename)" assertEquals 'unexpected root uid' '0' "${result}" } oneTimeSetUp() { # Provide a mock passwd file for testing. This will be cleaned up # automatically by shUnit2. MOCK_PASSWD="${SHUNIT_TMPDIR}/passwd" cat <"${MOCK_PASSWD}" nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false root:*:0:0:System Administrator:/var/root:/bin/sh daemon:*:1:1:System Services:/var/root:/usr/bin/false EOF # Load script under test. . './mock_file.sh' } # Load and run shUnit2. [ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0 . ../shunit2 shunit2-2.1.8/examples/party_test.sh000077500000000000000000000007301363770417100175020ustar00rootroot00000000000000#! /bin/sh # file: examples/party_test.sh # # This test is mostly for fun. Technically, it is a bad example of a unit test # because of the temporal requirement, namely that the year be 1999. A better # test would have been to pass in both a known-bad and known-good year into a # function, and test for the expected result. testPartyLikeItIs1999() { year=`date '+%Y'` assertEquals "It's not 1999 :-(" \ '1999' "${year}" } # Load and run shUnit2. . ../shunit2 shunit2-2.1.8/examples/suite_test.sh000077500000000000000000000017321363770417100174770ustar00rootroot00000000000000#!/bin/sh # file: examples/suite_test.sh # # This test demonstrates the use of suites. Note: the suite functionality is # deprecated as of v2.1.0, and will be removed in a future major release. # suite is a special function called by shUnit2 to setup a suite of tests. It # enables a developer to call a set of functions that contain tests without # needing to rename the functions to start with "test". # # Tests that are to be called from within `suite()` are added to the list of # executable tests by means of the `suite_addTest()` function. suite() { # Add the suite_test_one() function to the list of executable tests. suite_addTest suite_test_one # Call the suite_test_two() function, but note that the test results will not # be added to the global stats, and therefore not reported at the end of the # unit test execution. suite_test_two } suite_test_one() { assertEquals 1 1 } suite_test_two() { assertNotEquals 1 2 } # Load and run shUnit2. . ../shunit2 shunit2-2.1.8/lib/000077500000000000000000000000001363770417100136755ustar00rootroot00000000000000shunit2-2.1.8/lib/shflags000066400000000000000000001127211363770417100152530ustar00rootroot00000000000000# vim:et:ft=sh:sts=2:sw=2 # # Copyright 2008-2017 Kate Ward. All Rights Reserved. # Released under the Apache License 2.0 license. # http://www.apache.org/licenses/LICENSE-2.0 # # shFlags -- Advanced command-line flag library for Unix shell scripts. # https://github.com/kward/shflags # # Author: kate.ward@forestent.com (Kate Ward) # # This module implements something like the gflags library available # from https://github.com/gflags/gflags. # # FLAG TYPES: This is a list of the DEFINE_*'s that you can do. All flags take # a name, default value, help-string, and optional 'short' name (one-letter # name). Some flags have other arguments, which are described with the flag. # # DEFINE_string: takes any input, and interprets it as a string. # # DEFINE_boolean: does not take any arguments. Say --myflag to set # FLAGS_myflag to true, or --nomyflag to set FLAGS_myflag to false. For short # flags, passing the flag on the command-line negates the default value, i.e. # if the default is true, passing the flag sets the value to false. # # DEFINE_float: takes an input and interprets it as a floating point number. As # shell does not support floats per-se, the input is merely validated as # being a valid floating point value. # # DEFINE_integer: takes an input and interprets it as an integer. # # SPECIAL FLAGS: There are a few flags that have special meaning: # --help (or -?) prints a list of all the flags in a human-readable fashion # --flagfile=foo read flags from foo. (not implemented yet) # -- as in getopt(), terminates flag-processing # # EXAMPLE USAGE: # # -- begin hello.sh -- # #! /bin/sh # . ./shflags # DEFINE_string name 'world' "somebody's name" n # FLAGS "$@" || exit $? # eval set -- "${FLAGS_ARGV}" # echo "Hello, ${FLAGS_name}." # -- end hello.sh -- # # $ ./hello.sh -n Kate # Hello, Kate. # # CUSTOMIZABLE BEHAVIOR: # # A script can override the default 'getopt' command by providing the path to # an alternate implementation by defining the FLAGS_GETOPT_CMD variable. # # NOTES: # # * Not all systems include a getopt version that supports long flags. On these # systems, only short flags are recognized. #============================================================================== # shFlags # # Shared attributes: # flags_error: last error message # flags_output: last function output (rarely valid) # flags_return: last return value # # __flags_longNames: list of long names for all flags # __flags_shortNames: list of short names for all flags # __flags_boolNames: list of boolean flag names # # __flags_opts: options parsed by getopt # # Per-flag attributes: # FLAGS_: contains value of flag named 'flag_name' # __flags__default: the default flag value # __flags__help: the flag help string # __flags__short: the flag short name # __flags__type: the flag type # # Notes: # - lists of strings are space separated, and a null value is the '~' char. # ### ShellCheck (http://www.shellcheck.net/) # $() are not fully portable (POSIX != portable). # shellcheck disable=SC2006 # [ p -a q ] are well defined enough (vs [ p ] && [ q ]). # shellcheck disable=SC2166 # Return if FLAGS already loaded. [ -n "${FLAGS_VERSION:-}" ] && return 0 FLAGS_VERSION='1.2.3pre' # Return values that scripts can use. FLAGS_TRUE=0 FLAGS_FALSE=1 FLAGS_ERROR=2 # Logging levels. FLAGS_LEVEL_DEBUG=0 FLAGS_LEVEL_INFO=1 FLAGS_LEVEL_WARN=2 FLAGS_LEVEL_ERROR=3 FLAGS_LEVEL_FATAL=4 __FLAGS_LEVEL_DEFAULT=${FLAGS_LEVEL_WARN} # Determine some reasonable command defaults. __FLAGS_EXPR_CMD='expr --' __FLAGS_UNAME_S=`uname -s` if [ "${__FLAGS_UNAME_S}" = 'BSD' ]; then __FLAGS_EXPR_CMD='gexpr --' else _flags_output_=`${__FLAGS_EXPR_CMD} 2>&1` if [ $? -eq ${FLAGS_TRUE} -a "${_flags_output_}" = '--' ]; then # We are likely running inside BusyBox. __FLAGS_EXPR_CMD='expr' fi unset _flags_output_ fi # Commands a user can override if desired. FLAGS_EXPR_CMD=${FLAGS_EXPR_CMD:-${__FLAGS_EXPR_CMD}} FLAGS_GETOPT_CMD=${FLAGS_GETOPT_CMD:-getopt} # Specific shell checks. if [ -n "${ZSH_VERSION:-}" ]; then setopt |grep "^shwordsplit$" >/dev/null if [ $? -ne ${FLAGS_TRUE} ]; then _flags_fatal 'zsh shwordsplit option is required for proper zsh operation' fi if [ -z "${FLAGS_PARENT:-}" ]; then _flags_fatal "zsh does not pass \$0 through properly. please declare' \ \"FLAGS_PARENT=\$0\" before calling shFlags" fi fi # Can we use built-ins? ( echo "${FLAGS_TRUE#0}"; ) >/dev/null 2>&1 if [ $? -eq ${FLAGS_TRUE} ]; then __FLAGS_USE_BUILTIN=${FLAGS_TRUE} else __FLAGS_USE_BUILTIN=${FLAGS_FALSE} fi # # Constants. # # Reserved flag names. __FLAGS_RESERVED_LIST=' ARGC ARGV ERROR FALSE GETOPT_CMD HELP PARENT TRUE ' __FLAGS_RESERVED_LIST="${__FLAGS_RESERVED_LIST} VERSION " # Determined getopt version (standard or enhanced). __FLAGS_GETOPT_VERS_STD=0 __FLAGS_GETOPT_VERS_ENH=1 # shellcheck disable=SC2120 _flags_getopt_vers() { _flags_getopt_cmd_=${1:-${FLAGS_GETOPT_CMD}} case "`${_flags_getopt_cmd_} -lfoo '' --foo 2>&1`" in ' -- --foo') echo ${__FLAGS_GETOPT_VERS_STD} ;; ' --foo --') echo ${__FLAGS_GETOPT_VERS_ENH} ;; # Unrecognized output. Assuming standard getopt version. *) echo ${__FLAGS_GETOPT_VERS_STD} ;; esac unset _flags_getopt_cmd_ } # shellcheck disable=SC2119 __FLAGS_GETOPT_VERS=`_flags_getopt_vers` # getopt optstring lengths __FLAGS_OPTSTR_SHORT=0 __FLAGS_OPTSTR_LONG=1 __FLAGS_NULL='~' # Flag info strings. __FLAGS_INFO_DEFAULT='default' __FLAGS_INFO_HELP='help' __FLAGS_INFO_SHORT='short' __FLAGS_INFO_TYPE='type' # Flag lengths. __FLAGS_LEN_SHORT=0 __FLAGS_LEN_LONG=1 # Flag types. __FLAGS_TYPE_NONE=0 __FLAGS_TYPE_BOOLEAN=1 __FLAGS_TYPE_FLOAT=2 __FLAGS_TYPE_INTEGER=3 __FLAGS_TYPE_STRING=4 # Set the constants readonly. __flags_constants=`set |awk -F= '/^FLAGS_/ || /^__FLAGS_/ {print $1}'` for __flags_const in ${__flags_constants}; do # Skip certain flags. case ${__flags_const} in FLAGS_HELP) continue ;; FLAGS_PARENT) continue ;; esac # Set flag readonly. if [ -z "${ZSH_VERSION:-}" ]; then readonly "${__flags_const}" continue fi case ${ZSH_VERSION} in [123].*) readonly "${__flags_const}" ;; *) readonly -g "${__flags_const}" ;; # Declare readonly constants globally. esac done unset __flags_const __flags_constants # # Internal variables. # # Space separated lists. __flags_boolNames=' ' # Boolean flag names. __flags_longNames=' ' # Long flag names. __flags_shortNames=' ' # Short flag names. __flags_definedNames=' ' # Defined flag names (used for validation). __flags_columns='' # Screen width in columns. __flags_level=0 # Default logging level. __flags_opts='' # Temporary storage for parsed getopt flags. #------------------------------------------------------------------------------ # Private functions. # # Logging functions. _flags_debug() { [ ${__flags_level} -le ${FLAGS_LEVEL_DEBUG} ] || return echo "flags:DEBUG $*" >&2 } _flags_info() { [ ${__flags_level} -le ${FLAGS_LEVEL_INFO} ] || return echo "flags:INFO $*" >&2 } _flags_warn() { [ ${__flags_level} -le ${FLAGS_LEVEL_WARN} ] || return echo "flags:WARN $*" >&2 } _flags_error() { [ ${__flags_level} -le ${FLAGS_LEVEL_ERROR} ] || return echo "flags:ERROR $*" >&2 } _flags_fatal() { [ ${__flags_level} -le ${FLAGS_LEVEL_FATAL} ] || return echo "flags:FATAL $*" >&2 exit ${FLAGS_ERROR} } # Get the logging level. flags_loggingLevel() { echo ${__flags_level}; } # Set the logging level. # # Args: # _flags_level_: integer: new logging level # Returns: # nothing flags_setLoggingLevel() { [ $# -ne 1 ] && _flags_fatal "flags_setLevel(): logging level missing" _flags_level_=$1 [ "${_flags_level_}" -ge "${FLAGS_LEVEL_DEBUG}" \ -a "${_flags_level_}" -le "${FLAGS_LEVEL_FATAL}" ] \ || _flags_fatal "Invalid logging level '${_flags_level_}' specified." __flags_level=$1 unset _flags_level_ } # Define a flag. # # Calling this function will define the following info variables for the # specified flag: # FLAGS_flagname - the name for this flag (based upon the long flag name) # __flags__default - the default value # __flags_flagname_help - the help string # __flags_flagname_short - the single letter alias # __flags_flagname_type - the type of flag (one of __FLAGS_TYPE_*) # # Args: # _flags_type_: integer: internal type of flag (__FLAGS_TYPE_*) # _flags_name_: string: long flag name # _flags_default_: default flag value # _flags_help_: string: help string # _flags_short_: string: (optional) short flag name # Returns: # integer: success of operation, or error _flags_define() { if [ $# -lt 4 ]; then flags_error='DEFINE error: too few arguments' flags_return=${FLAGS_ERROR} _flags_error "${flags_error}" return ${flags_return} fi _flags_type_=$1 _flags_name_=$2 _flags_default_=$3 _flags_help_=${4:-§} # Special value '§' indicates no help string provided. _flags_short_=${5:-${__FLAGS_NULL}} _flags_debug "type:${_flags_type_} name:${_flags_name_}" \ "default:'${_flags_default_}' help:'${_flags_help_}'" \ "short:${_flags_short_}" _flags_return_=${FLAGS_TRUE} _flags_usName_="`_flags_underscoreName "${_flags_name_}"`" # Check whether the flag name is reserved. _flags_itemInList "${_flags_usName_}" "${__FLAGS_RESERVED_LIST}" if [ $? -eq ${FLAGS_TRUE} ]; then flags_error="flag name (${_flags_name_}) is reserved" _flags_return_=${FLAGS_ERROR} fi # Require short option for getopt that don't support long options. if [ ${_flags_return_} -eq ${FLAGS_TRUE} \ -a "${__FLAGS_GETOPT_VERS}" -ne "${__FLAGS_GETOPT_VERS_ENH}" \ -a "${_flags_short_}" = "${__FLAGS_NULL}" ] then flags_error="short flag required for (${_flags_name_}) on this platform" _flags_return_=${FLAGS_ERROR} fi # Check for existing long name definition. if [ ${_flags_return_} -eq ${FLAGS_TRUE} ]; then if _flags_itemInList "${_flags_usName_}" "${__flags_definedNames}"; then flags_error="definition for ([no]${_flags_name_}) already exists" _flags_warn "${flags_error}" _flags_return_=${FLAGS_FALSE} fi fi # Check for existing short name definition. if [ ${_flags_return_} -eq ${FLAGS_TRUE} \ -a "${_flags_short_}" != "${__FLAGS_NULL}" ] then if _flags_itemInList "${_flags_short_}" "${__flags_shortNames}"; then flags_error="flag short name (${_flags_short_}) already defined" _flags_warn "${flags_error}" _flags_return_=${FLAGS_FALSE} fi fi # Handle default value. Note, on several occasions the 'if' portion of an # if/then/else contains just a ':' which does nothing. A binary reversal via # '!' is not done because it does not work on all shells. if [ ${_flags_return_} -eq ${FLAGS_TRUE} ]; then case ${_flags_type_} in ${__FLAGS_TYPE_BOOLEAN}) if _flags_validBool "${_flags_default_}"; then case ${_flags_default_} in true|t|0) _flags_default_=${FLAGS_TRUE} ;; false|f|1) _flags_default_=${FLAGS_FALSE} ;; esac else flags_error="invalid default flag value '${_flags_default_}'" _flags_return_=${FLAGS_ERROR} fi ;; ${__FLAGS_TYPE_FLOAT}) if _flags_validFloat "${_flags_default_}"; then : else flags_error="invalid default flag value '${_flags_default_}'" _flags_return_=${FLAGS_ERROR} fi ;; ${__FLAGS_TYPE_INTEGER}) if _flags_validInt "${_flags_default_}"; then : else flags_error="invalid default flag value '${_flags_default_}'" _flags_return_=${FLAGS_ERROR} fi ;; ${__FLAGS_TYPE_STRING}) ;; # Everything in shell is a valid string. *) flags_error="unrecognized flag type '${_flags_type_}'" _flags_return_=${FLAGS_ERROR} ;; esac fi if [ ${_flags_return_} -eq ${FLAGS_TRUE} ]; then # Store flag information. eval "FLAGS_${_flags_usName_}='${_flags_default_}'" eval "__flags_${_flags_usName_}_${__FLAGS_INFO_TYPE}=${_flags_type_}" eval "__flags_${_flags_usName_}_${__FLAGS_INFO_DEFAULT}=\ \"${_flags_default_}\"" eval "__flags_${_flags_usName_}_${__FLAGS_INFO_HELP}=\"${_flags_help_}\"" eval "__flags_${_flags_usName_}_${__FLAGS_INFO_SHORT}='${_flags_short_}'" # append flag names to name lists __flags_shortNames="${__flags_shortNames}${_flags_short_} " __flags_longNames="${__flags_longNames}${_flags_name_} " [ "${_flags_type_}" -eq "${__FLAGS_TYPE_BOOLEAN}" ] && \ __flags_boolNames="${__flags_boolNames}no${_flags_name_} " # Append flag names to defined names for later validation checks. __flags_definedNames="${__flags_definedNames}${_flags_usName_} " [ "${_flags_type_}" -eq "${__FLAGS_TYPE_BOOLEAN}" ] && \ __flags_definedNames="${__flags_definedNames}no${_flags_usName_} " fi flags_return=${_flags_return_} unset _flags_default_ _flags_help_ _flags_name_ _flags_return_ \ _flags_short_ _flags_type_ _flags_usName_ [ ${flags_return} -eq ${FLAGS_ERROR} ] && _flags_error "${flags_error}" return ${flags_return} } # Underscore a flag name by replacing dashes with underscores. # # Args: # unnamed: string: log flag name # Output: # string: underscored name _flags_underscoreName() { echo "$1" |tr '-' '_' } # Return valid getopt options using currently defined list of long options. # # This function builds a proper getopt option string for short (and long) # options, using the current list of long options for reference. # # Args: # _flags_optStr: integer: option string type (__FLAGS_OPTSTR_*) # Output: # string: generated option string for getopt # Returns: # boolean: success of operation (always returns True) _flags_genOptStr() { _flags_optStrType_=$1 _flags_opts_='' for _flags_name_ in ${__flags_longNames}; do _flags_usName_="`_flags_underscoreName "${_flags_name_}"`" _flags_type_="`_flags_getFlagInfo "${_flags_usName_}" "${__FLAGS_INFO_TYPE}"`" [ $? -eq ${FLAGS_TRUE} ] || _flags_fatal 'call to _flags_type_ failed' case ${_flags_optStrType_} in ${__FLAGS_OPTSTR_SHORT}) _flags_shortName_="`_flags_getFlagInfo \ "${_flags_usName_}" "${__FLAGS_INFO_SHORT}"`" if [ "${_flags_shortName_}" != "${__FLAGS_NULL}" ]; then _flags_opts_="${_flags_opts_}${_flags_shortName_}" # getopt needs a trailing ':' to indicate a required argument. [ "${_flags_type_}" -ne "${__FLAGS_TYPE_BOOLEAN}" ] && \ _flags_opts_="${_flags_opts_}:" fi ;; ${__FLAGS_OPTSTR_LONG}) _flags_opts_="${_flags_opts_:+${_flags_opts_},}${_flags_name_}" # getopt needs a trailing ':' to indicate a required argument [ "${_flags_type_}" -ne "${__FLAGS_TYPE_BOOLEAN}" ] && \ _flags_opts_="${_flags_opts_}:" ;; esac done echo "${_flags_opts_}" unset _flags_name_ _flags_opts_ _flags_optStrType_ _flags_shortName_ \ _flags_type_ _flags_usName_ return ${FLAGS_TRUE} } # Returns flag details based on a flag name and flag info. # # Args: # string: underscored flag name # string: flag info (see the _flags_define function for valid info types) # Output: # string: value of dereferenced flag variable # Returns: # integer: one of FLAGS_{TRUE|FALSE|ERROR} _flags_getFlagInfo() { # Note: adding gFI to variable names to prevent naming conflicts with calling # functions _flags_gFI_usName_=$1 _flags_gFI_info_=$2 # Example: given argument usName (underscored flag name) of 'my_flag', and # argument info of 'help', set the _flags_infoValue_ variable to the value of # ${__flags_my_flag_help}, and see if it is non-empty. _flags_infoVar_="__flags_${_flags_gFI_usName_}_${_flags_gFI_info_}" _flags_strToEval_="_flags_infoValue_=\"\${${_flags_infoVar_}:-}\"" eval "${_flags_strToEval_}" if [ -n "${_flags_infoValue_}" ]; then # Special value '§' indicates no help string provided. [ "${_flags_gFI_info_}" = ${__FLAGS_INFO_HELP} \ -a "${_flags_infoValue_}" = '§' ] && _flags_infoValue_='' flags_return=${FLAGS_TRUE} else # See if the _flags_gFI_usName_ variable is a string as strings can be # empty... # Note: the DRY principle would say to have this function call itself for # the next three lines, but doing so results in an infinite loop as an # invalid _flags_name_ will also not have the associated _type variable. # Because it doesn't (it will evaluate to an empty string) the logic will # try to find the _type variable of the _type variable, and so on. Not so # good ;-) # # Example cont.: set the _flags_typeValue_ variable to the value of # ${__flags_my_flag_type}, and see if it equals '4'. _flags_typeVar_="__flags_${_flags_gFI_usName_}_${__FLAGS_INFO_TYPE}" _flags_strToEval_="_flags_typeValue_=\"\${${_flags_typeVar_}:-}\"" eval "${_flags_strToEval_}" # shellcheck disable=SC2154 if [ "${_flags_typeValue_}" = "${__FLAGS_TYPE_STRING}" ]; then flags_return=${FLAGS_TRUE} else flags_return=${FLAGS_ERROR} flags_error="missing flag info variable (${_flags_infoVar_})" fi fi echo "${_flags_infoValue_}" unset _flags_gFI_usName_ _flags_gfI_info_ _flags_infoValue_ _flags_infoVar_ \ _flags_strToEval_ _flags_typeValue_ _flags_typeVar_ [ ${flags_return} -eq ${FLAGS_ERROR} ] && _flags_error "${flags_error}" return ${flags_return} } # Check for presence of item in a list. # # Passed a string (e.g. 'abc'), this function will determine if the string is # present in the list of strings (e.g. ' foo bar abc '). # # Args: # _flags_str_: string: string to search for in a list of strings # unnamed: list: list of strings # Returns: # boolean: true if item is in the list _flags_itemInList() { _flags_str_=$1 shift case " ${*:-} " in *\ ${_flags_str_}\ *) flags_return=${FLAGS_TRUE} ;; *) flags_return=${FLAGS_FALSE} ;; esac unset _flags_str_ return ${flags_return} } # Returns the width of the current screen. # # Output: # integer: width in columns of the current screen. _flags_columns() { if [ -z "${__flags_columns}" ]; then if eval stty size >/dev/null 2>&1; then # stty size worked :-) # shellcheck disable=SC2046 set -- `stty size` __flags_columns="${2:-}" fi fi if [ -z "${__flags_columns}" ]; then if eval tput cols >/dev/null 2>&1; then # shellcheck disable=SC2046 set -- `tput cols` __flags_columns="${1:-}" fi fi echo "${__flags_columns:-80}" } # Validate a boolean. # # Args: # _flags__bool: boolean: value to validate # Returns: # bool: true if the value is a valid boolean _flags_validBool() { _flags_bool_=$1 flags_return=${FLAGS_TRUE} case "${_flags_bool_}" in true|t|0) ;; false|f|1) ;; *) flags_return=${FLAGS_FALSE} ;; esac unset _flags_bool_ return ${flags_return} } # Validate a float. # # Args: # _flags_float_: float: value to validate # Returns: # bool: true if the value is a valid integer _flags_validFloat() { flags_return=${FLAGS_FALSE} [ -n "$1" ] || return ${flags_return} _flags_float_=$1 if _flags_validInt "${_flags_float_}"; then flags_return=${FLAGS_TRUE} elif _flags_useBuiltin; then _flags_float_whole_=${_flags_float_%.*} _flags_float_fraction_=${_flags_float_#*.} if _flags_validInt "${_flags_float_whole_:-0}" -a \ _flags_validInt "${_flags_float_fraction_}"; then flags_return=${FLAGS_TRUE} fi unset _flags_float_whole_ _flags_float_fraction_ else flags_return=${FLAGS_TRUE} case ${_flags_float_} in -*) # Negative floats. _flags_test_=`${FLAGS_EXPR_CMD} "${_flags_float_}" :\ '\(-[0-9]*\.[0-9]*\)'` ;; *) # Positive floats. _flags_test_=`${FLAGS_EXPR_CMD} "${_flags_float_}" :\ '\([0-9]*\.[0-9]*\)'` ;; esac [ "${_flags_test_}" != "${_flags_float_}" ] && flags_return=${FLAGS_FALSE} unset _flags_test_ fi unset _flags_float_ _flags_float_whole_ _flags_float_fraction_ return ${flags_return} } # Validate an integer. # # Args: # _flags_int_: integer: value to validate # Returns: # bool: true if the value is a valid integer _flags_validInt() { flags_return=${FLAGS_FALSE} [ -n "$1" ] || return ${flags_return} _flags_int_=$1 case ${_flags_int_} in -*.*) ;; # Ignore negative floats (we'll invalidate them later). -*) # Strip possible leading negative sign. if _flags_useBuiltin; then _flags_int_=${_flags_int_#-} else _flags_int_=`${FLAGS_EXPR_CMD} "${_flags_int_}" : '-\([0-9][0-9]*\)'` fi ;; esac case ${_flags_int_} in *[!0-9]*) flags_return=${FLAGS_FALSE} ;; *) flags_return=${FLAGS_TRUE} ;; esac unset _flags_int_ return ${flags_return} } # Parse command-line options using the standard getopt. # # Note: the flag options are passed around in the global __flags_opts so that # the formatting is not lost due to shell parsing and such. # # Args: # @: varies: command-line options to parse # Returns: # integer: a FLAGS success condition _flags_getoptStandard() { flags_return=${FLAGS_TRUE} _flags_shortOpts_=`_flags_genOptStr ${__FLAGS_OPTSTR_SHORT}` # Check for spaces in passed options. for _flags_opt_ in "$@"; do # Note: the silliness with the x's is purely for ksh93 on Ubuntu 6.06. _flags_match_=`echo "x${_flags_opt_}x" |sed 's/ //g'` if [ "${_flags_match_}" != "x${_flags_opt_}x" ]; then flags_error='the available getopt does not support spaces in options' flags_return=${FLAGS_ERROR} break fi done if [ ${flags_return} -eq ${FLAGS_TRUE} ]; then __flags_opts=`getopt "${_flags_shortOpts_}" "$@" 2>&1` _flags_rtrn_=$? if [ ${_flags_rtrn_} -ne ${FLAGS_TRUE} ]; then _flags_warn "${__flags_opts}" flags_error='unable to parse provided options with getopt.' flags_return=${FLAGS_ERROR} fi fi unset _flags_match_ _flags_opt_ _flags_rtrn_ _flags_shortOpts_ return ${flags_return} } # Parse command-line options using the enhanced getopt. # # Note: the flag options are passed around in the global __flags_opts so that # the formatting is not lost due to shell parsing and such. # # Args: # @: varies: command-line options to parse # Returns: # integer: a FLAGS success condition _flags_getoptEnhanced() { flags_return=${FLAGS_TRUE} _flags_shortOpts_=`_flags_genOptStr ${__FLAGS_OPTSTR_SHORT}` _flags_boolOpts_=`echo "${__flags_boolNames}" \ |sed 's/^ *//;s/ *$//;s/ /,/g'` _flags_longOpts_=`_flags_genOptStr ${__FLAGS_OPTSTR_LONG}` __flags_opts=`${FLAGS_GETOPT_CMD} \ -o "${_flags_shortOpts_}" \ -l "${_flags_longOpts_},${_flags_boolOpts_}" \ -- "$@" 2>&1` _flags_rtrn_=$? if [ ${_flags_rtrn_} -ne ${FLAGS_TRUE} ]; then _flags_warn "${__flags_opts}" flags_error='unable to parse provided options with getopt.' flags_return=${FLAGS_ERROR} fi unset _flags_boolOpts_ _flags_longOpts_ _flags_rtrn_ _flags_shortOpts_ return ${flags_return} } # Dynamically parse a getopt result and set appropriate variables. # # This function does the actual conversion of getopt output and runs it through # the standard case structure for parsing. The case structure is actually quite # dynamic to support any number of flags. # # Args: # argc: int: original command-line argument count # @: varies: output from getopt parsing # Returns: # integer: a FLAGS success condition _flags_parseGetopt() { _flags_argc_=$1 shift flags_return=${FLAGS_TRUE} if [ "${__FLAGS_GETOPT_VERS}" -ne "${__FLAGS_GETOPT_VERS_ENH}" ]; then # The @$ must be unquoted as it needs to be re-split. # shellcheck disable=SC2068 set -- $@ else # Note the quotes around the `$@' -- they are essential! eval set -- "$@" fi # Provide user with the number of arguments to shift by later. # NOTE: the FLAGS_ARGC variable is obsolete as of 1.0.3 because it does not # properly give user access to non-flag arguments mixed in between flag # arguments. Its usage was replaced by FLAGS_ARGV, and it is being kept only # for backwards compatibility reasons. FLAGS_ARGC=`_flags_math "$# - 1 - ${_flags_argc_}"` export FLAGS_ARGC # Handle options. note options with values must do an additional shift. while true; do _flags_opt_=$1 _flags_arg_=${2:-} _flags_type_=${__FLAGS_TYPE_NONE} _flags_name_='' # Determine long flag name. case "${_flags_opt_}" in --) shift; break ;; # Discontinue option parsing. --*) # Long option. if _flags_useBuiltin; then _flags_opt_=${_flags_opt_#*--} else _flags_opt_=`${FLAGS_EXPR_CMD} "${_flags_opt_}" : '--\(.*\)'` fi _flags_len_=${__FLAGS_LEN_LONG} if _flags_itemInList "${_flags_opt_}" "${__flags_longNames}"; then _flags_name_=${_flags_opt_} else # Check for negated long boolean version. if _flags_itemInList "${_flags_opt_}" "${__flags_boolNames}"; then if _flags_useBuiltin; then _flags_name_=${_flags_opt_#*no} else _flags_name_=`${FLAGS_EXPR_CMD} "${_flags_opt_}" : 'no\(.*\)'` fi _flags_type_=${__FLAGS_TYPE_BOOLEAN} _flags_arg_=${__FLAGS_NULL} fi fi ;; -*) # Short option. if _flags_useBuiltin; then _flags_opt_=${_flags_opt_#*-} else _flags_opt_=`${FLAGS_EXPR_CMD} "${_flags_opt_}" : '-\(.*\)'` fi _flags_len_=${__FLAGS_LEN_SHORT} if _flags_itemInList "${_flags_opt_}" "${__flags_shortNames}"; then # Yes. Match short name to long name. Note purposeful off-by-one # (too high) with awk calculations. _flags_pos_=`echo "${__flags_shortNames}" \ |awk 'BEGIN{RS=" ";rn=0}$0==e{rn=NR}END{print rn}' \ e="${_flags_opt_}"` _flags_name_=`echo "${__flags_longNames}" \ |awk 'BEGIN{RS=" "}rn==NR{print $0}' rn="${_flags_pos_}"` fi ;; esac # Die if the flag was unrecognized. if [ -z "${_flags_name_}" ]; then flags_error="unrecognized option (${_flags_opt_})" flags_return=${FLAGS_ERROR} break fi # Set new flag value. _flags_usName_=`_flags_underscoreName "${_flags_name_}"` [ ${_flags_type_} -eq ${__FLAGS_TYPE_NONE} ] && \ _flags_type_=`_flags_getFlagInfo \ "${_flags_usName_}" ${__FLAGS_INFO_TYPE}` case ${_flags_type_} in ${__FLAGS_TYPE_BOOLEAN}) if [ ${_flags_len_} -eq ${__FLAGS_LEN_LONG} ]; then if [ "${_flags_arg_}" != "${__FLAGS_NULL}" ]; then eval "FLAGS_${_flags_usName_}=${FLAGS_TRUE}" else eval "FLAGS_${_flags_usName_}=${FLAGS_FALSE}" fi else _flags_strToEval_="_flags_val_=\ \${__flags_${_flags_usName_}_${__FLAGS_INFO_DEFAULT}}" eval "${_flags_strToEval_}" # shellcheck disable=SC2154 if [ "${_flags_val_}" -eq ${FLAGS_FALSE} ]; then eval "FLAGS_${_flags_usName_}=${FLAGS_TRUE}" else eval "FLAGS_${_flags_usName_}=${FLAGS_FALSE}" fi fi ;; ${__FLAGS_TYPE_FLOAT}) if _flags_validFloat "${_flags_arg_}"; then eval "FLAGS_${_flags_usName_}='${_flags_arg_}'" else flags_error="invalid float value (${_flags_arg_})" flags_return=${FLAGS_ERROR} break fi ;; ${__FLAGS_TYPE_INTEGER}) if _flags_validInt "${_flags_arg_}"; then eval "FLAGS_${_flags_usName_}='${_flags_arg_}'" else flags_error="invalid integer value (${_flags_arg_})" flags_return=${FLAGS_ERROR} break fi ;; ${__FLAGS_TYPE_STRING}) eval "FLAGS_${_flags_usName_}='${_flags_arg_}'" ;; esac # Handle special case help flag. if [ "${_flags_usName_}" = 'help' ]; then # shellcheck disable=SC2154 if [ "${FLAGS_help}" -eq ${FLAGS_TRUE} ]; then flags_help flags_error='help requested' flags_return=${FLAGS_FALSE} break fi fi # Shift the option and non-boolean arguments out. shift [ "${_flags_type_}" != ${__FLAGS_TYPE_BOOLEAN} ] && shift done # Give user back non-flag arguments. FLAGS_ARGV='' while [ $# -gt 0 ]; do FLAGS_ARGV="${FLAGS_ARGV:+${FLAGS_ARGV} }'$1'" shift done unset _flags_arg_ _flags_len_ _flags_name_ _flags_opt_ _flags_pos_ \ _flags_strToEval_ _flags_type_ _flags_usName_ _flags_val_ return ${flags_return} } # Perform some path using built-ins. # # Args: # $@: string: math expression to evaluate # Output: # integer: the result # Returns: # bool: success of math evaluation _flags_math() { if [ $# -eq 0 ]; then flags_return=${FLAGS_FALSE} elif _flags_useBuiltin; then # Variable assignment is needed as workaround for Solaris Bourne shell, # which cannot parse a bare $((expression)). # shellcheck disable=SC2016 _flags_expr_='$(($@))' eval echo ${_flags_expr_} flags_return=$? unset _flags_expr_ else eval expr "$@" flags_return=$? fi return ${flags_return} } # Cross-platform strlen() implementation. # # Args: # _flags_str: string: to determine length of # Output: # integer: length of string # Returns: # bool: success of strlen evaluation _flags_strlen() { _flags_str_=${1:-} if [ -z "${_flags_str_}" ]; then flags_output=0 elif _flags_useBuiltin; then flags_output=${#_flags_str_} else flags_output=`${FLAGS_EXPR_CMD} "${_flags_str_}" : '.*'` fi flags_return=$? unset _flags_str_ echo "${flags_output}" return ${flags_return} } # Use built-in helper function to enable unit testing. # # Args: # None # Returns: # bool: true if built-ins should be used _flags_useBuiltin() { return ${__FLAGS_USE_BUILTIN}; } #------------------------------------------------------------------------------ # public functions # # A basic boolean flag. Boolean flags do not take any arguments, and their # value is either 1 (false) or 0 (true). For long flags, the false value is # specified on the command line by prepending the word 'no'. With short flags, # the presence of the flag toggles the current value between true and false. # Specifying a short boolean flag twice on the command results in returning the # value back to the default value. # # A default value is required for boolean flags. # # For example, lets say a Boolean flag was created whose long name was 'update' # and whose short name was 'x', and the default value was 'false'. This flag # could be explicitly set to 'true' with '--update' or by '-x', and it could be # explicitly set to 'false' with '--noupdate'. DEFINE_boolean() { _flags_define ${__FLAGS_TYPE_BOOLEAN} "$@"; } # Other basic flags. DEFINE_float() { _flags_define ${__FLAGS_TYPE_FLOAT} "$@"; } DEFINE_integer() { _flags_define ${__FLAGS_TYPE_INTEGER} "$@"; } DEFINE_string() { _flags_define ${__FLAGS_TYPE_STRING} "$@"; } # Parse the flags. # # Args: # unnamed: list: command-line flags to parse # Returns: # integer: success of operation, or error FLAGS() { # Define a standard 'help' flag if one isn't already defined. [ -z "${__flags_help_type:-}" ] && \ DEFINE_boolean 'help' false 'show this help' 'h' # Parse options. if [ $# -gt 0 ]; then if [ "${__FLAGS_GETOPT_VERS}" -ne "${__FLAGS_GETOPT_VERS_ENH}" ]; then _flags_getoptStandard "$@" else _flags_getoptEnhanced "$@" fi flags_return=$? else # Nothing passed; won't bother running getopt. __flags_opts='--' flags_return=${FLAGS_TRUE} fi if [ ${flags_return} -eq ${FLAGS_TRUE} ]; then _flags_parseGetopt $# "${__flags_opts}" flags_return=$? fi [ ${flags_return} -eq ${FLAGS_ERROR} ] && _flags_fatal "${flags_error}" return ${flags_return} } # This is a helper function for determining the 'getopt' version for platforms # where the detection isn't working. It simply outputs debug information that # can be included in a bug report. # # Args: # none # Output: # debug info that can be included in a bug report # Returns: # nothing flags_getoptInfo() { # Platform info. _flags_debug "uname -a: `uname -a`" _flags_debug "PATH: ${PATH}" # Shell info. if [ -n "${BASH_VERSION:-}" ]; then _flags_debug 'shell: bash' _flags_debug "BASH_VERSION: ${BASH_VERSION}" elif [ -n "${ZSH_VERSION:-}" ]; then _flags_debug 'shell: zsh' _flags_debug "ZSH_VERSION: ${ZSH_VERSION}" fi # getopt info. ${FLAGS_GETOPT_CMD} >/dev/null _flags_getoptReturn=$? _flags_debug "getopt return: ${_flags_getoptReturn}" _flags_debug "getopt --version: `${FLAGS_GETOPT_CMD} --version 2>&1`" unset _flags_getoptReturn } # Returns whether the detected getopt version is the enhanced version. # # Args: # none # Output: # none # Returns: # bool: true if getopt is the enhanced version flags_getoptIsEnh() { test "${__FLAGS_GETOPT_VERS}" -eq "${__FLAGS_GETOPT_VERS_ENH}" } # Returns whether the detected getopt version is the standard version. # # Args: # none # Returns: # bool: true if getopt is the standard version flags_getoptIsStd() { test "${__FLAGS_GETOPT_VERS}" -eq "${__FLAGS_GETOPT_VERS_STD}" } # This is effectively a 'usage()' function. It prints usage information and # exits the program with ${FLAGS_FALSE} if it is ever found in the command line # arguments. Note this function can be overridden so other apps can define # their own --help flag, replacing this one, if they want. # # Args: # none # Returns: # integer: success of operation (always returns true) flags_help() { if [ -n "${FLAGS_HELP:-}" ]; then echo "${FLAGS_HELP}" >&2 else echo "USAGE: ${FLAGS_PARENT:-$0} [flags] args" >&2 fi if [ -n "${__flags_longNames}" ]; then echo 'flags:' >&2 for flags_name_ in ${__flags_longNames}; do flags_flagStr_='' flags_boolStr_='' flags_usName_=`_flags_underscoreName "${flags_name_}"` flags_default_=`_flags_getFlagInfo \ "${flags_usName_}" ${__FLAGS_INFO_DEFAULT}` flags_help_=`_flags_getFlagInfo \ "${flags_usName_}" ${__FLAGS_INFO_HELP}` flags_short_=`_flags_getFlagInfo \ "${flags_usName_}" ${__FLAGS_INFO_SHORT}` flags_type_=`_flags_getFlagInfo \ "${flags_usName_}" ${__FLAGS_INFO_TYPE}` [ "${flags_short_}" != "${__FLAGS_NULL}" ] && \ flags_flagStr_="-${flags_short_}" if [ "${__FLAGS_GETOPT_VERS}" -eq "${__FLAGS_GETOPT_VERS_ENH}" ]; then [ "${flags_short_}" != "${__FLAGS_NULL}" ] && \ flags_flagStr_="${flags_flagStr_}," # Add [no] to long boolean flag names, except the 'help' flag. [ "${flags_type_}" -eq ${__FLAGS_TYPE_BOOLEAN} \ -a "${flags_usName_}" != 'help' ] && \ flags_boolStr_='[no]' flags_flagStr_="${flags_flagStr_}--${flags_boolStr_}${flags_name_}:" fi case ${flags_type_} in ${__FLAGS_TYPE_BOOLEAN}) if [ "${flags_default_}" -eq ${FLAGS_TRUE} ]; then flags_defaultStr_='true' else flags_defaultStr_='false' fi ;; ${__FLAGS_TYPE_FLOAT}|${__FLAGS_TYPE_INTEGER}) flags_defaultStr_=${flags_default_} ;; ${__FLAGS_TYPE_STRING}) flags_defaultStr_="'${flags_default_}'" ;; esac flags_defaultStr_="(default: ${flags_defaultStr_})" flags_helpStr_=" ${flags_flagStr_} ${flags_help_:+${flags_help_} }${flags_defaultStr_}" _flags_strlen "${flags_helpStr_}" >/dev/null flags_helpStrLen_=${flags_output} flags_columns_=`_flags_columns` if [ "${flags_helpStrLen_}" -lt "${flags_columns_}" ]; then echo "${flags_helpStr_}" >&2 else echo " ${flags_flagStr_} ${flags_help_}" >&2 # Note: the silliness with the x's is purely for ksh93 on Ubuntu 6.06 # because it doesn't like empty strings when used in this manner. flags_emptyStr_="`echo \"x${flags_flagStr_}x\" \ |awk '{printf "%"length($0)-2"s", ""}'`" flags_helpStr_=" ${flags_emptyStr_} ${flags_defaultStr_}" _flags_strlen "${flags_helpStr_}" >/dev/null flags_helpStrLen_=${flags_output} if [ "${__FLAGS_GETOPT_VERS}" -eq "${__FLAGS_GETOPT_VERS_STD}" \ -o "${flags_helpStrLen_}" -lt "${flags_columns_}" ]; then # Indented to match help string. echo "${flags_helpStr_}" >&2 else # Indented four from left to allow for longer defaults as long flag # names might be used too, making things too long. echo " ${flags_defaultStr_}" >&2 fi fi done fi unset flags_boolStr_ flags_default_ flags_defaultStr_ flags_emptyStr_ \ flags_flagStr_ flags_help_ flags_helpStr flags_helpStrLen flags_name_ \ flags_columns_ flags_short_ flags_type_ flags_usName_ return ${FLAGS_TRUE} } # Reset shflags back to an uninitialized state. # # Args: # none # Returns: # nothing flags_reset() { for flags_name_ in ${__flags_longNames}; do flags_usName_=`_flags_underscoreName "${flags_name_}"` flags_strToEval_="unset FLAGS_${flags_usName_}" for flags_type_ in \ ${__FLAGS_INFO_DEFAULT} \ ${__FLAGS_INFO_HELP} \ ${__FLAGS_INFO_SHORT} \ ${__FLAGS_INFO_TYPE} do flags_strToEval_=\ "${flags_strToEval_} __flags_${flags_usName_}_${flags_type_}" done eval "${flags_strToEval_}" done # Reset internal variables. __flags_boolNames=' ' __flags_longNames=' ' __flags_shortNames=' ' __flags_definedNames=' ' # Reset logging level back to default. flags_setLoggingLevel ${__FLAGS_LEVEL_DEFAULT} unset flags_name_ flags_type_ flags_strToEval_ flags_usName_ } # # Initialization # # Set the default logging level. flags_setLoggingLevel ${__FLAGS_LEVEL_DEFAULT} shunit2-2.1.8/lib/versions000077500000000000000000000175311363770417100155020ustar00rootroot00000000000000#! /bin/sh # vim:et:ft=sh:sts=2:sw=2 # # Versions determines the versions of all installed shells. # # Copyright 2008-2018 Kate Ward. All Rights Reserved. # Released under the Apache 2.0 License. # # Author: kate.ward@forestent.com (Kate Ward) # https://github.com/kward/shlib # # This library provides reusable functions that determine actual names and # versions of installed shells and the OS. The library can also be run as a # script if set executable. # # Disable checks that aren't fully portable (POSIX != portable). # shellcheck disable=SC2006 ARGV0=`basename "$0"` LSB_RELEASE='/etc/lsb-release' VERSIONS_SHELLS='ash /bin/bash /bin/dash /bin/ksh /bin/pdksh /bin/zsh /bin/sh /usr/xpg4/bin/sh /sbin/sh' true; TRUE=$? false; FALSE=$? ERROR=2 UNAME_R=`uname -r` UNAME_S=`uname -s` __versions_haveStrings=${ERROR} versions_osName() { os_name_='unrecognized' os_system_=${UNAME_S} os_release_=${UNAME_R} case ${os_system_} in CYGWIN_NT-*) os_name_='Cygwin' ;; Darwin) os_name_=`/usr/bin/sw_vers -productName` os_version_=`versions_osVersion` case ${os_version_} in 10.4|10.4.[0-9]*) os_name_='Mac OS X Tiger' ;; 10.5|10.5.[0-9]*) os_name_='Mac OS X Leopard' ;; 10.6|10.6.[0-9]*) os_name_='Mac OS X Snow Leopard' ;; 10.7|10.7.[0-9]*) os_name_='Mac OS X Lion' ;; 10.8|10.8.[0-9]*) os_name_='Mac OS X Mountain Lion' ;; 10.9|10.9.[0-9]*) os_name_='Mac OS X Mavericks' ;; 10.10|10.10.[0-9]*) os_name_='Mac OS X Yosemite' ;; 10.11|10.11.[0-9]*) os_name_='Mac OS X El Capitan' ;; 10.12|10.12.[0-9]*) os_name_='macOS Sierra' ;; 10.13|10.13.[0-9]*) os_name_='macOS High Sierra' ;; 10.14|10.14.[0-9]*) os_name_='macOS Mojave' ;; *) os_name_='macOS' ;; esac ;; FreeBSD) os_name_='FreeBSD' ;; Linux) os_name_='Linux' ;; SunOS) os_name_='SunOS' if [ -r '/etc/release' ]; then if grep 'OpenSolaris' /etc/release >/dev/null; then os_name_='OpenSolaris' else os_name_='Solaris' fi fi ;; esac echo ${os_name_} unset os_name_ os_system_ os_release_ os_version_ } versions_osVersion() { os_version_='unrecognized' os_system_=${UNAME_S} os_release_=${UNAME_R} case ${os_system_} in CYGWIN_NT-*) os_version_=`expr "${os_release_}" : '\([0-9]*\.[0-9]\.[0-9]*\).*'` ;; Darwin) os_version_=`/usr/bin/sw_vers -productVersion` ;; FreeBSD) os_version_=`expr "${os_release_}" : '\([0-9]*\.[0-9]*\)-.*'` ;; Linux) if [ -r '/etc/os-release' ]; then os_version_=`awk -F= '$1~/PRETTY_NAME/{print $2}' /etc/os-release \ |sed 's/"//g'` elif [ -r '/etc/redhat-release' ]; then os_version_=`cat /etc/redhat-release` elif [ -r '/etc/SuSE-release' ]; then os_version_=`head -n 1 /etc/SuSE-release` elif [ -r "${LSB_RELEASE}" ]; then if grep -q 'DISTRIB_ID=Ubuntu' "${LSB_RELEASE}"; then # shellcheck disable=SC2002 os_version_=`cat "${LSB_RELEASE}" \ |awk -F= '$1~/DISTRIB_DESCRIPTION/{print $2}' \ |sed 's/"//g;s/ /-/g'` fi fi ;; SunOS) if [ -r '/etc/release' ]; then if grep 'OpenSolaris' /etc/release >/dev/null; then # OpenSolaris os_version_=`grep 'OpenSolaris' /etc/release |awk '{print $2"("$3")"}'` else # Solaris major_=`echo "${os_release_}" |sed 's/[0-9]*\.\([0-9]*\)/\1/'` minor_=`grep Solaris /etc/release |sed 's/[^u]*\(u[0-9]*\).*/\1/'` os_version_="${major_}${minor_}" fi fi ;; esac echo "${os_version_}" unset os_release_ os_system_ os_version_ major_ minor_ } versions_shellVersion() { shell_=$1 shell_present_=${FALSE} case "${shell_}" in ash) [ -x '/bin/busybox' ] && shell_present_=${TRUE} ;; *) [ -x "${shell_}" ] && shell_present_=${TRUE} ;; esac if [ ${shell_present_} -eq ${FALSE} ]; then echo 'not installed' return ${FALSE} fi version_='' case ${shell_} in /sbin/sh) ;; # SunOS /usr/xpg4/bin/sh) version_=`versions_shell_xpg4 "${shell_}"` ;; # SunOS */sh) # This could be one of any number of shells. Try until one fits. version_='' [ -z "${version_}" ] && version_=`versions_shell_bash "${shell_}"` # dash cannot be self determined yet [ -z "${version_}" ] && version_=`versions_shell_ksh "${shell_}"` # pdksh is covered in versions_shell_ksh() [ -z "${version_}" ] && version_=`versions_shell_xpg4 "${shell_}"` [ -z "${version_}" ] && version_=`versions_shell_zsh "${shell_}"` ;; ash) version_=`versions_shell_ash "${shell_}"` ;; */bash) version_=`versions_shell_bash "${shell_}"` ;; */dash) # Assuming Ubuntu Linux until somebody comes up with a better test. The # following test will return an empty string if dash is not installed. version_=`versions_shell_dash` ;; */ksh) version_=`versions_shell_ksh "${shell_}"` ;; */pdksh) version_=`versions_shell_pdksh "${shell_}"` ;; */zsh) version_=`versions_shell_zsh "${shell_}"` ;; *) version_='invalid' esac echo "${version_:-unknown}" unset shell_ version_ } # The ash shell is included in BusyBox. versions_shell_ash() { busybox --help |head -1 |sed 's/BusyBox v\([0-9.]*\) .*/\1/' } versions_shell_bash() { $1 --version : 2>&1 |grep 'GNU bash' |sed 's/.*version \([^ ]*\).*/\1/' } versions_shell_dash() { eval dpkg >/dev/null 2>&1 [ $? -eq 127 ] && return # Return if dpkg not found. dpkg -l |grep ' dash ' |awk '{print $3}' } versions_shell_ksh() { versions_shell_=$1 versions_version_='' # Try a few different ways to figure out the version. versions_version_=`${versions_shell_} --version : 2>&1` # shellcheck disable=SC2181 if [ $? -eq 0 ]; then versions_version_=`echo "${versions_version_}" \ |sed 's/.*\([0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\).*/\1/'` else versions_version_='' fi if [ -z "${versions_version_}" ]; then _versions_have_strings versions_version_=`strings "${versions_shell_}" 2>&1 \ |grep Version \ |sed 's/^.*Version \(.*\)$/\1/;s/ s+ \$$//;s/ /-/g'` fi if [ -z "${versions_version_}" ]; then versions_version_=`versions_shell_pdksh "${versions_shell_}"` fi echo "${versions_version_}" unset versions_shell_ versions_version_ } versions_shell_pdksh() { _versions_have_strings strings "$1" 2>&1 \ |grep 'PD KSH' \ |sed -e 's/.*PD KSH \(.*\)/\1/;s/ /-/g' } versions_shell_xpg4() { _versions_have_strings strings "$1" 2>&1 \ |grep 'Version' \ |sed -e 's/^@(#)Version //' } versions_shell_zsh() { versions_shell_=$1 # Try a few different ways to figure out the version. # shellcheck disable=SC2016 versions_version_=`echo 'echo ${ZSH_VERSION}' |${versions_shell_}` if [ -z "${versions_version_}" ]; then versions_version_=`${versions_shell_} --version : 2>&1` # shellcheck disable=SC2181 if [ $? -eq 0 ]; then versions_version_=`echo "${versions_version_}" |awk '{print $2}'` else versions_version_='' fi fi echo "${versions_version_}" unset versions_shell_ versions_version_ } # Determine if the 'strings' binary installed. _versions_have_strings() { [ ${__versions_haveStrings} -ne ${ERROR} ] && return if eval strings /dev/null >/dev/null 2>&1; then __versions_haveStrings=${TRUE} return fi echo 'WARN: strings not installed. try installing binutils?' >&2 __versions_haveStrings=${FALSE} } versions_main() { # Treat unset variables as an error. set -u os_name=`versions_osName` os_version=`versions_osVersion` echo "os: ${os_name} version: ${os_version}" for shell in ${VERSIONS_SHELLS}; do shell_version=`versions_shellVersion "${shell}"` echo "shell: ${shell} version: ${shell_version}" done } if [ "${ARGV0}" = 'versions' ]; then versions_main "$@" fi shunit2-2.1.8/shunit2000077500000000000000000001151221363770417100144530ustar00rootroot00000000000000#! /bin/sh # vim:et:ft=sh:sts=2:sw=2 # # Copyright 2008-2020 Kate Ward. All Rights Reserved. # Released under the Apache 2.0 license. # http://www.apache.org/licenses/LICENSE-2.0 # # shUnit2 -- Unit testing framework for Unix shell scripts. # https://github.com/kward/shunit2 # # Author: kate.ward@forestent.com (Kate Ward) # # shUnit2 is a xUnit based unit test framework for Bourne shell scripts. It is # based on the popular JUnit unit testing framework for Java. # # $() are not fully portable (POSIX != portable). # shellcheck disable=SC2006 # expr may be antiquated, but it is the only solution in some cases. # shellcheck disable=SC2003 # Return if shunit2 already loaded. command [ -n "${SHUNIT_VERSION:-}" ] && exit 0 SHUNIT_VERSION='2.1.8' # Return values that scripts can use. SHUNIT_TRUE=0 SHUNIT_FALSE=1 SHUNIT_ERROR=2 # Logging functions. _shunit_warn() { ${__SHUNIT_CMD_ECHO_ESC} \ "${__shunit_ansi_yellow}shunit2:WARN${__shunit_ansi_none} $*" >&2 } _shunit_error() { ${__SHUNIT_CMD_ECHO_ESC} \ "${__shunit_ansi_red}shunit2:ERROR${__shunit_ansi_none} $*" >&2 } _shunit_fatal() { ${__SHUNIT_CMD_ECHO_ESC} \ "${__shunit_ansi_red}shunit2:FATAL${__shunit_ansi_none} $*" >&2 exit ${SHUNIT_ERROR} } # Determine some reasonable command defaults. __SHUNIT_CMD_ECHO_ESC='echo -e' # shellcheck disable=SC2039 command [ "`echo -e test`" = '-e test' ] && __SHUNIT_CMD_ECHO_ESC='echo' __SHUNIT_UNAME_S=`uname -s` case "${__SHUNIT_UNAME_S}" in BSD) __SHUNIT_CMD_EXPR='gexpr' ;; *) __SHUNIT_CMD_EXPR='expr' ;; esac __SHUNIT_CMD_TPUT='tput' # Commands a user can override if needed. SHUNIT_CMD_EXPR=${SHUNIT_CMD_EXPR:-${__SHUNIT_CMD_EXPR}} SHUNIT_CMD_TPUT=${SHUNIT_CMD_TPUT:-${__SHUNIT_CMD_TPUT}} # Enable color output. Options are 'never', 'always', or 'auto'. SHUNIT_COLOR=${SHUNIT_COLOR:-auto} # Specific shell checks. if command [ -n "${ZSH_VERSION:-}" ]; then setopt |grep "^shwordsplit$" >/dev/null if command [ $? -ne ${SHUNIT_TRUE} ]; then _shunit_fatal 'zsh shwordsplit option is required for proper operation' fi if command [ -z "${SHUNIT_PARENT:-}" ]; then _shunit_fatal "zsh does not pass \$0 through properly. please declare \ \"SHUNIT_PARENT=\$0\" before calling shUnit2" fi fi # # Constants # __SHUNIT_MODE_SOURCED='sourced' __SHUNIT_MODE_STANDALONE='standalone' __SHUNIT_PARENT=${SHUNIT_PARENT:-$0} # User provided test prefix to display in front of the name of the test being # executed. Define by setting the SHUNIT_TEST_PREFIX variable. __SHUNIT_TEST_PREFIX=${SHUNIT_TEST_PREFIX:-} # ANSI colors. __SHUNIT_ANSI_NONE='\033[0m' __SHUNIT_ANSI_RED='\033[1;31m' __SHUNIT_ANSI_GREEN='\033[1;32m' __SHUNIT_ANSI_YELLOW='\033[1;33m' __SHUNIT_ANSI_CYAN='\033[1;36m' # Set the constants readonly. __shunit_constants=`set |grep '^__SHUNIT_' |cut -d= -f1` echo "${__shunit_constants}" |grep '^Binary file' >/dev/null && \ __shunit_constants=`set |grep -a '^__SHUNIT_' |cut -d= -f1` for __shunit_const in ${__shunit_constants}; do if command [ -z "${ZSH_VERSION:-}" ]; then readonly "${__shunit_const}" else case ${ZSH_VERSION} in [123].*) readonly "${__shunit_const}" ;; *) readonly -g "${__shunit_const}" # Declare readonly constants globally. esac fi done unset __shunit_const __shunit_constants # # Internal variables. # # Variables. __shunit_lineno='' # Line number of executed test. __shunit_mode=${__SHUNIT_MODE_SOURCED} # Operating mode. __shunit_reportGenerated=${SHUNIT_FALSE} # Is report generated. __shunit_script='' # Filename of unittest script (standalone mode). __shunit_skip=${SHUNIT_FALSE} # Is skipping enabled. __shunit_suite='' # Suite of tests to execute. __shunit_clean=${SHUNIT_FALSE} # _shunit_cleanup() was already called. # ANSI colors (populated by _shunit_configureColor()). __shunit_ansi_none='' __shunit_ansi_red='' __shunit_ansi_green='' __shunit_ansi_yellow='' __shunit_ansi_cyan='' # Counts of tests. __shunit_testSuccess=${SHUNIT_TRUE} __shunit_testsTotal=0 __shunit_testsPassed=0 __shunit_testsFailed=0 # Counts of asserts. __shunit_assertsTotal=0 __shunit_assertsPassed=0 __shunit_assertsFailed=0 __shunit_assertsSkipped=0 # # Macros. # # shellcheck disable=SC2016,SC2089 _SHUNIT_LINENO_='eval __shunit_lineno=""; if command [ "${1:-}" = "--lineno" ]; then command [ -n "$2" ] && __shunit_lineno="[$2] "; shift 2; fi' #----------------------------------------------------------------------------- # Assertion functions. # # Assert that two values are equal to one another. # # Args: # message: string: failure message [optional] # expected: string: expected value # actual: string: actual value # Returns: # integer: success (TRUE/FALSE/ERROR constant) assertEquals() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} if command [ $# -lt 2 -o $# -gt 3 ]; then _shunit_error "assertEquals() requires two or three arguments; $# given" _shunit_assertFail return ${SHUNIT_ERROR} fi _shunit_shouldSkip && return ${SHUNIT_TRUE} shunit_message_=${__shunit_lineno} if command [ $# -eq 3 ]; then shunit_message_="${shunit_message_}$1" shift fi shunit_expected_=$1 shunit_actual_=$2 shunit_return=${SHUNIT_TRUE} if command [ "${shunit_expected_}" = "${shunit_actual_}" ]; then _shunit_assertPass else failNotEquals "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}" shunit_return=${SHUNIT_FALSE} fi unset shunit_message_ shunit_expected_ shunit_actual_ return ${shunit_return} } # shellcheck disable=SC2016,SC2034 _ASSERT_EQUALS_='eval assertEquals --lineno "${LINENO:-}"' # Assert that two values are not equal to one another. # # Args: # message: string: failure message [optional] # expected: string: expected value # actual: string: actual value # Returns: # integer: success (TRUE/FALSE/ERROR constant) assertNotEquals() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} if command [ $# -lt 2 -o $# -gt 3 ]; then _shunit_error "assertNotEquals() requires two or three arguments; $# given" _shunit_assertFail return ${SHUNIT_ERROR} fi _shunit_shouldSkip && return ${SHUNIT_TRUE} shunit_message_=${__shunit_lineno} if command [ $# -eq 3 ]; then shunit_message_="${shunit_message_}$1" shift fi shunit_expected_=$1 shunit_actual_=$2 shunit_return=${SHUNIT_TRUE} if command [ "${shunit_expected_}" != "${shunit_actual_}" ]; then _shunit_assertPass else failSame "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}" shunit_return=${SHUNIT_FALSE} fi unset shunit_message_ shunit_expected_ shunit_actual_ return ${shunit_return} } # shellcheck disable=SC2016,SC2034 _ASSERT_NOT_EQUALS_='eval assertNotEquals --lineno "${LINENO:-}"' # Assert that a container contains a content. # # Args: # message: string: failure message [optional] # container: string: container to analyze # content: string: content to find # Returns: # integer: success (TRUE/FALSE/ERROR constant) assertContains() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} if command [ $# -lt 2 -o $# -gt 3 ]; then _shunit_error "assertContains() requires two or three arguments; $# given" _shunit_assertFail return ${SHUNIT_ERROR} fi _shunit_shouldSkip && return ${SHUNIT_TRUE} shunit_message_=${__shunit_lineno} if command [ $# -eq 3 ]; then shunit_message_="${shunit_message_}$1" shift fi shunit_container_=$1 shunit_content_=$2 shunit_return=${SHUNIT_TRUE} if echo "$shunit_container_" | grep -F -- "$shunit_content_" > /dev/null; then _shunit_assertPass else failNotFound "${shunit_message_}" "${shunit_content_}" shunit_return=${SHUNIT_FALSE} fi unset shunit_message_ shunit_container_ shunit_content_ return ${shunit_return} } # shellcheck disable=SC2016,SC2034 _ASSERT_CONTAINS_='eval assertContains --lineno "${LINENO:-}"' # Assert that a container does not contain a content. # # Args: # message: string: failure message [optional] # container: string: container to analyze # content: string: content to look for # Returns: # integer: success (TRUE/FALSE/ERROR constant) assertNotContains() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} if command [ $# -lt 2 -o $# -gt 3 ]; then _shunit_error "assertNotContains() requires two or three arguments; $# given" _shunit_assertFail return ${SHUNIT_ERROR} fi _shunit_shouldSkip && return ${SHUNIT_TRUE} shunit_message_=${__shunit_lineno} if command [ $# -eq 3 ]; then shunit_message_="${shunit_message_}$1" shift fi shunit_container_=$1 shunit_content_=$2 shunit_return=${SHUNIT_TRUE} if echo "$shunit_container_" | grep -F -- "$shunit_content_" > /dev/null; then failFound "${shunit_message_}" "${shunit_content_}" shunit_return=${SHUNIT_FALSE} else _shunit_assertPass fi unset shunit_message_ shunit_container_ shunit_content_ return ${shunit_return} } # shellcheck disable=SC2016,SC2034 _ASSERT_NOT_CONTAINS_='eval assertNotContains --lineno "${LINENO:-}"' # Assert that a value is null (i.e. an empty string) # # Args: # message: string: failure message [optional] # actual: string: actual value # Returns: # integer: success (TRUE/FALSE/ERROR constant) assertNull() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} if command [ $# -lt 1 -o $# -gt 2 ]; then _shunit_error "assertNull() requires one or two arguments; $# given" _shunit_assertFail return ${SHUNIT_ERROR} fi _shunit_shouldSkip && return ${SHUNIT_TRUE} shunit_message_=${__shunit_lineno} if command [ $# -eq 2 ]; then shunit_message_="${shunit_message_}$1" shift fi assertTrue "${shunit_message_}" "[ -z '$1' ]" shunit_return=$? unset shunit_message_ return ${shunit_return} } # shellcheck disable=SC2016,SC2034 _ASSERT_NULL_='eval assertNull --lineno "${LINENO:-}"' # Assert that a value is not null (i.e. a non-empty string) # # Args: # message: string: failure message [optional] # actual: string: actual value # Returns: # integer: success (TRUE/FALSE/ERROR constant) assertNotNull() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} if command [ $# -gt 2 ]; then # allowing 0 arguments as $1 might actually be null _shunit_error "assertNotNull() requires one or two arguments; $# given" _shunit_assertFail return ${SHUNIT_ERROR} fi _shunit_shouldSkip && return ${SHUNIT_TRUE} shunit_message_=${__shunit_lineno} if command [ $# -eq 2 ]; then shunit_message_="${shunit_message_}$1" shift fi shunit_actual_=`_shunit_escapeCharactersInString "${1:-}"` test -n "${shunit_actual_}" assertTrue "${shunit_message_}" $? shunit_return=$? unset shunit_actual_ shunit_message_ return ${shunit_return} } # shellcheck disable=SC2016,SC2034 _ASSERT_NOT_NULL_='eval assertNotNull --lineno "${LINENO:-}"' # Assert that two values are the same (i.e. equal to one another). # # Args: # message: string: failure message [optional] # expected: string: expected value # actual: string: actual value # Returns: # integer: success (TRUE/FALSE/ERROR constant) assertSame() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} if command [ $# -lt 2 -o $# -gt 3 ]; then _shunit_error "assertSame() requires two or three arguments; $# given" _shunit_assertFail return ${SHUNIT_ERROR} fi _shunit_shouldSkip && return ${SHUNIT_TRUE} shunit_message_=${__shunit_lineno} if command [ $# -eq 3 ]; then shunit_message_="${shunit_message_}$1" shift fi assertEquals "${shunit_message_}" "$1" "$2" shunit_return=$? unset shunit_message_ return ${shunit_return} } # shellcheck disable=SC2016,SC2034 _ASSERT_SAME_='eval assertSame --lineno "${LINENO:-}"' # Assert that two values are not the same (i.e. not equal to one another). # # Args: # message: string: failure message [optional] # expected: string: expected value # actual: string: actual value # Returns: # integer: success (TRUE/FALSE/ERROR constant) assertNotSame() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} if command [ $# -lt 2 -o $# -gt 3 ]; then _shunit_error "assertNotSame() requires two or three arguments; $# given" _shunit_assertFail return ${SHUNIT_ERROR} fi _shunit_shouldSkip && return ${SHUNIT_TRUE} shunit_message_=${__shunit_lineno} if command [ $# -eq 3 ]; then shunit_message_="${shunit_message_:-}$1" shift fi assertNotEquals "${shunit_message_}" "$1" "$2" shunit_return=$? unset shunit_message_ return ${shunit_return} } # shellcheck disable=SC2016,SC2034 _ASSERT_NOT_SAME_='eval assertNotSame --lineno "${LINENO:-}"' # Assert that a value or shell test condition is true. # # In shell, a value of 0 is true and a non-zero value is false. Any integer # value passed can thereby be tested. # # Shell supports much more complicated tests though, and a means to support # them was needed. As such, this function tests that conditions are true or # false through evaluation rather than just looking for a true or false. # # The following test will succeed: # assertTrue 0 # assertTrue "[ 34 -gt 23 ]" # The following test will fail with a message: # assertTrue 123 # assertTrue "test failed" "[ -r '/non/existent/file' ]" # # Args: # message: string: failure message [optional] # condition: string: integer value or shell conditional statement # Returns: # integer: success (TRUE/FALSE/ERROR constant) assertTrue() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} if command [ $# -lt 1 -o $# -gt 2 ]; then _shunit_error "assertTrue() takes one or two arguments; $# given" _shunit_assertFail return ${SHUNIT_ERROR} fi _shunit_shouldSkip && return ${SHUNIT_TRUE} shunit_message_=${__shunit_lineno} if command [ $# -eq 2 ]; then shunit_message_="${shunit_message_}$1" shift fi shunit_condition_=$1 # See if condition is an integer, i.e. a return value. shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'` shunit_return=${SHUNIT_TRUE} if command [ -z "${shunit_condition_}" ]; then # Null condition. shunit_return=${SHUNIT_FALSE} elif command [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ] then # Possible return value. Treating 0 as true, and non-zero as false. command [ "${shunit_condition_}" -ne 0 ] && shunit_return=${SHUNIT_FALSE} else # Hopefully... a condition. ( eval "${shunit_condition_}" ) >/dev/null 2>&1 command [ $? -ne 0 ] && shunit_return=${SHUNIT_FALSE} fi # Record the test. if command [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then _shunit_assertPass else _shunit_assertFail "${shunit_message_}" fi unset shunit_message_ shunit_condition_ shunit_match_ return ${shunit_return} } # shellcheck disable=SC2016,SC2034 _ASSERT_TRUE_='eval assertTrue --lineno "${LINENO:-}"' # Assert that a value or shell test condition is false. # # In shell, a value of 0 is true and a non-zero value is false. Any integer # value passed can thereby be tested. # # Shell supports much more complicated tests though, and a means to support # them was needed. As such, this function tests that conditions are true or # false through evaluation rather than just looking for a true or false. # # The following test will succeed: # assertFalse 1 # assertFalse "[ 'apples' = 'oranges' ]" # The following test will fail with a message: # assertFalse 0 # assertFalse "test failed" "[ 1 -eq 1 -a 2 -eq 2 ]" # # Args: # message: string: failure message [optional] # condition: string: integer value or shell conditional statement # Returns: # integer: success (TRUE/FALSE/ERROR constant) assertFalse() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} if command [ $# -lt 1 -o $# -gt 2 ]; then _shunit_error "assertFalse() requires one or two arguments; $# given" _shunit_assertFail return ${SHUNIT_ERROR} fi _shunit_shouldSkip && return ${SHUNIT_TRUE} shunit_message_=${__shunit_lineno} if command [ $# -eq 2 ]; then shunit_message_="${shunit_message_}$1" shift fi shunit_condition_=$1 # See if condition is an integer, i.e. a return value. shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'` shunit_return=${SHUNIT_TRUE} if command [ -z "${shunit_condition_}" ]; then # Null condition. shunit_return=${SHUNIT_FALSE} elif command [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ] then # Possible return value. Treating 0 as true, and non-zero as false. command [ "${shunit_condition_}" -eq 0 ] && shunit_return=${SHUNIT_FALSE} else # Hopefully... a condition. ( eval "${shunit_condition_}" ) >/dev/null 2>&1 command [ $? -eq 0 ] && shunit_return=${SHUNIT_FALSE} fi # Record the test. if command [ "${shunit_return}" -eq "${SHUNIT_TRUE}" ]; then _shunit_assertPass else _shunit_assertFail "${shunit_message_}" fi unset shunit_message_ shunit_condition_ shunit_match_ return "${shunit_return}" } # shellcheck disable=SC2016,SC2034 _ASSERT_FALSE_='eval assertFalse --lineno "${LINENO:-}"' #----------------------------------------------------------------------------- # Failure functions. # # Records a test failure. # # Args: # message: string: failure message [optional] # Returns: # integer: success (TRUE/FALSE/ERROR constant) fail() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} if command [ $# -gt 1 ]; then _shunit_error "fail() requires zero or one arguments; $# given" return ${SHUNIT_ERROR} fi _shunit_shouldSkip && return ${SHUNIT_TRUE} shunit_message_=${__shunit_lineno} if command [ $# -eq 1 ]; then shunit_message_="${shunit_message_}$1" shift fi _shunit_assertFail "${shunit_message_}" unset shunit_message_ return ${SHUNIT_FALSE} } # shellcheck disable=SC2016,SC2034 _FAIL_='eval fail --lineno "${LINENO:-}"' # Records a test failure, stating two values were not equal. # # Args: # message: string: failure message [optional] # expected: string: expected value # actual: string: actual value # Returns: # integer: success (TRUE/FALSE/ERROR constant) failNotEquals() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} if command [ $# -lt 2 -o $# -gt 3 ]; then _shunit_error "failNotEquals() requires one or two arguments; $# given" return ${SHUNIT_ERROR} fi _shunit_shouldSkip && return ${SHUNIT_TRUE} shunit_message_=${__shunit_lineno} if command [ $# -eq 3 ]; then shunit_message_="${shunit_message_}$1" shift fi shunit_expected_=$1 shunit_actual_=$2 shunit_message_=${shunit_message_%% } _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected:<${shunit_expected_}> but was:<${shunit_actual_}>" unset shunit_message_ shunit_expected_ shunit_actual_ return ${SHUNIT_FALSE} } # shellcheck disable=SC2016,SC2034 _FAIL_NOT_EQUALS_='eval failNotEquals --lineno "${LINENO:-}"' # Records a test failure, stating a value was found. # # Args: # message: string: failure message [optional] # content: string: found value # Returns: # integer: success (TRUE/FALSE/ERROR constant) failFound() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} if command [ $# -lt 1 -o $# -gt 2 ]; then _shunit_error "failFound() requires one or two arguments; $# given" return ${SHUNIT_ERROR} fi _shunit_shouldSkip && return ${SHUNIT_TRUE} shunit_message_=${__shunit_lineno} if command [ $# -eq 2 ]; then shunit_message_="${shunit_message_}$1" shift fi shunit_message_=${shunit_message_%% } _shunit_assertFail "${shunit_message_:+${shunit_message_} }Found" unset shunit_message_ return ${SHUNIT_FALSE} } # shellcheck disable=SC2016,SC2034 _FAIL_FOUND_='eval failFound --lineno "${LINENO:-}"' # Records a test failure, stating a content was not found. # # Args: # message: string: failure message [optional] # content: string: content not found # Returns: # integer: success (TRUE/FALSE/ERROR constant) failNotFound() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} if command [ $# -lt 1 -o $# -gt 2 ]; then _shunit_error "failNotFound() requires one or two arguments; $# given" return ${SHUNIT_ERROR} fi _shunit_shouldSkip && return ${SHUNIT_TRUE} shunit_message_=${__shunit_lineno} if command [ $# -eq 2 ]; then shunit_message_="${shunit_message_}$1" shift fi shunit_content_=$1 shunit_message_=${shunit_message_%% } _shunit_assertFail "${shunit_message_:+${shunit_message_} }Not found:<${shunit_content_}>" unset shunit_message_ shunit_content_ return ${SHUNIT_FALSE} } # shellcheck disable=SC2016,SC2034 _FAIL_NOT_FOUND_='eval failNotFound --lineno "${LINENO:-}"' # Records a test failure, stating two values should have been the same. # # Args: # message: string: failure message [optional] # expected: string: expected value # actual: string: actual value # Returns: # integer: success (TRUE/FALSE/ERROR constant) failSame() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} if command [ $# -lt 2 -o $# -gt 3 ]; then _shunit_error "failSame() requires two or three arguments; $# given" return ${SHUNIT_ERROR} fi _shunit_shouldSkip && return ${SHUNIT_TRUE} shunit_message_=${__shunit_lineno} if command [ $# -eq 3 ]; then shunit_message_="${shunit_message_}$1" shift fi shunit_message_=${shunit_message_%% } _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected not same" unset shunit_message_ return ${SHUNIT_FALSE} } # shellcheck disable=SC2016,SC2034 _FAIL_SAME_='eval failSame --lineno "${LINENO:-}"' # Records a test failure, stating two values were not equal. # # This is functionally equivalent to calling failNotEquals(). # # Args: # message: string: failure message [optional] # expected: string: expected value # actual: string: actual value # Returns: # integer: success (TRUE/FALSE/ERROR constant) failNotSame() { # shellcheck disable=SC2090 ${_SHUNIT_LINENO_} if command [ $# -lt 2 -o $# -gt 3 ]; then _shunit_error "failNotSame() requires one or two arguments; $# given" return ${SHUNIT_ERROR} fi _shunit_shouldSkip && return ${SHUNIT_TRUE} shunit_message_=${__shunit_lineno} if command [ $# -eq 3 ]; then shunit_message_="${shunit_message_}$1" shift fi failNotEquals "${shunit_message_}" "$1" "$2" shunit_return=$? unset shunit_message_ return ${shunit_return} } # shellcheck disable=SC2016,SC2034 _FAIL_NOT_SAME_='eval failNotSame --lineno "${LINENO:-}"' #----------------------------------------------------------------------------- # Skipping functions. # # Force remaining assert and fail functions to be "skipped". # # This function forces the remaining assert and fail functions to be "skipped", # i.e. they will have no effect. Each function skipped will be recorded so that # the total of asserts and fails will not be altered. # # Args: # None startSkipping() { __shunit_skip=${SHUNIT_TRUE}; } # Resume the normal recording behavior of assert and fail calls. # # Args: # None endSkipping() { __shunit_skip=${SHUNIT_FALSE}; } # Returns the state of assert and fail call skipping. # # Args: # None # Returns: # boolean: (TRUE/FALSE constant) isSkipping() { return ${__shunit_skip}; } #----------------------------------------------------------------------------- # Suite functions. # # Stub. This function should contains all unit test calls to be made. # # DEPRECATED (as of 2.1.0) # # This function can be optionally overridden by the user in their test suite. # # If this function exists, it will be called when shunit2 is sourced. If it # does not exist, shunit2 will search the parent script for all functions # beginning with the word 'test', and they will be added dynamically to the # test suite. # # This function should be overridden by the user in their unit test suite. # Note: see _shunit_mktempFunc() for actual implementation # # Args: # None #suite() { :; } # DO NOT UNCOMMENT THIS FUNCTION # Adds a function name to the list of tests schedule for execution. # # This function should only be called from within the suite() function. # # Args: # function: string: name of a function to add to current unit test suite suite_addTest() { shunit_func_=${1:-} __shunit_suite="${__shunit_suite:+${__shunit_suite} }${shunit_func_}" __shunit_testsTotal=`expr ${__shunit_testsTotal} + 1` unset shunit_func_ } # Stub. This function will be called once before any tests are run. # # Common one-time environment preparation tasks shared by all tests can be # defined here. # # This function should be overridden by the user in their unit test suite. # Note: see _shunit_mktempFunc() for actual implementation # # Args: # None #oneTimeSetUp() { :; } # DO NOT UNCOMMENT THIS FUNCTION # Stub. This function will be called once after all tests are finished. # # Common one-time environment cleanup tasks shared by all tests can be defined # here. # # This function should be overridden by the user in their unit test suite. # Note: see _shunit_mktempFunc() for actual implementation # # Args: # None #oneTimeTearDown() { :; } # DO NOT UNCOMMENT THIS FUNCTION # Stub. This function will be called before each test is run. # # Common environment preparation tasks shared by all tests can be defined here. # # This function should be overridden by the user in their unit test suite. # Note: see _shunit_mktempFunc() for actual implementation # # Args: # None #setUp() { :; } # DO NOT UNCOMMENT THIS FUNCTION # Note: see _shunit_mktempFunc() for actual implementation # Stub. This function will be called after each test is run. # # Common environment cleanup tasks shared by all tests can be defined here. # # This function should be overridden by the user in their unit test suite. # Note: see _shunit_mktempFunc() for actual implementation # # Args: # None #tearDown() { :; } # DO NOT UNCOMMENT THIS FUNCTION #------------------------------------------------------------------------------ # Internal shUnit2 functions. # # Create a temporary directory to store various run-time files in. # # This function is a cross-platform temporary directory creation tool. Not all # OSes have the `mktemp` function, so one is included here. # # Args: # None # Outputs: # string: the temporary directory that was created _shunit_mktempDir() { # Try the standard `mktemp` function. ( exec mktemp -dqt shunit.XXXXXX 2>/dev/null ) && return # The standard `mktemp` didn't work. Use our own. # shellcheck disable=SC2039 if command [ -r '/dev/urandom' -a -x '/usr/bin/od' ]; then _shunit_random_=`/usr/bin/od -vAn -N4 -tx4 "${_shunit_file_}" #! /bin/sh exit ${SHUNIT_TRUE} EOF command chmod +x "${_shunit_file_}" done unset _shunit_file_ } # Final cleanup function to leave things as we found them. # # Besides removing the temporary directory, this function is in charge of the # final exit code of the unit test. The exit code is based on how the script # was ended (e.g. normal exit, or via Ctrl-C). # # Args: # name: string: name of the trap called (specified when trap defined) _shunit_cleanup() { _shunit_name_=$1 case "${_shunit_name_}" in EXIT) ;; INT) _shunit_signal_=130 ;; # 2+128 TERM) _shunit_signal_=143 ;; # 15+128 *) _shunit_error "unrecognized trap value (${_shunit_name_})" _shunit_signal_=0 ;; esac if command [ "${_shunit_name_}" != 'EXIT' ]; then _shunit_warn "trapped and now handling the (${_shunit_name_}) signal" fi # Do our work. if command [ ${__shunit_clean} -eq ${SHUNIT_FALSE} ]; then # Ensure tear downs are only called once. __shunit_clean=${SHUNIT_TRUE} tearDown command [ $? -eq ${SHUNIT_TRUE} ] \ || _shunit_warn "tearDown() returned non-zero return code." oneTimeTearDown command [ $? -eq ${SHUNIT_TRUE} ] \ || _shunit_warn "oneTimeTearDown() returned non-zero return code." command rm -fr "${__shunit_tmpDir}" fi if command [ "${_shunit_name_}" != 'EXIT' ]; then # Handle all non-EXIT signals. trap - 0 # Disable EXIT trap. exit ${_shunit_signal_} elif command [ ${__shunit_reportGenerated} -eq ${SHUNIT_FALSE} ]; then _shunit_assertFail 'unknown failure encountered running a test' _shunit_generateReport exit ${SHUNIT_ERROR} fi unset _shunit_name_ _shunit_signal_ } # configureColor based on user color preference. # # Args: # color: string: color mode (one of `always`, `auto`, or `none`). _shunit_configureColor() { _shunit_color_=${SHUNIT_FALSE} # By default, no color. case $1 in 'always') _shunit_color_=${SHUNIT_TRUE} ;; 'auto') command [ "`_shunit_colors`" -ge 8 ] && _shunit_color_=${SHUNIT_TRUE} ;; 'none') ;; *) _shunit_fatal "unrecognized color option '$1'" ;; esac case ${_shunit_color_} in ${SHUNIT_TRUE}) __shunit_ansi_none=${__SHUNIT_ANSI_NONE} __shunit_ansi_red=${__SHUNIT_ANSI_RED} __shunit_ansi_green=${__SHUNIT_ANSI_GREEN} __shunit_ansi_yellow=${__SHUNIT_ANSI_YELLOW} __shunit_ansi_cyan=${__SHUNIT_ANSI_CYAN} ;; ${SHUNIT_FALSE}) __shunit_ansi_none='' __shunit_ansi_red='' __shunit_ansi_green='' __shunit_ansi_yellow='' __shunit_ansi_cyan='' ;; esac unset _shunit_color_ _shunit_tput_ } # colors returns the number of supported colors for the TERM. _shunit_colors() { _shunit_tput_=`${SHUNIT_CMD_TPUT} colors 2>/dev/null` if command [ $? -eq 0 ]; then echo "${_shunit_tput_}" else echo 16 fi unset _shunit_tput_ } # The actual running of the tests happens here. # # Args: # None _shunit_execSuite() { for _shunit_test_ in ${__shunit_suite}; do __shunit_testSuccess=${SHUNIT_TRUE} # Disable skipping. endSkipping # Execute the per-test setup function. setUp command [ $? -eq ${SHUNIT_TRUE} ] \ || _shunit_fatal "setup() returned non-zero return code." # Execute the test. echo "${__SHUNIT_TEST_PREFIX}${_shunit_test_}" eval "${_shunit_test_}" if command [ $? -ne ${SHUNIT_TRUE} ]; then _shunit_error "${_shunit_test_}() returned non-zero return code." __shunit_testSuccess=${SHUNIT_ERROR} _shunit_incFailedCount fi # Execute the per-test tear-down function. tearDown command [ $? -eq ${SHUNIT_TRUE} ] \ || _shunit_fatal "tearDown() returned non-zero return code." # Update stats. if command [ ${__shunit_testSuccess} -eq ${SHUNIT_TRUE} ]; then __shunit_testsPassed=`expr ${__shunit_testsPassed} + 1` else __shunit_testsFailed=`expr ${__shunit_testsFailed} + 1` fi done unset _shunit_test_ } # Generates the user friendly report with appropriate OK/FAILED message. # # Args: # None # Output: # string: the report of successful and failed tests, as well as totals. _shunit_generateReport() { command [ "${__shunit_reportGenerated}" -eq ${SHUNIT_TRUE} ] && return _shunit_ok_=${SHUNIT_TRUE} # If no exit code was provided, determine an appropriate one. command [ "${__shunit_testsFailed}" -gt 0 \ -o ${__shunit_testSuccess} -eq ${SHUNIT_FALSE} ] \ && _shunit_ok_=${SHUNIT_FALSE} echo _shunit_msg_="Ran ${__shunit_ansi_cyan}${__shunit_testsTotal}${__shunit_ansi_none}" if command [ "${__shunit_testsTotal}" -eq 1 ]; then ${__SHUNIT_CMD_ECHO_ESC} "${_shunit_msg_} test." else ${__SHUNIT_CMD_ECHO_ESC} "${_shunit_msg_} tests." fi if command [ ${_shunit_ok_} -eq ${SHUNIT_TRUE} ]; then _shunit_msg_="${__shunit_ansi_green}OK${__shunit_ansi_none}" command [ "${__shunit_assertsSkipped}" -gt 0 ] \ && _shunit_msg_="${_shunit_msg_} (${__shunit_ansi_yellow}skipped=${__shunit_assertsSkipped}${__shunit_ansi_none})" else _shunit_msg_="${__shunit_ansi_red}FAILED${__shunit_ansi_none}" _shunit_msg_="${_shunit_msg_} (${__shunit_ansi_red}failures=${__shunit_assertsFailed}${__shunit_ansi_none}" command [ "${__shunit_assertsSkipped}" -gt 0 ] \ && _shunit_msg_="${_shunit_msg_},${__shunit_ansi_yellow}skipped=${__shunit_assertsSkipped}${__shunit_ansi_none}" _shunit_msg_="${_shunit_msg_})" fi echo ${__SHUNIT_CMD_ECHO_ESC} "${_shunit_msg_}" __shunit_reportGenerated=${SHUNIT_TRUE} unset _shunit_msg_ _shunit_ok_ } # Test for whether a function should be skipped. # # Args: # None # Returns: # boolean: whether the test should be skipped (TRUE/FALSE constant) _shunit_shouldSkip() { command [ ${__shunit_skip} -eq ${SHUNIT_FALSE} ] && return ${SHUNIT_FALSE} _shunit_assertSkip } # Records a successful test. # # Args: # None _shunit_assertPass() { __shunit_assertsPassed=`expr ${__shunit_assertsPassed} + 1` __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1` } # Records a test failure. # # Args: # message: string: failure message to provide user _shunit_assertFail() { __shunit_testSuccess=${SHUNIT_FALSE} _shunit_incFailedCount \[ $# -gt 0 ] && ${__SHUNIT_CMD_ECHO_ESC} \ "${__shunit_ansi_red}ASSERT:${__shunit_ansi_none}$*" } # Increment the count of failed asserts. # # Args: # none _shunit_incFailedCount() { __shunit_assertsFailed=`expr "${__shunit_assertsFailed}" + 1` __shunit_assertsTotal=`expr "${__shunit_assertsTotal}" + 1` } # Records a skipped test. # # Args: # None _shunit_assertSkip() { __shunit_assertsSkipped=`expr "${__shunit_assertsSkipped}" + 1` __shunit_assertsTotal=`expr "${__shunit_assertsTotal}" + 1` } # Prepare a script filename for sourcing. # # Args: # script: string: path to a script to source # Returns: # string: filename prefixed with ./ (if necessary) _shunit_prepForSourcing() { _shunit_script_=$1 case "${_shunit_script_}" in /*|./*) echo "${_shunit_script_}" ;; *) echo "./${_shunit_script_}" ;; esac unset _shunit_script_ } # Escape a character in a string. # # Args: # c: string: unescaped character # s: string: to escape character in # Returns: # string: with escaped character(s) _shunit_escapeCharInStr() { command [ -n "$2" ] || return # No point in doing work on an empty string. # Note: using shorter variable names to prevent conflicts with # _shunit_escapeCharactersInString(). _shunit_c_=$1 _shunit_s_=$2 # Escape the character. # shellcheck disable=SC1003,SC2086 echo ''${_shunit_s_}'' |command sed 's/\'${_shunit_c_}'/\\\'${_shunit_c_}'/g' unset _shunit_c_ _shunit_s_ } # Escape a character in a string. # # Args: # str: string: to escape characters in # Returns: # string: with escaped character(s) _shunit_escapeCharactersInString() { command [ -n "$1" ] || return # No point in doing work on an empty string. _shunit_str_=$1 # Note: using longer variable names to prevent conflicts with # _shunit_escapeCharInStr(). for _shunit_char_ in '"' '$' "'" '`'; do _shunit_str_=`_shunit_escapeCharInStr "${_shunit_char_}" "${_shunit_str_}"` done echo "${_shunit_str_}" unset _shunit_char_ _shunit_str_ } # Extract list of functions to run tests against. # # Args: # script: string: name of script to extract functions from # Returns: # string: of function names _shunit_extractTestFunctions() { _shunit_script_=$1 # Extract the lines with test function names, strip of anything besides the # function name, and output everything on a single line. _shunit_regex_='^\s*((function test[A-Za-z0-9_-]*)|(test[A-Za-z0-9_-]* *\(\)))' # shellcheck disable=SC2196 egrep "${_shunit_regex_}" "${_shunit_script_}" \ |command sed 's/^[^A-Za-z0-9_-]*//;s/^function //;s/\([A-Za-z0-9_-]*\).*/\1/g' \ |xargs unset _shunit_regex_ _shunit_script_ } #------------------------------------------------------------------------------ # Main. # # Determine the operating mode. if command [ $# -eq 0 -o "${1:-}" = '--' ]; then __shunit_script=${__SHUNIT_PARENT} __shunit_mode=${__SHUNIT_MODE_SOURCED} else __shunit_script=$1 command [ -r "${__shunit_script}" ] || \ _shunit_fatal "unable to read from ${__shunit_script}" __shunit_mode=${__SHUNIT_MODE_STANDALONE} fi # Create a temporary storage location. __shunit_tmpDir=`_shunit_mktempDir` # Provide a public temporary directory for unit test scripts. # TODO(kward): document this. SHUNIT_TMPDIR="${__shunit_tmpDir}/tmp" command mkdir "${SHUNIT_TMPDIR}" # Setup traps to clean up after ourselves. trap '_shunit_cleanup EXIT' 0 trap '_shunit_cleanup INT' 2 trap '_shunit_cleanup TERM' 15 # Create phantom functions to work around issues with Cygwin. _shunit_mktempFunc PATH="${__shunit_tmpDir}:${PATH}" # Make sure phantom functions are executable. This will bite if `/tmp` (or the # current `$TMPDIR`) points to a path on a partition that was mounted with the # 'noexec' option. The noexec command was created with `_shunit_mktempFunc()`. noexec 2>/dev/null || _shunit_fatal \ 'Please declare TMPDIR with path on partition with exec permission.' # We must manually source the tests in standalone mode. if command [ "${__shunit_mode}" = "${__SHUNIT_MODE_STANDALONE}" ]; then # shellcheck disable=SC1090 command . "`_shunit_prepForSourcing \"${__shunit_script}\"`" fi # Configure default output coloring behavior. _shunit_configureColor "${SHUNIT_COLOR}" # Execute the oneTimeSetUp function (if it exists). oneTimeSetUp command [ $? -eq ${SHUNIT_TRUE} ] \ || _shunit_fatal "oneTimeSetUp() returned non-zero return code." # Command line selected tests or suite selected tests if command [ "$#" -ge 2 ]; then # Argument $1 is either the filename of tests or '--'; either way, skip it. shift # Remaining arguments ($2 .. $#) are assumed to be test function names. # Interate through all remaining args in "$@" in a POSIX (likely portable) way. # Helpful tip: https://unix.stackexchange.com/questions/314032/how-to-use-arguments-like-1-2-in-a-for-loop for _shunit_arg_ do suite_addTest "${_shunit_arg_}" done unset _shunit_arg_ else # Execute the suite function defined in the parent test script. # DEPRECATED as of 2.1.0. suite fi # If no tests or suite specified, dynamically build a list of functions. if command [ -z "${__shunit_suite}" ]; then shunit_funcs_=`_shunit_extractTestFunctions "${__shunit_script}"` for shunit_func_ in ${shunit_funcs_}; do suite_addTest "${shunit_func_}" done fi unset shunit_func_ shunit_funcs_ # Execute the suite of unit tests. _shunit_execSuite # Execute the oneTimeTearDown function (if it exists). oneTimeTearDown command [ $? -eq ${SHUNIT_TRUE} ] \ || _shunit_fatal "oneTimeTearDown() returned non-zero return code." # Generate a report summary. _shunit_generateReport # That's it folks. command [ "${__shunit_testsFailed}" -eq 0 ] exit $? shunit2-2.1.8/shunit2_args_test.sh000077500000000000000000000040071363770417100171360ustar00rootroot00000000000000#!/bin/sh # # shunit2 unit test for running subset(s) of tests based upon command line arguments. # Also shows how non-default tests or a arbitrary subset of tests can be run. # # Disable source following. # shellcheck disable=SC1090,SC1091 # Load test helpers. . ./shunit2_test_helpers # This test does not nomrally run because it does not begin "test*" # Will be run by settting the arguments to the script to include the name of this test. non_default_test() { # arbitrary assert assertTrue 0 # true intent is to set this variable, which will be tested below NON_DEFAULT_TEST_RAN="yup, we ran" } # Test that the "non_default_test" ran, otherwise fail test_non_default_ran() { assertNotNull "'non_default_test' did not run" "$NON_DEFAULT_TEST_RAN" } # fail if this test runs, which is shouldn't if args are set correctly. test_will_fail() { fail "test_will_fail should not be run if arg-parsing works" } oneTimeSetUp() { th_oneTimeSetUp # prime with "null" value NON_DEFAULT_TEST_RAN="" } # Load and run shunit2. # shellcheck disable=SC2034 [ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0 # If zero/one argument(s) are provided, this test is being run in it's # entirety, and therefore we want to set the arguments to the script # to (simulate and) test the processing of command-line specified # tests. If we don't, then the "test_will_fail" test will run (by # default) and the overall test will fail. # # However, if two or more arguments are provided, then assume this # test script is being run by hand to experiment with command-line # test specification, and then don't override the user provided # arguments. if command [ "$#" -le 1 ]; then # We set the arguments in a POSIX way, inasmuch as we can; # helpful tip: # https://unix.stackexchange.com/questions/258512/how-to-remove-a-positional-parameter-from set -- "--" "non_default_test" "test_non_default_ran" fi # Load and run tests, but only if running as a script, not if being sourced by shunit2 command [ -z "${SHUNIT_VERSION:-}" ] && . "${TH_SHUNIT}" shunit2-2.1.8/shunit2_asserts_test.sh000077500000000000000000000221001363770417100176600ustar00rootroot00000000000000#! /bin/sh # vim:et:ft=sh:sts=2:sw=2 # # shunit2 unit test for assert functions. # # Copyright 2008-2017 Kate Ward. All Rights Reserved. # Released under the Apache 2.0 license. # # Author: kate.ward@forestent.com (Kate Ward) # https://github.com/kward/shunit2 # # Disable source following. # shellcheck disable=SC1090,SC1091 # These variables will be overridden by the test helpers. stdoutF="${TMPDIR:-/tmp}/STDOUT" stderrF="${TMPDIR:-/tmp}/STDERR" # Load test helpers. . ./shunit2_test_helpers commonEqualsSame() { fn=$1 ( ${fn} 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'equal' $? "${stdoutF}" "${stderrF}" ( ${fn} "${MSG}" 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'equal; with msg' $? "${stdoutF}" "${stderrF}" ( ${fn} 'abc def' 'abc def' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'equal with spaces' $? "${stdoutF}" "${stderrF}" ( ${fn} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'not equal' $? "${stdoutF}" "${stderrF}" ( ${fn} '' '' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'null values' $? "${stdoutF}" "${stderrF}" ( ${fn} arg1 >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" ( ${fn} arg1 arg2 arg3 arg4 >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" } commonNotEqualsSame() { fn=$1 ( ${fn} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'not same' $? "${stdoutF}" "${stderrF}" ( ${fn} "${MSG}" 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'not same, with msg' $? "${stdoutF}" "${stderrF}" ( ${fn} 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'same' $? "${stdoutF}" "${stderrF}" ( ${fn} '' '' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'null values' $? "${stdoutF}" "${stderrF}" ( ${fn} arg1 >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" ( ${fn} arg1 arg2 arg3 arg4 >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" } testAssertEquals() { commonEqualsSame 'assertEquals' } testAssertNotEquals() { commonNotEqualsSame 'assertNotEquals' } testAssertSame() { commonEqualsSame 'assertSame' } testAssertNotSame() { commonNotEqualsSame 'assertNotSame' } testAssertContains() { ( assertContains 'abcdef' 'abc' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'found' $? "${stdoutF}" "${stderrF}" ( assertContains 'abcdef' 'bcd' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'found' $? "${stdoutF}" "${stderrF}" ( assertContains 'abcdef' 'def' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'found' $? "${stdoutF}" "${stderrF}" ( assertContains 'abc -Xabc def' '-Xabc' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'content starts with "-"' $? "${stdoutF}" "${stderrF}" ( assertContains "${MSG}" 'abcdef' 'abc' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'found, with msg' $? "${stdoutF}" "${stderrF}" ( assertContains 'abcdef' 'xyz' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'not found' $? "${stdoutF}" "${stderrF}" ( assertContains 'abcdef' 'zab' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'not found' $? "${stdoutF}" "${stderrF}" ( assertContains 'abcdef' 'efg' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'not found' $? "${stdoutF}" "${stderrF}" ( assertContains 'abcdef' 'acf' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'not found' $? "${stdoutF}" "${stderrF}" ( assertContains 'abcdef' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" ( assertContains arg1 arg2 arg3 arg4 >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" } testAssertNotContains() { ( assertNotContains 'abcdef' 'xyz' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'not found' $? "${stdoutF}" "${stderrF}" ( assertNotContains 'abcdef' 'zab' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'not found' $? "${stdoutF}" "${stderrF}" ( assertNotContains 'abcdef' 'efg' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'not found' $? "${stdoutF}" "${stderrF}" ( assertNotContains 'abcdef' 'acf' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'not found' $? "${stdoutF}" "${stderrF}" ( assertNotContains "${MSG}" 'abcdef' 'xyz' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'not found, with msg' $? "${stdoutF}" "${stderrF}" ( assertNotContains 'abcdef' 'abc' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'found' $? "${stdoutF}" "${stderrF}" ( assertNotContains 'abcdef' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" ( assertNotContains arg1 arg2 arg3 arg4 >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" } testAssertNull() { ( assertNull '' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'null' $? "${stdoutF}" "${stderrF}" ( assertNull "${MSG}" '' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'null, with msg' $? "${stdoutF}" "${stderrF}" ( assertNull 'x' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'not null' $? "${stdoutF}" "${stderrF}" ( assertNull >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" ( assertNull arg1 arg2 arg3 >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" } testAssertNotNull() { ( assertNotNull 'x' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'not null' $? "${stdoutF}" "${stderrF}" ( assertNotNull "${MSG}" 'x' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'not null, with msg' $? "${stdoutF}" "${stderrF}" ( assertNotNull 'x"b' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'not null, with double-quote' $? \ "${stdoutF}" "${stderrF}" ( assertNotNull "x'b" >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'not null, with single-quote' $? \ "${stdoutF}" "${stderrF}" # shellcheck disable=SC2016 ( assertNotNull 'x$b' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'not null, with dollar' $? \ "${stdoutF}" "${stderrF}" ( assertNotNull 'x`b' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'not null, with backtick' $? \ "${stdoutF}" "${stderrF}" ( assertNotNull '' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'null' $? "${stdoutF}" "${stderrF}" # There is no test for too few arguments as $1 might actually be null. ( assertNotNull arg1 arg2 arg3 >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" } testAssertTrue() { ( assertTrue 0 >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'true' $? "${stdoutF}" "${stderrF}" ( assertTrue "${MSG}" 0 >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'true, with msg' $? "${stdoutF}" "${stderrF}" ( assertTrue '[ 0 -eq 0 ]' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'true condition' $? "${stdoutF}" "${stderrF}" ( assertTrue 1 >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'false' $? "${stdoutF}" "${stderrF}" ( assertTrue '[ 0 -eq 1 ]' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'false condition' $? "${stdoutF}" "${stderrF}" ( assertTrue '' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'null' $? "${stdoutF}" "${stderrF}" ( assertTrue >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" ( assertTrue arg1 arg2 arg3 >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" } testAssertFalse() { ( assertFalse 1 >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'false' $? "${stdoutF}" "${stderrF}" ( assertFalse "${MSG}" 1 >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'false, with msg' $? "${stdoutF}" "${stderrF}" ( assertFalse '[ 0 -eq 1 ]' >"${stdoutF}" 2>"${stderrF}" ) th_assertTrueWithNoOutput 'false condition' $? "${stdoutF}" "${stderrF}" ( assertFalse 0 >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'true' $? "${stdoutF}" "${stderrF}" ( assertFalse '[ 0 -eq 0 ]' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'true condition' $? "${stdoutF}" "${stderrF}" ( assertFalse '' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'true condition' $? "${stdoutF}" "${stderrF}" ( assertFalse >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" ( assertFalse arg1 arg2 arg3 >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" } oneTimeSetUp() { th_oneTimeSetUp MSG='This is a test message' } # Load and run shunit2. # shellcheck disable=SC2034 [ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0 . "${TH_SHUNIT}" shunit2-2.1.8/shunit2_failures_test.sh000077500000000000000000000052561363770417100200230ustar00rootroot00000000000000#! /bin/sh # vim:et:ft=sh:sts=2:sw=2 # # Copyright 2008-2019 Kate Ward. All Rights Reserved. # Released under the Apache 2.0 license. # http://www.apache.org/licenses/LICENSE-2.0 # # shUnit2 -- Unit testing framework for Unix shell scripts. # https://github.com/kward/shunit2 # # Author: kate.ward@forestent.com (Kate Ward) # # shUnit2 unit test for failure functions # # Disable source following. # shellcheck disable=SC1090,SC1091 # These variables will be overridden by the test helpers. stdoutF="${TMPDIR:-/tmp}/STDOUT" stderrF="${TMPDIR:-/tmp}/STDERR" # Load test helpers. . ./shunit2_test_helpers testFail() { ( fail >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'fail' $? "${stdoutF}" "${stderrF}" ( fail "${MSG}" >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'fail with msg' $? "${stdoutF}" "${stderrF}" ( fail arg1 >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'too many arguments' $? "${stdoutF}" "${stderrF}" } testFailNotEquals() { ( failNotEquals 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'same' $? "${stdoutF}" "${stderrF}" ( failNotEquals "${MSG}" 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'same with msg' $? "${stdoutF}" "${stderrF}" ( failNotEquals 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'not same' $? "${stdoutF}" "${stderrF}" ( failNotEquals '' '' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'null values' $? "${stdoutF}" "${stderrF}" ( failNotEquals >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" ( failNotEquals arg1 arg2 arg3 arg4 >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" } testFailSame() { ( failSame 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'same' $? "${stdoutF}" "${stderrF}" ( failSame "${MSG}" 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'same with msg' $? "${stdoutF}" "${stderrF}" ( failSame 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'not same' $? "${stdoutF}" "${stderrF}" ( failSame '' '' >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithOutput 'null values' $? "${stdoutF}" "${stderrF}" ( failSame >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithError 'too few arguments' $? "${stdoutF}" "${stderrF}" ( failSame arg1 arg2 arg3 arg4 >"${stdoutF}" 2>"${stderrF}" ) th_assertFalseWithError 'too many arguments' $? "${stdoutF}" "${stderrF}" } oneTimeSetUp() { th_oneTimeSetUp MSG='This is a test message' } # Load and run shUnit2. # shellcheck disable=SC2034 [ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0 . "${TH_SHUNIT}" shunit2-2.1.8/shunit2_macros_test.sh000077500000000000000000000176571363770417100175050ustar00rootroot00000000000000#! /bin/sh # vim:et:ft=sh:sts=2:sw=2 # # shunit2 unit test for macros. # # Copyright 2008-2017 Kate Ward. All Rights Reserved. # Released under the Apache 2.0 license. # # Author: kate.ward@forestent.com (Kate Ward) # https://github.com/kward/shunit2 # ### ShellCheck http://www.shellcheck.net/ # Disable source following. # shellcheck disable=SC1090,SC1091 # Presence of LINENO variable is checked. # shellcheck disable=SC2039 # These variables will be overridden by the test helpers. stdoutF="${TMPDIR:-/tmp}/STDOUT" stderrF="${TMPDIR:-/tmp}/STDERR" # Load test helpers. . ./shunit2_test_helpers testAssertEquals() { # Start skipping if LINENO not available. [ -z "${LINENO:-}" ] && startSkipping ( ${_ASSERT_EQUALS_} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_ASSERT_EQUALS_ failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 ( ${_ASSERT_EQUALS_} '"some msg"' 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_ASSERT_EQUALS_ w/ msg failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 return 0 } testAssertNotEquals() { # Start skipping if LINENO not available. [ -z "${LINENO:-}" ] && startSkipping ( ${_ASSERT_NOT_EQUALS_} 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_ASSERT_NOT_EQUALS_ failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 ( ${_ASSERT_NOT_EQUALS_} '"some msg"' 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_ASSERT_NOT_EQUALS_ w/ msg failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 return 0 } testSame() { # Start skipping if LINENO not available. [ -z "${LINENO:-}" ] && startSkipping ( ${_ASSERT_SAME_} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_ASSERT_SAME_ failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 ( ${_ASSERT_SAME_} '"some msg"' 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_ASSERT_SAME_ w/ msg failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 return 0 } testNotSame() { # Start skipping if LINENO not available. [ -z "${LINENO:-}" ] && startSkipping ( ${_ASSERT_NOT_SAME_} 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_ASSERT_NOT_SAME_ failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 ( ${_ASSERT_NOT_SAME_} '"some msg"' 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_ASSERT_NOT_SAME_ w/ msg failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 return 0 } testNull() { # Start skipping if LINENO not available. [ -z "${LINENO:-}" ] && startSkipping ( ${_ASSERT_NULL_} 'x' >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_ASSERT_NULL_ failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 ( ${_ASSERT_NULL_} '"some msg"' 'x' >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_ASSERT_NULL_ w/ msg failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 return 0 } testNotNull() { # start skipping if LINENO not available [ -z "${LINENO:-}" ] && startSkipping ( ${_ASSERT_NOT_NULL_} '' >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_ASSERT_NOT_NULL_ failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 ( ${_ASSERT_NOT_NULL_} '"some msg"' '""' >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_ASSERT_NOT_NULL_ w/ msg failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stdoutF}" "${stderrF}" >&2 return 0 } testAssertTrue() { # Start skipping if LINENO not available. [ -z "${LINENO:-}" ] && startSkipping ( ${_ASSERT_TRUE_} "${SHUNIT_FALSE}" >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_ASSERT_TRUE_ failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 ( ${_ASSERT_TRUE_} '"some msg"' "${SHUNIT_FALSE}" >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_ASSERT_TRUE_ w/ msg failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 return 0 } testAssertFalse() { # Start skipping if LINENO not available. [ -z "${LINENO:-}" ] && startSkipping ( ${_ASSERT_FALSE_} "${SHUNIT_TRUE}" >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_ASSERT_FALSE_ failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 ( ${_ASSERT_FALSE_} '"some msg"' "${SHUNIT_TRUE}" >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_ASSERT_FALSE_ w/ msg failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 return 0 } testFail() { # Start skipping if LINENO not available. [ -z "${LINENO:-}" ] && startSkipping ( ${_FAIL_} >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_FAIL_ failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 ( ${_FAIL_} '"some msg"' >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_FAIL_ w/ msg failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 return 0 } testFailNotEquals() { # start skipping if LINENO not available [ -z "${LINENO:-}" ] && startSkipping ( ${_FAIL_NOT_EQUALS_} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_FAIL_NOT_EQUALS_ failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 ( ${_FAIL_NOT_EQUALS_} '"some msg"' 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_FAIL_NOT_EQUALS_ w/ msg failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 return 0 } testFailSame() { # Start skipping if LINENO not available. [ -z "${LINENO:-}" ] && startSkipping ( ${_FAIL_SAME_} 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_FAIL_SAME_ failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 ( ${_FAIL_SAME_} '"some msg"' 'x' 'x' >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_FAIL_SAME_ w/ msg failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 return 0 } testFailNotSame() { # Start skipping if LINENO not available. [ -z "${LINENO:-}" ] && startSkipping ( ${_FAIL_NOT_SAME_} 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_FAIL_NOT_SAME_ failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 ( ${_FAIL_NOT_SAME_} '"some msg"' 'x' 'y' >"${stdoutF}" 2>"${stderrF}" ) grep '^ASSERT:\[[0-9]*\] *' "${stdoutF}" >/dev/null rtrn=$? assertTrue '_FAIL_NOT_SAME_ w/ msg failure' ${rtrn} [ "${rtrn}" -ne "${SHUNIT_TRUE}" ] && cat "${stderrF}" >&2 return 0 } oneTimeSetUp() { th_oneTimeSetUp } # Disable output coloring as it breaks the tests. SHUNIT_COLOR='none'; export SHUNIT_COLOR # Load and run shUnit2. # shellcheck disable=SC2034 [ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT="$0" . "${TH_SHUNIT}" shunit2-2.1.8/shunit2_misc_test.sh000077500000000000000000000222631363770417100171410ustar00rootroot00000000000000#! /bin/sh # vim:et:ft=sh:sts=2:sw=2 # # shUnit2 unit tests of miscellaneous things # # Copyright 2008-2018 Kate Ward. All Rights Reserved. # Released under the Apache 2.0 license. # # Author: kate.ward@forestent.com (Kate Ward) # https://github.com/kward/shunit2 # ### ShellCheck http://www.shellcheck.net/ # $() are not fully portable (POSIX != portable). # shellcheck disable=SC2006 # Disable source following. # shellcheck disable=SC1090,SC1091 # Not wanting to escape single quotes. # shellcheck disable=SC1003 # These variables will be overridden by the test helpers. stdoutF="${TMPDIR:-/tmp}/STDOUT" stderrF="${TMPDIR:-/tmp}/STDERR" # Load test helpers. . ./shunit2_test_helpers # Note: the test script is prefixed with '#' chars so that shUnit2 does not # incorrectly interpret the embedded functions as real functions. testUnboundVariable() { unittestF="${SHUNIT_TMPDIR}/unittest" sed 's/^#//' >"${unittestF}" <"${stdoutF}" 2>"${stderrF}" ) assertFalse 'expected a non-zero exit value' $? grep '^ASSERT:unknown failure' "${stdoutF}" >/dev/null assertTrue 'assert message was not generated' $? grep '^Ran [0-9]* test' "${stdoutF}" >/dev/null assertTrue 'test count message was not generated' $? grep '^FAILED' "${stdoutF}" >/dev/null assertTrue 'failure message was not generated' $? } # assertEquals repeats message argument. # https://github.com/kward/shunit2/issues/7 testIssue7() { # Disable coloring so 'ASSERT:' lines can be matched correctly. _shunit_configureColor 'none' ( assertEquals 'Some message.' 1 2 >"${stdoutF}" 2>"${stderrF}" ) diff "${stdoutF}" - >/dev/null < but was:<2> EOF rtrn=$? assertEquals "${SHUNIT_TRUE}" "${rtrn}" [ "${rtrn}" -eq "${SHUNIT_TRUE}" ] || cat "${stderrF}" >&2 } # Support prefixes on test output. # https://github.com/kward/shunit2/issues/29 testIssue29() { unittestF="${SHUNIT_TMPDIR}/unittest" sed 's/^#//' >"${unittestF}" <"${stdoutF}" 2>"${stderrF}" ) grep '^--- test_assert' "${stdoutF}" >/dev/null rtrn=$? assertEquals "${SHUNIT_TRUE}" "${rtrn}" [ "${rtrn}" -eq "${SHUNIT_TRUE}" ] || cat "${stdoutF}" >&2 } # shUnit2 should not exit with 0 when it has syntax errors. # https://github.com/kward/shunit2/issues/69 testIssue69() { unittestF="${SHUNIT_TMPDIR}/unittest" for t in Equals NotEquals Null NotNull Same NotSame True False; do assert="assert${t}" sed 's/^#//' >"${unittestF}" <"${stdoutF}" 2>"${stderrF}" ) grep '^FAILED' "${stdoutF}" >/dev/null assertTrue "failure message for ${assert} was not generated" $? done } # Ensure that test fails if setup/teardown functions fail. testIssue77() { unittestF="${SHUNIT_TMPDIR}/unittest" for func in oneTimeSetUp setUp tearDown oneTimeTearDown; do sed 's/^#//' >"${unittestF}" <"${stdoutF}" 2>"${stderrF}" grep '^FAILED' "${stdoutF}" >/dev/null assertTrue "failure of ${func}() did not end test" $? done } # Ensure a test failure is recorded for code containing syntax errors. # https://github.com/kward/shunit2/issues/84 testIssue84() { unittestF="${SHUNIT_TMPDIR}/unittest" sed 's/^#//' >"${unittestF}" <<\EOF ## Function with syntax error. #syntax_error() { ${!#3442} -334 a$@2[1]; } #test_syntax_error() { # syntax_error # assertTrue ${SHUNIT_TRUE} #} #SHUNIT_COLOR='none' #SHUNIT_TEST_PREFIX='--- ' #. ${TH_SHUNIT} EOF ( exec "${SHELL:-sh}" "${unittestF}" >"${stdoutF}" 2>"${stderrF}" ) grep '^FAILED' "${stdoutF}" >/dev/null assertTrue "failure message for ${assert} was not generated" $? } testPrepForSourcing() { assertEquals '/abc' "`_shunit_prepForSourcing '/abc'`" assertEquals './abc' "`_shunit_prepForSourcing './abc'`" assertEquals './abc' "`_shunit_prepForSourcing 'abc'`" } testEscapeCharInStr() { while read -r desc char str want; do got=`_shunit_escapeCharInStr "${char}" "${str}"` assertEquals "${desc}" "${want}" "${got}" done <<'EOF' backslash \ '' '' backslash_pre \ \def \\def backslash_mid \ abc\def abc\\def backslash_post \ abc\ abc\\ quote " '' '' quote_pre " "def \"def quote_mid " abc"def abc\"def quote_post " abc" abc\" string $ '' '' string_pre $ $def \$def string_mid $ abc$def abc\$def string_post $ abc$ abc\$ EOF # TODO(20170924:kward) fix or remove. # actual=`_shunit_escapeCharInStr "'" ''` # assertEquals '' "${actual}" # assertEquals "abc\\'" `_shunit_escapeCharInStr "'" "abc'"` # assertEquals "abc\\'def" `_shunit_escapeCharInStr "'" "abc'def"` # assertEquals "\\'def" `_shunit_escapeCharInStr "'" "'def"` # # Must put the backtick in a variable so the shell doesn't misinterpret it # # while inside a backticked sequence (e.g. `echo '`'` would fail). # backtick='`' # actual=`_shunit_escapeCharInStr ${backtick} ''` # assertEquals '' "${actual}" # assertEquals '\`abc' \ # `_shunit_escapeCharInStr "${backtick}" ${backtick}'abc'` # assertEquals 'abc\`' \ # `_shunit_escapeCharInStr "${backtick}" 'abc'${backtick}` # assertEquals 'abc\`def' \ # `_shunit_escapeCharInStr "${backtick}" 'abc'${backtick}'def'` } testEscapeCharInStr_specialChars() { # Make sure our forward slash doesn't upset sed. assertEquals '/' "`_shunit_escapeCharInStr '\' '/'`" # Some shells escape these differently. # TODO(20170924:kward) fix or remove. #assertEquals '\\a' `_shunit_escapeCharInStr '\' '\a'` #assertEquals '\\b' `_shunit_escapeCharInStr '\' '\b'` } # Test the various ways of declaring functions. # # Prefixing (then stripping) with comment symbol so these functions aren't # treated as real functions by shUnit2. testExtractTestFunctions() { f="${SHUNIT_TMPDIR}/extract_test_functions" sed 's/^#//' <"${f}" ## Function on a single line. #testABC() { echo 'ABC'; } ## Multi-line function with '{' on next line. #test_def() # { # echo 'def' #} ## Multi-line function with '{' on first line. #testG3 () { # echo 'G3' #} ## Function with numerical values in name. #function test4() { echo '4'; } ## Leading space in front of function. # test5() { echo '5'; } ## Function with '_' chars in name. #some_test_function() { echo 'some func'; } ## Function that sets variables. #func_with_test_vars() { # testVariable=1234 #} ## Function with keyword but no parenthesis #function test6 { echo '6'; } ## Function with keyword but no parenthesis, multi-line #function test7 { # echo '7'; #} ## Function with no parenthesis, '{' on next line #function test8 #{ # echo '8' #} ## Function with hyphenated name #test-9() { # echo '9'; #} ## Function without parenthesis or keyword #test_foobar { echo 'hello world'; } ## Function with multiple function keywords #function function test_test_test() { echo 'lorem'; } EOF actual=`_shunit_extractTestFunctions "${f}"` assertEquals 'testABC test_def testG3 test4 test5 test6 test7 test8 test-9' "${actual}" } # Test that certain external commands sometimes "stubbed" by users are escaped. testIssue54() { for c in mkdir rm cat chmod sed; do grep "^[^#]*${c} " "${TH_SHUNIT}" | grep -qv "command ${c}" assertFalse "external call to ${c} not protected somewhere" $? done grep '^[^#]*[^ ] *\[' "${TH_SHUNIT}" | grep -qv 'command \[' assertFalse "call to [ ... ] not protected somewhere" $? grep '^[^#]* *\.' "${TH_SHUNIT}" | grep -qv 'command \.' assertFalse "call to . not protected somewhere" $? } mock_tput() { if [ -z "${TERM}" ]; then # shellcheck disable=SC2016 echo 'tput: No value for $TERM and no -T specified' return 2 fi if [ "$1" = 'colors' ]; then echo 256 return 0 fi return 1 } testColors() { while read -r desc cmd colors; do SHUNIT_CMD_TPUT=${cmd} got=`_shunit_colors` want=${colors} assertEquals "${got}" "${want}" done <<'EOF' missing missing_tput 16 mock mock_tput 256 EOF } testColorsWitoutTERM() { SHUNIT_CMD_TPUT='mock_tput' got=`TERM='' _shunit_colors` want=16 assertEquals "${got}" "${want}" } setUp() { for f in "${stdoutF}" "${stderrF}"; do cp /dev/null "${f}" done # Reconfigure coloring as some tests override default behavior. _shunit_configureColor "${SHUNIT_COLOR_DEFAULT}" # shellcheck disable=SC2034,SC2153 SHUNIT_CMD_TPUT=${__SHUNIT_CMD_TPUT} } oneTimeSetUp() { SHUNIT_COLOR_DEFAULT="${SHUNIT_COLOR}" th_oneTimeSetUp } # Load and run shUnit2. # shellcheck disable=SC2034 [ -n "${ZSH_VERSION:-}" ] && SHUNIT_PARENT=$0 . "${TH_SHUNIT}" shunit2-2.1.8/shunit2_standalone_test.sh000077500000000000000000000017311363770417100203330ustar00rootroot00000000000000#! /bin/sh # vim:et:ft=sh:sts=2:sw=2 # # shUnit2 unit test for standalone operation. # # Copyright 2010-2017 Kate Ward. All Rights Reserved. # Released under the Apache 2.0 license. # # Author: kate.ward@forestent.com (Kate Ward) # https://github.com/kward/shunit2 # # This unit test is purely to test that calling shunit2 directly, while passing # the name of a unit test script, works. When run, this script determines if it # is running as a standalone program, and calls main() if it is. # ### ShellCheck http://www.shellcheck.net/ # $() are not fully portable (POSIX != portable). # shellcheck disable=SC2006 # Disable source following. # shellcheck disable=SC1090,SC1091 ARGV0="`basename "$0"`" # Load test helpers. . ./shunit2_test_helpers testStandalone() { assertTrue "${SHUNIT_TRUE}" } main() { ${TH_SHUNIT} "${ARGV0}" } # Are we running as a standalone? if [ "${ARGV0}" = 'shunit2_test_standalone.sh' ]; then if [ $# -gt 0 ]; then main "$@"; else main; fi fi shunit2-2.1.8/shunit2_test_helpers000066400000000000000000000152261363770417100172350ustar00rootroot00000000000000# vim:et:ft=sh:sts=2:sw=2 # # shUnit2 unit test common functions # # Copyright 2008 Kate Ward. All Rights Reserved. # Released under the Apache 2.0 license. # # Author: kate.ward@forestent.com (Kate Ward) # https://github.com/kward/shunit2 # ### ShellCheck (http://www.shellcheck.net/) # Commands are purposely escaped so they can be mocked outside shUnit2. # shellcheck disable=SC1001,SC1012 # expr may be antiquated, but it is the only solution in some cases. # shellcheck disable=SC2003 # $() are not fully portable (POSIX != portable). # shellcheck disable=SC2006 # Treat unset variables as an error when performing parameter expansion. set -u # Set shwordsplit for zsh. \[ -n "${ZSH_VERSION:-}" ] && setopt shwordsplit # # Constants. # # Path to shUnit2 library. Can be overridden by setting SHUNIT_INC. TH_SHUNIT=${SHUNIT_INC:-./shunit2}; export TH_SHUNIT # Configure debugging. Set the DEBUG environment variable to any # non-empty value to enable debug output, or TRACE to enable trace # output. TRACE=${TRACE:+'th_trace '} \[ -n "${TRACE}" ] && DEBUG=1 \[ -z "${TRACE}" ] && TRACE=':' DEBUG=${DEBUG:+'th_debug '} \[ -z "${DEBUG}" ] && DEBUG=':' # # Variables. # th_RANDOM=0 # # Functions. # # Logging functions. th_trace() { echo "${MY_NAME}:TRACE $*" >&2; } th_debug() { echo "${MY_NAME}:DEBUG $*" >&2; } th_info() { echo "${MY_NAME}:INFO $*" >&2; } th_warn() { echo "${MY_NAME}:WARN $*" >&2; } th_error() { echo "${MY_NAME}:ERROR $*" >&2; } th_fatal() { echo "${MY_NAME}:FATAL $*" >&2; } # Output subtest name. th_subtest() { echo " $*" >&2; } th_oneTimeSetUp() { # These files will be cleaned up automatically by shUnit2. stdoutF="${SHUNIT_TMPDIR}/stdout" stderrF="${SHUNIT_TMPDIR}/stderr" returnF="${SHUNIT_TMPDIR}/return" expectedF="${SHUNIT_TMPDIR}/expected" export stdoutF stderrF returnF expectedF } # Generate a random number. th_generateRandom() { tfgr_random=${th_RANDOM} while \[ "${tfgr_random}" = "${th_RANDOM}" ]; do # shellcheck disable=SC2039 if \[ -n "${RANDOM:-}" ]; then # $RANDOM works # shellcheck disable=SC2039 tfgr_random=${RANDOM}${RANDOM}${RANDOM}$$ elif \[ -r '/dev/urandom' ]; then tfgr_random=`od -vAn -N4 -tu4 >> STDOUT' >&2 \cat "${_th_stdout_}" >&2 fi if \[ -n "${_th_stderr_}" -a -s "${_th_stderr_}" ]; then echo '>>> STDERR' >&2 \cat "${_th_stderr_}" >&2 fi if \[ -n "${_th_stdout_}" -o -n "${_th_stderr_}" ]; then echo '<<< end output' >&2 fi fi unset _th_return_ _th_stdout_ _th_stderr_ } # # Main. # ${TRACE} 'trace output enabled' ${DEBUG} 'debug output enabled' shunit2-2.1.8/test_runner000077500000000000000000000110511363770417100154230ustar00rootroot00000000000000#! /bin/sh # vim:et:ft=sh:sts=2:sw=2 # # Unit test suite runner. # # Copyright 2008-2018 Kate Ward. All Rights Reserved. # Released under the Apache 2.0 license. # # Author: kate.ward@forestent.com (Kate Ward) # https://github.com/kward/shlib # # This script runs all the unit tests that can be found, and generates a nice # report of the tests. # ### ShellCheck (http://www.shellcheck.net/) # Disable source following. # shellcheck disable=SC1090,SC1091 # expr may be antiquated, but it is the only solution in some cases. # shellcheck disable=SC2003 # $() are not fully portable (POSIX != portable). # shellcheck disable=SC2006 # Return if test_runner already loaded. [ -z "${RUNNER_LOADED:-}" ] || return 0 RUNNER_LOADED=0 RUNNER_ARGV0=`basename "$0"` RUNNER_SHELLS='/bin/sh ash /bin/bash /bin/dash /bin/ksh /bin/pdksh /bin/zsh' RUNNER_TEST_SUFFIX='_test.sh' true; RUNNER_TRUE=$? false; RUNNER_FALSE=$? runner_warn() { echo "runner:WARN $*" >&2; } runner_error() { echo "runner:ERROR $*" >&2; } runner_fatal() { echo "runner:FATAL $*" >&2; exit 1; } runner_usage() { echo "usage: ${RUNNER_ARGV0} [-e key=val ...] [-s shell(s)] [-t test(s)]" } _runner_tests() { echo ./*${RUNNER_TEST_SUFFIX} |sed 's#./##g'; } _runner_testName() { # shellcheck disable=SC1117 _runner_testName_=`expr "${1:-}" : "\(.*\)${RUNNER_TEST_SUFFIX}"` if [ -n "${_runner_testName_}" ]; then echo "${_runner_testName_}" else echo 'unknown' fi unset _runner_testName_ } main() { # Find and load versions library. for _runner_dir_ in . ${LIB_DIR:-lib}; do if [ -r "${_runner_dir_}/versions" ]; then _runner_lib_dir_="${_runner_dir_}" break fi done [ -n "${_runner_lib_dir_}" ] || runner_fatal 'Unable to find versions library.' . "${_runner_lib_dir_}/versions" || runner_fatal 'Unable to load versions library.' unset _runner_dir_ _runner_lib_dir_ # Process command line flags. env='' while getopts 'e:hs:t:' opt; do case ${opt} in e) # set an environment variable key=`expr "${OPTARG}" : '\([^=]*\)='` val=`expr "${OPTARG}" : '[^=]*=\(.*\)'` # shellcheck disable=SC2166 if [ -z "${key}" -o -z "${val}" ]; then runner_usage exit 1 fi eval "${key}='${val}'" eval "export ${key}" env="${env:+${env} }${key}" ;; h) runner_usage; exit 0 ;; # help output s) shells=${OPTARG} ;; # list of shells to run t) tests=${OPTARG} ;; # list of tests to run *) runner_usage; exit 1 ;; esac done shift "`expr ${OPTIND} - 1`" # Fill shells and/or tests. shells=${shells:-${RUNNER_SHELLS}} [ -z "${tests}" ] && tests=`_runner_tests` # Error checking. if [ -z "${tests}" ]; then runner_error 'no tests found to run; exiting' exit 1 fi cat <&1; ) test "${runner_passing_}" -eq ${RUNNER_TRUE} -a $? -eq ${RUNNER_TRUE} runner_passing_=$? done done return ${runner_passing_} } # Execute main() if this is run in standalone mode (i.e. not from a unit test). [ -z "${SHUNIT_VERSION}" ] && main "$@"