aboutsummaryrefslogtreecommitdiff
path: root/tests/cases/test_locking.sh
blob: 4667d5841ef46be5ccdbffea862b5a969164c081 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#!/usr/bin/env bash
# ==============================================================================
# Concurrency Locking Tests
# ==============================================================================
# rsyncshot acquires an exclusive lock before the backup phase. These tests hold
# the lock in the test process (a separate open file description), then run the
# script with LOCKFILE pointed at the same file, so the script's flock -n fails
# exactly as a second concurrent backup would.

source "$(dirname "${BASH_SOURCE[0]}")/../lib/test_helpers.sh"

# ------------------------------------------------------------------------------
# A real backup refuses to start while another holds the lock
# ------------------------------------------------------------------------------
test_refuses_when_lock_held() {
    setup_test_env
    create_test_config >/dev/null
    create_test_includes >/dev/null
    create_test_excludes >/dev/null

    local lock="$TEST_DIR/rsyncshot.lock"
    exec 9>"$lock"
    if ! flock -n 9; then
        echo "FAIL: test could not acquire the lock to set up the scenario"
        exec 9>&-
        teardown_test_env
        return 1
    fi

    local output rc
    output=$(INSTALLHOME="$TEST_CONFIG_DIR" LOCKFILE="$lock" RSYNCSHOT_SKIP_MOUNT_CHECK=1 "$SCRIPT_PATH" manual 1 2>&1)
    rc=$?
    exec 9>&-   # release the lock for later tests

    assert_exit_code 1 "$rc" "should exit 1 when another instance holds the lock" || { teardown_test_env; return 1; }
    assert_contains "$output" "already running" "should report the lock contention" || { teardown_test_env; return 1; }

    teardown_test_env
}

# ------------------------------------------------------------------------------
# A dryrun is read-only and must NOT be blocked by a held lock
# ------------------------------------------------------------------------------
test_dryrun_not_blocked_by_lock() {
    setup_test_env
    create_test_config >/dev/null
    create_test_includes >/dev/null
    create_test_excludes >/dev/null

    local lock="$TEST_DIR/rsyncshot.lock"
    exec 9>"$lock"
    if ! flock -n 9; then
        echo "FAIL: test could not acquire the lock to set up the scenario"
        exec 9>&-
        teardown_test_env
        return 1
    fi

    local rc
    INSTALLHOME="$TEST_CONFIG_DIR" LOCKFILE="$lock" RSYNCSHOT_SKIP_MOUNT_CHECK=1 "$SCRIPT_PATH" dryrun manual 1 >/dev/null 2>&1
    rc=$?
    exec 9>&-

    assert_exit_code 0 "$rc" "dryrun should not be blocked by a held lock" || { teardown_test_env; return 1; }

    teardown_test_env
}

# ------------------------------------------------------------------------------
# Two sequential real backups both succeed (lock is released on exit)
# ------------------------------------------------------------------------------
test_sequential_runs_each_acquire_lock() {
    setup_test_env
    create_test_config >/dev/null
    create_test_includes >/dev/null
    create_test_excludes >/dev/null

    local lock="$TEST_DIR/rsyncshot.lock"
    INSTALLHOME="$TEST_CONFIG_DIR" LOCKFILE="$lock" RSYNCSHOT_SKIP_MOUNT_CHECK=1 "$SCRIPT_PATH" manual 2 >/dev/null 2>&1
    local rc1=$?
    INSTALLHOME="$TEST_CONFIG_DIR" LOCKFILE="$lock" RSYNCSHOT_SKIP_MOUNT_CHECK=1 "$SCRIPT_PATH" manual 2 >/dev/null 2>&1
    local rc2=$?

    assert_exit_code 0 "$rc1" "first run should succeed and release the lock" || { teardown_test_env; return 1; }
    assert_exit_code 0 "$rc2" "second run should acquire the freed lock and succeed" || { teardown_test_env; return 1; }

    teardown_test_env
}

# ------------------------------------------------------------------------------
# Run tests
# ------------------------------------------------------------------------------
run_locking_tests() {
    echo ""
    echo "Running locking tests..."
    echo "------------------------------------------------------------"

    run_test "refuses to run when the lock is held" test_refuses_when_lock_held
    run_test "dryrun is not blocked by the lock" test_dryrun_not_blocked_by_lock
    run_test "sequential runs each acquire the lock" test_sequential_runs_each_acquire_lock
}

# Run if executed directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    run_locking_tests
    print_summary
fi