Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 28 additions & 12 deletions bin/.test-in-docker
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,45 @@ all_slugs="$*"

exit_code=0

# Runs the tests against /tmp/solution/${snake}.zig, recording any failure
# under the given description.
check_solution() {
bin/run.sh "${slug}" /tmp/solution /tmp/solution > /dev/null
if ! jq -e '.status == "pass"' /tmp/solution/results.json > /dev/null; then
# A compile error reports at the top level; failing tests report per test.
errors="${errors}\n\n${1} is incorrect:\n$(jq -r '
.message
// ([.tests[]? | select(.status != "pass") | "\(.name):\n\(.message // "no message")"] | join("\n\n"))
' /tmp/solution/results.json)"
local_exit_code=1;
fi
}

for slug in $all_slugs; do
snake="${slug//-/_}"
local_exit_code=0

rm -rf /tmp/solution
cp -r "/exercises/practice/${slug}" /tmp/solution

errors=""

# Integration test: Check if the example solution passes the tests
# as run by the zig-test-runner similiarly to a student submitting
# their solution.
cp /tmp/solution/.meta/example.zig "/tmp/solution/${snake}.zig"
bin/run.sh "${slug}" /tmp/solution /tmp/solution > /dev/null

errors=""

if ! jq -e '.status == "pass"' /tmp/solution/results.json > /dev/null; then
# A compile error reports at the top level; failing tests report per test.
errors="${errors}\n\nSolution is incorrect:\n$(jq -r '
.message
// ([.tests[]? | select(.status != "pass") | "\(.name):\n\(.message // "no message")"] | join("\n\n"))
' /tmp/solution/results.json)"
local_exit_code=1;
fi
check_solution "Solution"

