--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+get_first_boot_id() {
+ journalctl -b "${1:?}" -o json | jq -sr '.[0]._BOOT_ID'
+}
+
+get_last_boot_id() {
+ journalctl -b "${1:?}" -o json -n 1 | jq -r '._BOOT_ID'
+}
+
+get_first_timestamp() {
+ journalctl -b "${1:?}" -o json | jq -sr '.[0].__REALTIME_TIMESTAMP'
+}
+
+get_last_timestamp() {
+ journalctl -b "${1:?}" -o json -n 1 | jq -r '.__REALTIME_TIMESTAMP'
+}
+
+# Issue: #29275, second part
+# Now let's check if the boot entries are in the correct/expected order
+index=0
+SYSTEMD_LOG_LEVEL=debug journalctl --list-boots
+journalctl --list-boots -o json | jq -r '.[] | [.index, .boot_id, .first_entry, .last_entry] | @tsv' |
+ while read -r offset boot_id first_ts last_ts; do
+ : "Boot #$((++index)) ($offset) with ID $boot_id"
+
+ # Try the "regular" (non-json) variants first, as they provide a helpful
+ # error message if something is not right
+ SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$index"
+ SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$offset"
+ SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$boot_id"
+
+ # Check the boot ID of the first entry
+ entry_boot_id="$(get_first_boot_id "$index")"
+ assert_eq "$entry_boot_id" "$boot_id"
+ entry_boot_id="$(get_first_boot_id "$offset")"
+ assert_eq "$entry_boot_id" "$boot_id"
+ entry_boot_id="$(get_first_boot_id "$boot_id")"
+ assert_eq "$entry_boot_id" "$boot_id"
+
+ # Check the timestamp of the first entry
+ entry_ts="$(get_first_timestamp "$index")"
+ assert_eq "$entry_ts" "$first_ts"
+ entry_ts="$(get_first_timestamp "$offset")"
+ assert_eq "$entry_ts" "$first_ts"
+ entry_ts="$(get_first_timestamp "$boot_id")"
+ assert_eq "$entry_ts" "$first_ts"
+
+ # Check the boot ID of the last entry
+ entry_boot_id="$(get_last_boot_id "$index")"
+ assert_eq "$entry_boot_id" "$boot_id"
+ entry_boot_id="$(get_last_boot_id "$offset")"
+ assert_eq "$entry_boot_id" "$boot_id"
+ entry_boot_id="$(get_last_boot_id "$boot_id")"
+ assert_eq "$entry_boot_id" "$boot_id"
+
+ # Check the timestamp of the last entry
+ if [[ "$offset" != "0" ]]; then
+ entry_ts="$(get_last_timestamp "$index")"
+ assert_eq "$entry_ts" "$last_ts"
+ entry_ts="$(get_last_timestamp "$offset")"
+ assert_eq "$entry_ts" "$last_ts"
+ entry_ts="$(get_last_timestamp "$boot_id")"
+ assert_eq "$entry_ts" "$last_ts"
+ fi
+ done
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+NUM_REBOOT=4
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+systemd-cat echo "Reboot count: $REBOOT_COUNT"
+systemd-cat journalctl --list-boots
+
+run_subtests
+
+if [[ "$REBOOT_COUNT" -lt "$NUM_REBOOT" ]]; then
+ systemctl_final reboot
+elif [[ "$REBOOT_COUNT" -gt "$NUM_REBOOT" ]]; then
+ assert_not_reached
+fi
+
+touch /testok
# Utility functions for shell tests
+# shellcheck disable=SC2034
+[[ -e /var/tmp/.systemd_reboot_count ]] && REBOOT_COUNT="$(</var/tmp/.systemd_reboot_count)" || REBOOT_COUNT=0
+
assert_true() {(
set +ex
fi
)}
-
assert_eq() {(
set +ex
assert_eq "$rc" "$exp"
)}
+assert_not_reached() {
+ echo >&2 "Code should not be reached at ${BASH_SOURCE[1]}:${BASH_LINENO[1]}, function ${FUNCNAME[1]}()"
+ exit 1
+}
+
run_and_grep() {(
set +ex
cp -a /testsuite-13-container-template/* "$root"
coverage_create_nspawn_dropin "$root"
}
+
+# Bump the reboot counter and call systemctl with the given arguments
+systemctl_final() {
+ local counter
+
+ if [[ $# -eq 0 ]]; then
+ echo >&2 "Missing arguments"
+ exit 1
+ fi
+
+ [[ -e /var/tmp/.systemd_reboot_count ]] && counter="$(</var/tmp/.systemd_reboot_count)" || counter=0
+ echo "$((counter + 1))" >/var/tmp/.systemd_reboot_count
+
+ systemctl "$@"
+}