diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..4cb66d4 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,6 @@ +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence, +# @Josee9988 will be requested for +# review when someone opens a pull request. +# if you want to add more owners just write it after @Josee9988 +* @Josee9988 diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..b60dfc1 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to make participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behaviour 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 behaviour 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 +behaviour and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behaviour. + +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 behaviours 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 behaviour may be +reported by contacting the project team at jgracia9988@gmail.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality concerning 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 + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see + diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..dbd4ce7 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,23 @@ +# **Contributing** + +When contributing to this repository, please first discuss the change you wish to make via issue, +email, or any other method with the owners of this repository before making a change. + +Please note we have a [code of conduct](CODE_OF_CONDUCT.md), please follow it in all your interactions with the project. + +## Pull Request Process + +1. Ensure any install or build dependencies are removed before the end of the layer when doing a + build. +2. Update the README.md with details of changes to the interface, this includes new environment variables, exposed ports useful file locations and container parameters. +3. Increase the version numbers in any examples files and the README.md to the new version that this + Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). +4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you + do not have permission to do that you may request the second reviewer to merge it for you. + +## Issue Report Process + +1. Go to the project's issues. +2. Select the template that better fits your issue. +3. Read carefully the instructions and write within the template guidelines. +4. Submit it and wait for support. diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..c942e4e --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +# add your own funding links diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..bf21ae5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,41 @@ + + +# **Blank Issue Report** + +## **Describe the issue** + + +* + +--- + +### **Media prove** + + +--- + +### **Your environment** + + + +* OS: +* Node version: +* Npm version: +* Browser name and version: + +--- + +### **Additional context** + + +* diff --git a/.github/ISSUE_TEMPLATE/1-bug-report.md b/.github/ISSUE_TEMPLATE/1-bug-report.md new file mode 100644 index 0000000..1376422 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/1-bug-report.md @@ -0,0 +1,79 @@ +--- +name: "🐞 Bug Report" +about: "Report an issue to help the project improve." +title: "[Bug] " +labels: "Type: Bug" +assignees: Josee9988 + +--- + +# **🐞 Bug Report** + +## **Describe the bug** + + +* + +--- + +### **Is this a regression?** + + + +--- + +### **To Reproduce** + + + + + +1. +2. +3. +4. + +--- + +### **Expected behaviour** + + +* + +--- + +### **Media prove** + + +--- + +### **Your environment** + + + +* OS: +* Node version: +* Npm version: +* Browser name and version: + +--- + +### **Additional context** + + +* + + diff --git a/.github/ISSUE_TEMPLATE/2-failing-test.md b/.github/ISSUE_TEMPLATE/2-failing-test.md new file mode 100644 index 0000000..a239f2a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/2-failing-test.md @@ -0,0 +1,41 @@ +--- +name: "πŸ’‰ Failing Test" +about: "Report failing tests or CI jobs." +title: "[Test] " +labels: "Type: Test" +assignees: Josee9988 + +--- + +# **πŸ’‰ Failing Test** + +## **Which jobs/test(s) are failing** + + +* + +--- + +## **Reason for failure/description** + + +--- + +### **Media prove** + + +--- + +### **Additional context** + + +* + + diff --git a/.github/ISSUE_TEMPLATE/3-docs-bug.md b/.github/ISSUE_TEMPLATE/3-docs-bug.md new file mode 100644 index 0000000..f987493 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/3-docs-bug.md @@ -0,0 +1,60 @@ +--- +name: "πŸ“š Documentation or README.md issue report" +about: "Report an issue in the project's documentation or README.md file." +title: "" +labels: "Documentation" +assignees: Josee9988 + +--- +# **πŸ“š Documentation Issue Report** + +## **Describe the bug** + + +* + +--- + +### **To Reproduce** + + + + + +1. +2. +3. +4. + +--- + +### **Media prove** + + +--- + +## **Describe the solution you'd like** + + +* + +--- + +### **Additional context** + + +* + + diff --git a/.github/ISSUE_TEMPLATE/4-feature-request.md b/.github/ISSUE_TEMPLATE/4-feature-request.md new file mode 100644 index 0000000..33ecf94 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/4-feature-request.md @@ -0,0 +1,45 @@ +--- +name: "πŸš€πŸ†• Feature Request" +about: "Suggest an idea or possible new feature for this project." +title: "" +labels: "Type: Feature" +assignees: Josee9988 + +--- + +# **πŸš€ Feature Request** + +## **Is your feature request related to a problem? Please describe.** + + +* + +--- + +## **Describe the solution you'd like** + + +* + +--- + +## **Describe alternatives you've considered** + + +* + +--- + +### **Additional context** + + +* + + diff --git a/.github/ISSUE_TEMPLATE/5-enhancement-request.md b/.github/ISSUE_TEMPLATE/5-enhancement-request.md new file mode 100644 index 0000000..2b29bd5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/5-enhancement-request.md @@ -0,0 +1,45 @@ +--- +name: "πŸš€βž• Enhancement Request" +about: "Suggest an enhancement for this project. Improve an existing feature" +title: "" +labels: "Type: Enhancement" +assignees: Josee9988 + +--- + +# **πŸš€ Enhancement Request** + +## **Is your enhancement request related to a problem? Please describe.** + + +* + +--- + +## **Describe the solution you'd like** + + +* + +--- + +## **Describe alternatives you've considered** + + +* + +--- + +### **Additional context** + + +* + + diff --git a/.github/ISSUE_TEMPLATE/6-security-report.md b/.github/ISSUE_TEMPLATE/6-security-report.md new file mode 100644 index 0000000..81f6fef --- /dev/null +++ b/.github/ISSUE_TEMPLATE/6-security-report.md @@ -0,0 +1,100 @@ +--- +name: "⚠️ Security Report" +about: "Report an issue to help the project improve." +title: "" +labels: "Type: Security" +assignees: Josee9988 + +--- + + + +# **⚠️ Security Report** + +## **Describe the security issue** + + +* + +--- + +### **To Reproduce** + + + + + +1. +2. +3. +4. + +--- + +### **Expected behaviour** + + +* + +--- + +### **Media prove** + + +--- + +### **Your environment** + + + +* OS: +* Node version: +* Npm version: +* Browser name and version: + +--- + +### **Additional context** + + +* diff --git a/.github/ISSUE_TEMPLATE/7-question-support.md b/.github/ISSUE_TEMPLATE/7-question-support.md new file mode 100644 index 0000000..ec98c81 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/7-question-support.md @@ -0,0 +1,28 @@ +--- +name: "❓ Question or Support Request" +about: "Questions and requests for support." +title: "" +labels: "Type: Question" +assignees: Josee9988 + +--- + +# **❓ Question or Support Request** + +## **Describe your question or ask for support.** + + +* + + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..a053b0d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Send an e-mail the developer + url: mailto:jgracia9988@gmail.com + about: Please do NOT use this email to post issues or feature requests (only important business/personal contact). diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..1d67596 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,11 @@ +# **Reporting Security Issues** + +The project's team and community take security issues. + +We appreciate your efforts to responsibly disclose your findings and will make every effort to acknowledge your contributions. + +To report a security issue, go to the project's issues and create a new issue using the ⚠️ Security Report 'issue template'. + +Read carefully the instructions of this issue template, and if your report could leak data or might expose how to gain access to a restricted area or break the system, please email [jgracia9988@gmail.com](mailto:jgracia9988@gmail.com) and include the word "SECURITY" in the subject line. + +We'll endeavour to respond quickly, and will keep you updated throughout the process. diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md new file mode 100644 index 0000000..a158031 --- /dev/null +++ b/.github/SUPPORT.md @@ -0,0 +1,11 @@ +# **Support** + +## Obtain direct support from the project's owners + +1. Open a new issue and select the issue with the template called "❓ Question or Support Request". +2. Read carefully the instructions of written in that template and submit the issue asking for support +or asking any question. + +## Bug reports + +See the [contributing guidelines](CONTRIBUTING.md) for sharing bug reports and read our [code of conduct](CODE_OF_CONDUCT.md). diff --git a/.github/config.yml b/.github/config.yml new file mode 100644 index 0000000..aa54d39 --- /dev/null +++ b/.github/config.yml @@ -0,0 +1,20 @@ +# Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome + +# Comment to be posted to on first time issues +newIssueWelcomeComment: > + Thanks for opening your first issue in Josee9988/Josee9988! Be sure to follow the issue template and provide every bit of information to help the developers! + +# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome + +# Comment to be posted to on PRs from first time contributors in your repository +newPRWelcomeComment: > + Thanks for opening this pull request! Please check out our contributing guidelines and make sure to follow the pull request template. + +# Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge + +# Comment to be posted to on pull requests merged by a first time user +firstPRMergeComment: > + Congrats on merging your first pull request! Keep making great things with us, thanks!! + +todo: + keyword: '@todo' diff --git a/.github/issue_label_bot.yaml b/.github/issue_label_bot.yaml new file mode 100644 index 0000000..b83d27f --- /dev/null +++ b/.github/issue_label_bot.yaml @@ -0,0 +1,4 @@ +label-alias: + bug: 'Type: Bug' + feature_request: 'Type: Feature' + question: 'Type: Question' diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..8d31ec6 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,29 @@ +# **Name of PR** + + + +## **Description** + + + +* + +--- + +### **Additional context** + + + +* + + diff --git a/.github/settings.yml b/.github/settings.yml new file mode 100644 index 0000000..8790525 --- /dev/null +++ b/.github/settings.yml @@ -0,0 +1,184 @@ +repository: + # See https://developer.github.com/v3/repos/#edit for all available settings. + + # The name of the repository. Changing this will rename the repository + #name: repo-name + + # A short description of the repository that will show up on GitHub + #description: description of repo + + # A URL with more information about the repository + #homepage: https://example.github.io/ + + # A comma-separated list of topics to set on the repository + #topics: project, template, Josee9988 + + # Either `true` to make the repository private, or `false` to make it public. + #private: false + + # Either `true` to enable issues for this repository, `false` to disable them. + #has_issues: true + + # Either `true` to enable the wiki for this repository, `false` to disable it. + #has_wiki: true + + # Either `true` to enable downloads for this repository, `false` to disable them. + #has_downloads: true + + # Updates the default branch for this repository. + #default_branch: master + + # Either `true` to allow squash-merging pull requests, or `false` to prevent + # squash-merging. + #allow_squash_merge: true + + # Either `true` to allow merging pull requests with a merge commit, or `false` + # to prevent merging pull requests with merge commits. + #allow_merge_commit: true + + # Either `true` to allow rebase-merging pull requests, or `false` to prevent + # rebase-merging. + #allow_rebase_merge: true + +# Labels: define labels for Issues and Pull Requests +labels: + - name: 'Type: Bug' + color: e80c0c + description: Something isn't working as expected. + + - name: 'Type: Enhancement' + color: 54b2ff + description: Suggest an improvement for an existing feature. + + - name: 'Type: Feature' + color: 54b2ff + description: Suggest a new feature. + + - name: 'Type: Security' + color: fbff00 + description: A problem or enhancement related to a security issue. + + - name: 'Type: Question' + color: 9309ab + description: Request for information. + + - name: 'Type: Test' + color: ce54e3 + description: A problem or enhancement related to a test. + + - name: 'Status: Awaiting Review' + color: 24d15d + description: Ready for review. + + - name: 'Status: WIP' + color: 07b340 + description: Currently being worked on. + + - name: 'Status: Waiting' + color: 38C968 + description: Waiting on something else to be ready. + + - name: 'Status: Stale' + color: 66b38a + description: Has had no activity for some time. + + - name: 'Duplicate' + color: EB862D + description: Duplicate of another issue. + + - name: 'Invalid' + color: faef50 + description: This issue doesn't seem right. + + - name: 'Priority: High +' + color: ff008c + description: Task is considered higher-priority. + + - name: 'Priority: Low -' + color: 690a34 + description: Task is considered lower-priority. + + - name: 'Documentation' + color: 2fbceb + description: An issue/change with the documentation. + + - name: "Won't fix" + color: C8D9E6 + description: Reported issue is working as intended. + + - name: '3rd party issue' + color: e88707 + description: This issue might be caused by a 3rd party script/package/other reasons + + - name: 'Os: Windows' + color: AEB1C2 + description: Is Windows-specific + + - name: 'Os: Mac' + color: AEB1C2 + description: Is Mac-specific + + - name: 'Os: Linux' + color: AEB1C2 + description: Is Linux-specific +# # Collaborators: give specific users access to this repository. +# # See https://developer.github.com/v3/repos/collaborators/#add-user-as-a-collaborator for available options +# collaborators: +# # - username: bkeepers +# # permission: push +# # - username: hubot +# # permission: pull + +# # Note: `permission` is only valid on organization-owned repositories. +# # The permission to grant the collaborator. Can be one of: +# # * `pull` - can pull, but not push to or administer this repository. +# # * `push` - can pull and push, but not administer this repository. +# # * `admin` - can pull, push and administer this repository. +# # * `maintain` - Recommended for project managers who need to manage the repository without access to sensitive or destructive actions. +# # * `triage` - Recommended for contributors who need to proactively manage issues and pull requests without write access. + +# # See https://developer.github.com/v3/teams/#add-or-update-team-repository for available options +# teams: +# - name: core +# # The permission to grant the team. Can be one of: +# # * `pull` - can pull, but not push to or administer this repository. +# # * `push` - can pull and push, but not administer this repository. +# # * `admin` - can pull, push and administer this repository. +# # * `maintain` - Recommended for project managers who need to manage the repository without access to sensitive or destructive actions. +# # * `triage` - Recommended for contributors who need to proactively manage issues and pull requests without write access. +# permission: admin +# - name: docs +# permission: push + +# branches: +# - name: master +# # https://developer.github.com/v3/repos/branches/#update-branch-protection +# # Branch Protection settings. Set to null to disable +# protection: +# # Required. Require at least one approving review on a pull request, before merging. Set to null to disable. +# required_pull_request_reviews: +# # The number of approvals required. (1-6) +# required_approving_review_count: 1 +# # Dismiss approved reviews automatically when a new commit is pushed. +# dismiss_stale_reviews: true +# # Blocks merge until code owners have reviewed. +# require_code_owner_reviews: true +# # Specify which users and teams can dismiss pull request reviews. Pass an empty dismissal_restrictions object to disable. User and team dismissal_restrictions are only available for organization-owned repositories. Omit this parameter for personal repositories. +# dismissal_restrictions: +# users: [] +# teams: [] +# # Required. Require status checks to pass before merging. Set to null to disable +# required_status_checks: +# # Required. Require branches to be up to date before merging. +# strict: true +# # Required. The list of status checks to require in order to merge into this branch +# contexts: [] +# # Required. Enforce all configured restrictions for administrators. Set to true to enforce required status checks for repository administrators. Set to null to disable. +# enforce_admins: true +# # Prevent merge commits from being pushed to matching branches +# required_linear_history: true +# # Required. Restrict who can push to this branch. Team and user restrictions are only available for organization-owned repositories. Set to null to disable. +# restrictions: +# apps: [] +# users: [] +# teams: [] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..58d6bae --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +###> Josee9988/Josee9988 ### + +# Folders +.vscode/ +.idea/ + +# Files +ignore.* +*.ignore.* + +# Environment variables and secret keys +.env +.env.test +*.pem + +###< Josee9988/Josee9988 ### diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e5384db --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,14 @@ + +# **Change Log** πŸ“œπŸ“ + +All notable changes to the "**Josee9988**" Github profile README will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +--- + +## [**0.0.1**] - 2021-07-11 + +### Added + +* The basic project structure from **[josee9988/project-template](https://github.com/Josee9988/project-template)**. diff --git a/README.md b/README.md index dbf9dda..f3591db 100644 --- a/README.md +++ b/README.md @@ -1,80 +1,97 @@ - + + +# πŸ”₯ **Josee9988/Josee9988** -# Hi there, I'm **[Jose Gracia Berenguer](https://jgracia.es)**! πŸ‘‹ +
+ + Issues + + + GitHub pull requests + +
+ +--- -Full-time **computer engineering student** ([CE](https://www.upv.es/contenidos/SMAT/epsa/756188normalc.html))πŸ§‘β€πŸŽ“πŸŽ“. Senior technician in web applications development ([DAW](https://www.todofp.es/que-como-y-donde-estudiar/que-estudiar/familia/loe/informatica-comunicaciones/des-aplicaciones-web.html)). Technician in microcomputer systems and networks ([SMR](https://www.todofp.es/que-como-y-donde-estudiar/que-estudiar/familia/loe/informatica-comunicaciones/sistemas-microniformaticos-redes.html)). +## πŸ€” **About the project** + +* --- -## **😊 About me** +## ⚑ **Installation** -- πŸ”­ I’m currently working on my engineer's degree. -- 🌱 I’m learning concurrency in C and the analysis of algorithms. -- πŸ‘― I’m looking to start a brand-new project/idea. -- πŸ€” I’m looking for help finding a job while I finish up my university degree. -- πŸ’¬ Ask me about anything, but for now, I'm fully open to new projects, collaborations or ideas! -- ⚑ Fun fact: Others turn coffee into code; I prefer working out πŸ‹πŸ½ or going out with the bike 🚴 +* --- -## **πŸ“Š Stats** - -
- - Josee9988's Github stats - - - Josee9988's Github commit streak - - - Josee9988's wakatime last year stats - -
+## πŸš€ **Usage** + +* --- -## **πŸ“« How to reach me** - -
- - Josee9988's Gmail - - - Josee9988's StackOverflow - - - Josee9988's Wakatime - - - Josee9988's CodersRank - - - Josee9988's LinkedIn - - - Josee9988's discord - - - Josee9988's octolife - - - Josee9988's dev.to - -
+## 🌲 **Project tree** + + + +--- + +## πŸ“ **Additional notes** + +* + +--- + +## πŸ“Έ **Screenshots** + + --- -
+## 🍰 **Supporters and donators** + +We are currently looking for new donators to help and maintain this project! ❀️ + +By donating, you will help the development of this project, and *you will be featured in this Josee9988's README.md* so everyone can see your kindness and visit your content ⭐. + + + + + + + +--- + +Josee9988 was generated from *[Josee9988/project-template](https://github.com/Josee9988/project-template)* πŸ“š + +--- + +## πŸ•΅οΈ Extra recommendations + +* + +--- + +## πŸŽ‰ Was the Github profile README helpful? Help us raise these numbers up + +[![Github followers](https://img.shields.io/github/followers/Josee9988.svg?style=social)](https://github.com/Josee9988) +[![Github stars](https://img.shields.io/github/stars/Josee9988/Josee9988.svg?style=social)](https://github.com/Josee9988/Josee9988/stargazers) +[![Github watchers](https://img.shields.io/github/watchers/Josee9988/Josee9988.svg?style=social)](https://github.com/Josee9988/Josee9988/watchers) +[![Github forks](https://img.shields.io/github/forks/Josee9988/Josee9988.svg?style=social)](https://github.com/Josee9988/Josee9988/network/members) + +[![Sponsor](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=github-sponsors&color=red&style=social)](https://github.com/sponsors/Josee9988) + +Enjoy! πŸ˜ƒ + +--- + +## βš–οΈπŸ“ **License and Changelog** + +See the license in the '**[LICENSE](LICENSE)**' file. + +Watch the changes in the '**[CHANGELOG.md](CHANGELOG.md)**' file. + +--- _Made with a lot of ❀️❀️ by **[@Josee9988](https://github.com/Josee9988)**_ diff --git a/SETUP_TEMPLATE.sh b/SETUP_TEMPLATE.sh new file mode 100644 index 0000000..0a25c09 --- /dev/null +++ b/SETUP_TEMPLATE.sh @@ -0,0 +1,118 @@ +#!/bin/bash + +#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~# +# SCRIPT: SETUP_TEMPLATE.sh +# USAGE: bash SETUP_TEMPLATE.sh | ./SETUP_TEMPLATE.sh +# PURPOSE: Shell script that prompts the user for their GitHub username, email name of their project and +# the type of project that it is. Then it will replace the author's (josee9988) username and email +# with the new ones (the user that is using this template). It will fully remove the LICENSE file, +# delete all of the content of the README.md file, and add a simple header, +# delete all of the content of the CHANGELOG.md and add a basic CHANGELOG versioning and 1st version, +# then this script will auto remove itself, to leave the new project without this file (SETUP_TEMPLATE.sh). +# TITLE: SETUP_TEMPLATE +# AUTHOR: Jose Gracia +# VERSION: See in CHANGELOG.md +# NOTES: This script will auto remove itself, and in case of wanting to run it again, the user must download +# it again or do a 'git stash' and revert the changes. +# BASH_VERSION: 5.1.4(1)-release (x86_64-pc-linux-gnu) +# LICENSE: see in LICENSE (project root) or https://github.com/Josee9988/project-template/blob/master/LICENSE +# GITHUB: https://github.com/Josee9988/ +# REPOSITORY: https://github.com/Josee9988/project-template +# ISSUES: https://github.com/Josee9988/project-template/issues +# MAIL: jgracia9988@gmail.com +#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~# + +RED='\033[1;31m' +NC='\033[0m' # No Color +UPurple='\033[4;35m' +BBLUE='\033[1;34m' +GREEN='\033[1;32m' +OMIT_STR="--omit-commit-and-confirmation" +FILE_FUNCTION_HELPERS=bin/FUNCTION_HELPERS.sh + +if [ ! -f "$FILE_FUNCTION_HELPERS" ]; then # check if the function helpers file is not found + echo -e "${RED}Can not find ${FILE_FUNCTION_HELPERS}" + exit 1 # it will exit if the function helpers file is not found +else + # shellcheck source=bin/FUNCTION_HELPERS.sh disable=SC1091 + source $FILE_FUNCTION_HELPERS || exit # obtain some global functions and variables, if the file isn't found exit +fi + +helpCommand "$1" # check if the help argument has been specified; if so it will exit +checkFiles # check if the main files exist before starting the project + +# READ GITHUB USERNAME AND GITHUB PROJECT NAME +NAME_AND_PROJECT_UNPARSED=$(git ls-remote --get-url) + +if [ -z "$1" ]; then # if the username has been manually specified + NEW_USERNAME=$(echo "$NAME_AND_PROJECT_UNPARSED" | cut -d':' -f 2 | cut -d'/' -f 1) +else + NEW_USERNAME=$1 +fi + +if [ -z "$2" ]; then # if the project name has been manually specified + PROJECT_NAME=$(echo "$NAME_AND_PROJECT_UNPARSED" | cut -d'/' -f 2 | cut -d'.' -f 1) +else + PROJECT_NAME=$2 +fi + +if [ -z "$3" ]; then # if the user mail has been manually specified + NEW_EMAIL=$(git config user.email) +else + NEW_EMAIL=$3 +fi + +echo -e "Thanks for using ${GREEN}Josee9988/project-template${NC}" +echo -e "Read carefully all the documentation before you continue executing this script: ${UPurple}https://github.com/Josee9988/project-template${NC}\n" + +if [ -n "$4" ]; then # if the project's type has been manually specified + PROJECT_TYPE=$4 +else + read -p "Enter $(echo -e "$BBLUE""what your project is""$NC") (program/extension/API/web/CLI tool/backend/frontend/scrapper/automation tool/etc): " PROJECT_TYPE +fi + +if [ "$5" = "$OMIT_STR" ]; then # if the ignore flag has been manually specified + choice="y" +else + read -p "Is this data correct: username \"$(echo -e "$GREEN""$NEW_USERNAME""$NC")\", email: \"$(echo -e "$GREEN""$NEW_EMAIL""$NC")\", project name: \"$(echo -e "$GREEN""$PROJECT_NAME""$NC")\", of type: \"$(echo -e "$GREEN""$PROJECT_TYPE""$NC")\" (y/n)? " choice +fi + +###### START OF THE SCRIPT ###### + +# confirm that the data is correct +case "$choice" in +y | Y) + center "Setting everything up for you ;)" + + # replace the username and email + find .github/ -type f -name "*" -print0 | xargs -0 sed -i "s/Josee9988/${NEW_USERNAME}/g" + find .github/ -type f -name "*" -print0 | xargs -0 sed -i "s/jgracia9988@gmail.com/${NEW_EMAIL}/g" + find .github/ -type f -name "*" -print0 | xargs -0 sed -i "s/project-template/${PROJECT_NAME}/g" + find .gitignore -type f -name "*" -print0 | xargs -0 sed -i "s/Josee9988\/project-template/${NEW_USERNAME}\/${PROJECT_NAME}/g" + + rm LICENSE # remove the license + rm -r bin/ # remove the bin folder + rm -r tests/ 2>/dev/null || : # remove the tests folder + writeREADME # write the new README.md + writeCHANGELOG # write the basic structure of the CHANGELOG.md + echo -e "# add your own funding links" >.github/FUNDING.yml # remove author's custom funding links + + if [ ! "$5" = "$OMIT_STR" ]; then # if the ignore option for tests has been specified + git add CHANGELOG.md README.md .gitignore .github SETUP_TEMPLATE.sh LICENSE bin # commit the new files + git -c color.status=always status | less -REX # show git status with colours + echo -e "Committing the changes for you :)\n" + git commit -m "Set up '@Josee9988/project-template' template: Personalized files by executing the SETUP_TEMPLATE.sh script.πŸš€" + echo -e "\nRemember to review every file and customize it as you like.\nYou are ready to start your brand new awesome projectπŸš€πŸš€." else + fi + + # self remove this script + rm -- "$0" + ;; +n | N) + echo -e "\nIf your username, project name or email were NOT right (the auto selection wasn't successful), execute the script and give as a first argument your username, as a second argument your project name and as a third you email.\n" + displayHelpTexts + ;; +*) echo -e "${RED}Invalid option${NC}" ;; +esac + +exit 0 diff --git a/tests/TESTS_RUNNER.sh b/tests/TESTS_RUNNER.sh new file mode 100755 index 0000000..5820df9 --- /dev/null +++ b/tests/TESTS_RUNNER.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~# +# PURPOSE: Main test script that will call all the individual unitary tests. +# TITLE: TESTS_RUNNER +# AUTHOR: Jose Gracia +# VERSION: See in CHANGELOG.md +# NOTES: This script does not contain any test, it only calls all the tests one by one. +# BASH_VERSION: 5.1.4(1)-release (x86_64-pc-linux-gnu) +# LICENSE: see in ../LICENSE (project root) or https://github.com/Josee9988/project-template/blob/master/LICENSE +# GITHUB: https://github.com/Josee9988/ +# REPOSITORY: https://github.com/Josee9988/project-template +# ISSUES: https://github.com/Josee9988/project-template/issues +# MAIL: jgracia9988@gmail.com +#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~# + +LGREEN='\033[0;32m' +NC='\033[0m' # No Color +TESTS_TRASH_DIR="tests/.ignore.tests_trash/" +declare -a test_files=("tests/project_scaffolding_test.sh" "tests/custom_data_test.sh") # all the tests + +center() { + term_width="$(tput cols)" + padding="$(printf '%0.1s' +{1..500})" + echo -e "\n${LGREEN}$(printf '%*.*s %s %*.*s\n' 0 "$(((term_width - 2 - ${#1}) / 2))" "$padding" "$1" 0 "$(((term_width - 1 - ${#1}) / 2))" "$padding")${NC}" +} + +rm -r $TESTS_TRASH_DIR 2>/dev/null || : # remove any possible old test run trash files + +# run all the tests +for file in "${test_files[@]}"; do + mkdir -p tests/.ignore.tests_trash # create the files where the tests will attack upon + center "TEST: running test ./$file" + ./"$file" # run the test + rm -r $TESTS_TRASH_DIR # remove the previously created files +done diff --git a/tests/custom_data_test.sh b/tests/custom_data_test.sh new file mode 100755 index 0000000..4c9d327 --- /dev/null +++ b/tests/custom_data_test.sh @@ -0,0 +1,101 @@ +#! /bin/bash + +#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~# +# PURPOSE: Test suite for testing the modified custom data, after executing the SETUP_TEMPLATE script.ΒΊ +# TITLE: Custom Data tests +# AUTHOR: Jose Gracia +# VERSION: See in CHANGELOG.md +# NOTES: This script is called by the TESTS_RUNNER.sh script. And it unit tests all the custom data modified +# by the SETUP_TEMPLATE script. +# BASH_VERSION: 5.1.4(1)-release (x86_64-pc-linux-gnu) +# LICENSE: see in ../LICENSE (project root) or https://github.com/Josee9988/project-template/blob/master/LICENSE +# GITHUB: https://github.com/Josee9988/ +# REPOSITORY: https://github.com/Josee9988/project-template +# ISSUES: https://github.com/Josee9988/project-template/issues +# MAIL: jgracia9988@gmail.com +#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~# + +TESTS_TRASH_DIR="tests/.ignore.tests_trash" +USERNAME="FAKE_USERNAME_TESTS" +NAME="FAKE_NAME_TESTS" +MAIL="FAKE_EMAIL_TESTS" +TYPE="FAKE_TYPE_TESTS" +OMIT_STR="--omit-commit-and-confirmation" + +oneTimeSetUp() { + cp -r * $TESTS_TRASH_DIR --copy-content 2>/dev/null || : + cp -r .github/ $TESTS_TRASH_DIR --copy-contents + cp -r bin/ $TESTS_TRASH_DIR --copy-contents + cp .gitignore $TESTS_TRASH_DIR --copy-contents + rm -r $TESTS_TRASH_DIR/tests/ 2>/dev/null || : + rm -r $TESTS_TRASH_DIR/.git/ 2>/dev/null || : + cd $TESTS_TRASH_DIR || exit + bash SETUP_TEMPLATE.sh $USERNAME $NAME $MAIL $TYPE $OMIT_STR >/dev/null # run the setup script +} + +oneTimeTearDown() { + cd "../.." || exit +} + +# TESTS +suite() { + suite_addTest testDotGithubISSUE_TEMPLATEAsignees + suite_addTest testDotGithubISSUE_TEMPLATEConfig + suite_addTest testDotGithubConfig + suite_addTest testDotGithubSecurity + suite_addTest testDotGithubCODEOWNERS + suite_addTest testDotGitignore + suite_addTest testChangelog +} + +testDotGithubISSUE_TEMPLATEAsignees() { + assigneesWithName="assignees: $USERNAME" + declare -a files=( + ".github/ISSUE_TEMPLATE/1-bug-report.md" ".github/ISSUE_TEMPLATE/2-failing-test.md" + ".github/ISSUE_TEMPLATE/3-docs-bug.md" ".github/ISSUE_TEMPLATE/4-feature-request.md" + ".github/ISSUE_TEMPLATE/5-enhancement-request.md" ".github/ISSUE_TEMPLATE/6-security-report.md" + ".github/ISSUE_TEMPLATE/7-question-support.md") + + for file in "${files[@]}"; do + assertTrue " assignee was not found for file $file" "grep -q \"$assigneesWithName\" \"$file\"" + done +} +testDotGithubISSUE_TEMPLATEConfig() { + mailInConfig="url: mailto:$MAIL" + assertTrue "The mail $MAIL was not found in .github/ISSUE_TEMPLATE/config.yml" "grep -q \"$mailInConfig\" \".github/ISSUE_TEMPLATE/config.yml\"" +} + +testDotGithubConfig() { + newIssueWelcomeComment="Thanks for opening your first issue in $USERNAME/$NAME! Be sure to" + assertTrue "The $USERNAME/$NAME was not found in " "grep -q \"$newIssueWelcomeComment\" \".github/config.yml\"" +} + +testDotGithubSecurity() { + securityData1="(mailto:$MAIL)" + securityData2="[$MAIL]" + securityData3="he project's team and community take security issues" + assertTrue "The mail $MAIL was not found in .github/SECURITY.md" "grep -q \"$securityData1\" \".github/SECURITY.md\" && grep -q \"$securityData2\" \".github/SECURITY.md\" && grep -q \"$securityData3\" \".github/SECURITY.md\"" +} + +testDotGithubCODEOWNERS() { + usernameData="$USERNAME" + assertTrue "Could not find the username $USERNAME in the file .github/CODEOWNERS" "grep -q \"$usernameData\" \".github/CODEOWNERS\"" +} + +testDotGitignore() { + gitignoreData1="###> $USERNAME/$NAME ###" + gitignoreData2="###< $USERNAME/$NAME ###" + + assertTrue "Could not find the username $USERNAME and the name $NAME in the .gitignore file" "grep -q \"$gitignoreData1\" \".gitignore\" && grep -q \"$gitignoreData2\" \".gitignore\"" +} + +testChangelog() { + changelogData1="All notable changes to the" + changelogData2="**$NAME**" + changelogData3="$TYPE will be documented in this file." + + assertTrue "Could not find the name $NAME or the type $TYPE in the CHANGELOG.md file" "grep -q \"$changelogData1\" \"CHANGELOG.md\" && grep -q \"$changelogData2\" \"CHANGELOG.md\" && grep -q \"$changelogData3\" \"CHANGELOG.md\"" +} + +# Load and run shUnit2. +. tests/shunit2 diff --git a/tests/project_scaffolding_test.sh b/tests/project_scaffolding_test.sh new file mode 100755 index 0000000..0a99a3b --- /dev/null +++ b/tests/project_scaffolding_test.sh @@ -0,0 +1,111 @@ +#! /bin/bash + +#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~# +# PURPOSE: Test suite for testing the proejct scaffolding after executing the SETUP_TEMPLATE script. +# TITLE: Project Scaffolding tests +# AUTHOR: Jose Gracia +# VERSION: See in CHANGELOG.md +# NOTES: This script is called by the TESTS_RUNNER.sh script. And it unit tests the newly generated scaffolding +# generated by the SETUP_TEMPLATE script. +# BASH_VERSION: 5.1.4(1)-release (x86_64-pc-linux-gnu) +# LICENSE: see in ../LICENSE (project root) or https://github.com/Josee9988/project-template/blob/master/LICENSE +# GITHUB: https://github.com/Josee9988/ +# REPOSITORY: https://github.com/Josee9988/project-template +# ISSUES: https://github.com/Josee9988/project-template/issues +# MAIL: jgracia9988@gmail.com +#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~# + +TESTS_TRASH_DIR="tests/.ignore.tests_trash" +USERNAME="FAKE_USERNAME_TESTS" +NAME="FAKE_NAME_TESTS" +MAIL="FAKE_EMAIL_TESTS" +TYPE="FAKE_TYPE_TESTS" +OMIT_STR="--omit-commit-and-confirmation" + +oneTimeSetUp() { + cp -r * $TESTS_TRASH_DIR --copy-content 2>/dev/null || : + cp -r .github/ $TESTS_TRASH_DIR --copy-contents + cp -r bin/ $TESTS_TRASH_DIR --copy-contents + cp .gitignore $TESTS_TRASH_DIR --copy-contents + rm -r $TESTS_TRASH_DIR/tests/ 2>/dev/null || : + rm -r $TESTS_TRASH_DIR/.git/ 2>/dev/null || : + cd $TESTS_TRASH_DIR || exit + bash SETUP_TEMPLATE.sh $USERNAME $NAME $MAIL $TYPE $OMIT_STR >/dev/null # run the setup script +} + +oneTimeTearDown() { + cd "../.." || exit +} + +# TESTS +suite() { + suite_addTest testDotGithubFolder + suite_addTest testDotGithubISSUE_TEMPLATE + suite_addTest testDotGithubISSUE_TEMPLATEFiles + suite_addTest testDotGithubFiles + suite_addTest testTestRemovedFiles + suite_addTest testGlobalFiles + suite_addTest testRemovedFiles +} + +testDotGithubFolder() { + if [ ! -e ".github/" ]; then + assertEquals ".github file does not exist" 1 0 # error + fi +} + +testDotGithubISSUE_TEMPLATE() { + if [ ! -e ".github/ISSUE_TEMPLATE" ]; then + assertEquals ".github/ISSUE_TEMPLATE was not found" 1 0 # error + fi +} + +testDotGithubISSUE_TEMPLATEFiles() { + declare -a files=( + ".github/ISSUE_TEMPLATE/1-bug-report.md" ".github/ISSUE_TEMPLATE/2-failing-test.md" + ".github/ISSUE_TEMPLATE/3-docs-bug.md" ".github/ISSUE_TEMPLATE/4-feature-request.md" + ".github/ISSUE_TEMPLATE/5-enhancement-request.md" ".github/ISSUE_TEMPLATE/6-security-report.md" + ".github/ISSUE_TEMPLATE/7-question-support.md") + + for file in "${files[@]}"; do + assertTrue " $file does not exist" "[ -e \"$file\" ]" + done +} + +testDotGithubFiles() { + declare -a files=( + ".github/CODEOWNERS" ".github/CODE_OF_CONDUCT.md" ".github/CONTRIBUTING.md" ".github/ISSUE_TEMPLATE.md" + ".github/pull_request_template.md" ".github/SECURITY.md" ".github/SUPPORT.md" ".github/issue_label_bot.yaml" + ".github/config.yml" ".github/FUNDING.yml" ".github/settings.yml") + + for file in "${files[@]}"; do + assertTrue " $file does not exist" "[ -e \"$file\" ]" + done +} + +testTestRemovedFiles() { + filesFound=0 + if [ -e "tests/" ] && [ -e "tests/shunit2" ] && [ -e "tests/TESTS_RUNNER.sh" ]; then + filesFound=1 + fi + assertNotEquals " tests folder of some files were found" 1 $filesFound +} + +testGlobalFiles() { + filesFound=0 + if [ -e ".gitignore" ] && [ -e "CHANGELOG.md" ] && [ -e "README.md" ]; then + filesFound=1 + fi + assertEquals " gitignore, changelog or readme were not found" 1 $filesFound +} + +testRemovedFiles() { + filesFound=0 + if [ -e "LICENSE" ] && [ -e "bin" ] && [ -e "bin/FUNCTION_HELPERS.sh" ]; then + filesFound=1 + fi + assertNotEquals " LICENSE or the bin directory were found" 1 $filesFound +} + +# Load and run shUnit2. +. tests/shunit2 diff --git a/tests/shunit2 b/tests/shunit2 new file mode 100755 index 0000000..6239683 --- /dev/null +++ b/tests/shunit2 @@ -0,0 +1,1343 @@ +#! /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 $?