diff options
Diffstat (limited to 'test/units/TEST-45-TIMEDATE.sh')
-rwxr-xr-x | test/units/TEST-45-TIMEDATE.sh | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/test/units/TEST-45-TIMEDATE.sh b/test/units/TEST-45-TIMEDATE.sh new file mode 100755 index 0000000..dff3ed0 --- /dev/null +++ b/test/units/TEST-45-TIMEDATE.sh @@ -0,0 +1,408 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later + +set -eux +set -o pipefail + +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +testcase_timedatectl() { + timedatectl --no-pager --help + timedatectl --version + + timedatectl + timedatectl --no-ask-password + timedatectl status --machine=testuser@.host + timedatectl status + timedatectl show + timedatectl show --all + timedatectl show -p NTP + timedatectl show -p NTP --value + timedatectl list-timezones + + if ! systemd-detect-virt -qc; then + systemctl enable --runtime --now systemd-timesyncd + timedatectl timesync-status + timedatectl show-timesync + fi +} + +restore_timezone() { + if [[ -f /tmp/timezone.bak ]]; then + mv /tmp/timezone.bak /etc/timezone + else + rm -f /etc/timezone + fi +} + +testcase_timezone() { + local ORIG_TZ= + + # Debian/Ubuntu specific file + if [[ -f /etc/timezone ]]; then + mv /etc/timezone /tmp/timezone.bak + fi + + trap restore_timezone RETURN + + if [[ -L /etc/localtime ]]; then + ORIG_TZ=$(readlink /etc/localtime | sed 's#^.*zoneinfo/##') + echo "original tz: $ORIG_TZ" + fi + + echo 'timedatectl works' + assert_in "Local time:" "$(timedatectl --no-pager)" + + echo 'change timezone' + assert_eq "$(timedatectl --no-pager set-timezone Europe/Kyiv 2>&1)" "" + assert_eq "$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')" "Europe/Kyiv" + if [[ -f /etc/timezone ]]; then + assert_eq "$(cat /etc/timezone)" "Europe/Kyiv" + fi + assert_in "Time zone: Europe/Kyiv \(EES*T, \+0[0-9]00\)" "$(timedatectl)" + + if [[ -n "$ORIG_TZ" ]]; then + echo 'reset timezone to original' + assert_eq "$(timedatectl set-timezone "$ORIG_TZ" 2>&1)" "" + assert_eq "$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')" "$ORIG_TZ" + if [[ -f /etc/timezone ]]; then + assert_eq "$(cat /etc/timezone)" "$ORIG_TZ" + fi + fi +} + +restore_adjtime() { + if [[ -e /etc/adjtime.bak ]]; then + mv /etc/adjtime.bak /etc/adjtime + else + rm /etc/adjtime + fi +} + +check_adjtime_not_exist() { + if [[ -e /etc/adjtime ]]; then + echo "/etc/adjtime unexpectedly exists." >&2 + exit 1 + fi +} + +testcase_adjtime() { + # test setting UTC vs. LOCAL in /etc/adjtime + if [[ -e /etc/adjtime ]]; then + mv /etc/adjtime /etc/adjtime.bak + fi + + trap restore_adjtime RETURN + + echo 'no adjtime file' + rm -f /etc/adjtime + timedatectl set-local-rtc 0 + check_adjtime_not_exist + timedatectl set-local-rtc 1 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +LOCAL" + timedatectl set-local-rtc 0 + check_adjtime_not_exist + + echo 'UTC set in adjtime file' + printf '0.0 0 0\n0\nUTC\n' >/etc/adjtime + timedatectl set-local-rtc 0 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +UTC" + timedatectl set-local-rtc 1 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +LOCAL" + + echo 'non-zero values in adjtime file' + printf '0.1 123 0\n0\nLOCAL\n' >/etc/adjtime + timedatectl set-local-rtc 0 + assert_eq "$(cat /etc/adjtime)" "0.1 123 0 +0 +UTC" + timedatectl set-local-rtc 1 + assert_eq "$(cat /etc/adjtime)" "0.1 123 0 +0 +LOCAL" + + echo 'fourth line adjtime file' + printf '0.0 0 0\n0\nLOCAL\nsomethingelse\n' >/etc/adjtime + timedatectl set-local-rtc 0 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +UTC +somethingelse" + timedatectl set-local-rtc 1 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +LOCAL +somethingelse" + + echo 'no final newline in adjtime file' + printf '0.0 0 0\n0\nUTC' >/etc/adjtime + timedatectl set-local-rtc 0 + check_adjtime_not_exist + printf '0.0 0 0\n0\nUTC' >/etc/adjtime + timedatectl set-local-rtc 1 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +LOCAL" + + echo 'only one line in adjtime file' + printf '0.0 0 0\n' >/etc/adjtime + timedatectl set-local-rtc 0 + check_adjtime_not_exist + printf '0.0 0 0\n' >/etc/adjtime + timedatectl set-local-rtc 1 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +LOCAL" + + echo 'only one line in adjtime file, no final newline' + printf '0.0 0 0' >/etc/adjtime + timedatectl set-local-rtc 0 + check_adjtime_not_exist + printf '0.0 0 0' >/etc/adjtime + timedatectl set-local-rtc 1 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +LOCAL" + + echo 'only two lines in adjtime file' + printf '0.0 0 0\n0\n' >/etc/adjtime + timedatectl set-local-rtc 0 + check_adjtime_not_exist + printf '0.0 0 0\n0\n' >/etc/adjtime + timedatectl set-local-rtc 1 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +LOCAL" + + echo 'only two lines in adjtime file, no final newline' + printf '0.0 0 0\n0' >/etc/adjtime + timedatectl set-local-rtc 0 + check_adjtime_not_exist + printf '0.0 0 0\n0' >/etc/adjtime + timedatectl set-local-rtc 1 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +LOCAL" + + echo 'unknown value in 3rd line of adjtime file' + printf '0.0 0 0\n0\nFOO\n' >/etc/adjtime + timedatectl set-local-rtc 0 + check_adjtime_not_exist + printf '0.0 0 0\n0\nFOO\n' >/etc/adjtime + timedatectl set-local-rtc 1 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +LOCAL" +} + +assert_ntp() { + local value="${1:?}" + + for _ in {0..9}; do + [[ "$(busctl get-property org.freedesktop.timedate1 /org/freedesktop/timedate1 org.freedesktop.timedate1 NTP)" == "b $value" ]] && return 0 + sleep .5 + done + + return 1 +} + +assert_timedated_signal() { + local timestamp="${1:?}" + local value="${2:?}" + local args=(-q -n 1 --since="$timestamp" -p info _SYSTEMD_UNIT="busctl-monitor.service") + + journalctl --sync + + for _ in {0..9}; do + if journalctl "${args[@]}" --grep .; then + [[ "$(journalctl "${args[@]}" -o cat | jq -r '.payload.data[1].NTP.data')" == "$value" ]]; + return 0 + fi + + sleep .5 + done + + return 1 +} + +assert_timesyncd_state() { + local state="${1:?}" + + for _ in {0..9}; do + [[ "$(systemctl show systemd-timesyncd.service -P ActiveState)" == "$state" ]] && return 0 + sleep .5 + done + + return 1 +} + +testcase_ntp() { + # timesyncd has ConditionVirtualization=!container by default; drop/mock that for testing + if systemd-detect-virt --container --quiet; then + systemctl disable --quiet --now systemd-timesyncd + mkdir -p /run/systemd/system/systemd-timesyncd.service.d + cat >/run/systemd/system/systemd-timesyncd.service.d/container.conf <<EOF +[Unit] +ConditionVirtualization= + +[Service] +Type=simple +AmbientCapabilities= +ExecStart= +ExecStart=sleep infinity +EOF + systemctl daemon-reload + fi + + systemd-run --unit busctl-monitor.service --service-type=notify \ + busctl monitor --json=short --match="type=signal,sender=org.freedesktop.timedate1,member=PropertiesChanged,path=/org/freedesktop/timedate1" + + : 'Disable NTP' + ts="$(date +"%F %T.%6N")" + timedatectl set-ntp false + assert_timedated_signal "$ts" "false" + assert_timesyncd_state "inactive" + assert_ntp "false" + assert_rc 3 systemctl is-active --quiet systemd-timesyncd + + : 'Enable NTP' + ts="$(date +"%F %T.%6N")" + timedatectl set-ntp true + assert_timedated_signal "$ts" "true" + assert_ntp "true" + assert_timesyncd_state "active" + assert_rc 0 systemctl is-active --quiet systemd-timesyncd + + : 'Re-disable NTP' + ts="$(date +"%F %T.%6N")" + timedatectl set-ntp false + assert_timedated_signal "$ts" "false" + assert_ntp "false" + assert_rc 3 systemctl is-active --quiet systemd-timesyncd + + systemctl stop busctl-monitor.service + rm -rf /run/systemd/system/systemd-timesyncd.service.d/ + systemctl daemon-reload +} + +assert_timesyncd_signal() { + local timestamp="${1:?}" + local property="${2:?}" + local value="${3:?}" + local args=(-q --since="$timestamp" -p info _SYSTEMD_UNIT="busctl-monitor.service") + + journalctl --sync + + for _ in {0..9}; do + if journalctl "${args[@]}" --grep .; then + [[ "$(journalctl "${args[@]}" -o cat | jq -r ".payload.data[1].$property.data | join(\" \")")" == "$value" ]]; + return 0 + fi + + sleep .5 + done + + return 1 +} + +assert_networkd_ntp() { + local interface="${1:?}" + local value="${2:?}" + # Go through the array of NTP servers and for each entry do: + # - if the entry is an IPv4 address, join the Address array into a dot separated string + # - if the entry is a server address, select it unchanged + # These steps produce an array of strings, that is then joined into a space-separated string + # Note: this doesn't support IPv6 addresses, since converting them to a string is a bit more + # involved than a simple join(), but let's leave that to another time + local expr='[.NTP[] | (select(.Family == 2).Address | join(".")), select(has("Server")).Server] | join(" ")' + + [[ "$(networkctl status "$interface" --json=short | jq -r "$expr")" == "$value" ]] +} + +testcase_timesyncd() { + if systemd-detect-virt -cq; then + echo "This test case requires a VM, skipping..." + return 0 + fi + + if ! command -v networkctl >/dev/null; then + echo "This test requires systemd-networkd, skipping..." + return 0 + fi + + # Create a dummy interface managed by networkd, so we can configure link NTP servers + mkdir -p /run/systemd/network/ + cat >/etc/systemd/network/10-ntp99.netdev <<EOF +[NetDev] +Name=ntp99 +Kind=dummy +EOF + cat >/etc/systemd/network/10-ntp99.network <<EOF +[Match] +Name=ntp99 + +[Network] +Address=10.0.0.1/24 +EOF + + systemctl unmask systemd-timesyncd systemd-networkd + systemctl restart systemd-timesyncd + systemctl restart systemd-networkd + networkctl status ntp99 + + systemd-run --unit busctl-monitor.service --service-type=notify \ + busctl monitor --json=short --match="type=signal,sender=org.freedesktop.timesync1,member=PropertiesChanged,path=/org/freedesktop/timesync1" + + # LinkNTPServers + # + # Single IP + ts="$(date +"%F %T.%6N")" + timedatectl ntp-servers ntp99 10.0.0.1 + assert_networkd_ntp ntp99 10.0.0.1 + assert_timesyncd_signal "$ts" LinkNTPServers 10.0.0.1 + # Setting NTP servers to the same value shouldn't emit a PropertiesChanged signal + ts="$(date +"%F %T.%6N")" + timedatectl ntp-servers ntp99 10.0.0.1 + assert_networkd_ntp ntp99 10.0.0.1 + (! assert_timesyncd_signal "$ts" LinkNTPServers 10.0.0.1) + # Multiple IPs + ts="$(date +"%F %T.%6N")" + timedatectl ntp-servers ntp99 10.0.0.1 192.168.0.99 + assert_networkd_ntp ntp99 "10.0.0.1 192.168.0.99" + assert_timesyncd_signal "$ts" LinkNTPServers "10.0.0.1 192.168.0.99" + # Multiple IPs + servers + ts="$(date +"%F %T.%6N")" + timedatectl ntp-servers ntp99 10.0.0.1 192.168.0.99 foo.localhost foo 10.11.12.13 + assert_networkd_ntp ntp99 "10.0.0.1 192.168.0.99 foo.localhost foo 10.11.12.13" + assert_timesyncd_signal "$ts" LinkNTPServers "10.0.0.1 192.168.0.99 foo.localhost foo 10.11.12.13" + + # RuntimeNTPServers + # + # There's no user-facing API that allows changing this property (afaik), so let's + # call SetRuntimeNTPServers() directly to test things out. The inner workings should + # be exactly the same as in the previous case, so do just one test to make sure + # things work + ts="$(date +"%F %T.%6N")" + busctl call org.freedesktop.timesync1 /org/freedesktop/timesync1 org.freedesktop.timesync1.Manager \ + SetRuntimeNTPServers as 4 "10.0.0.1" foo "192.168.99.1" bar + servers="$(busctl get-property org.freedesktop.timesync1 /org/freedesktop/timesync1 org.freedesktop.timesync1.Manager RuntimeNTPServers)" + [[ "$servers" == 'as 4 "10.0.0.1" "foo" "192.168.99.1" "bar"' ]] + assert_timesyncd_signal "$ts" RuntimeNTPServers "10.0.0.1 foo 192.168.99.1 bar" + + # Cleanup + systemctl stop systemd-networkd systemd-timesyncd + rm -f /run/systemd/network/ntp99.* +} + +run_testcases + +touch /testok |