# Approach docs: by convention, the first ```zig block in each
# approach's content.md is a complete solution, so it must also pass.
for content in "/exercises/practice/${slug}"/.approaches/*/content.md; do
[ -e "${content}" ] || continue
approach=$(basename "$(dirname "${content}")")
awk '/^```zig$/ && !found { in_block = 1; found = 1; next }
in_block && /^```$/ { exit }
in_block { print }' "${content}" > "/tmp/solution/${snake}.zig"
check_solution "Approach ${approach} solution"
done

if [ $local_exit_code = 0 ]; then
echo -e "${slug}: \e[32mPASSED\e[0m"
Expand Down
13 changes: 13 additions & 0 deletions bin/run-tests
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ execute_test() {
printf '%s\n' "Error: ${exercise_name} lacks its '.meta/example.zig' file" >&2
exit 1
fi

# Approach docs: by convention, the first ```zig block in each
# approach's content.md is a complete solution, so it must also pass.
local content
for content in .approaches/*/content.md; do
[[ -e "${content}" ]] || continue
printf '%s\n' "Running tests for ${1} approach $(basename "$(dirname "${content}")")..." >&2
awk '/^```zig$/ && !found { in_block = 1; found = 1; next }
in_block && /^```$/ { exit }
in_block { print }' "${content}" > "${exercise_name}".zig
zig test "test_${exercise_name}.zig"
printf '\n' >&2
done
)
}

Expand Down
22 changes: 22 additions & 0 deletions bin/verify-exercises-in-docker
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,27 @@ run_tests() {
jq -e '.status == "pass"' "${PWD}/results.json" >/dev/null 2>&1
}

first_zig_block() {
awk '/^```zig$/ && !found { in_block = 1; found = 1; next }
in_block && /^```$/ { exit }
in_block { print }' "${1}"
}

verify_approaches() {
local slug content approach solution
slug="${1}"
solution=$(jq -r '.files.solution[0]' .meta/config.json)

# Approach docs: by convention, the first ```zig block in each
# approach's content.md is a complete solution, so it must also pass.
for content in .approaches/*/content.md; do
approach=$(basename "$(dirname "${content}")")
echo "Verifying ${slug} approach: ${approach}..."
first_zig_block "${content}" > "${solution}"
run_tests "${slug}" || { cat results.json; return 1; }
done
}

verify_exercise() {
local dir
local slug
Expand All @@ -66,6 +87,7 @@ verify_exercise() {

copy_example_or_examplar_to_solution
run_tests "${slug}" || { cat "${tmp_dir}/results.json"; exit 1; }
verify_approaches "${slug}" || exit 1
)
}

Expand Down
31 changes: 31 additions & 0 deletions exercises/practice/isogram/.meta/supplements.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"cases": [
{
"description": "long isogram with spaces and hyphens",
"property": "isIsogram",
"comment": "151 bytes: more than two SIMD blocks of 64 bytes (AVX-512), with a partial block left over for 8, 16, 32 and 64 lanes.",
"input": {
"phrase": "a - - B - - c - - D - - e - - F - - g - - H - - i - - J - - k - - L - - m - - N - - o - - P - - q - - R - - s - - T - - u - - V - - w - - X - - y - - Z"
},
"expected": true
},
{
"description": "long phrase with duplicated character in mixed case",
"property": "isIsogram",
"comment": "The duplicates are at bytes 0 and 102, so a SIMD solution sees them in different full blocks for 8, 16, 32 and 64 lanes.",
"input": {
"phrase": "a - - B - - c - - D - - e - - F - - g - - H - - i - - J - - k - - L - - m - - N - - o - - P - - q - - A - - s - - T - - u - - V - - w - - X - - y - - Z"
},
"expected": false
},
{
"description": "long phrase with first letter repeated at the end",
"property": "isIsogram",
"comment": "The duplicates are at bytes 0 and 150, so a SIMD solution sees the second one in the final partial block for 8, 16, 32 and 64 lanes.",
"input": {
"phrase": "a - - B - - c - - D - - e - - F - - g - - H - - i - - J - - k - - L - - m - - N - - o - - P - - q - - R - - s - - T - - u - - V - - w - - X - - y - - A"
},
"expected": false
}
]
}
12 changes: 12 additions & 0 deletions exercises/practice/isogram/test_isogram.zig
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,15 @@ test "same first and last characters" {
test "word with duplicated character and with two hyphens" {
try testIsIsogram("up-to-date", false);
}

test "long isogram with spaces and hyphens" {
try testIsIsogram("a - - B - - c - - D - - e - - F - - g - - H - - i - - J - - k - - L - - m - - N - - o - - P - - q - - R - - s - - T - - u - - V - - w - - X - - y - - Z", true);
}

test "long phrase with duplicated character in mixed case" {
try testIsIsogram("a - - B - - c - - D - - e - - F - - g - - H - - i - - J - - k - - L - - m - - N - - o - - P - - q - - A - - s - - T - - u - - V - - w - - X - - y - - Z", false);
}

test "long phrase with first letter repeated at the end" {
try testIsIsogram("a - - B - - c - - D - - e - - F - - g - - H - - i - - J - - k - - L - - m - - N - - o - - P - - q - - R - - s - - T - - u - - V - - w - - X - - y - - A", false);
}
13 changes: 13 additions & 0 deletions exercises/practice/rna-transcription/.meta/supplements.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"cases": [
{
"description": "long rna complement",
"property": "toRna",
"comment": "108 nucleotides: longer than a SIMD block of 64 bytes (AVX-512), with a partial block left over for 8, 16, 32 and 64 lanes.",
"input": {
"dna": "ACGTGGTCTTAAACGTGGTCTTAAACGTGGTCTTAAACGTGGTCTTAAACGTGGTCTTAAACGTGGTCTTAAACGTGGTCTTAAACGTGGTCTTAAACGTGGTCTTAA"
},
"expected": "UGCACCAGAAUUUGCACCAGAAUUUGCACCAGAAUUUGCACCAGAAUUUGCACCAGAAUUUGCACCAGAAUUUGCACCAGAAUUUGCACCAGAAUUUGCACCAGAAUU"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ test "rna complement of adenine is uracil" {
test "rna complement" {
try testTranscription("ACGTGGTCTTAA", "UGCACCAGAAUU");
}

test "long rna complement" {
try testTranscription("ACGTGGTCTTAAACGTGGTCTTAAACGTGGTCTTAAACGTGGTCTTAAACGTGGTCTTAAACGTGGTCTTAAACGTGGTCTTAAACGTGGTCTTAAACGTGGTCTTAA", "UGCACCAGAAUUUGCACCAGAAUUUGCACCAGAAUUUGCACCAGAAUUUGCACCAGAAUUUGCACCAGAAUUUGCACCAGAAUUUGCACCAGAAUUUGCACCAGAAUU");
}
Loading