feat(jq): anagram
Signed-off-by: Christina Sørensen <christina@cafkafk.com>
This commit is contained in:
parent
ca6b099b05
commit
34d983f443
16 changed files with 2131 additions and 0 deletions
19
jq/anagram/.exercism/config.json
Normal file
19
jq/anagram/.exercism/config.json
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"authors": [
|
||||||
|
"glennj"
|
||||||
|
],
|
||||||
|
"files": {
|
||||||
|
"solution": [
|
||||||
|
"anagram.jq"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"test-anagram.bats"
|
||||||
|
],
|
||||||
|
"example": [
|
||||||
|
".meta/example.jq"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"blurb": "Given a word and a list of possible anagrams, select the correct sublist.",
|
||||||
|
"source": "Inspired by the Extreme Startup game",
|
||||||
|
"source_url": "https://github.com/rchatley/extreme_startup"
|
||||||
|
}
|
1
jq/anagram/.exercism/metadata.json
Normal file
1
jq/anagram/.exercism/metadata.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"track":"jq","exercise":"anagram","id":"a93430feebc94b529d245edd6589ce95","url":"https://exercism.org/tracks/jq/exercises/anagram","handle":"cafkafk","is_requester":true,"auto_approve":false}
|
114
jq/anagram/HELP.md
Normal file
114
jq/anagram/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 anagram.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.
|
41
jq/anagram/README.md
Normal file
41
jq/anagram/README.md
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# Anagram
|
||||||
|
|
||||||
|
Welcome to Anagram on Exercism's jq Track.
|
||||||
|
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
At a garage sale, you find a lovely vintage typewriter at a bargain price!
|
||||||
|
Excitedly, you rush home, insert a sheet of paper, and start typing away.
|
||||||
|
However, your excitement wanes when you examine the output: all words are garbled!
|
||||||
|
For example, it prints "stop" instead of "post" and "least" instead of "stale."
|
||||||
|
Carefully, you try again, but now it prints "spot" and "slate."
|
||||||
|
After some experimentation, you find there is a random delay before each letter is printed, which messes up the order.
|
||||||
|
You now understand why they sold it for so little money!
|
||||||
|
|
||||||
|
You realize this quirk allows you to generate anagrams, which are words formed by rearranging the letters of another word.
|
||||||
|
Pleased with your finding, you spend the rest of the day generating hundreds of anagrams.
|
||||||
|
|
||||||
|
## Instructions
|
||||||
|
|
||||||
|
Your task is to, given a target word and a set of candidate words, to find the subset of the candidates that are anagrams of the target.
|
||||||
|
|
||||||
|
An anagram is a rearrangement of letters to form a new word: for example `"owns"` is an anagram of `"snow"`.
|
||||||
|
A word is _not_ its own anagram: for example, `"stop"` is not an anagram of `"stop"`.
|
||||||
|
|
||||||
|
The target and candidates are words of one or more ASCII alphabetic characters (`A`-`Z` and `a`-`z`).
|
||||||
|
Lowercase and uppercase characters are equivalent: for example, `"PoTS"` is an anagram of `"sTOp"`, but `StoP` is not an anagram of `sTOp`.
|
||||||
|
The anagram set is the subset of the candidate set that are anagrams of the target (in any order).
|
||||||
|
Words in the anagram set should have the same letter case as in the candidate set.
|
||||||
|
|
||||||
|
Given the target `"stone"` and candidates `"stone"`, `"tones"`, `"banana"`, `"tons"`, `"notes"`, `"Seton"`, the anagram set is `"tones"`, `"notes"`, `"Seton"`.
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
### Created by
|
||||||
|
|
||||||
|
- @glennj
|
||||||
|
|
||||||
|
### Based on
|
||||||
|
|
||||||
|
Inspired by the Extreme Startup game - https://github.com/rchatley/extreme_startup
|
12
jq/anagram/anagram.jq
Normal file
12
jq/anagram/anagram.jq
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
(.subject|ascii_downcase) as $s
|
||||||
|
| .candidates
|
||||||
|
| map({s: ., n: .|ascii_downcase|explode|add})
|
||||||
|
| map(
|
||||||
|
# Ensures strings have the same letters
|
||||||
|
select((.s|ascii_downcase/""|sort) == ($s/""|sort))
|
||||||
|
# Ensure the input string isn't output (anagrams aren't reflexive)
|
||||||
|
| select((.s|ascii_downcase) != $s)
|
||||||
|
# Ensures strings have the same ascii sum
|
||||||
|
| select(.n == ($s|explode|add))
|
||||||
|
.s
|
||||||
|
)
|
637
jq/anagram/bats-extra.bash
Normal file
637
jq/anagram/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/anagram/bats-jq.bash
Normal file
29
jq/anagram/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"
|
||||||
|
}
|
299
jq/anagram/test-anagram.bats
Normal file
299
jq/anagram/test-anagram.bats
Normal file
|
@ -0,0 +1,299 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
# generated on 2022-11-02T20:58:55Z
|
||||||
|
load bats-extra
|
||||||
|
load bats-jq
|
||||||
|
|
||||||
|
@test 'no matches' {
|
||||||
|
#
|
||||||
|
|
||||||
|
run jq -c -f anagram.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"subject": "diaper",
|
||||||
|
"candidates": [
|
||||||
|
"hello",
|
||||||
|
"world",
|
||||||
|
"zombies",
|
||||||
|
"pants"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
expected='[]'
|
||||||
|
assert_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'detects two anagrams' {
|
||||||
|
|
||||||
|
|
||||||
|
run jq -c -f anagram.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"subject": "solemn",
|
||||||
|
"candidates": [
|
||||||
|
"lemons",
|
||||||
|
"cherry",
|
||||||
|
"melons"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
expected='["lemons","melons"]'
|
||||||
|
assert_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'does not detect anagram subsets' {
|
||||||
|
|
||||||
|
|
||||||
|
run jq -c -f anagram.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"subject": "good",
|
||||||
|
"candidates": [
|
||||||
|
"dog",
|
||||||
|
"goody"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
expected='[]'
|
||||||
|
assert_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'detects anagram' {
|
||||||
|
|
||||||
|
|
||||||
|
run jq -c -f anagram.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"subject": "listen",
|
||||||
|
"candidates": [
|
||||||
|
"enlists",
|
||||||
|
"google",
|
||||||
|
"inlets",
|
||||||
|
"banana"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
expected='["inlets"]'
|
||||||
|
assert_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'detects three anagrams' {
|
||||||
|
|
||||||
|
|
||||||
|
run jq -c -f anagram.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"subject": "allergy",
|
||||||
|
"candidates": [
|
||||||
|
"gallery",
|
||||||
|
"ballerina",
|
||||||
|
"regally",
|
||||||
|
"clergy",
|
||||||
|
"largely",
|
||||||
|
"leading"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
expected='["gallery","regally","largely"]'
|
||||||
|
assert_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'detects multiple anagrams with different case' {
|
||||||
|
|
||||||
|
|
||||||
|
run jq -c -f anagram.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"subject": "nose",
|
||||||
|
"candidates": [
|
||||||
|
"Eons",
|
||||||
|
"ONES"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
expected='["Eons","ONES"]'
|
||||||
|
assert_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'does not detect non-anagrams with identical checksum' {
|
||||||
|
|
||||||
|
|
||||||
|
run jq -c -f anagram.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"subject": "mass",
|
||||||
|
"candidates": [
|
||||||
|
"last"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
expected='[]'
|
||||||
|
assert_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'detects anagrams case-insensitively' {
|
||||||
|
|
||||||
|
|
||||||
|
run jq -c -f anagram.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"subject": "Orchestra",
|
||||||
|
"candidates": [
|
||||||
|
"cashregister",
|
||||||
|
"Carthorse",
|
||||||
|
"radishes"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
expected='["Carthorse"]'
|
||||||
|
assert_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'detects anagrams using case-insensitive subject' {
|
||||||
|
|
||||||
|
|
||||||
|
run jq -c -f anagram.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"subject": "Orchestra",
|
||||||
|
"candidates": [
|
||||||
|
"cashregister",
|
||||||
|
"carthorse",
|
||||||
|
"radishes"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
expected='["carthorse"]'
|
||||||
|
assert_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'detects anagrams using case-insensitive possible matches' {
|
||||||
|
|
||||||
|
|
||||||
|
run jq -c -f anagram.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"subject": "orchestra",
|
||||||
|
"candidates": [
|
||||||
|
"cashregister",
|
||||||
|
"Carthorse",
|
||||||
|
"radishes"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
expected='["Carthorse"]'
|
||||||
|
assert_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'does not detect an anagram if the original word is repeated' {
|
||||||
|
|
||||||
|
|
||||||
|
run jq -c -f anagram.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"subject": "go",
|
||||||
|
"candidates": [
|
||||||
|
"goGoGO"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
expected='[]'
|
||||||
|
assert_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'anagrams must use all letters exactly once' {
|
||||||
|
|
||||||
|
|
||||||
|
run jq -c -f anagram.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"subject": "tapper",
|
||||||
|
"candidates": [
|
||||||
|
"patter"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
expected='[]'
|
||||||
|
assert_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'words are not anagrams of themselves' {
|
||||||
|
|
||||||
|
|
||||||
|
run jq -c -f anagram.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"subject": "BANANA",
|
||||||
|
"candidates": [
|
||||||
|
"BANANA"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
expected='[]'
|
||||||
|
assert_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'words are not anagrams of themselves even if letter case is partially different' {
|
||||||
|
|
||||||
|
|
||||||
|
run jq -c -f anagram.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"subject": "BANANA",
|
||||||
|
"candidates": [
|
||||||
|
"Banana"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
expected='[]'
|
||||||
|
assert_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'words are not anagrams of themselves even if letter case is completely different' {
|
||||||
|
|
||||||
|
|
||||||
|
run jq -c -f anagram.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"subject": "BANANA",
|
||||||
|
"candidates": [
|
||||||
|
"banana"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
expected='[]'
|
||||||
|
assert_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'words other than themselves can be anagrams' {
|
||||||
|
|
||||||
|
|
||||||
|
run jq -c -f anagram.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"subject": "LISTEN",
|
||||||
|
"candidates": [
|
||||||
|
"LISTEN",
|
||||||
|
"Silent"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
expected='["Silent"]'
|
||||||
|
assert_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
17
jq/satellite/.exercism/config.json
Normal file
17
jq/satellite/.exercism/config.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"authors": [
|
||||||
|
"glennj"
|
||||||
|
],
|
||||||
|
"files": {
|
||||||
|
"solution": [
|
||||||
|
"satellite.jq"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"test-satellite.bats"
|
||||||
|
],
|
||||||
|
"example": [
|
||||||
|
".meta/example.jq"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"blurb": "Rebuild binary trees from pre-order and in-order traversals."
|
||||||
|
}
|
1
jq/satellite/.exercism/metadata.json
Normal file
1
jq/satellite/.exercism/metadata.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"track":"jq","exercise":"satellite","id":"74e2e759415f479f8feb0f7991629a62","url":"https://exercism.org/tracks/jq/exercises/satellite","handle":"cafkafk","is_requester":true,"auto_approve":false}
|
114
jq/satellite/HELP.md
Normal file
114
jq/satellite/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 satellite.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.
|
38
jq/satellite/README.md
Normal file
38
jq/satellite/README.md
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# Satellite
|
||||||
|
|
||||||
|
Welcome to Satellite on Exercism's jq Track.
|
||||||
|
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||||
|
|
||||||
|
## Instructions
|
||||||
|
|
||||||
|
Imagine you need to transmit a binary tree to a satellite approaching Alpha Centauri and you have limited bandwidth.
|
||||||
|
Since the tree has no repeating items it can be uniquely represented by its [pre-order and in-order traversals][wiki].
|
||||||
|
|
||||||
|
Write the software for the satellite to rebuild the tree from the traversals.
|
||||||
|
|
||||||
|
A pre-order traversal reads the value of the current node before (hence "pre") reading the left subtree in pre-order.
|
||||||
|
Afterwards the right subtree is read in pre-order.
|
||||||
|
|
||||||
|
An in-order traversal reads the left subtree in-order then the current node and finally the right subtree in-order.
|
||||||
|
So in order from left to right.
|
||||||
|
|
||||||
|
For example the pre-order traversal of this tree is [a, i, x, f, r].
|
||||||
|
The in-order traversal of this tree is [i, a, f, x, r]
|
||||||
|
|
||||||
|
```text
|
||||||
|
a
|
||||||
|
/ \
|
||||||
|
i x
|
||||||
|
/ \
|
||||||
|
f r
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: the first item in the pre-order traversal is always the root.
|
||||||
|
|
||||||
|
[wiki]: https://en.wikipedia.org/wiki/Tree_traversal
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
### Created by
|
||||||
|
|
||||||
|
- @glennj
|
637
jq/satellite/bats-extra.bash
Normal file
637
jq/satellite/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/satellite/bats-jq.bash
Normal file
29
jq/satellite/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"
|
||||||
|
}
|
1
jq/satellite/satellite.jq
Normal file
1
jq/satellite/satellite.jq
Normal file
|
@ -0,0 +1 @@
|
||||||
|
"Remove this line and implement your solution" | halt_error
|
142
jq/satellite/test-satellite.bats
Normal file
142
jq/satellite/test-satellite.bats
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
# generated on 2023-11-07T18:49:22Z
|
||||||
|
load bats-extra
|
||||||
|
load bats-jq
|
||||||
|
|
||||||
|
assert_objects_equal() {
|
||||||
|
local result=$(
|
||||||
|
jq -n --argjson actual "$1" \
|
||||||
|
--argjson expected "$2" \
|
||||||
|
'$actual == $expected'
|
||||||
|
)
|
||||||
|
[[ $result == "true" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'Empty tree' {
|
||||||
|
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
|
||||||
|
run jq -c -f satellite.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"preorder": [],
|
||||||
|
"inorder": []
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
expected='{}'
|
||||||
|
assert_objects_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'Tree with one item' {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
|
||||||
|
run jq -c -f satellite.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"preorder": [
|
||||||
|
"a"
|
||||||
|
],
|
||||||
|
"inorder": [
|
||||||
|
"a"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
expected='{"v":"a","l":{},"r":{}}'
|
||||||
|
assert_objects_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'Tree with many items' {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
|
||||||
|
run jq -c -f satellite.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"preorder": [
|
||||||
|
"a",
|
||||||
|
"i",
|
||||||
|
"x",
|
||||||
|
"f",
|
||||||
|
"r"
|
||||||
|
],
|
||||||
|
"inorder": [
|
||||||
|
"i",
|
||||||
|
"a",
|
||||||
|
"f",
|
||||||
|
"x",
|
||||||
|
"r"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
expected='{"v":"a","l":{"v":"i","l":{},"r":{}},"r":{"v":"x","l":{"v":"f","l":{},"r":{}},"r":{"v":"r","l":{},"r":{}}}}'
|
||||||
|
assert_objects_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'Reject traversals of different length' {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
|
||||||
|
run jq -c -f satellite.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"preorder": [
|
||||||
|
"a",
|
||||||
|
"b"
|
||||||
|
],
|
||||||
|
"inorder": [
|
||||||
|
"b",
|
||||||
|
"a",
|
||||||
|
"r"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_failure
|
||||||
|
expected='traversals must have the same length'
|
||||||
|
assert_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'Reject inconsistent traversals of same length' {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
|
||||||
|
run jq -c -f satellite.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"preorder": [
|
||||||
|
"x",
|
||||||
|
"y",
|
||||||
|
"z"
|
||||||
|
],
|
||||||
|
"inorder": [
|
||||||
|
"a",
|
||||||
|
"b",
|
||||||
|
"c"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_failure
|
||||||
|
expected='traversals must have the same elements'
|
||||||
|
assert_equal "$output" "$expected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'Reject traversals with repeated items' {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
|
||||||
|
run jq -c -f satellite.jq << 'END_INPUT'
|
||||||
|
{
|
||||||
|
"preorder": [
|
||||||
|
"a",
|
||||||
|
"b",
|
||||||
|
"a"
|
||||||
|
],
|
||||||
|
"inorder": [
|
||||||
|
"b",
|
||||||
|
"a",
|
||||||
|
"a"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
END_INPUT
|
||||||
|
|
||||||
|
assert_failure
|
||||||
|
expected='traversals must contain unique items'
|
||||||
|
assert_equal "$output" "$expected"
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue