git p4 test: use 'test_atexit' to kill p4d and the watchdog process
[git] / t / lib-git-p4.sh
1 #
2 # Library code for git p4 tests
3 #
4
5 # p4 tests never use the top-level repo; always build/clone into
6 # a subdirectory called "$git"
7 TEST_NO_CREATE_REPO=NoThanks
8
9 # Some operations require multiple attempts to be successful. Define
10 # here the maximal retry timeout in seconds.
11 RETRY_TIMEOUT=60
12
13 # Sometimes p4d seems to hang. Terminate the p4d process automatically after
14 # the defined timeout in seconds.
15 P4D_TIMEOUT=300
16
17 . ./test-lib.sh
18
19 if ! test_have_prereq PYTHON
20 then
21         skip_all='skipping git p4 tests; python not available'
22         test_done
23 fi
24 ( p4 -h && p4d -h ) >/dev/null 2>&1 || {
25         skip_all='skipping git p4 tests; no p4 or p4d'
26         test_done
27 }
28
29 # On cygwin, the NT version of Perforce can be used.  When giving
30 # it paths, either on the command-line or in client specifications,
31 # be sure to use the native windows form.
32 #
33 # Older versions of perforce were available compiled natively for
34 # cygwin.  Those do not accept native windows paths, so make sure
35 # not to convert for them.
36 native_path () {
37         path="$1" &&
38         if test_have_prereq CYGWIN && ! p4 -V | grep -q CYGWIN
39         then
40                 path=$(cygpath --windows "$path")
41         else
42                 path=$(test-tool path-utils real_path "$path")
43         fi &&
44         echo "$path"
45 }
46
47 # On Solaris the 'date +%s' function is not supported and therefore we
48 # need this replacement.
49 # Attention: This function is not safe again against time offset updates
50 # at runtime (e.g. via NTP). The 'clock_gettime(CLOCK_MONOTONIC)'
51 # function could fix that but it is not in Python until 3.3.
52 time_in_seconds () {
53         (cd / && "$PYTHON_PATH" -c 'import time; print(int(time.time()))')
54 }
55
56 test_set_port P4DPORT
57
58 P4PORT=localhost:$P4DPORT
59 P4CLIENT=client
60 P4USER=author
61 P4EDITOR=true
62 unset P4CHARSET
63 export P4PORT P4CLIENT P4USER P4EDITOR P4CHARSET
64
65 db="$TRASH_DIRECTORY/db"
66 cli="$TRASH_DIRECTORY/cli"
67 git="$TRASH_DIRECTORY/git"
68 pidfile="$TRASH_DIRECTORY/p4d.pid"
69
70 # Sometimes "prove" seems to hang on exit because p4d is still running
71 cleanup () {
72         if test -f "$pidfile"
73         then
74                 kill -9 $(cat "$pidfile") 2>/dev/null && exit 255
75         fi
76 }
77
78 # git p4 submit generates a temp file, which will
79 # not get cleaned up if the submission fails.  Don't
80 # clutter up /tmp on the test machine.
81 TMPDIR="$TRASH_DIRECTORY"
82 export TMPDIR
83
84 registered_stop_p4d_atexit_handler=
85 start_p4d () {
86         # One of the test scripts stops and then re-starts p4d.
87         # Don't register and then run the same atexit handlers several times.
88         if test -z "$registered_stop_p4d_atexit_handler"
89         then
90                 test_atexit 'kill_p4d; cleanup'
91                 registered_stop_p4d_atexit_handler=AlreadyDone
92         fi
93
94         mkdir -p "$db" "$cli" "$git" &&
95         rm -f "$pidfile" &&
96         (
97                 cd "$db" &&
98                 {
99                         p4d -q -p $P4DPORT "$@" &
100                         echo $! >"$pidfile"
101                 }
102         ) &&
103
104         # This gives p4d a long time to start up, as it can be
105         # quite slow depending on the machine.  Set this environment
106         # variable to something smaller to fail faster in, say,
107         # an automated test setup.  If the p4d process dies, that
108         # will be caught with the "kill -0" check below.
109         i=${P4D_START_PATIENCE:-300}
110         pid=$(cat "$pidfile")
111
112         timeout=$(($(time_in_seconds) + $P4D_TIMEOUT))
113         while true
114         do
115                 if test $(time_in_seconds) -gt $timeout
116                 then
117                         kill -9 $pid
118                         exit 1
119                 fi
120                 sleep 1
121         done &
122         watchdog_pid=$!
123
124         ready=
125         while test $i -gt 0
126         do
127                 # succeed when p4 client commands start to work
128                 if p4 info >/dev/null 2>&1
129                 then
130                         ready=true
131                         break
132                 fi
133                 # fail if p4d died
134                 kill -0 $pid 2>/dev/null || break
135                 echo waiting for p4d to start
136                 sleep 1
137                 i=$(( $i - 1 ))
138         done
139
140         if test -z "$ready"
141         then
142                 # p4d failed to start
143                 return 1
144         fi
145
146         # build a p4 user so author@example.com has an entry
147         p4_add_user author
148
149         # build a client
150         client_view "//depot/... //client/..." &&
151
152         return 0
153 }
154
155 p4_add_user () {
156         name=$1 &&
157         p4 user -f -i <<-EOF
158         User: $name
159         Email: $name@example.com
160         FullName: Dr. $name
161         EOF
162 }
163
164 p4_add_job () {
165         p4 job -f -i <<-EOF
166         Job: $1
167         Status: open
168         User: dummy
169         Description:
170         EOF
171 }
172
173 retry_until_success () {
174         timeout=$(($(time_in_seconds) + $RETRY_TIMEOUT))
175         until "$@" 2>/dev/null || test $(time_in_seconds) -gt $timeout
176         do
177                 sleep 1
178         done
179 }
180
181 retry_until_fail () {
182         timeout=$(($(time_in_seconds) + $RETRY_TIMEOUT))
183         until ! "$@" 2>/dev/null || test $(time_in_seconds) -gt $timeout
184         do
185                 sleep 1
186         done
187 }
188
189 kill_p4d () {
190         pid=$(cat "$pidfile")
191         retry_until_fail kill $pid
192         retry_until_fail kill -9 $pid
193         # complain if it would not die
194         test_must_fail kill $pid >/dev/null 2>&1 &&
195         rm -rf "$db" "$cli" "$pidfile" &&
196         retry_until_fail kill -9 $watchdog_pid
197 }
198
199 cleanup_git () {
200         retry_until_success rm -r "$git"
201         test_must_fail test -d "$git" &&
202         retry_until_success mkdir "$git"
203 }
204
205 marshal_dump () {
206         what=$1 &&
207         line=${2:-1} &&
208         cat >"$TRASH_DIRECTORY/marshal-dump.py" <<-EOF &&
209         import marshal
210         import sys
211         instream = getattr(sys.stdin, 'buffer', sys.stdin)
212         for i in range($line):
213             d = marshal.load(instream)
214         print(d[b'$what'].decode('utf-8'))
215         EOF
216         "$PYTHON_PATH" "$TRASH_DIRECTORY/marshal-dump.py"
217 }
218
219 #
220 # Construct a client with this list of View lines
221 #
222 client_view () {
223         (
224                 cat <<-EOF &&
225                 Client: $P4CLIENT
226                 Description: $P4CLIENT
227                 Root: $cli
228                 AltRoots: $(native_path "$cli")
229                 LineEnd: unix
230                 View:
231                 EOF
232                 printf "\t%s\n" "$@"
233         ) | p4 client -i
234 }
235
236 is_cli_file_writeable () {
237         # cygwin version of p4 does not set read-only attr,
238         # will be marked 444 but -w is true
239         file="$1" &&
240         if test_have_prereq CYGWIN && p4 -V | grep -q CYGWIN
241         then
242                 stat=$(stat --format=%a "$file") &&
243                 test $stat = 644
244         else
245                 test -w "$file"
246         fi
247 }