feat: more stuff, not all perfect but ohh well
Signed-off-by: Christina Sørensen <christina@cafkafk.com>
This commit is contained in:
parent
a7087bbb67
commit
6378584930
24 changed files with 3235 additions and 0 deletions
19
jq/bob/.exercism/config.json
Normal file
19
jq/bob/.exercism/config.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"authors": [
|
||||
"glennj"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"bob.jq"
|
||||
],
|
||||
"test": [
|
||||
"test-bob.bats"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.jq"
|
||||
]
|
||||
},
|
||||
"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"
|
||||
}
|
1
jq/bob/.exercism/metadata.json
Normal file
1
jq/bob/.exercism/metadata.json
Normal file
|
@ -0,0 +1 @@
|
|||
{"track":"jq","exercise":"bob","id":"79f3be870a8e495dae6be7cea1488b03","url":"https://exercism.org/tracks/jq/exercises/bob","handle":"cafkafk","is_requester":true,"auto_approve":false}
|
114
jq/bob/HELP.md
Normal file
114
jq/bob/HELP.md
Normal file
|
@ -0,0 +1,114 @@
|
|||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
Each exercise contains a test file.
|
||||
Run the tests using the `bats` program.
|
||||
|
||||
```bash
|
||||
bats test-hello-world.bats
|
||||
```
|
||||
|
||||
`bats` will need to be installed.
|
||||
See the [Testing on the Bash track][bash] page for instructions to install `bats` for your system.
|
||||
|
||||
### bats is implemented in bash
|
||||
|
||||
The bats file is a bash script, with some special functions recognized by the `bats` command.
|
||||
You'll see some tests that look like
|
||||
|
||||
```sh
|
||||
jq -f some-exercise.jq <<< "{some,json,here}"
|
||||
```
|
||||
|
||||
That `<<<` syntax is a bash [Here String][here-string].
|
||||
It sends the string on the right-hand side into the standard input of the program on the left-hand side.
|
||||
It is ([approximately][so]) the same as
|
||||
|
||||
```sh
|
||||
echo "{some,json,here}" | jq -f some-exercise.jq
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
## 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
|
||||
|
||||
[[ $BATS_RUN_SKIPPED == true ]] || skip
|
||||
|
||||
annotations prepending other tests.
|
||||
|
||||
## Overriding skips
|
||||
|
||||
To run all tests, including the ones with `skip` annotations, you can run:
|
||||
|
||||
```bash
|
||||
BATS_RUN_SKIPPED=true bats test-some-exercise.bats
|
||||
```
|
||||
|
||||
It can be convenient to use a wrapper function to save on typing: in `bash` you can do:
|
||||
|
||||
```bash
|
||||
bats() {
|
||||
BATS_RUN_SKIPPED=true command bats *.bats
|
||||
}
|
||||
```
|
||||
|
||||
Then run tests with just:
|
||||
|
||||
```bash
|
||||
bats
|
||||
```
|
||||
|
||||
## Debugging in `jq`
|
||||
|
||||
`jq` comes with a handy [`debug`][debug] filter.
|
||||
Use it while you are developing your exercise solutions to inspect the data that is currently in the jq pipline.
|
||||
See the [debugging doc][debugging] for more details.
|
||||
|
||||
|
||||
[bash]: https://exercism.org/docs/tracks/bash/tests
|
||||
[bats-assert]: https://github.com/bats-core/bats-assert
|
||||
[here-string]: https://www.gnu.org/software/bash/manual/bash.html#Here-Strings
|
||||
[so]: https://unix.stackexchange.com/a/80372/4667
|
||||
[debug]: https://jqlang.github.io/jq/manual/v1.7/#debug
|
||||
[debugging]: https://exercism.org/docs/tracks/jq/debugging
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit bob.jq` 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 [jq track's documentation](https://exercism.org/docs/tracks/jq)
|
||||
- The [jq track's programming category on the forum](https://forum.exercism.org/c/programming/jq)
|
||||
- [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.
|
||||
|
||||
## Need more help?
|
||||
|
||||
- Go to the [Exercism Community forum](https://forum.exercism.org) to get support and ask questions (or just chat!)
|
||||
- Use the [Exercism Support](https://forum.exercism.org/c/support/8) category if you face any issues with working in the web editor, or downloading or submitting your exercises locally.
|
||||
- Use the [Programming:jq](https://forum.exercism.org/c/programming/jq/133) category for jq-specific topics.
|
||||
- Join the community on [Exercism's Discord server](https://exercism.org/r/discord).
|
||||
- [StackOverflow](https://stackoverflow.com/questions/tagged/jq) can be used to search for your problem and see if it has been answered already.
|
||||
You can also ask and answer questions.
|
||||
- [Github issue tracker](https://github.com/exercism/jq/issues) is where we track our development and maintainance of `jq` exercises in exercism.
|
||||
If none of the above links help you, feel free to post an issue here.
|
45
jq/bob/README.md
Normal file
45
jq/bob/README.md
Normal file
|
@ -0,0 +1,45 @@
|
|||
# Bob
|
||||
|
||||
Welcome to Bob on Exercism's jq 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
|
||||
|
||||
- @glennj
|
||||
|
||||
### Based on
|
||||
|
||||
Inspired by the 'Deaf Grandma' exercise in Chris Pine's Learn to Program tutorial. - https://pine.fm/LearnToProgram/?Chapter=06
|
637
jq/bob/bats-extra.bash
Normal file
637
jq/bob/bats-extra.bash
Normal file
|
@ -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 <zoltan dot tombol at gmail dot com>
|
||||
#
|
||||
# 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
|
||||
# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
#
|
||||
|
||||
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
|
||||
}
|
29
jq/bob/bats-jq.bash
Normal file
29
jq/bob/bats-jq.bash
Normal file
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# `bats-core` will consume both stdout and stderr for the `run` command's output.
|
||||
# However `jq` prints its DEBUG output on stderr.
|
||||
#
|
||||
# Lines starting with `["DEBUG:",` will be prefixed with a hash and printed on file descriptor 3.
|
||||
# Other lines on stderr will remain on stderr for bats to consume.
|
||||
#
|
||||
# See `bats-core` docs:
|
||||
# - "Printing to the terminal", https://bats-core.readthedocs.io/en/stable/writing-tests.html#printing-to-the-terminal
|
||||
# - "File descriptor 3", https://bats-core.readthedocs.io/en/stable/writing-tests.html#file-descriptor-3-read-this-if-bats-hangs
|
||||
|
||||
|
||||
jq() {
|
||||
local output stderr rc line
|
||||
stderr=$(mktemp)
|
||||
output=$(command jq "$@" 2> "$stderr")
|
||||
rc=$?
|
||||
while IFS= read -r line || [[ -n $line ]]; do
|
||||
if [[ $line == '["DEBUG:",'* ]]; then
|
||||
echo "# $line" >&3
|
||||
else
|
||||
echo "$line" >&2
|
||||
fi
|
||||
done < "$stderr"
|
||||
rm -f "$stderr"
|
||||
echo "$output"
|
||||
return "$rc"
|
||||
}
|
8
jq/bob/bob.jq
Normal file
8
jq/bob/bob.jq
Normal file
|
@ -0,0 +1,8 @@
|
|||
.["heyBob"]|
|
||||
if .|test("lbalbslb") then "Fine. Be that way!"
|
||||
elif .|test("^[A-Z\\d\\s\\p{P}]+\\?$") then "Calm down, I know what I'm doing!"
|
||||
elif .|test("^([A-Za-z]+[\\d\\s\\p{P}]*)*\\?$") then "Sure."
|
||||
elif .|test("^[A-Z\\d\\s\\p{P}]+$") then "Whoa, chill out!"
|
||||
else
|
||||
"Whatever."
|
||||
end
|
355
jq/bob/test-bob.bats
Normal file
355
jq/bob/test-bob.bats
Normal file
|
@ -0,0 +1,355 @@
|
|||
#!/usr/bin/env bats
|
||||
# generated on 2022-11-02T20:59:00Z
|
||||
load bats-extra
|
||||
load bats-jq
|
||||
|
||||
@test 'stating something' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "Tom-ay-to, tom-aaaah-to."
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Whatever.'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'shouting' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "WATCH OUT!"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Whoa, chill out!'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'shouting gibberish' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "FCECDFCAAB"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Whoa, chill out!'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'asking a question' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "Does this cryogenic chamber make me look fat?"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Sure.'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'asking a numeric question' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "You are, what, like 15?"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Sure.'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'asking gibberish' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "fffbbcbeab?"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Sure.'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'talking forcefully' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "Hi there!"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Whatever.'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'using acronyms in regular speech' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "It's OK if you don't want to go work for NASA."
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Whatever.'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'forceful question' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "WHAT'S GOING ON?"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Calm down, I know what I'\''m doing!'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'shouting numbers' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "1, 2, 3 GO!"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Whoa, chill out!'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'no letters' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "1, 2, 3"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Whatever.'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'question with no letters' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "4?"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Sure.'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'shouting with special characters' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Whoa, chill out!'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'shouting with no exclamation mark' {
|
||||
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "I HATE THE DENTIST"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Whoa, chill out!'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'statement containing question mark' {
|
||||
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "Ending with ? means a question."
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Whatever.'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'non-letters with question' {
|
||||
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": ":) ?"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Sure.'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'prattling on' {
|
||||
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "Wait! Hang on. Are you going to be OK?"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Sure.'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'silence' {
|
||||
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": ""
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Fine. Be that way!'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'prolonged silence' {
|
||||
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": " "
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Fine. Be that way!'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'alternate silence' {
|
||||
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "\t\t\t\t\t\t\t\t\t\t"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Fine. Be that way!'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'multiple line question' {
|
||||
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "\nDoes this cryogenic chamber make me look fat?\nNo."
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Whatever.'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'starting with whitespace' {
|
||||
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": " hmmmmmmm..."
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Whatever.'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'ending with whitespace' {
|
||||
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "Okay if like my spacebar quite a bit? "
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Sure.'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'other whitespace' {
|
||||
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "\n\r \t"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Fine. Be that way!'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'non-question ending with whitespace' {
|
||||
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f bob.jq << 'END_INPUT'
|
||||
{
|
||||
"heyBob": "This is a statement ending with whitespace "
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='Whatever.'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
19
jq/rna-transcription/.exercism/config.json
Normal file
19
jq/rna-transcription/.exercism/config.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"authors": [
|
||||
"glennj"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"rna-transcription.jq"
|
||||
],
|
||||
"test": [
|
||||
"test-rna-transcription.bats"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.jq"
|
||||
]
|
||||
},
|
||||
"blurb": "Given a DNA strand, return its RNA Complement Transcription.",
|
||||
"source": "Hyperphysics",
|
||||
"source_url": "https://web.archive.org/web/20220408112140/http://hyperphysics.phy-astr.gsu.edu/hbase/Organic/transcription.html"
|
||||
}
|
1
jq/rna-transcription/.exercism/metadata.json
Normal file
1
jq/rna-transcription/.exercism/metadata.json
Normal file
|
@ -0,0 +1 @@
|
|||
{"track":"jq","exercise":"rna-transcription","id":"b98cf32fe990443abcb8ad76c5dd21eb","url":"https://exercism.org/tracks/jq/exercises/rna-transcription","handle":"cafkafk","is_requester":true,"auto_approve":false}
|
114
jq/rna-transcription/HELP.md
Normal file
114
jq/rna-transcription/HELP.md
Normal file
|
@ -0,0 +1,114 @@
|
|||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
Each exercise contains a test file.
|
||||
Run the tests using the `bats` program.
|
||||
|
||||
```bash
|
||||
bats test-hello-world.bats
|
||||
```
|
||||
|
||||
`bats` will need to be installed.
|
||||
See the [Testing on the Bash track][bash] page for instructions to install `bats` for your system.
|
||||
|
||||
### bats is implemented in bash
|
||||
|
||||
The bats file is a bash script, with some special functions recognized by the `bats` command.
|
||||
You'll see some tests that look like
|
||||
|
||||
```sh
|
||||
jq -f some-exercise.jq <<< "{some,json,here}"
|
||||
```
|
||||
|
||||
That `<<<` syntax is a bash [Here String][here-string].
|
||||
It sends the string on the right-hand side into the standard input of the program on the left-hand side.
|
||||
It is ([approximately][so]) the same as
|
||||
|
||||
```sh
|
||||
echo "{some,json,here}" | jq -f some-exercise.jq
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
## 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
|
||||
|
||||
[[ $BATS_RUN_SKIPPED == true ]] || skip
|
||||
|
||||
annotations prepending other tests.
|
||||
|
||||
## Overriding skips
|
||||
|
||||
To run all tests, including the ones with `skip` annotations, you can run:
|
||||
|
||||
```bash
|
||||
BATS_RUN_SKIPPED=true bats test-some-exercise.bats
|
||||
```
|
||||
|
||||
It can be convenient to use a wrapper function to save on typing: in `bash` you can do:
|
||||
|
||||
```bash
|
||||
bats() {
|
||||
BATS_RUN_SKIPPED=true command bats *.bats
|
||||
}
|
||||
```
|
||||
|
||||
Then run tests with just:
|
||||
|
||||
```bash
|
||||
bats
|
||||
```
|
||||
|
||||
## Debugging in `jq`
|
||||
|
||||
`jq` comes with a handy [`debug`][debug] filter.
|
||||
Use it while you are developing your exercise solutions to inspect the data that is currently in the jq pipline.
|
||||
See the [debugging doc][debugging] for more details.
|
||||
|
||||
|
||||
[bash]: https://exercism.org/docs/tracks/bash/tests
|
||||
[bats-assert]: https://github.com/bats-core/bats-assert
|
||||
[here-string]: https://www.gnu.org/software/bash/manual/bash.html#Here-Strings
|
||||
[so]: https://unix.stackexchange.com/a/80372/4667
|
||||
[debug]: https://jqlang.github.io/jq/manual/v1.7/#debug
|
||||
[debugging]: https://exercism.org/docs/tracks/jq/debugging
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit rna-transcription.jq` 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 [jq track's documentation](https://exercism.org/docs/tracks/jq)
|
||||
- The [jq track's programming category on the forum](https://forum.exercism.org/c/programming/jq)
|
||||
- [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.
|
||||
|
||||
## Need more help?
|
||||
|
||||
- Go to the [Exercism Community forum](https://forum.exercism.org) to get support and ask questions (or just chat!)
|
||||
- Use the [Exercism Support](https://forum.exercism.org/c/support/8) category if you face any issues with working in the web editor, or downloading or submitting your exercises locally.
|
||||
- Use the [Programming:jq](https://forum.exercism.org/c/programming/jq/133) category for jq-specific topics.
|
||||
- Join the community on [Exercism's Discord server](https://exercism.org/r/discord).
|
||||
- [StackOverflow](https://stackoverflow.com/questions/tagged/jq) can be used to search for your problem and see if it has been answered already.
|
||||
You can also ask and answer questions.
|
||||
- [Github issue tracker](https://github.com/exercism/jq/issues) is where we track our development and maintainance of `jq` exercises in exercism.
|
||||
If none of the above links help you, feel free to post an issue here.
|
61
jq/rna-transcription/README.md
Normal file
61
jq/rna-transcription/README.md
Normal file
|
@ -0,0 +1,61 @@
|
|||
# Rna Transcription
|
||||
|
||||
Welcome to Rna Transcription on Exercism's jq Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
|
||||
## Introduction
|
||||
|
||||
You work for a bioengineering company that specializes in developing therapeutic solutions.
|
||||
|
||||
Your team has just been given a new project to develop a targeted therapy for a rare type of cancer.
|
||||
|
||||
~~~~exercism/note
|
||||
It's all very complicated, but the basic idea is that sometimes people's bodies produce too much of a given protein.
|
||||
That can cause all sorts of havoc.
|
||||
|
||||
But if you can create a very specific molecule (called a micro-RNA), it can prevent the protein from being produced.
|
||||
|
||||
This technique is called [RNA Interference][rnai].
|
||||
|
||||
[rnai]: https://admin.acceleratingscience.com/ask-a-scientist/what-is-rnai/
|
||||
~~~~
|
||||
|
||||
## Instructions
|
||||
|
||||
Your task is determine the RNA complement of a given DNA sequence.
|
||||
|
||||
Both DNA and RNA strands are a sequence of nucleotides.
|
||||
|
||||
The four nucleotides found in DNA are adenine (**A**), cytosine (**C**), guanine (**G**) and thymine (**T**).
|
||||
|
||||
The four nucleotides found in RNA are adenine (**A**), cytosine (**C**), guanine (**G**) and uracil (**U**).
|
||||
|
||||
Given a DNA strand, its transcribed RNA strand is formed by replacing each nucleotide with its complement:
|
||||
|
||||
- `G` -> `C`
|
||||
- `C` -> `G`
|
||||
- `T` -> `A`
|
||||
- `A` -> `U`
|
||||
|
||||
~~~~exercism/note
|
||||
If you want to look at how the inputs and outputs are structured, take a look at the examples in the test suite.
|
||||
~~~~
|
||||
|
||||
## jq-specific instructions
|
||||
|
||||
Your requirement for this exercise is to define a `toRna` function.
|
||||
This function will expect a DNA sequence (as a string) as input, and will output the transcribed RNA sequence.
|
||||
|
||||
Refer to [Defining Functions][def] in the jq manual.
|
||||
|
||||
[def]: https://jqlang.github.io/jq/manual/#DefiningFunctions
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @glennj
|
||||
|
||||
### Based on
|
||||
|
||||
Hyperphysics - https://web.archive.org/web/20220408112140/http://hyperphysics.phy-astr.gsu.edu/hbase/Organic/transcription.html
|
637
jq/rna-transcription/bats-extra.bash
Normal file
637
jq/rna-transcription/bats-extra.bash
Normal file
|
@ -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 <zoltan dot tombol at gmail dot com>
|
||||
#
|
||||
# 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
|
||||
# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
#
|
||||
|
||||
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
|
||||
}
|
29
jq/rna-transcription/bats-jq.bash
Normal file
29
jq/rna-transcription/bats-jq.bash
Normal file
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# `bats-core` will consume both stdout and stderr for the `run` command's output.
|
||||
# However `jq` prints its DEBUG output on stderr.
|
||||
#
|
||||
# Lines starting with `["DEBUG:",` will be prefixed with a hash and printed on file descriptor 3.
|
||||
# Other lines on stderr will remain on stderr for bats to consume.
|
||||
#
|
||||
# See `bats-core` docs:
|
||||
# - "Printing to the terminal", https://bats-core.readthedocs.io/en/stable/writing-tests.html#printing-to-the-terminal
|
||||
# - "File descriptor 3", https://bats-core.readthedocs.io/en/stable/writing-tests.html#file-descriptor-3-read-this-if-bats-hangs
|
||||
|
||||
|
||||
jq() {
|
||||
local output stderr rc line
|
||||
stderr=$(mktemp)
|
||||
output=$(command jq "$@" 2> "$stderr")
|
||||
rc=$?
|
||||
while IFS= read -r line || [[ -n $line ]]; do
|
||||
if [[ $line == '["DEBUG:",'* ]]; then
|
||||
echo "# $line" >&3
|
||||
else
|
||||
echo "$line" >&2
|
||||
fi
|
||||
done < "$stderr"
|
||||
rm -f "$stderr"
|
||||
echo "$output"
|
||||
return "$rc"
|
||||
}
|
8
jq/rna-transcription/rna-transcription.jq
Normal file
8
jq/rna-transcription/rna-transcription.jq
Normal file
|
@ -0,0 +1,8 @@
|
|||
def toRna:
|
||||
if . == "" then .
|
||||
elif . == "C" then "G"
|
||||
elif . == "G" then "C"
|
||||
elif . == "T" then "A"
|
||||
elif . == "A" then "U"
|
||||
else (./""|.[0]|toRna) + (.[1:]|toRna)
|
||||
end;
|
89
jq/rna-transcription/test-rna-transcription.bats
Normal file
89
jq/rna-transcription/test-rna-transcription.bats
Normal file
|
@ -0,0 +1,89 @@
|
|||
#!/usr/bin/env bats
|
||||
# generated on 2022-11-02T20:59:43Z
|
||||
load bats-extra
|
||||
load bats-jq
|
||||
|
||||
@test 'Empty RNA sequence' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r 'include "./rna-transcription"; .dna | toRna' << 'END_INPUT'
|
||||
{
|
||||
"dna": ""
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected=''
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'RNA complement of cytosine is guanine' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r 'include "./rna-transcription"; .dna | toRna' << 'END_INPUT'
|
||||
{
|
||||
"dna": "C"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='G'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'RNA complement of guanine is cytosine' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r 'include "./rna-transcription"; .dna | toRna' << 'END_INPUT'
|
||||
{
|
||||
"dna": "G"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='C'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'RNA complement of thymine is adenine' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r 'include "./rna-transcription"; .dna | toRna' << 'END_INPUT'
|
||||
{
|
||||
"dna": "T"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='A'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'RNA complement of adenine is uracil' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r 'include "./rna-transcription"; .dna | toRna' << 'END_INPUT'
|
||||
{
|
||||
"dna": "A"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='U'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'RNA complement' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r 'include "./rna-transcription"; .dna | toRna' << 'END_INPUT'
|
||||
{
|
||||
"dna": "ACGTGGTCTTAA"
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected='UGCACCAGAAUU'
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
19
jq/series/.exercism/config.json
Normal file
19
jq/series/.exercism/config.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"authors": [
|
||||
"IsaacG"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"series.jq"
|
||||
],
|
||||
"test": [
|
||||
"test-series.bats"
|
||||
],
|
||||
"example": [
|
||||
".meta/example.jq"
|
||||
]
|
||||
},
|
||||
"blurb": "Given a string of digits, output all the contiguous substrings of length `n` in that string.",
|
||||
"source": "A subset of the Problem 8 at Project Euler",
|
||||
"source_url": "https://projecteuler.net/problem=8"
|
||||
}
|
1
jq/series/.exercism/metadata.json
Normal file
1
jq/series/.exercism/metadata.json
Normal file
|
@ -0,0 +1 @@
|
|||
{"track":"jq","exercise":"series","id":"590f99f1ee894b85af6f65ce9baec7a5","url":"https://exercism.org/tracks/jq/exercises/series","handle":"cafkafk","is_requester":true,"auto_approve":false}
|
114
jq/series/HELP.md
Normal file
114
jq/series/HELP.md
Normal file
|
@ -0,0 +1,114 @@
|
|||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
Each exercise contains a test file.
|
||||
Run the tests using the `bats` program.
|
||||
|
||||
```bash
|
||||
bats test-hello-world.bats
|
||||
```
|
||||
|
||||
`bats` will need to be installed.
|
||||
See the [Testing on the Bash track][bash] page for instructions to install `bats` for your system.
|
||||
|
||||
### bats is implemented in bash
|
||||
|
||||
The bats file is a bash script, with some special functions recognized by the `bats` command.
|
||||
You'll see some tests that look like
|
||||
|
||||
```sh
|
||||
jq -f some-exercise.jq <<< "{some,json,here}"
|
||||
```
|
||||
|
||||
That `<<<` syntax is a bash [Here String][here-string].
|
||||
It sends the string on the right-hand side into the standard input of the program on the left-hand side.
|
||||
It is ([approximately][so]) the same as
|
||||
|
||||
```sh
|
||||
echo "{some,json,here}" | jq -f some-exercise.jq
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
## 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
|
||||
|
||||
[[ $BATS_RUN_SKIPPED == true ]] || skip
|
||||
|
||||
annotations prepending other tests.
|
||||
|
||||
## Overriding skips
|
||||
|
||||
To run all tests, including the ones with `skip` annotations, you can run:
|
||||
|
||||
```bash
|
||||
BATS_RUN_SKIPPED=true bats test-some-exercise.bats
|
||||
```
|
||||
|
||||
It can be convenient to use a wrapper function to save on typing: in `bash` you can do:
|
||||
|
||||
```bash
|
||||
bats() {
|
||||
BATS_RUN_SKIPPED=true command bats *.bats
|
||||
}
|
||||
```
|
||||
|
||||
Then run tests with just:
|
||||
|
||||
```bash
|
||||
bats
|
||||
```
|
||||
|
||||
## Debugging in `jq`
|
||||
|
||||
`jq` comes with a handy [`debug`][debug] filter.
|
||||
Use it while you are developing your exercise solutions to inspect the data that is currently in the jq pipline.
|
||||
See the [debugging doc][debugging] for more details.
|
||||
|
||||
|
||||
[bash]: https://exercism.org/docs/tracks/bash/tests
|
||||
[bats-assert]: https://github.com/bats-core/bats-assert
|
||||
[here-string]: https://www.gnu.org/software/bash/manual/bash.html#Here-Strings
|
||||
[so]: https://unix.stackexchange.com/a/80372/4667
|
||||
[debug]: https://jqlang.github.io/jq/manual/v1.7/#debug
|
||||
[debugging]: https://exercism.org/docs/tracks/jq/debugging
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit series.jq` 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 [jq track's documentation](https://exercism.org/docs/tracks/jq)
|
||||
- The [jq track's programming category on the forum](https://forum.exercism.org/c/programming/jq)
|
||||
- [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.
|
||||
|
||||
## Need more help?
|
||||
|
||||
- Go to the [Exercism Community forum](https://forum.exercism.org) to get support and ask questions (or just chat!)
|
||||
- Use the [Exercism Support](https://forum.exercism.org/c/support/8) category if you face any issues with working in the web editor, or downloading or submitting your exercises locally.
|
||||
- Use the [Programming:jq](https://forum.exercism.org/c/programming/jq/133) category for jq-specific topics.
|
||||
- Join the community on [Exercism's Discord server](https://exercism.org/r/discord).
|
||||
- [StackOverflow](https://stackoverflow.com/questions/tagged/jq) can be used to search for your problem and see if it has been answered already.
|
||||
You can also ask and answer questions.
|
||||
- [Github issue tracker](https://github.com/exercism/jq/issues) is where we track our development and maintainance of `jq` exercises in exercism.
|
||||
If none of the above links help you, feel free to post an issue here.
|
34
jq/series/README.md
Normal file
34
jq/series/README.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
# Series
|
||||
|
||||
Welcome to Series on Exercism's jq Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
|
||||
## Instructions
|
||||
|
||||
Given a string of digits, output all the contiguous substrings of length `n` in that string in the order that they appear.
|
||||
|
||||
For example, the string "49142" has the following 3-digit series:
|
||||
|
||||
- "491"
|
||||
- "914"
|
||||
- "142"
|
||||
|
||||
And the following 4-digit series:
|
||||
|
||||
- "4914"
|
||||
- "9142"
|
||||
|
||||
And if you ask for a 6-digit series from a 5-digit string, you deserve whatever you get.
|
||||
|
||||
Note that these series are only required to occupy _adjacent positions_ in the input;
|
||||
the digits need not be _numerically consecutive_.
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @IsaacG
|
||||
|
||||
### Based on
|
||||
|
||||
A subset of the Problem 8 at Project Euler - https://projecteuler.net/problem=8
|
637
jq/series/bats-extra.bash
Normal file
637
jq/series/bats-extra.bash
Normal file
|
@ -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 <zoltan dot tombol at gmail dot com>
|
||||
#
|
||||
# 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
|
||||
# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
#
|
||||
|
||||
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
|
||||
}
|
29
jq/series/bats-jq.bash
Normal file
29
jq/series/bats-jq.bash
Normal file
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# `bats-core` will consume both stdout and stderr for the `run` command's output.
|
||||
# However `jq` prints its DEBUG output on stderr.
|
||||
#
|
||||
# Lines starting with `["DEBUG:",` will be prefixed with a hash and printed on file descriptor 3.
|
||||
# Other lines on stderr will remain on stderr for bats to consume.
|
||||
#
|
||||
# See `bats-core` docs:
|
||||
# - "Printing to the terminal", https://bats-core.readthedocs.io/en/stable/writing-tests.html#printing-to-the-terminal
|
||||
# - "File descriptor 3", https://bats-core.readthedocs.io/en/stable/writing-tests.html#file-descriptor-3-read-this-if-bats-hangs
|
||||
|
||||
|
||||
jq() {
|
||||
local output stderr rc line
|
||||
stderr=$(mktemp)
|
||||
output=$(command jq "$@" 2> "$stderr")
|
||||
rc=$?
|
||||
while IFS= read -r line || [[ -n $line ]]; do
|
||||
if [[ $line == '["DEBUG:",'* ]]; then
|
||||
echo "# $line" >&3
|
||||
else
|
||||
echo "$line" >&2
|
||||
fi
|
||||
done < "$stderr"
|
||||
rm -f "$stderr"
|
||||
echo "$output"
|
||||
return "$rc"
|
||||
}
|
18
jq/series/series.jq
Normal file
18
jq/series/series.jq
Normal file
|
@ -0,0 +1,18 @@
|
|||
# I just reimplemented glennj's solution because I was stuck
|
||||
# https://exercism.org/tracks/jq/exercises/series/solutions/glennj
|
||||
def assert(cond; errmsg):
|
||||
if (cond | not) then errmsg | halt_error
|
||||
else .
|
||||
end;
|
||||
|
||||
(.series|length) as $strlen
|
||||
|
||||
| assert($strlen > 0; "series cannot be empty")
|
||||
| assert(.sliceLength <= $strlen; "slice length cannot be greater than series length")
|
||||
| assert(.sliceLength >= 0; "slice length cannot be negative")
|
||||
| assert(.sliceLength > 0; "slice length cannot be zero")
|
||||
|
||||
| reduce range($strlen - .sliceLength + 1) as $i (. + {slices: []};
|
||||
.slices += [.series[$i:$i + .sliceLength]]
|
||||
)
|
||||
| .slices
|
217
jq/series/test-series.bats
Normal file
217
jq/series/test-series.bats
Normal file
|
@ -0,0 +1,217 @@
|
|||
#!/usr/bin/env bats
|
||||
# generated on 2023-08-25T15:56:38Z
|
||||
load bats-extra
|
||||
load bats-jq
|
||||
|
||||
@test 'slices of one from one' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f series.jq << 'END_INPUT'
|
||||
{
|
||||
"series": "1",
|
||||
"sliceLength": 1
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected=$(cat << 'END_EXPECTED'
|
||||
[
|
||||
"1"
|
||||
]
|
||||
END_EXPECTED
|
||||
)
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'slices of one from two' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f series.jq << 'END_INPUT'
|
||||
{
|
||||
"series": "12",
|
||||
"sliceLength": 1
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected=$(cat << 'END_EXPECTED'
|
||||
[
|
||||
"1",
|
||||
"2"
|
||||
]
|
||||
END_EXPECTED
|
||||
)
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'slices of two' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f series.jq << 'END_INPUT'
|
||||
{
|
||||
"series": "35",
|
||||
"sliceLength": 2
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected=$(cat << 'END_EXPECTED'
|
||||
[
|
||||
"35"
|
||||
]
|
||||
END_EXPECTED
|
||||
)
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'slices of two overlap' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f series.jq << 'END_INPUT'
|
||||
{
|
||||
"series": "9142",
|
||||
"sliceLength": 2
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected=$(cat << 'END_EXPECTED'
|
||||
[
|
||||
"91",
|
||||
"14",
|
||||
"42"
|
||||
]
|
||||
END_EXPECTED
|
||||
)
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'slices can include duplicates' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f series.jq << 'END_INPUT'
|
||||
{
|
||||
"series": "777777",
|
||||
"sliceLength": 3
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected=$(cat << 'END_EXPECTED'
|
||||
[
|
||||
"777",
|
||||
"777",
|
||||
"777",
|
||||
"777"
|
||||
]
|
||||
END_EXPECTED
|
||||
)
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'slices of a long series' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f series.jq << 'END_INPUT'
|
||||
{
|
||||
"series": "918493904243",
|
||||
"sliceLength": 5
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_success
|
||||
expected=$(cat << 'END_EXPECTED'
|
||||
[
|
||||
"91849",
|
||||
"18493",
|
||||
"84939",
|
||||
"49390",
|
||||
"93904",
|
||||
"39042",
|
||||
"90424",
|
||||
"04243"
|
||||
]
|
||||
END_EXPECTED
|
||||
)
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'slice length is too large' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f series.jq << 'END_INPUT'
|
||||
{
|
||||
"series": "12345",
|
||||
"sliceLength": 6
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_failure
|
||||
expected='slice length cannot be greater than series length'
|
||||
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'slice length is way too large' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f series.jq << 'END_INPUT'
|
||||
{
|
||||
"series": "12345",
|
||||
"sliceLength": 42
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_failure
|
||||
expected='slice length cannot be greater than series length'
|
||||
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'slice length cannot be zero' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f series.jq << 'END_INPUT'
|
||||
{
|
||||
"series": "12345",
|
||||
"sliceLength": 0
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_failure
|
||||
expected='slice length cannot be zero'
|
||||
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'slice length cannot be negative' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f series.jq << 'END_INPUT'
|
||||
{
|
||||
"series": "123",
|
||||
"sliceLength": -1
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_failure
|
||||
expected='slice length cannot be negative'
|
||||
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
||||
|
||||
@test 'empty series is invalid' {
|
||||
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||
|
||||
run jq -r -f series.jq << 'END_INPUT'
|
||||
{
|
||||
"series": "",
|
||||
"sliceLength": 1
|
||||
}
|
||||
END_INPUT
|
||||
|
||||
assert_failure
|
||||
expected='series cannot be empty'
|
||||
|
||||
assert_equal "$output" "$expected"
|
||||
}
|
Loading…
Reference in a new issue