When I test my food chain solution on my own machine, I get a weird error:
BATS_RUN_SKIPPED=true bats test-food-chain.bats
test-food-chain.bats
✓ fly
✓ spider
✓ bird
✓ cat
✓ dog
✓ goat
bats: unknown test name `test_horse' 7/10
✗ cow
(in test file test-food-chain.bats, line 180)
`)' failed
/Users/bhl/Exercism/jq/food-chain/test-food-chain.bats: command substitution: line 191: syntax error near unexpected token `)'
/Users/bhl/Exercism/jq/food-chain/test-food-chain.bats: command substitution: line 191: `)'
✓ multiple verses
✓ full song
bats warning: Executed 9 instead of expected 10 tests
10 tests, 1 failure, 1 not run
I don’t know anything about bats, but staring at the code, I don’t see any obvious syntax error. The cow and horse tests look just the same as the rest. When I commented out both of these tests, the remaining ones ran without any problem, but if I commented out just one (either one), I would get another strange error:
✗ setup_file failed
(in test file test-food-chain.bats, line 180)
`)' failed
bats warning: Executed 1 instead of expected 9 tests
9 tests, 1 failure, 8 not run
I pasted my solution into the browser and there all of the tests ran successfully.
I guess that my local installation of bats is somehow out of whack? Any ideas?
It seems that messages are limited to 7000 characters and the test file is about 8000, so I will post it in two pieces.
Part 1 (first 184 lines, single-verse tests):
#!/usr/bin/env bats
# generated on 2024-07-17T15:57:54Z
load bats-extra
load bats-jq
@test 'fly' {
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
run jq -r -f food-chain.jq << 'END_INPUT'
{
"startVerse": 1,
"endVerse": 1
}
END_INPUT
expected=$(cat <<END_EXPECTED
I know an old lady who swallowed a fly.
I don't know why she swallowed the fly. Perhaps she'll die.
END_EXPECTED
)
assert_success
assert_equal "$output" "$expected"
}
@test 'spider' {
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
run jq -r -f food-chain.jq << 'END_INPUT'
{
"startVerse": 2,
"endVerse": 2
}
END_INPUT
expected=$(cat <<END_EXPECTED
I know an old lady who swallowed a spider.
It wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die.
END_EXPECTED
)
assert_success
assert_equal "$output" "$expected"
}
@test 'bird' {
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
run jq -r -f food-chain.jq << 'END_INPUT'
{
"startVerse": 3,
"endVerse": 3
}
END_INPUT
expected=$(cat <<END_EXPECTED
I know an old lady who swallowed a bird.
How absurd to swallow a bird!
She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die.
END_EXPECTED
)
assert_success
assert_equal "$output" "$expected"
}
@test 'cat' {
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
run jq -r -f food-chain.jq << 'END_INPUT'
{
"startVerse": 4,
"endVerse": 4
}
END_INPUT
expected=$(cat <<END_EXPECTED
I know an old lady who swallowed a cat.
Imagine that, to swallow a cat!
She swallowed the cat to catch the bird.
She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die.
END_EXPECTED
)
assert_success
assert_equal "$output" "$expected"
}
@test 'dog' {
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
run jq -r -f food-chain.jq << 'END_INPUT'
{
"startVerse": 5,
"endVerse": 5
}
END_INPUT
expected=$(cat <<END_EXPECTED
I know an old lady who swallowed a dog.
What a hog, to swallow a dog!
She swallowed the dog to catch the cat.
She swallowed the cat to catch the bird.
She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die.
END_EXPECTED
)
assert_success
assert_equal "$output" "$expected"
}
@test 'goat' {
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
run jq -r -f food-chain.jq << 'END_INPUT'
{
"startVerse": 6,
"endVerse": 6
}
END_INPUT
expected=$(cat <<END_EXPECTED
I know an old lady who swallowed a goat.
Just opened her throat and swallowed a goat!
She swallowed the goat to catch the dog.
She swallowed the dog to catch the cat.
She swallowed the cat to catch the bird.
She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die.
END_EXPECTED
)
assert_success
assert_equal "$output" "$expected"
}
@test 'cow' {
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
run jq -r -f food-chain.jq << 'END_INPUT'
{
"startVerse": 7,
"endVerse": 7
}
END_INPUT
expected=$(cat <<END_EXPECTED
I know an old lady who swallowed a cow.
I don't know how she swallowed a cow!
She swallowed the cow to catch the goat.
She swallowed the goat to catch the dog.
She swallowed the dog to catch the cat.
She swallowed the cat to catch the bird.
She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die.
END_EXPECTED
)
assert_success
assert_equal "$output" "$expected"
}
@test 'horse' {
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
run jq -r -f food-chain.jq << 'END_INPUT'
{
"startVerse": 8,
"endVerse": 8
}
END_INPUT
expected=$(cat <<END_EXPECTED
I know an old lady who swallowed a horse.
She's dead, of course!
END_EXPECTED
)
assert_success
assert_equal "$output" "$expected"
}
@test 'multiple verses' {
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
run jq -r -f food-chain.jq << 'END_INPUT'
{
"startVerse": 1,
"endVerse": 3
}
END_INPUT
expected=$(cat <<END_EXPECTED
I know an old lady who swallowed a fly.
I don't know why she swallowed the fly. Perhaps she'll die.
I know an old lady who swallowed a spider.
It wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die.
I know an old lady who swallowed a bird.
How absurd to swallow a bird!
She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die.
END_EXPECTED
)
assert_success
assert_equal "$output" "$expected"
}
@test 'full song' {
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
run jq -r -f food-chain.jq << 'END_INPUT'
{
"startVerse": 1,
"endVerse": 8
}
END_INPUT
expected=$(cat <<END_EXPECTED
I know an old lady who swallowed a fly.
I don't know why she swallowed the fly. Perhaps she'll die.
I know an old lady who swallowed a spider.
It wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die.
I know an old lady who swallowed a bird.
How absurd to swallow a bird!
She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die.
I know an old lady who swallowed a cat.
Imagine that, to swallow a cat!
She swallowed the cat to catch the bird.
She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die.
I know an old lady who swallowed a dog.
What a hog, to swallow a dog!
She swallowed the dog to catch the cat.
She swallowed the cat to catch the bird.
She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die.
I know an old lady who swallowed a goat.
Just opened her throat and swallowed a goat!
She swallowed the goat to catch the dog.
She swallowed the dog to catch the cat.
She swallowed the cat to catch the bird.
She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die.
I know an old lady who swallowed a cow.
I don't know how she swallowed a cow!
She swallowed the cow to catch the goat.
She swallowed the goat to catch the dog.
She swallowed the dog to catch the cat.
She swallowed the cat to catch the bird.
She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die.
I know an old lady who swallowed a horse.
She's dead, of course!
END_EXPECTED
)
assert_success
assert_equal "$output" "$expected"
}
Yes, it throws the error. FWIW, I have done several jq exercises in the last few days and none of them had a similar problem. Nothing has changed in my environment as far as I know.
bhl % bash --version
GNU bash, version 3.2.57(1)-release (arm64-apple-darwin23)
Copyright (C) 2007 Free Software Foundation, Inc.
bhl % bats --version
Bats 1.10.0
Can confirm. IIRC, it’s fairly easy to upgrade using Homebrew, but it is not quite as straightforward to change the default shell (which is now zsh), or point the bash shortcut to the updated bash version.
I found this and this SO post helpful reminders to get things running nicely again.
I think the above is more or less covered in the Exercism bash docs, but it might be worth a couple more links/reminders.
I’m doing some debugging, and bash 3.2 is having a problem with the END_EXPECTED heredoc in the cow test: it’s not recognizing the end marker of the document, even if I change it to something unique like END_COW. Additionally if I comment-out the horse test, the error message becomes line 150: unexpected EOF while looking for matching )'`
There are no control chars around the cow ending heredoc marker, nor around the end parenthesis of the command substitution.
bash v3.2 is the only version with this problem (that I’ve tested). I’m about to dig into the bash change notes for v4.0.
That’s it. An odd number of single quotes in the heredoc.
If I escape the single quote, the test merely fails because the expected value contains a backslash. I’ll have to awkwardly quote the expected value like
expected="I know an old lady who swallowed a cow.
I don't know how she swallowed a cow!
She swallowed the cow to catch the goat.
She swallowed the goat to catch the dog.
She swallowed the dog to catch the cat.
She swallowed the cat to catch the bird.
She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
She swallowed the spider to catch the fly.
I don't know why she swallowed the fly. Perhaps she'll die."
or perhaps this is less worse?
expected="I know an old lady who swallowed a cow."$'\n'
expected+="I don't know how she swallowed a cow!"$'\n'
expected+="She swallowed the cow to catch the goat."$'\n'
expected+="She swallowed the goat to catch the dog."$'\n'
expected+="She swallowed the dog to catch the cat."$'\n'
expected+="She swallowed the cat to catch the bird."$'\n'
expected+="She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her."$'\n'
expected+="She swallowed the spider to catch the fly."$'\n'
expected+="I don't know why she swallowed the fly. Perhaps she'll die."
Or change the lyrics?
I have no idea how she swallowed a cow!
...
She died of course!
After the close parenthesis, we’re still at the PS2 prompt. However bash has parsed what we’ve entered so far, it has not recognized the end of the command substitution. You can take the cat ... END out of the command substitution and bash handles it as a valid heredoc.