summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-06-02 22:37:51 +0300
committerPaul Buetow <paul@buetow.org>2026-06-02 22:37:51 +0300
commitd01a346deb25f41eb199f2a678edb49d49b14835 (patch)
tree6bb030140bd01f7870f5b365ecfa4b9856b5d81a /tests
parenteff352ff2c1f9b0578a5884e3e0d81b1a26c6031 (diff)
Add modern CLI test coverage for pi0
Diffstat (limited to 'tests')
-rwxr-xr-xtests/cli.sh798
-rwxr-xr-xtests/helpers.sh286
2 files changed, 772 insertions, 312 deletions
diff --git a/tests/cli.sh b/tests/cli.sh
index 3dadc7b..77d08da 100755
--- a/tests/cli.sh
+++ b/tests/cli.sh
@@ -1,201 +1,146 @@
#!/usr/bin/env bash
set -euo pipefail
-declare -r REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
-declare -r PHOTOALBUM="${PHOTOALBUM:-$REPO_ROOT/bin/photoalbum}"
-TEST_TMPDIR="${TEST_TMPDIR:-}"
+declare -r TEST_REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+declare -r TEST_PHOTOALBUM="${PHOTOALBUM:-$TEST_REPO_ROOT/bin/photoalbum}"
-setup() {
- TEST_TMPDIR=$(mktemp -d)
-}
-
-teardown() {
- if [ -n "$TEST_TMPDIR" ]; then
- rm -rf "$TEST_TMPDIR"
- fi
-}
+# shellcheck source=tests/helpers.sh
+source "$TEST_REPO_ROOT/tests/helpers.sh"
-run_test() {
- local -r description="$1"; shift
- local output_file
- local -i status=0
-
- output_file=$(mktemp)
- set +e
- ( set -euo pipefail; trap teardown EXIT; "$@" ) > "$output_file" 2>&1
- status=$?
- set -e
-
- if (( status != 0 )); then
- echo "FAIL: $description" >&2
- cat "$output_file" >&2
- rm -f "$output_file"
- exit 1
- fi
-
- rm -f "$output_file"
-}
-
-assert_failure() {
- local -r description="$1"; shift
- local output_file
- local -i status=0
-
- output_file=$(mktemp)
- set +e
- "$@" > "$output_file" 2>&1
- status=$?
- set -e
-
- if (( status == 0 )); then
- echo "FAIL: $description" >&2
- cat "$output_file" >&2
- rm -f "$output_file"
- exit 1
- fi
-
- rm -f "$output_file"
-}
-
-capture_failure_output() {
+test_version() {
local output
- local output_file
- local -i status=0
-
- output_file=$(mktemp)
- set +e
- "$@" > "$output_file" 2>&1
- status=$?
- set -e
-
- if (( status == 0 )); then
- echo 'FAIL: expected command to fail' >&2
- output=$(<"$output_file")
- echo "$output" >&2
- rm -f "$output_file"
- exit 1
- fi
-
- output=$(<"$output_file")
- rm -f "$output_file"
- printf '%s\n' "$output"
-}
-assert_contains() {
- local -r needle="$1"; shift
- local -r haystack="$1"; shift
-
- if [[ "$haystack" != *"$needle"* ]]; then
- echo "FAIL: expected output to contain $needle" >&2
- echo "$haystack" >&2
- exit 1
- fi
+ output=$(test::run_photoalbum --version)
+ test::assert_contains 'This is Photoalbum Version' "$output"
}
-assert_not_contains() {
- local -r needle="$1"; shift
- local -r haystack="$1"; shift
-
- if [[ "$haystack" == *"$needle"* ]]; then
- echo "FAIL: expected output not to contain $needle" >&2
- echo "$haystack" >&2
- exit 1
- fi
-}
+test_init() {
+ local config
-run_photoalbum() {
- "$PHOTOALBUM" "$@" 2>&1
+ test::setup
+ (
+ cd "$TEST_TMPDIR"
+ PHOTOALBUM_DEFAULT_RC="$TEST_TMPDIR/missing" \
+ "$TEST_PHOTOALBUM" --init >/dev/null
+ test::assert_file_exists photoalbum.conf
+ test::assert_path_absent Makefile
+ )
+ config=$(<"$TEST_TMPDIR/photoalbum.conf")
+ test::assert_contains \
+ "TEMPLATE_DIR=$TEST_REPO_ROOT/share/templates/default" \
+ "$config"
+ test::teardown
}
-test_version() {
+test_init_existing_config_fails_without_overwrite() {
local output
- output=$(run_photoalbum --version)
- assert_contains 'This is Photoalbum Version' "$output"
-}
+ test::setup
+ printf 'sentinel\n' > "$TEST_TMPDIR/photoalbum.conf"
-test_init() {
- setup
- (
+ output=$(
cd "$TEST_TMPDIR"
- PHOTOALBUM_DEFAULT_RC="$TEST_TMPDIR/missing" \
- "$PHOTOALBUM" --init >/dev/null
- test -f photoalbum.conf
- test ! -f Makefile
- grep -q \
- "^TEMPLATE_DIR=$REPO_ROOT/share/templates/default$" \
- photoalbum.conf
+ test::capture_failure_output "$TEST_PHOTOALBUM" --init
)
- teardown
+
+ test::assert_contains 'Error: photoalbum.conf already exists' "$output"
+ test "$(cat "$TEST_TMPDIR/photoalbum.conf")" = 'sentinel'
+ test::teardown
}
test_clean() {
- setup
+ test::setup
printf 'DIST_DIR=%q/dist\n' "$TEST_TMPDIR" \
> "$TEST_TMPDIR/photoalbum.conf"
mkdir -p "$TEST_TMPDIR/dist"
(
cd "$TEST_TMPDIR"
- "$PHOTOALBUM" --clean
- test ! -e "$TEST_TMPDIR/dist"
+ "$TEST_PHOTOALBUM" --clean
+ test::assert_path_absent "$TEST_TMPDIR/dist"
)
- teardown
+ test::teardown
}
test_clean_with_config() {
local config_file
- setup
+ test::setup
config_file="$TEST_TMPDIR/custom.conf"
printf 'DIST_DIR=%q/custom-dist\n' "$TEST_TMPDIR" > "$config_file"
mkdir -p "$TEST_TMPDIR/custom-dist"
(
cd "$TEST_TMPDIR"
- "$PHOTOALBUM" --clean --config "$config_file"
- test ! -e "$TEST_TMPDIR/custom-dist"
+ "$TEST_PHOTOALBUM" --clean --config "$config_file"
+ test::assert_path_absent "$TEST_TMPDIR/custom-dist"
)
- teardown
+ test::teardown
}
test_clean_cli_dist_overrides_config() {
local config_file
- setup
+ test::setup
config_file="$TEST_TMPDIR/photoalbum.conf"
printf 'DIST_DIR=%q/config-dist\n' "$TEST_TMPDIR" > "$config_file"
mkdir -p "$TEST_TMPDIR/config-dist" "$TEST_TMPDIR/cli-dist"
(
cd "$TEST_TMPDIR"
- "$PHOTOALBUM" --clean --dist "$TEST_TMPDIR/cli-dist"
- test -d "$TEST_TMPDIR/config-dist"
- test ! -e "$TEST_TMPDIR/cli-dist"
+ "$TEST_PHOTOALBUM" --clean --dist "$TEST_TMPDIR/cli-dist"
+ test::assert_dir_exists "$TEST_TMPDIR/config-dist"
+ test::assert_path_absent "$TEST_TMPDIR/cli-dist"
)
- teardown
+ test::teardown
}
-test_clean_missing_config_fails() {
- local output
+test_missing_config_fails_without_legacy_fallbacks() {
+ local default_rc
+ local home_dir
+ local generate_output
+ local clean_output
- setup
- printf 'DIST_DIR=%q/dist\n' "$TEST_TMPDIR" > "$TEST_TMPDIR/photoalbumrc"
- output=$(
+ test::setup
+ home_dir="$TEST_TMPDIR/home"
+ default_rc="$TEST_TMPDIR/default-photoalbum"
+ mkdir -p "$home_dir"
+
+ printf 'DIST_DIR=%q/legacy-dist\n' "$TEST_TMPDIR" \
+ > "$TEST_TMPDIR/photoalbumrc"
+ printf 'DIST_DIR=%q/home-dist\n' "$TEST_TMPDIR" > "$home_dir/.photoalbumrc"
+ printf 'DIST_DIR=%q/default-dist\n' "$TEST_TMPDIR" > "$default_rc"
+
+ generate_output=$(
cd "$TEST_TMPDIR"
- capture_failure_output "$PHOTOALBUM" --clean
+ HOME="$home_dir" PHOTOALBUM_DEFAULT_RC="$default_rc" \
+ test::capture_failure_output "$TEST_PHOTOALBUM" --generate
+ )
+ clean_output=$(
+ cd "$TEST_TMPDIR"
+ HOME="$home_dir" PHOTOALBUM_DEFAULT_RC="$default_rc" \
+ test::capture_failure_output "$TEST_PHOTOALBUM" --clean
)
- assert_contains 'Error: Can not find config file ./photoalbum.conf' "$output"
- assert_contains 'Run photoalbum --init to create ./photoalbum.conf.' "$output"
- teardown
+ test::assert_contains 'Error: Can not find config file ./photoalbum.conf' \
+ "$generate_output"
+ test::assert_contains 'Run photoalbum --init to create ./photoalbum.conf.' \
+ "$generate_output"
+ test::assert_contains 'Error: Can not find config file ./photoalbum.conf' \
+ "$clean_output"
+ test::assert_contains 'Run photoalbum --init to create ./photoalbum.conf.' \
+ "$clean_output"
+ test::assert_path_absent "$TEST_TMPDIR/legacy-dist"
+ test::assert_path_absent "$TEST_TMPDIR/home-dist"
+ test::assert_path_absent "$TEST_TMPDIR/default-dist"
+ test::teardown
}
test_generate_with_config_missing_incoming_fails() {
local config_file
local output
- setup
+ test::setup
config_file="$TEST_TMPDIR/custom.conf"
{
printf 'INCOMING_DIR=%q/missing\n' "$TEST_TMPDIR"
@@ -204,12 +149,40 @@ test_generate_with_config_missing_incoming_fails() {
output=$(
cd "$TEST_TMPDIR"
- capture_failure_output "$PHOTOALBUM" --generate --config "$config_file"
+ test::capture_failure_output \
+ "$TEST_PHOTOALBUM" --generate --config "$config_file"
)
- assert_contains "ERROR: You have to create $TEST_TMPDIR/missing first" \
+ test::assert_contains \
+ "ERROR: You have to create $TEST_TMPDIR/missing first" \
"$output"
- teardown
+ test::teardown
+}
+
+test_generate_with_config_succeeds_without_default_config() {
+ local config_file
+ local fake_bin
+
+ test::setup
+ fake_bin="$TEST_TMPDIR/bin"
+ config_file="$TEST_TMPDIR/custom.conf"
+
+ test::install_fake_imagemagick "$fake_bin"
+ test::generate_fixture_images "$TEST_TMPDIR/custom-incoming"
+ test::write_album_config \
+ "$config_file" "$TEST_TMPDIR/custom-incoming" \
+ "$TEST_TMPDIR/custom-dist" 'Custom config album' 3
+
+ (
+ cd "$TEST_TMPDIR"
+ test::assert_path_absent photoalbum.conf
+ PATH="$fake_bin:$PATH" "$TEST_PHOTOALBUM" \
+ --generate --config "$config_file"
+ )
+
+ test::assert_file_exists "$TEST_TMPDIR/custom-dist/photos/01-landscape.jpg"
+ test::assert_file_exists "$TEST_TMPDIR/custom-dist/html/page-2.html"
+ test::teardown
}
test_generate_cli_overrides_config_values() {
@@ -218,22 +191,13 @@ test_generate_cli_overrides_config_values() {
local page_html
local view_html
- setup
+ test::setup
fake_bin="$TEST_TMPDIR/bin"
config_file="$TEST_TMPDIR/photoalbum.conf"
- mkdir -p "$fake_bin" "$TEST_TMPDIR/cli-incoming"
- cat > "$fake_bin/magick" <<'MAGICK'
-#!/usr/bin/env bash
-set -euo pipefail
-
-dest="${@: -1}"
-mkdir -p "$(dirname "$dest")"
-printf 'fake image\n' > "$dest"
-MAGICK
- chmod 0755 "$fake_bin/magick"
- printf 'fake image\n' > "$TEST_TMPDIR/cli-incoming/01.jpg"
- printf 'fake image\n' > "$TEST_TMPDIR/cli-incoming/02.jpg"
+ test::install_fake_imagemagick "$fake_bin"
+ PATH="$fake_bin:$PATH" \
+ test::generate_fixture_images "$TEST_TMPDIR/cli-incoming"
{
printf 'TITLE=%q\n' 'Config title'
@@ -249,11 +213,11 @@ MAGICK
(
cd "$TEST_TMPDIR"
- PATH="$fake_bin:$PATH" "$PHOTOALBUM" \
+ PATH="$fake_bin:$PATH" "$TEST_PHOTOALBUM" \
--generate \
--incoming "$TEST_TMPDIR/cli-incoming" \
--dist "$TEST_TMPDIR/cli-dist" \
- --template "$REPO_ROOT/share/templates/default" \
+ --template "$TEST_REPO_ROOT/share/templates/default" \
--title 'CLI title' \
--height 456 \
--thumbheight 45 \
@@ -265,77 +229,256 @@ MAGICK
page_html=$(<"$TEST_TMPDIR/cli-dist/html/page-1.html")
view_html=$(<"$TEST_TMPDIR/cli-dist/html/1-1.html")
- test -f "$TEST_TMPDIR/cli-dist/photos/01.jpg"
- test -f "$TEST_TMPDIR/cli-dist/photos/02.jpg"
- test ! -e "$TEST_TMPDIR/config-dist"
- test ! -e "$TEST_TMPDIR/cli-dist/cli-incoming-"*.tar
- assert_contains '<title>CLI title</title>' "$page_html"
- assert_contains 'height: 45px;' "$page_html"
- assert_contains 'max-height: 456px;' "$view_html"
- assert_contains 'Next 1 pictures' "$page_html"
- assert_not_contains 'Config title' "$page_html"
-
- teardown
+ test::assert_file_exists "$TEST_TMPDIR/cli-dist/photos/01-landscape.jpg"
+ test::assert_file_exists "$TEST_TMPDIR/cli-dist/photos/02-portrait.jpg"
+ test::assert_file_exists "$TEST_TMPDIR/cli-dist/photos/03-square.jpg"
+ test::assert_file_exists \
+ "$TEST_TMPDIR/cli-dist/photos/04 filename with spaces.jpg"
+ test::assert_file_exists "$TEST_TMPDIR/cli-dist/photos/05-extra.jpg"
+ test::assert_file_exists "$TEST_TMPDIR/cli-dist/photos/06-extra.jpg"
+ test::assert_path_absent "$TEST_TMPDIR/config-dist"
+ test::assert_find_count 0 "$TEST_TMPDIR/cli-dist" '*.tar'
+ test::assert_contains '<title>CLI title</title>' "$page_html"
+ test::assert_contains 'height: 45px;' "$page_html"
+ test::assert_contains 'max-height: 456px;' "$view_html"
+ test::assert_contains 'Next 1 pictures' "$page_html"
+ test::assert_not_contains 'Config title' "$page_html"
+
+ test::teardown
}
-test_generate_cli_tarball_overrides_config() {
+test_generate_shuffle_override_uses_random_order() {
local config_file
local fake_bin
- local tarball_count
+ local sort_log_output
+ local sort_log
- setup
+ test::setup
fake_bin="$TEST_TMPDIR/bin"
config_file="$TEST_TMPDIR/photoalbum.conf"
+ sort_log="$TEST_TMPDIR/sort.log"
- mkdir -p "$fake_bin" "$TEST_TMPDIR/incoming"
- cat > "$fake_bin/magick" <<'MAGICK'
-#!/usr/bin/env bash
-set -euo pipefail
+ test::install_fake_imagemagick "$fake_bin"
+ test::install_sort_spy "$fake_bin"
+ PATH="$fake_bin:$PATH" \
+ test::generate_fixture_images "$TEST_TMPDIR/incoming"
+ test::write_album_config \
+ "$config_file" "$TEST_TMPDIR/incoming" "$TEST_TMPDIR/dist" \
+ 'Shuffle override' 40
-dest="${@: -1}"
-mkdir -p "$(dirname "$dest")"
-printf 'fake image\n' > "$dest"
-MAGICK
- chmod 0755 "$fake_bin/magick"
- printf 'fake image\n' > "$TEST_TMPDIR/incoming/01.jpg"
+ (
+ cd "$TEST_TMPDIR"
+ PATH="$fake_bin:$PATH" TEST_SORT_LOG="$sort_log" \
+ "$TEST_PHOTOALBUM" --generate --shuffle
+ )
- {
- printf 'TITLE=%q\n' 'Tarball override'
- printf 'THUMBHEIGHT=30\n'
- printf 'HEIGHT=120\n'
- printf 'MAXPREVIEWS=40\n'
- printf 'INCOMING_DIR=%q/incoming\n' "$TEST_TMPDIR"
- printf 'DIST_DIR=%q/dist\n' "$TEST_TMPDIR"
- printf 'TEMPLATE_DIR=%q/share/templates/default\n' "$REPO_ROOT"
- printf 'TARBALL_INCLUDE=no\n'
- } > "$config_file"
+ sort_log_output=$(<"$sort_log")
+ test::assert_contains 'photo-shuffle -R' "$sort_log_output"
+ test::teardown
+}
+
+test_generate_no_shuffle_override_uses_sorted_order() {
+ local config_file
+ local fake_bin
+ local page_html
+ local sort_log
+
+ test::setup
+ fake_bin="$TEST_TMPDIR/bin"
+ config_file="$TEST_TMPDIR/photoalbum.conf"
+ sort_log="$TEST_TMPDIR/sort.log"
+
+ test::install_fake_imagemagick "$fake_bin"
+ test::install_sort_spy "$fake_bin"
+ PATH="$fake_bin:$PATH" \
+ test::generate_fixture_images "$TEST_TMPDIR/incoming"
+ test::write_album_config \
+ "$config_file" "$TEST_TMPDIR/incoming" "$TEST_TMPDIR/dist" \
+ 'No shuffle override' 40
+ printf 'SHUFFLE=yes\n' >> "$config_file"
+
+ (
+ cd "$TEST_TMPDIR"
+ PATH="$fake_bin:$PATH" TEST_SORT_LOG="$sort_log" \
+ "$TEST_PHOTOALBUM" --generate --no-shuffle
+ )
+
+ page_html=$(<"$TEST_TMPDIR/dist/html/page-1.html")
+ test::assert_contains_before \
+ "name='01-landscape.jpg'" \
+ "name='06-extra.jpg'" \
+ "$page_html"
+ test::assert_path_absent "$sort_log"
+ test::teardown
+}
+
+test_generate_cli_tarball_overrides_config() {
+ local config_file
+ local fake_bin
+ local tarball
+ local tarball_listing
+
+ test::setup
+ fake_bin="$TEST_TMPDIR/bin"
+ config_file="$TEST_TMPDIR/photoalbum.conf"
+
+ test::install_fake_imagemagick "$fake_bin"
+ PATH="$fake_bin:$PATH" \
+ test::generate_fixture_images "$TEST_TMPDIR/incoming"
+ test::write_album_config \
+ "$config_file" "$TEST_TMPDIR/incoming" "$TEST_TMPDIR/dist" \
+ 'Tarball override' 40
(
cd "$TEST_TMPDIR"
- PATH="$fake_bin:$PATH" "$PHOTOALBUM" --generate --tarball
+ PATH="$fake_bin:$PATH" "$TEST_PHOTOALBUM" --generate --tarball
)
- tarball_count=$(find "$TEST_TMPDIR/dist" -maxdepth 1 -name '*.tar' \
- | wc -l)
- test "$tarball_count" -eq 1
+ tarball=$(find "$TEST_TMPDIR/dist" -maxdepth 1 -name '*.tar' -print)
+ test::assert_contains "$TEST_TMPDIR/dist/incoming-" "$tarball"
+ tarball_listing=$(tar -tf "$tarball")
+ test::assert_contains 'incoming/01-landscape.jpg' "$tarball_listing"
+
+ test::teardown
+}
+
+test_generate_cli_no_tarball_overrides_config() {
+ local config_file
+ local fake_bin
+
+ test::setup
+ fake_bin="$TEST_TMPDIR/bin"
+ config_file="$TEST_TMPDIR/photoalbum.conf"
+
+ test::install_fake_imagemagick "$fake_bin"
+ PATH="$fake_bin:$PATH" \
+ test::generate_fixture_images "$TEST_TMPDIR/incoming"
+ test::write_album_config \
+ "$config_file" "$TEST_TMPDIR/incoming" "$TEST_TMPDIR/dist" \
+ 'No tarball override' 40
+ printf 'TARBALL_INCLUDE=yes\n' >> "$config_file"
- teardown
+ (
+ cd "$TEST_TMPDIR"
+ PATH="$fake_bin:$PATH" "$TEST_PHOTOALBUM" --generate --no-tarball
+ )
+
+ test::assert_find_count 0 "$TEST_TMPDIR/dist" '*.tar'
+ test::teardown
}
test_generate_missing_incoming_fails() {
- setup
+ local output
+
+ test::setup
{
printf 'INCOMING_DIR=%q/missing\n' "$TEST_TMPDIR"
printf 'DIST_DIR=%q/dist\n' "$TEST_TMPDIR"
} > "$TEST_TMPDIR/photoalbum.conf"
+ output=$(
+ cd "$TEST_TMPDIR"
+ test::capture_failure_output "$TEST_PHOTOALBUM" --generate
+ )
+
+ test::assert_contains \
+ "ERROR: You have to create $TEST_TMPDIR/missing first" \
+ "$output"
+ test::teardown
+}
+
+test_integration_generates_album_outputs_and_cleans() {
+ local config_file
+ local fake_bin
+ local page_html
+ local tarball
+ local tarball_listing
+ local top_index_html
+
+ test::setup
+ fake_bin="$TEST_TMPDIR/bin"
+ config_file="$TEST_TMPDIR/photoalbum.conf"
+
+ test::install_fake_imagemagick "$fake_bin"
+ PATH="$fake_bin:$PATH" \
+ test::generate_fixture_images "$TEST_TMPDIR/incoming"
+ test::write_album_config \
+ "$config_file" "$TEST_TMPDIR/incoming" "$TEST_TMPDIR/dist" \
+ 'Integration album' 2
+
+ (
+ cd "$TEST_TMPDIR"
+ PATH="$fake_bin:$PATH" "$TEST_PHOTOALBUM" --generate
+ )
+
+ test::assert_file_exists "$TEST_TMPDIR/dist/photos/01-landscape.jpg"
+ test::assert_file_exists "$TEST_TMPDIR/dist/photos/02-portrait.jpg"
+ test::assert_file_exists "$TEST_TMPDIR/dist/photos/03-square.jpg"
+ test::assert_file_exists \
+ "$TEST_TMPDIR/dist/photos/04 filename with spaces.jpg"
+ test::assert_file_exists "$TEST_TMPDIR/dist/thumbs/01-landscape.jpg"
+ test::assert_file_exists "$TEST_TMPDIR/dist/blurs/01-landscape.jpg"
+ test::assert_file_exists "$TEST_TMPDIR/dist/html/page-1.html"
+ test::assert_file_exists "$TEST_TMPDIR/dist/html/page-2.html"
+ test::assert_file_exists "$TEST_TMPDIR/dist/html/page-3.html"
+ test::assert_file_exists "$TEST_TMPDIR/dist/html/1-1.html"
+ test::assert_file_exists "$TEST_TMPDIR/dist/html/3-2.html"
+ test::assert_file_exists "$TEST_TMPDIR/dist/html/index.html"
+ test::assert_file_exists "$TEST_TMPDIR/dist/index.html"
+
+ page_html=$(<"$TEST_TMPDIR/dist/html/page-1.html")
+ top_index_html=$(<"$TEST_TMPDIR/dist/index.html")
+ test::assert_contains "name='04 filename with spaces.jpg'" \
+ "$(<"$TEST_TMPDIR/dist/html/page-2.html")"
+ test::assert_contains 'Next 2 pictures' "$page_html"
+ test::assert_contains 'url=./html/index.html' "$top_index_html"
+ test::assert_find_count 0 "$TEST_TMPDIR/dist" '*.tar'
+
+ (
+ cd "$TEST_TMPDIR"
+ PATH="$fake_bin:$PATH" "$TEST_PHOTOALBUM" --generate --tarball
+ )
+ tarball=$(find "$TEST_TMPDIR/dist" -maxdepth 1 -name '*.tar' -print)
+ test::assert_contains "$TEST_TMPDIR/dist/incoming-" "$tarball"
+ tarball_listing=$(tar -tf "$tarball")
+ test::assert_contains \
+ 'incoming/04 filename with spaces.jpg' \
+ "$tarball_listing"
+
(
cd "$TEST_TMPDIR"
- assert_failure \
- '--generate fails when INCOMING_DIR is missing' \
- "$PHOTOALBUM" --generate
+ "$TEST_PHOTOALBUM" --clean
+ )
+ test::assert_path_absent "$TEST_TMPDIR/dist"
+ test::teardown
+}
+
+test_generate_missing_imagemagick_fails() {
+ local config_file
+ local output
+ local path_bin
+
+ test::setup
+ config_file="$TEST_TMPDIR/photoalbum.conf"
+ path_bin="$TEST_TMPDIR/path-bin"
+
+ test::install_coreutils_without_imagemagick "$path_bin"
+ mkdir -p "$TEST_TMPDIR/incoming"
+ printf 'fake image\n' > "$TEST_TMPDIR/incoming/01.jpg"
+ test::write_album_config \
+ "$config_file" "$TEST_TMPDIR/incoming" "$TEST_TMPDIR/dist" \
+ 'Missing ImageMagick' 40
+
+ output=$(
+ cd "$TEST_TMPDIR"
+ PATH="$path_bin" test::capture_failure_output \
+ "$TEST_PHOTOALBUM" --generate
)
- teardown
+
+ test::assert_contains \
+ 'ERROR: ImageMagick is required; install magick or convert' \
+ "$output"
+ test::teardown
}
test_generate_escapes_html_values() {
@@ -351,7 +494,7 @@ test_generate_escapes_html_values() {
local title_html
local view_html
- setup
+ test::setup
fake_bin="$TEST_TMPDIR/bin"
config_file="$TEST_TMPDIR/photoalbum.conf"
photo_name="kid's_\"<tag>&.jpg"
@@ -362,16 +505,8 @@ test_generate_escapes_html_values() {
original_basepath="https://example.test/original?album=\"<x>&owner=O'Neil"
original_basepath_html='https://example.test/original?album=&quot;&lt;x&gt;&amp;owner=O&#39;Neil'
- mkdir -p "$fake_bin" "$TEST_TMPDIR/incoming"
- cat > "$fake_bin/magick" <<'MAGICK'
-#!/usr/bin/env bash
-set -euo pipefail
-
-dest="${@: -1}"
-mkdir -p "$(dirname "$dest")"
-printf 'fake image\n' > "$dest"
-MAGICK
- chmod 0755 "$fake_bin/magick"
+ test::install_fake_imagemagick "$fake_bin"
+ mkdir -p "$TEST_TMPDIR/incoming"
printf 'fake image\n' > "$TEST_TMPDIR/incoming/$photo_name"
{
@@ -381,7 +516,7 @@ MAGICK
printf 'MAXPREVIEWS=40\n'
printf 'INCOMING_DIR=%q/incoming\n' "$TEST_TMPDIR"
printf 'DIST_DIR=%q/dist\n' "$TEST_TMPDIR"
- printf 'TEMPLATE_DIR=%q/share/templates/default\n' "$REPO_ROOT"
+ printf 'TEMPLATE_DIR=%q/share/templates/default\n' "$TEST_REPO_ROOT"
printf 'ORIGINAL_BASEPATH=%q\n' "$original_basepath"
printf 'TARBALL_INCLUDE=yes\n'
printf 'TARBALL_SUFFIX=%q\n' '&"'\''.tar'
@@ -389,28 +524,28 @@ MAGICK
(
cd "$TEST_TMPDIR"
- PATH="$fake_bin:$PATH" "$PHOTOALBUM" --generate
+ PATH="$fake_bin:$PATH" "$TEST_PHOTOALBUM" --generate
)
page_html=$(<"$TEST_TMPDIR/dist/html/page-1.html")
view_html=$(<"$TEST_TMPDIR/dist/html/1-1.html")
- assert_contains "<title>$title_html</title>" "$page_html"
- assert_contains \
+ test::assert_contains "<title>$title_html</title>" "$page_html"
+ test::assert_contains \
"background-image: url(\"../blurs/$css_photo\");" \
"$page_html"
- assert_contains "name='$photo_html'" "$page_html"
- assert_contains "src='../thumbs/$photo_html'" "$page_html"
- assert_contains '&amp;&quot;&#39;.tar' "$page_html"
- assert_contains "href=\"page-1.html#$photo_html\"" "$view_html"
- assert_contains "href ='../photos/$photo_html'" "$view_html"
- assert_contains \
+ test::assert_contains "name='$photo_html'" "$page_html"
+ test::assert_contains "src='../thumbs/$photo_html'" "$page_html"
+ test::assert_contains '&amp;&quot;&#39;.tar' "$page_html"
+ test::assert_contains "href=\"page-1.html#$photo_html\"" "$view_html"
+ test::assert_contains "href ='../photos/$photo_html'" "$view_html"
+ test::assert_contains \
"href=\"$original_basepath_html/$photo_html\"" \
"$view_html"
- assert_not_contains '<title>A & "quoted" <title>' "$page_html"
- assert_not_contains "$photo_name" "$view_html"
+ test::assert_not_contains '<title>A & "quoted" <title>' "$page_html"
+ test::assert_not_contains "$photo_name" "$view_html"
- teardown
+ test::teardown
}
test_generate_preserves_space_filename_without_reprocessing() {
@@ -420,51 +555,36 @@ test_generate_preserves_space_filename_without_reprocessing() {
local photo_name
local second_output
- setup
+ test::setup
fake_bin="$TEST_TMPDIR/bin"
config_file="$TEST_TMPDIR/photoalbum.conf"
photo_name='a b.jpg'
- mkdir -p "$fake_bin" "$TEST_TMPDIR/incoming"
- cat > "$fake_bin/magick" <<'MAGICK'
-#!/usr/bin/env bash
-set -euo pipefail
-
-dest="${@: -1}"
-mkdir -p "$(dirname "$dest")"
-printf 'fake image\n' > "$dest"
-MAGICK
- chmod 0755 "$fake_bin/magick"
+ test::install_fake_imagemagick "$fake_bin"
+ mkdir -p "$TEST_TMPDIR/incoming"
printf 'fake image\n' > "$TEST_TMPDIR/incoming/$photo_name"
-
- {
- printf 'TITLE=%q\n' 'Space test'
- printf 'THUMBHEIGHT=30\n'
- printf 'HEIGHT=120\n'
- printf 'MAXPREVIEWS=40\n'
- printf 'INCOMING_DIR=%q/incoming\n' "$TEST_TMPDIR"
- printf 'DIST_DIR=%q/dist\n' "$TEST_TMPDIR"
- printf 'TEMPLATE_DIR=%q/share/templates/default\n' "$REPO_ROOT"
- printf 'TARBALL_INCLUDE=no\n'
- } > "$config_file"
+ test::write_album_config \
+ "$config_file" "$TEST_TMPDIR/incoming" "$TEST_TMPDIR/dist" \
+ 'Space test' 40
first_output=$(
cd "$TEST_TMPDIR"
- PATH="$fake_bin:$PATH" "$PHOTOALBUM" --generate
+ PATH="$fake_bin:$PATH" "$TEST_PHOTOALBUM" --generate
)
second_output=$(
cd "$TEST_TMPDIR"
- PATH="$fake_bin:$PATH" "$PHOTOALBUM" --generate
+ PATH="$fake_bin:$PATH" "$TEST_PHOTOALBUM" --generate
)
- test -f "$TEST_TMPDIR/dist/photos/$photo_name"
- test ! -e "$TEST_TMPDIR/dist/photos/a_b.jpg"
- assert_contains "Processing $photo_name to" "$first_output"
- assert_contains "Already exists: $TEST_TMPDIR/dist/photos/$photo_name" \
+ test::assert_file_exists "$TEST_TMPDIR/dist/photos/$photo_name"
+ test::assert_path_absent "$TEST_TMPDIR/dist/photos/a_b.jpg"
+ test::assert_contains "Processing $photo_name to" "$first_output"
+ test::assert_contains \
+ "Already exists: $TEST_TMPDIR/dist/photos/$photo_name" \
"$second_output"
- assert_not_contains "Processing $photo_name to" "$second_output"
+ test::assert_not_contains "Processing $photo_name to" "$second_output"
- teardown
+ test::teardown
}
test_generate_handles_space_and_underscore_names_distinctly() {
@@ -472,103 +592,157 @@ test_generate_handles_space_and_underscore_names_distinctly() {
local fake_bin
local page_html
- setup
+ test::setup
fake_bin="$TEST_TMPDIR/bin"
config_file="$TEST_TMPDIR/photoalbum.conf"
- mkdir -p "$fake_bin" "$TEST_TMPDIR/incoming"
- cat > "$fake_bin/magick" <<'MAGICK'
-#!/usr/bin/env bash
-set -euo pipefail
-
-dest="${@: -1}"
-mkdir -p "$(dirname "$dest")"
-printf 'fake image\n' > "$dest"
-MAGICK
- chmod 0755 "$fake_bin/magick"
+ test::install_fake_imagemagick "$fake_bin"
+ mkdir -p "$TEST_TMPDIR/incoming"
printf 'fake image\n' > "$TEST_TMPDIR/incoming/a b.jpg"
printf 'fake image\n' > "$TEST_TMPDIR/incoming/a_b.jpg"
-
- {
- printf 'TITLE=%q\n' 'Collision test'
- printf 'THUMBHEIGHT=30\n'
- printf 'HEIGHT=120\n'
- printf 'MAXPREVIEWS=40\n'
- printf 'INCOMING_DIR=%q/incoming\n' "$TEST_TMPDIR"
- printf 'DIST_DIR=%q/dist\n' "$TEST_TMPDIR"
- printf 'TEMPLATE_DIR=%q/share/templates/default\n' "$REPO_ROOT"
- printf 'TARBALL_INCLUDE=no\n'
- } > "$config_file"
+ test::write_album_config \
+ "$config_file" "$TEST_TMPDIR/incoming" "$TEST_TMPDIR/dist" \
+ 'Collision test' 40
(
cd "$TEST_TMPDIR"
- PATH="$fake_bin:$PATH" "$PHOTOALBUM" --generate
+ PATH="$fake_bin:$PATH" "$TEST_PHOTOALBUM" --generate
)
page_html=$(<"$TEST_TMPDIR/dist/html/page-1.html")
- test -f "$TEST_TMPDIR/dist/photos/a b.jpg"
- test -f "$TEST_TMPDIR/dist/photos/a_b.jpg"
- test -f "$TEST_TMPDIR/dist/thumbs/a b.jpg"
- test -f "$TEST_TMPDIR/dist/thumbs/a_b.jpg"
- assert_contains "src='../thumbs/a b.jpg'" "$page_html"
- assert_contains "src='../thumbs/a_b.jpg'" "$page_html"
+ test::assert_file_exists "$TEST_TMPDIR/dist/photos/a b.jpg"
+ test::assert_file_exists "$TEST_TMPDIR/dist/photos/a_b.jpg"
+ test::assert_file_exists "$TEST_TMPDIR/dist/thumbs/a b.jpg"
+ test::assert_file_exists "$TEST_TMPDIR/dist/thumbs/a_b.jpg"
+ test::assert_contains "src='../thumbs/a b.jpg'" "$page_html"
+ test::assert_contains "src='../thumbs/a_b.jpg'" "$page_html"
- teardown
+ test::teardown
}
-test_positional_commands_fail() {
- assert_failure 'positional clean is rejected' "$PHOTOALBUM" clean
- assert_failure 'positional generate is rejected' "$PHOTOALBUM" generate
- assert_failure 'positional version is rejected' "$PHOTOALBUM" version
+test_positional_commands_fail_without_deprecation() {
+ local output
+ local old_command
+ local -a old_commands=(clean generate version makemake)
+
+ for old_command in "${old_commands[@]}"; do
+ output=$(test::capture_failure_output "$TEST_PHOTOALBUM" "$old_command")
+ test::assert_contains 'Usage:' "$output"
+ test::assert_not_contains 'deprecat' "$output"
+ test::assert_not_contains 'makemake' "$output"
+ done
+}
+
+test_unknown_options_and_conflicting_actions_fail() {
+ test::assert_failure 'unsupported option is rejected' \
+ "$TEST_PHOTOALBUM" --unknown
+ test::assert_failure 'generate/clean conflict is rejected' \
+ "$TEST_PHOTOALBUM" --generate --clean
+ test::assert_failure 'generate/init conflict is rejected' \
+ "$TEST_PHOTOALBUM" --generate --init
+ test::assert_failure 'clean/version conflict is rejected' \
+ "$TEST_PHOTOALBUM" --clean --version
+}
+
+test_empty_args_fail() {
+ local output
+
+ output=$(test::capture_failure_output "$TEST_PHOTOALBUM")
+ test::assert_contains 'Usage:' "$output"
}
test_extra_args_fail() {
- assert_failure 'extra operand is rejected' "$PHOTOALBUM" --version extra
- assert_failure 'unsupported option is rejected' "$PHOTOALBUM" --unknown
- assert_failure 'missing config value is rejected' "$PHOTOALBUM" --config
- assert_failure 'missing incoming value is rejected' "$PHOTOALBUM" --incoming
- assert_failure 'missing title value is rejected' "$PHOTOALBUM" --title
- assert_failure \
+ test::assert_failure 'extra operand is rejected' "$TEST_PHOTOALBUM" --version extra
+ test::assert_failure \
'--incoming is rejected with --version' \
- "$PHOTOALBUM" --version --incoming /tmp/incoming
- assert_failure \
+ "$TEST_PHOTOALBUM" --version --incoming /tmp/incoming
+ test::assert_failure \
'--config is rejected with --init' \
- "$PHOTOALBUM" --init --config custom.conf
+ "$TEST_PHOTOALBUM" --init --config custom.conf
+}
+
+test_missing_option_values_fail() {
+ local option
+ local -a value_options=(
+ --config
+ --incoming
+ --dist
+ --template
+ --title
+ --height
+ --thumbheight
+ --maxpreviews
+ )
+
+ for option in "${value_options[@]}"; do
+ test::assert_failure "$option requires a value" "$TEST_PHOTOALBUM" "$option"
+ done
}
main() {
- trap teardown EXIT
-
- run_test '--version succeeds' test_version
- run_test '--init succeeds' test_init
- run_test '--clean succeeds' test_clean
- run_test '--clean --config succeeds' test_clean_with_config
- run_test '--clean --dist overrides config' test_clean_cli_dist_overrides_config
- run_test '--clean missing config fails clearly' test_clean_missing_config_fails
- run_test \
- '--generate --config reads selected config' \
+ trap test::teardown EXIT
+
+ test::run_case '--version succeeds' test_version
+ test::run_case '--init succeeds' test_init
+ test::run_case \
+ '--init refuses existing config without overwrite' \
+ test_init_existing_config_fails_without_overwrite
+ test::run_case '--clean succeeds' test_clean
+ test::run_case '--clean --config succeeds' test_clean_with_config
+ test::run_case '--clean --dist overrides config' \
+ test_clean_cli_dist_overrides_config
+ test::run_case \
+ 'missing config ignores legacy fallbacks' \
+ test_missing_config_fails_without_legacy_fallbacks
+ test::run_case \
+ '--generate --config reads selected config on failure path' \
test_generate_with_config_missing_incoming_fails
- run_test \
+ test::run_case \
+ '--generate --config succeeds without default config' \
+ test_generate_with_config_succeeds_without_default_config
+ test::run_case \
'--generate CLI options override config' \
test_generate_cli_overrides_config_values
- run_test \
+ test::run_case \
+ '--generate --shuffle overrides config' \
+ test_generate_shuffle_override_uses_random_order
+ test::run_case \
+ '--generate --no-shuffle overrides config' \
+ test_generate_no_shuffle_override_uses_sorted_order
+ test::run_case \
'--generate --tarball overrides config' \
test_generate_cli_tarball_overrides_config
- run_test \
+ test::run_case \
+ '--generate --no-tarball overrides config' \
+ test_generate_cli_no_tarball_overrides_config
+ test::run_case \
'--generate missing incoming fails' \
test_generate_missing_incoming_fails
- run_test \
+ test::run_case \
+ '--generate creates output structure and --clean removes it' \
+ test_integration_generates_album_outputs_and_cleans
+ test::run_case \
+ '--generate fails when ImageMagick is missing' \
+ test_generate_missing_imagemagick_fails
+ test::run_case \
'--generate escapes generated HTML values' \
test_generate_escapes_html_values
- run_test \
+ test::run_case \
'--generate preserves filenames with spaces without reprocessing' \
test_generate_preserves_space_filename_without_reprocessing
- run_test \
+ test::run_case \
'--generate handles spaces and underscores distinctly' \
test_generate_handles_space_and_underscore_names_distinctly
- run_test 'positional commands fail' test_positional_commands_fail
- run_test 'extra args fail' test_extra_args_fail
+ test::run_case \
+ 'positional commands fail without deprecation output' \
+ test_positional_commands_fail_without_deprecation
+ test::run_case \
+ 'unknown options and conflicting actions fail' \
+ test_unknown_options_and_conflicting_actions_fail
+ test::run_case 'empty args fail' test_empty_args_fail
+ test::run_case 'extra args fail' test_extra_args_fail
+ test::run_case 'missing option values fail' test_missing_option_values_fail
}
main "$@"
diff --git a/tests/helpers.sh b/tests/helpers.sh
new file mode 100755
index 0000000..8590b36
--- /dev/null
+++ b/tests/helpers.sh
@@ -0,0 +1,286 @@
+#!/usr/bin/env bash
+
+: "${TEST_REPO_ROOT:=${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}}"
+: "${TEST_PHOTOALBUM:=${PHOTOALBUM:-$TEST_REPO_ROOT/bin/photoalbum}}"
+: "${TEST_IMAGEMAGICK:=magick}"
+: "${TEST_TMPDIR:=}"
+
+test::setup() {
+ TEST_TMPDIR=$(mktemp -d)
+}
+
+test::teardown() {
+ if [ -n "$TEST_TMPDIR" ]; then
+ rm -rf "$TEST_TMPDIR"
+ TEST_TMPDIR=''
+ fi
+}
+
+test::run_case() {
+ local -r description="$1"; shift
+ local output_file
+ local -i status=0
+
+ output_file=$(mktemp)
+ set +e
+ ( set -euo pipefail; trap test::teardown EXIT; "$@" ) \
+ > "$output_file" 2>&1
+ status=$?
+ set -e
+
+ if (( status != 0 )); then
+ echo "FAIL: $description" >&2
+ cat "$output_file" >&2
+ rm -f "$output_file"
+ exit 1
+ fi
+
+ rm -f "$output_file"
+}
+
+test::assert_failure() {
+ local -r description="$1"; shift
+ local output_file
+ local -i status=0
+
+ output_file=$(mktemp)
+ set +e
+ "$@" > "$output_file" 2>&1
+ status=$?
+ set -e
+
+ if (( status == 0 )); then
+ echo "FAIL: $description" >&2
+ cat "$output_file" >&2
+ rm -f "$output_file"
+ exit 1
+ fi
+
+ rm -f "$output_file"
+}
+
+test::capture_failure_output() {
+ local output
+ local output_file
+ local -i status=0
+
+ output_file=$(mktemp)
+ set +e
+ "$@" > "$output_file" 2>&1
+ status=$?
+ set -e
+
+ if (( status == 0 )); then
+ echo 'FAIL: expected command to fail' >&2
+ output=$(<"$output_file")
+ echo "$output" >&2
+ rm -f "$output_file"
+ exit 1
+ fi
+
+ output=$(<"$output_file")
+ rm -f "$output_file"
+ printf '%s\n' "$output"
+}
+
+test::assert_contains() {
+ local -r needle="$1"; shift
+ local -r haystack="$1"; shift
+
+ if [[ "$haystack" != *"$needle"* ]]; then
+ echo "FAIL: expected output to contain $needle" >&2
+ echo "$haystack" >&2
+ exit 1
+ fi
+}
+
+test::assert_not_contains() {
+ local -r needle="$1"; shift
+ local -r haystack="$1"; shift
+
+ if [[ "$haystack" == *"$needle"* ]]; then
+ echo "FAIL: expected output not to contain $needle" >&2
+ echo "$haystack" >&2
+ exit 1
+ fi
+}
+
+test::assert_contains_before() {
+ local -r first="$1"; shift
+ local -r second="$1"; shift
+ local -r haystack="$1"; shift
+ local before_first
+
+ before_first="${haystack%%"$first"*}"
+
+ if [[ "$haystack" != *"$first"* \
+ || "$haystack" != *"$second"* \
+ || "$before_first" == *"$second"* ]]; then
+ echo "FAIL: expected $first to appear before $second" >&2
+ echo "$haystack" >&2
+ exit 1
+ fi
+}
+
+test::assert_file_exists() {
+ local -r path="$1"; shift
+
+ if [ ! -f "$path" ]; then
+ echo "FAIL: expected file $path to exist" >&2
+ exit 1
+ fi
+}
+
+test::assert_dir_exists() {
+ local -r path="$1"; shift
+
+ if [ ! -d "$path" ]; then
+ echo "FAIL: expected directory $path to exist" >&2
+ exit 1
+ fi
+}
+
+test::assert_path_absent() {
+ local -r path="$1"; shift
+
+ if [ -e "$path" ]; then
+ echo "FAIL: expected $path to be absent" >&2
+ exit 1
+ fi
+}
+
+test::assert_find_count() {
+ local -r expected="$1"; shift
+ local -r dir="$1"; shift
+ local -r name="$1"; shift
+ local actual
+
+ actual=$(find "$dir" -maxdepth 1 -type f -name "$name" | wc -l)
+
+ if [ "$actual" -ne "$expected" ]; then
+ echo "FAIL: expected $expected files matching $name in $dir" >&2
+ echo "found $actual" >&2
+ exit 1
+ fi
+}
+
+test::run_photoalbum() {
+ "$TEST_PHOTOALBUM" "$@" 2>&1
+}
+
+test::install_fake_imagemagick() {
+ local -r bin_dir="$1"; shift
+
+ mkdir -p "$bin_dir"
+ TEST_IMAGEMAGICK="$bin_dir/magick"
+
+ cat > "$bin_dir/magick" <<'MAGICK'
+#!/usr/bin/env bash
+set -euo pipefail
+
+dest="${@: -1}"
+mkdir -p "$(dirname "$dest")"
+{
+ printf 'fake image\n'
+ printf 'args:'
+ printf ' %q' "$@"
+ printf '\n'
+} > "$dest"
+MAGICK
+ chmod 0755 "$bin_dir/magick"
+ cp "$bin_dir/magick" "$bin_dir/convert"
+}
+
+test::install_sort_spy() {
+ local -r bin_dir="$1"; shift
+ local real_sort
+
+ mkdir -p "$bin_dir"
+ real_sort=$(command -v sort)
+
+ cat > "$bin_dir/sort" <<SORT
+#!/usr/bin/env bash
+set -euo pipefail
+
+input_file=\$(mktemp)
+cat > "\$input_file"
+
+if [[ -n "\${TEST_SORT_LOG:-}" && " \$* " == *" -R "* ]] \\
+ && grep -q '[.]jpg$' "\$input_file"; then
+ printf 'photo-shuffle %s\n' "\$*" >> "\$TEST_SORT_LOG"
+ tac "\$input_file"
+ rm -f "\$input_file"
+ exit 0
+fi
+
+"$real_sort" "\$@" "\$input_file"
+rm -f "\$input_file"
+SORT
+ chmod 0755 "$bin_dir/sort"
+}
+
+test::install_coreutils_without_imagemagick() {
+ local -r bin_dir="$1"; shift
+ local command_path
+ local name
+ local -a names=(
+ basename
+ bash
+ date
+ dirname
+ find
+ grep
+ mkdir
+ mktemp
+ rm
+ sed
+ sort
+ tac
+ tar
+ wc
+ )
+
+ mkdir -p "$bin_dir"
+
+ for name in "${names[@]}"; do
+ command_path=$(command -v "$name")
+ ln -s "$command_path" "$bin_dir/$name"
+ done
+}
+
+test::generate_fixture_images() {
+ local -r incoming_dir="$1"; shift
+
+ mkdir -p "$incoming_dir"
+ "$TEST_IMAGEMAGICK" -size 160x90 xc:red \
+ "$incoming_dir/01-landscape.jpg"
+ "$TEST_IMAGEMAGICK" -size 90x160 xc:blue \
+ "$incoming_dir/02-portrait.jpg"
+ "$TEST_IMAGEMAGICK" -size 120x120 xc:green \
+ "$incoming_dir/03-square.jpg"
+ "$TEST_IMAGEMAGICK" -size 100x80 xc:yellow \
+ "$incoming_dir/04 filename with spaces.jpg"
+ "$TEST_IMAGEMAGICK" -size 140x90 xc:purple \
+ "$incoming_dir/05-extra.jpg"
+ "$TEST_IMAGEMAGICK" -size 150x90 xc:orange \
+ "$incoming_dir/06-extra.jpg"
+}
+
+test::write_album_config() {
+ local -r config_file="$1"; shift
+ local -r incoming_dir="$1"; shift
+ local -r dist_dir="$1"; shift
+ local -r title="$1"; shift
+ local -r maxpreviews="$1"; shift
+
+ {
+ printf 'TITLE=%q\n' "$title"
+ printf 'THUMBHEIGHT=30\n'
+ printf 'HEIGHT=120\n'
+ printf 'MAXPREVIEWS=%q\n' "$maxpreviews"
+ printf 'INCOMING_DIR=%q\n' "$incoming_dir"
+ printf 'DIST_DIR=%q\n' "$dist_dir"
+ printf 'TEMPLATE_DIR=%q/share/templates/default\n' "$TEST_REPO_ROOT"
+ printf 'TARBALL_INCLUDE=no\n'
+ } > "$config_file"
+}