tests: remove acquire-lock newline termination
[pve-common.git] / test / lock_file.pl
1 #!/usr/bin/perl
2
3 use lib '../src';
4 use strict;
5 use warnings;
6
7 use Socket;
8 use POSIX (); # don't import assert()
9
10 use PVE::Tools 'lock_file_full';
11
12 my $name = "test.lockfile.$$-";
13
14 END {
15         system("rm $name*");
16 };
17
18 # Utilities:
19
20 sub forked($$) {
21     my ($code1, $code2) = @_;
22
23     pipe(my $except_r, my $except_w) or die "pipe: $!\n";
24
25     my $pid = fork();
26     die "fork failed: $!\n" if !defined($pid);
27
28     if ($pid == 0) {
29         close($except_r);
30         eval { $code1->() };
31         if ($@) {
32             print {$except_w} $@;
33             $except_w->flush();
34             POSIX::_exit(1);
35         }
36         POSIX::_exit(0);
37     }
38     close($except_w);
39
40     eval { $code2->() };
41     my $err = $@;
42     if ($err) {
43         kill(15, $pid);
44     } else {
45         my $err = do { local $/ = undef; <$except_r> };
46     }
47     die "interrupted\n" if waitpid($pid, 0) != $pid;
48     die $err if $err;
49
50     # Check exit code:
51     my $status = POSIX::WEXITSTATUS($?);
52     if ($? == -1) {
53         die "failed to execute\n";
54     } elsif (POSIX::WIFSIGNALED($?)) {
55         my $sig = POSIX::WTERMSIG($?);
56         die "got signal $sig\n";
57     } elsif ($status != 0) {
58         die "exit code $status\n";
59     }
60 }
61
62 # Book-keeping:
63
64 my %_ran;
65 sub new {
66         %_ran = ();
67 }
68 sub ran {
69         my ($what) = @_;
70         $_ran{$what} = 1;
71 }
72 sub assert {
73         my ($what) = @_;
74         die "code didn't run: $what\n" if !$_ran{$what};
75 }
76 sub assert_not {
77         my ($what) = @_;
78         die "code shouldn't have run: $what\n" if $_ran{$what};
79 }
80
81 # Regular lock:
82 new();
83 lock_file_full($name, 10, 0, sub { ran('single lock') });
84 assert('single lock');
85
86 # Lock multiple times in a row:
87 new();
88 lock_file_full($name, 10, 0, sub { ran('lock A') });
89 assert('lock A');
90 lock_file_full($name, 10, 0, sub { ran('lock B') });
91 assert('lock B');
92
93 # Nested lock:
94 new();
95 lock_file_full($name, 10, 0, sub {
96         ran('lock A');
97         lock_file_full($name, 10, 0, sub { ran('lock B') });
98         assert('lock B');
99         ran('lock C');
100 });
101 assert('lock A');
102 assert('lock B');
103 assert('lock C');
104
105 # Independent locks:
106 new();
107 lock_file_full($name, 10, 0, sub {
108         ran('lock A');
109         # locks file "${name}2"
110         lock_file_full($name.2, 10, 0, sub { ran('lock B') });
111         assert('lock B');
112         ran('lock C');
113 });
114 assert('lock A');
115 assert('lock B');
116 assert('lock C');
117
118 # Does it actually lock? (shared=0)
119 # Can we get two simultaneous shared locks? (shared=1)
120 sub forktest1($) {
121     my ($shared) = @_;
122     new();
123     # socket pair for synchronization
124     socketpair(my $fmain, my $fother, AF_UNIX, SOCK_STREAM, PF_UNSPEC)
125         or die "socketpair(): $!\n";
126     forked sub {
127             # other side
128             close($fmain);
129             my $line;
130             lock_file_full($name, 60, $shared, sub {
131                 ran('other side');
132                 # tell parent we've acquired the lock
133                 print {$fother} "1\n";
134                 $fother->flush();
135                 # wait for parent to be done trying to lock
136                 $line = <$fother>;
137             });
138             die $@ if $@;
139             die "parent failed\n" if !$line || $line ne "2\n";
140             assert('other side');
141     }, sub {
142             # main process
143             # Wait for our child to lock:
144             close($fother);
145             my $line = <$fmain>;
146             die "child failed to acquire a lock\n" if !$line || $line ne "1\n";
147             lock_file_full($name, 1, $shared, sub {
148                 ran('local side');
149             });
150             if ($shared) {
151                 assert('local side');
152             } else {
153                 assert_not('local side');
154             }
155             print {$fmain} "2\n";
156             $fmain->flush();
157     };
158     close($fmain);
159 }
160 forktest1(0);
161 forktest1(1);