diff --git a/bash/bob/.exercism/config.json b/bash/bob/.exercism/config.json new file mode 100644 index 0000000..43975eb --- /dev/null +++ b/bash/bob/.exercism/config.json @@ -0,0 +1,33 @@ +{ + "authors": [ + "kenden" + ], + "contributors": [ + "bkhl", + "budmc29", + "glennj", + "guygastineau", + "IsaacG", + "kotp", + "kytrinyx", + "platinumthinker", + "rpalo", + "sjwarner-bp", + "Smarticles101", + "ZapAnton" + ], + "files": { + "solution": [ + "bob.sh" + ], + "test": [ + "bob.bats" + ], + "example": [ + ".meta/example.sh" + ] + }, + "blurb": "Bob is a lackadaisical teenager. In conversation, his responses are very limited.", + "source": "Inspired by the 'Deaf Grandma' exercise in Chris Pine's Learn to Program tutorial.", + "source_url": "https://pine.fm/LearnToProgram/?Chapter=06" +} diff --git a/bash/bob/.exercism/metadata.json b/bash/bob/.exercism/metadata.json new file mode 100644 index 0000000..8625456 --- /dev/null +++ b/bash/bob/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"bash","exercise":"bob","id":"780465dbbed74811a05d1c8f1366aca3","url":"https://exercism.org/tracks/bash/exercises/bob","handle":"Kimawari","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/bash/bob/HELP.md b/bash/bob/HELP.md new file mode 100644 index 0000000..e712fca --- /dev/null +++ b/bash/bob/HELP.md @@ -0,0 +1,105 @@ +# Help + +## Running the tests + +Each exercise contains a test file. +Run the tests using the `bats` program. + +```bash +bats hello_world.bats +``` + +`bats` will need to be installed. +See the [Testing on the Bash track][tests] page for instructions to install `bats` for your system. + +[tests]: https://exercism.org/docs/tracks/bash/tests + +## Help for assert functions + +The tests use functions from the [bats-assert][bats-assert] library. +Help for the various `assert*` functions can be found there. + +[bats-assert]: https://github.com/bats-core/bats-assert + +## Skipped tests + +Solving an exercise means making all its tests pass. +By default, only one test (the first one) is executed when you run the tests. +This is intentional, as it allows you to focus on just making that one test pass. +Once it passes, you can enable the next test by commenting out or removing the next annotation: + +```bash +[[ $BATS_RUN_SKIPPED == true ]] || skip +``` + +## Overriding skips + +To run all tests, including the ones with `skip` annotations, you can run: + +```bash +BATS_RUN_SKIPPED=true bats exercise_name.bats +``` + +It can be convenient to use a wrapper function to save on typing: + +```bash +bats() { + BATS_RUN_SKIPPED=true command bats *.bats +} +``` + +Then run tests with just: + +```bash +bats +``` + +## Submitting your solution + +You can submit your solution using the `exercism submit bob.sh` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Bash track's documentation](https://exercism.org/docs/tracks/bash) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +Check your code for syntax errors: paste your code into +[https://shellcheck.net](https://shellcheck.net) (or [install it](https://github.com/koalaman/shellcheck#user-content-installing) on your machine). + +Stack Overflow will be your first stop for bash questions. + +* start with the [`bash` tag](https://stackoverflow.com/questions/tagged/bash) to search for your specific question: it's probably already been asked +* under the bash tag on Stackoverflow, the [Learn more...](https://stackoverflow.com/tags/bash/info) link has _tons_ of good information. + * the "Books and Resources" section is particularly useful. +* the [`bash` tag](https://unix.stackexchange.com/questions/tagged/bash) on Unix & Linux is also active + +## External utilities + +`bash` is a language to write "scripts" -- programs that can call +external tools, such as +[`sed`](https://www.gnu.org/software/sed/), +[`awk`](https://www.gnu.org/software/gawk/), +[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html) +and even programs written in other programming languages, +like [`Python`](https://www.python.org/). +This track does not restrict the usage of these utilities, and as long +as your solution is portable between systems and does not require +installation of third party applications, feel free to use them to solve +the exercise. + +For an extra challenge, if you would like to have a better understanding of +the language, try to re-implement the solution in pure bash, without using +any external tools. There are some types of problems that bash cannot solve, +such as floating point arithmetic and manipulating dates: for those, you +must call out to an external tool. \ No newline at end of file diff --git a/bash/bob/README.md b/bash/bob/README.md new file mode 100644 index 0000000..ad65860 --- /dev/null +++ b/bash/bob/README.md @@ -0,0 +1,60 @@ +# Bob + +Welcome to Bob on Exercism's Bash Track. +If you need help running the tests or submitting your code, check out `HELP.md`. + +## Introduction + +Bob is a [lackadaisical][] teenager. +He likes to think that he's very cool. +And he definitely doesn't get excited about things. +That wouldn't be cool. + +When people talk to him, his responses are pretty limited. + +[lackadaisical]: https://www.collinsdictionary.com/dictionary/english/lackadaisical + +## Instructions + +Your task is to determine what Bob will reply to someone when they say something to him or ask him a question. + +Bob only ever answers one of five things: + +- **"Sure."** + This is his response if you ask him a question, such as "How are you?" + The convention used for questions is that it ends with a question mark. +- **"Whoa, chill out!"** + This is his answer if you YELL AT HIM. + The convention used for yelling is ALL CAPITAL LETTERS. +- **"Calm down, I know what I'm doing!"** + This is what he says if you yell a question at him. +- **"Fine. Be that way!"** + This is how he responds to silence. + The convention used for silence is nothing, or various combinations of whitespace characters. +- **"Whatever."** + This is what he answers to anything else. + +## Source + +### Created by + +- @kenden + +### Contributed to by + +- @bkhl +- @budmc29 +- @glennj +- @guygastineau +- @IsaacG +- @kotp +- @kytrinyx +- @platinumthinker +- @rpalo +- @sjwarner-bp +- @Smarticles101 +- @ZapAnton + +### Based on + +Inspired by the 'Deaf Grandma' exercise in Chris Pine's Learn to Program tutorial. - https://pine.fm/LearnToProgram/?Chapter=06 \ No newline at end of file diff --git a/bash/bob/bats-extra.bash b/bash/bob/bats-extra.bash new file mode 100644 index 0000000..54d4807 --- /dev/null +++ b/bash/bob/bats-extra.bash @@ -0,0 +1,637 @@ +# This is the source code for bats-support and bats-assert, concatenated +# * https://github.com/bats-core/bats-support +# * https://github.com/bats-core/bats-assert +# +# Comments have been removed to save space. See the git repos for full source code. + +############################################################ +# +# bats-support - Supporting library for Bats test helpers +# +# Written in 2016 by Zoltan Tombol +# +# To the extent possible under law, the author(s) have dedicated all +# copyright and related and neighboring rights to this software to the +# public domain worldwide. This software is distributed without any +# warranty. +# +# You should have received a copy of the CC0 Public Domain Dedication +# along with this software. If not, see +# . +# + +fail() { + (( $# == 0 )) && batslib_err || batslib_err "$@" + return 1 +} + +batslib_is_caller() { + local -i is_mode_direct=1 + + # Handle options. + while (( $# > 0 )); do + case "$1" in + -i|--indirect) is_mode_direct=0; shift ;; + --) shift; break ;; + *) break ;; + esac + done + + # Arguments. + local -r func="$1" + + # Check call stack. + if (( is_mode_direct )); then + [[ $func == "${FUNCNAME[2]}" ]] && return 0 + else + local -i depth + for (( depth=2; depth<${#FUNCNAME[@]}; ++depth )); do + [[ $func == "${FUNCNAME[$depth]}" ]] && return 0 + done + fi + + return 1 +} + +batslib_err() { + { if (( $# > 0 )); then + echo "$@" + else + cat - + fi + } >&2 +} + +batslib_count_lines() { + local -i n_lines=0 + local line + while IFS='' read -r line || [[ -n $line ]]; do + (( ++n_lines )) + done < <(printf '%s' "$1") + echo "$n_lines" +} + +batslib_is_single_line() { + for string in "$@"; do + (( $(batslib_count_lines "$string") > 1 )) && return 1 + done + return 0 +} + +batslib_get_max_single_line_key_width() { + local -i max_len=-1 + while (( $# != 0 )); do + local -i key_len="${#1}" + batslib_is_single_line "$2" && (( key_len > max_len )) && max_len="$key_len" + shift 2 + done + echo "$max_len" +} + +batslib_print_kv_single() { + local -ir col_width="$1"; shift + while (( $# != 0 )); do + printf '%-*s : %s\n' "$col_width" "$1" "$2" + shift 2 + done +} + +batslib_print_kv_multi() { + while (( $# != 0 )); do + printf '%s (%d lines):\n' "$1" "$( batslib_count_lines "$2" )" + printf '%s\n' "$2" + shift 2 + done +} + +batslib_print_kv_single_or_multi() { + local -ir width="$1"; shift + local -a pairs=( "$@" ) + + local -a values=() + local -i i + for (( i=1; i < ${#pairs[@]}; i+=2 )); do + values+=( "${pairs[$i]}" ) + done + + if batslib_is_single_line "${values[@]}"; then + batslib_print_kv_single "$width" "${pairs[@]}" + else + local -i i + for (( i=1; i < ${#pairs[@]}; i+=2 )); do + pairs[$i]="$( batslib_prefix < <(printf '%s' "${pairs[$i]}") )" + done + batslib_print_kv_multi "${pairs[@]}" + fi +} + +batslib_prefix() { + local -r prefix="${1:- }" + local line + while IFS='' read -r line || [[ -n $line ]]; do + printf '%s%s\n' "$prefix" "$line" + done +} + +batslib_mark() { + local -r symbol="$1"; shift + # Sort line numbers. + set -- $( sort -nu <<< "$( printf '%d\n' "$@" )" ) + + local line + local -i idx=0 + while IFS='' read -r line || [[ -n $line ]]; do + if (( ${1:--1} == idx )); then + printf '%s\n' "${symbol}${line:${#symbol}}" + shift + else + printf '%s\n' "$line" + fi + (( ++idx )) + done +} + +batslib_decorate() { + echo + echo "-- $1 --" + cat - + echo '--' + echo +} + +############################################################ + +assert() { + if ! "$@"; then + batslib_print_kv_single 10 'expression' "$*" \ + | batslib_decorate 'assertion failed' \ + | fail + fi +} + +assert_equal() { + if [[ $1 != "$2" ]]; then + batslib_print_kv_single_or_multi 8 \ + 'expected' "$2" \ + 'actual' "$1" \ + | batslib_decorate 'values do not equal' \ + | fail + fi +} + +assert_failure() { + : "${output?}" + : "${status?}" + + (( $# > 0 )) && local -r expected="$1" + if (( status == 0 )); then + batslib_print_kv_single_or_multi 6 'output' "$output" \ + | batslib_decorate 'command succeeded, but it was expected to fail' \ + | fail + elif (( $# > 0 )) && (( status != expected )); then + { local -ir width=8 + batslib_print_kv_single "$width" \ + 'expected' "$expected" \ + 'actual' "$status" + batslib_print_kv_single_or_multi "$width" \ + 'output' "$output" + } \ + | batslib_decorate 'command failed as expected, but status differs' \ + | fail + fi +} + +assert_line() { + local -i is_match_line=0 + local -i is_mode_partial=0 + local -i is_mode_regexp=0 + : "${lines?}" + + # Handle options. + while (( $# > 0 )); do + case "$1" in + -n|--index) + if (( $# < 2 )) || ! [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then + echo "\`--index' requires an integer argument: \`$2'" \ + | batslib_decorate 'ERROR: assert_line' \ + | fail + return $? + fi + is_match_line=1 + local -ri idx="$2" + shift 2 + ;; + -p|--partial) is_mode_partial=1; shift ;; + -e|--regexp) is_mode_regexp=1; shift ;; + --) shift; break ;; + *) break ;; + esac + done + + if (( is_mode_partial )) && (( is_mode_regexp )); then + echo "\`--partial' and \`--regexp' are mutually exclusive" \ + | batslib_decorate 'ERROR: assert_line' \ + | fail + return $? + fi + + # Arguments. + local -r expected="$1" + + if (( is_mode_regexp == 1 )) && [[ '' =~ $expected ]] || (( $? == 2 )); then + echo "Invalid extended regular expression: \`$expected'" \ + | batslib_decorate 'ERROR: assert_line' \ + | fail + return $? + fi + + # Matching. + if (( is_match_line )); then + # Specific line. + if (( is_mode_regexp )); then + if ! [[ ${lines[$idx]} =~ $expected ]]; then + batslib_print_kv_single 6 \ + 'index' "$idx" \ + 'regexp' "$expected" \ + 'line' "${lines[$idx]}" \ + | batslib_decorate 'regular expression does not match line' \ + | fail + fi + elif (( is_mode_partial )); then + if [[ ${lines[$idx]} != *"$expected"* ]]; then + batslib_print_kv_single 9 \ + 'index' "$idx" \ + 'substring' "$expected" \ + 'line' "${lines[$idx]}" \ + | batslib_decorate 'line does not contain substring' \ + | fail + fi + else + if [[ ${lines[$idx]} != "$expected" ]]; then + batslib_print_kv_single 8 \ + 'index' "$idx" \ + 'expected' "$expected" \ + 'actual' "${lines[$idx]}" \ + | batslib_decorate 'line differs' \ + | fail + fi + fi + else + # Contained in output. + if (( is_mode_regexp )); then + local -i idx + for (( idx = 0; idx < ${#lines[@]}; ++idx )); do + [[ ${lines[$idx]} =~ $expected ]] && return 0 + done + { local -ar single=( 'regexp' "$expected" ) + local -ar may_be_multi=( 'output' "$output" ) + local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" + } \ + | batslib_decorate 'no output line matches regular expression' \ + | fail + elif (( is_mode_partial )); then + local -i idx + for (( idx = 0; idx < ${#lines[@]}; ++idx )); do + [[ ${lines[$idx]} == *"$expected"* ]] && return 0 + done + { local -ar single=( 'substring' "$expected" ) + local -ar may_be_multi=( 'output' "$output" ) + local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" + } \ + | batslib_decorate 'no output line contains substring' \ + | fail + else + local -i idx + for (( idx = 0; idx < ${#lines[@]}; ++idx )); do + [[ ${lines[$idx]} == "$expected" ]] && return 0 + done + { local -ar single=( 'line' "$expected" ) + local -ar may_be_multi=( 'output' "$output" ) + local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" + } \ + | batslib_decorate 'output does not contain line' \ + | fail + fi + fi +} + +assert_output() { + local -i is_mode_partial=0 + local -i is_mode_regexp=0 + local -i is_mode_nonempty=0 + local -i use_stdin=0 + : "${output?}" + + # Handle options. + if (( $# == 0 )); then + is_mode_nonempty=1 + fi + + while (( $# > 0 )); do + case "$1" in + -p|--partial) is_mode_partial=1; shift ;; + -e|--regexp) is_mode_regexp=1; shift ;; + -|--stdin) use_stdin=1; shift ;; + --) shift; break ;; + *) break ;; + esac + done + + if (( is_mode_partial )) && (( is_mode_regexp )); then + echo "\`--partial' and \`--regexp' are mutually exclusive" \ + | batslib_decorate 'ERROR: assert_output' \ + | fail + return $? + fi + + # Arguments. + local expected + if (( use_stdin )); then + expected="$(cat -)" + else + expected="${1-}" + fi + + # Matching. + if (( is_mode_nonempty )); then + if [ -z "$output" ]; then + echo 'expected non-empty output, but output was empty' \ + | batslib_decorate 'no output' \ + | fail + fi + elif (( is_mode_regexp )); then + if [[ '' =~ $expected ]] || (( $? == 2 )); then + echo "Invalid extended regular expression: \`$expected'" \ + | batslib_decorate 'ERROR: assert_output' \ + | fail + elif ! [[ $output =~ $expected ]]; then + batslib_print_kv_single_or_multi 6 \ + 'regexp' "$expected" \ + 'output' "$output" \ + | batslib_decorate 'regular expression does not match output' \ + | fail + fi + elif (( is_mode_partial )); then + if [[ $output != *"$expected"* ]]; then + batslib_print_kv_single_or_multi 9 \ + 'substring' "$expected" \ + 'output' "$output" \ + | batslib_decorate 'output does not contain substring' \ + | fail + fi + else + if [[ $output != "$expected" ]]; then + batslib_print_kv_single_or_multi 8 \ + 'expected' "$expected" \ + 'actual' "$output" \ + | batslib_decorate 'output differs' \ + | fail + fi + fi +} + +assert_success() { + : "${output?}" + : "${status?}" + + if (( status != 0 )); then + { local -ir width=6 + batslib_print_kv_single "$width" 'status' "$status" + batslib_print_kv_single_or_multi "$width" 'output' "$output" + } \ + | batslib_decorate 'command failed' \ + | fail + fi +} + +refute() { + if "$@"; then + batslib_print_kv_single 10 'expression' "$*" \ + | batslib_decorate 'assertion succeeded, but it was expected to fail' \ + | fail + fi +} + +refute_line() { + local -i is_match_line=0 + local -i is_mode_partial=0 + local -i is_mode_regexp=0 + : "${lines?}" + + # Handle options. + while (( $# > 0 )); do + case "$1" in + -n|--index) + if (( $# < 2 )) || ! [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then + echo "\`--index' requires an integer argument: \`$2'" \ + | batslib_decorate 'ERROR: refute_line' \ + | fail + return $? + fi + is_match_line=1 + local -ri idx="$2" + shift 2 + ;; + -p|--partial) is_mode_partial=1; shift ;; + -e|--regexp) is_mode_regexp=1; shift ;; + --) shift; break ;; + *) break ;; + esac + done + + if (( is_mode_partial )) && (( is_mode_regexp )); then + echo "\`--partial' and \`--regexp' are mutually exclusive" \ + | batslib_decorate 'ERROR: refute_line' \ + | fail + return $? + fi + + # Arguments. + local -r unexpected="$1" + + if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then + echo "Invalid extended regular expression: \`$unexpected'" \ + | batslib_decorate 'ERROR: refute_line' \ + | fail + return $? + fi + + # Matching. + if (( is_match_line )); then + # Specific line. + if (( is_mode_regexp )); then + if [[ ${lines[$idx]} =~ $unexpected ]]; then + batslib_print_kv_single 6 \ + 'index' "$idx" \ + 'regexp' "$unexpected" \ + 'line' "${lines[$idx]}" \ + | batslib_decorate 'regular expression should not match line' \ + | fail + fi + elif (( is_mode_partial )); then + if [[ ${lines[$idx]} == *"$unexpected"* ]]; then + batslib_print_kv_single 9 \ + 'index' "$idx" \ + 'substring' "$unexpected" \ + 'line' "${lines[$idx]}" \ + | batslib_decorate 'line should not contain substring' \ + | fail + fi + else + if [[ ${lines[$idx]} == "$unexpected" ]]; then + batslib_print_kv_single 5 \ + 'index' "$idx" \ + 'line' "${lines[$idx]}" \ + | batslib_decorate 'line should differ' \ + | fail + fi + fi + else + # Line contained in output. + if (( is_mode_regexp )); then + local -i idx + for (( idx = 0; idx < ${#lines[@]}; ++idx )); do + if [[ ${lines[$idx]} =~ $unexpected ]]; then + { local -ar single=( 'regexp' "$unexpected" 'index' "$idx" ) + local -a may_be_multi=( 'output' "$output" ) + local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + if batslib_is_single_line "${may_be_multi[1]}"; then + batslib_print_kv_single "$width" "${may_be_multi[@]}" + else + may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )" + batslib_print_kv_multi "${may_be_multi[@]}" + fi + } \ + | batslib_decorate 'no line should match the regular expression' \ + | fail + return $? + fi + done + elif (( is_mode_partial )); then + local -i idx + for (( idx = 0; idx < ${#lines[@]}; ++idx )); do + if [[ ${lines[$idx]} == *"$unexpected"* ]]; then + { local -ar single=( 'substring' "$unexpected" 'index' "$idx" ) + local -a may_be_multi=( 'output' "$output" ) + local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + if batslib_is_single_line "${may_be_multi[1]}"; then + batslib_print_kv_single "$width" "${may_be_multi[@]}" + else + may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )" + batslib_print_kv_multi "${may_be_multi[@]}" + fi + } \ + | batslib_decorate 'no line should contain substring' \ + | fail + return $? + fi + done + else + local -i idx + for (( idx = 0; idx < ${#lines[@]}; ++idx )); do + if [[ ${lines[$idx]} == "$unexpected" ]]; then + { local -ar single=( 'line' "$unexpected" 'index' "$idx" ) + local -a may_be_multi=( 'output' "$output" ) + local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + if batslib_is_single_line "${may_be_multi[1]}"; then + batslib_print_kv_single "$width" "${may_be_multi[@]}" + else + may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )" + batslib_print_kv_multi "${may_be_multi[@]}" + fi + } \ + | batslib_decorate 'line should not be in output' \ + | fail + return $? + fi + done + fi + fi +} + +refute_output() { + local -i is_mode_partial=0 + local -i is_mode_regexp=0 + local -i is_mode_empty=0 + local -i use_stdin=0 + : "${output?}" + + # Handle options. + if (( $# == 0 )); then + is_mode_empty=1 + fi + + while (( $# > 0 )); do + case "$1" in + -p|--partial) is_mode_partial=1; shift ;; + -e|--regexp) is_mode_regexp=1; shift ;; + -|--stdin) use_stdin=1; shift ;; + --) shift; break ;; + *) break ;; + esac + done + + if (( is_mode_partial )) && (( is_mode_regexp )); then + echo "\`--partial' and \`--regexp' are mutually exclusive" \ + | batslib_decorate 'ERROR: refute_output' \ + | fail + return $? + fi + + # Arguments. + local unexpected + if (( use_stdin )); then + unexpected="$(cat -)" + else + unexpected="${1-}" + fi + + if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then + echo "Invalid extended regular expression: \`$unexpected'" \ + | batslib_decorate 'ERROR: refute_output' \ + | fail + return $? + fi + + # Matching. + if (( is_mode_empty )); then + if [ -n "$output" ]; then + batslib_print_kv_single_or_multi 6 \ + 'output' "$output" \ + | batslib_decorate 'output non-empty, but expected no output' \ + | fail + fi + elif (( is_mode_regexp )); then + if [[ $output =~ $unexpected ]]; then + batslib_print_kv_single_or_multi 6 \ + 'regexp' "$unexpected" \ + 'output' "$output" \ + | batslib_decorate 'regular expression should not match output' \ + | fail + fi + elif (( is_mode_partial )); then + if [[ $output == *"$unexpected"* ]]; then + batslib_print_kv_single_or_multi 9 \ + 'substring' "$unexpected" \ + 'output' "$output" \ + | batslib_decorate 'output should not contain substring' \ + | fail + fi + else + if [[ $output == "$unexpected" ]]; then + batslib_print_kv_single_or_multi 6 \ + 'output' "$output" \ + | batslib_decorate 'output equals, but it was expected to differ' \ + | fail + fi + fi +} diff --git a/bash/bob/bob.bats b/bash/bob/bob.bats new file mode 100644 index 0000000..52354eb --- /dev/null +++ b/bash/bob/bob.bats @@ -0,0 +1,201 @@ +#!/usr/bin/env bats +load bats-extra + +# local version: 1.6.0.0 + +@test "stating something" { + #[[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh 'Tom-ay-to, tom-aaaah-to.' + assert_success + assert_output "Whatever." +} + +@test "shouting" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh 'WATCH OUT!' + assert_success + assert_output "Whoa, chill out!" +} + +@test "shouting gibberish" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh 'FCECDFCAAB' + assert_success + assert_output "Whoa, chill out!" +} + +@test "asking a question" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh 'Does this cryogenic chamber make me look fat?' + assert_success + assert_output "Sure." +} + +@test "asking a numeric question" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh 'You are, what, like 15?' + assert_success + assert_output "Sure." +} + +@test "asking gibberish" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh 'fffbbcbeab?' + assert_success + assert_output "Sure." +} + +@test "talking forcefully" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh "Hi there!" + assert_success + assert_output "Whatever." +} + +@test "using acronyms in regular speech" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh "It's OK if you don't want to go work for NASA." + assert_success + assert_output "Whatever." +} + +@test "forceful question" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh "WHAT'S GOING ON?" + assert_success + assert_output "Calm down, I know what I'm doing!" +} + +@test "shouting numbers" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh '1, 2, 3 GO!' + assert_success + assert_output "Whoa, chill out!" +} + +@test "no letters" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh '1, 2, 3' + assert_success + assert_output "Whatever." +} + +@test "question with no letters" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh '4?' + assert_success + assert_output "Sure." +} + +@test "shouting with special characters" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh 'ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!' + assert_success + assert_output "Whoa, chill out!" +} + +@test "shouting with no exclamation mark" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh 'I HATE THE DENTIST' + assert_success + assert_output "Whoa, chill out!" +} + +@test "statement containing question mark" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh 'Ending with ? means a question.' + assert_success + assert_output "Whatever." +} + +@test "non-letters with question" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh ':) ?' + assert_success + assert_output "Sure." +} + +@test "prattling on" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh 'Wait! Hang on. Are you going to be OK?' + assert_success + assert_output "Sure." +} + +@test "silence" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh '' + assert_success + assert_output "Fine. Be that way!" +} + +@test "prolonged silence" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh ' ' + assert_success + assert_output "Fine. Be that way!" +} + +@test "alternate silence" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh $'\t\t\t\t\t\t\t\t\t\t' + assert_success + assert_output "Fine. Be that way!" +} + +@test "multiple line question" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh $'\nDoes this cryogenic chamber make me look fat?\nNo' + assert_success + assert_output "Whatever." +} + +@test "starting with whitespace" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh ' hmmmmmmm...' + assert_success + assert_output "Whatever." +} + +@test "ending with whitespace" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh 'Okay if like my spacebar quite a bit? ' + assert_success + assert_output "Sure." +} +# This test might act differently depending on your platform +@test "other whitespace" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh $'\n\r \t' + assert_success + assert_output "Fine. Be that way!" +} + +@test "non-question ending with whitespace" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh 'This is a statement ending with whitespace ' + assert_success + assert_output "Whatever." +} + +@test "no input is silence" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh + assert_success + assert_output "Fine. Be that way!" +} + +# bash-specific tests +@test "yelling a filename expansion" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh '*READ* !' + assert_success + assert_output "Whoa, chill out!" +} + +@test "asking a filename expansion" { + [[ $BATS_RUN_SKIPPED == "true" ]] || skip + run bash bob.sh 'bob???' + assert_success + assert_output "Sure." +} diff --git a/bash/bob/bob.sh b/bash/bob/bob.sh new file mode 100644 index 0000000..edc9a85 --- /dev/null +++ b/bash/bob/bob.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +# matches any character that is not in the set of letters, digits, and question marks. +cleaned="${1//[^a-zA-Z0-9\?]}" + +# all capitals and ending with ? +if [[ "$cleaned" =~ ^[A-Z]+\?$ ]] ; then + echo "Calm down, I know what I'm doing!" +# ending with ? +elif [[ "$cleaned" =~ ^.*[a-z0-9]*.*\?$ ]] ; then + echo 'Sure.' +# all capitals and not only digits +elif [[ "$cleaned" =~ ^[A-Z0-9]+$ ]] && ! [[ "$cleaned" =~ ^[0-9]+$ ]] ; then + echo 'Whoa, chill out!' +# if empty +elif [[ -z "$cleaned" ]] ; then + echo 'Fine. Be that way!' +else + echo 'Whatever.' +fi