1#!/bin/bash
2#
3# A test suite for applypatch.  Run in a client where you have done
4# envsetup, choosecombo, etc.
5#
6# DO NOT RUN THIS ON A DEVICE YOU CARE ABOUT.  It will mess up your
7# system partition.
8#
9#
10# TODO: find some way to get this run regularly along with the rest of
11# the tests.
12
13EMULATOR_PORT=5580
14DATA_DIR=$ANDROID_BUILD_TOP/bootable/recovery/applypatch/testdata
15
16# This must be the filename that applypatch uses for its copies.
17CACHE_TEMP_SOURCE=/cache/saved.file
18
19# Put all binaries and files here.  We use /cache because it's a
20# temporary filesystem in the emulator; it's created fresh each time
21# the emulator starts.
22WORK_DIR=/system
23
24# partition that WORK_DIR is located on, without the leading slash
25WORK_FS=system
26
27# set to 0 to use a device instead
28USE_EMULATOR=1
29
30# ------------------------
31
32tmpdir=$(mktemp -d)
33
34if [ "$USE_EMULATOR" == 1 ]; then
35  emulator -wipe-data -noaudio -no-window -port $EMULATOR_PORT &
36  pid_emulator=$!
37  ADB="adb -s emulator-$EMULATOR_PORT "
38else
39  ADB="adb -d "
40fi
41
42echo "waiting to connect to device"
43$ADB wait-for-device
44echo "device is available"
45$ADB remount
46# free up enough space on the system partition for the test to run.
47$ADB shell rm -r /system/media
48
49# run a command on the device; exit with the exit status of the device
50# command.
51run_command() {
52  $ADB shell "$@" \; echo \$? | awk '{if (b) {print a}; a=$0; b=1} END {exit a}'
53}
54
55testname() {
56  echo
57  echo "$1"...
58  testname="$1"
59}
60
61fail() {
62  echo
63  echo FAIL: $testname
64  echo
65  [ "$open_pid" == "" ] || kill $open_pid
66  [ "$pid_emulator" == "" ] || kill $pid_emulator
67  exit 1
68}
69
70sha1() {
71  sha1sum $1 | awk '{print $1}'
72}
73
74free_space() {
75  run_command df | awk "/$1/ {print gensub(/K/, \"\", \"g\", \$6)}"
76}
77
78cleanup() {
79  # not necessary if we're about to kill the emulator, but nice for
80  # running on real devices or already-running emulators.
81  testname "removing test files"
82  run_command rm $WORK_DIR/bloat.dat
83  run_command rm $WORK_DIR/old.file
84  run_command rm $WORK_DIR/foo
85  run_command rm $WORK_DIR/patch.bsdiff
86  run_command rm $WORK_DIR/applypatch
87  run_command rm $CACHE_TEMP_SOURCE
88  run_command rm /cache/bloat*.dat
89
90  [ "$pid_emulator" == "" ] || kill $pid_emulator
91
92  if [ $# == 0 ]; then
93    rm -rf $tmpdir
94  fi
95}
96
97cleanup leave_tmp
98
99$ADB push $ANDROID_PRODUCT_OUT/system/bin/applypatch $WORK_DIR/applypatch
100
101BAD1_SHA1=$(printf "%040x" $RANDOM)
102BAD2_SHA1=$(printf "%040x" $RANDOM)
103OLD_SHA1=$(sha1 $DATA_DIR/old.file)
104NEW_SHA1=$(sha1 $DATA_DIR/new.file)
105NEW_SIZE=$(stat -c %s $DATA_DIR/new.file)
106
107# --------------- basic execution ----------------------
108
109testname "usage message"
110run_command $WORK_DIR/applypatch && fail
111
112testname "display license"
113run_command $WORK_DIR/applypatch -l | grep -q -i copyright || fail
114
115
116# --------------- check mode ----------------------
117
118$ADB push $DATA_DIR/old.file $WORK_DIR
119
120testname "check mode single"
121run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail
122
123testname "check mode multiple"
124run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail
125
126testname "check mode failure"
127run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail
128
129$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE
130# put some junk in the old file
131run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail
132
133testname "check mode cache (corrupted) single"
134run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail
135
136testname "check mode cache (corrupted) multiple"
137run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail
138
139testname "check mode cache (corrupted) failure"
140run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail
141
142# remove the old file entirely
143run_command rm $WORK_DIR/old.file
144
145testname "check mode cache (missing) single"
146run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail
147
148testname "check mode cache (missing) multiple"
149run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail
150
151testname "check mode cache (missing) failure"
152run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail
153
154
155# --------------- apply patch ----------------------
156
157$ADB push $DATA_DIR/old.file $WORK_DIR
158$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
159echo hello > $tmpdir/foo
160$ADB push $tmpdir/foo $WORK_DIR
161
162# Check that the partition has enough space to apply the patch without
163# copying.  If it doesn't, we'll be testing the low-space condition
164# when we intend to test the not-low-space condition.
165testname "apply patches (with enough space)"
166free_kb=$(free_space $WORK_FS)
167echo "${free_kb}kb free on /$WORK_FS."
168if (( free_kb * 1024 < NEW_SIZE * 3 / 2 )); then
169  echo "Not enough space on /$WORK_FS to patch test file."
170  echo
171  echo "This doesn't mean that applypatch is necessarily broken;"
172  echo "just that /$WORK_FS doesn't have enough free space to"
173  echo "properly run this test."
174  exit 1
175fi
176
177testname "apply bsdiff patch"
178run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
179$ADB pull $WORK_DIR/old.file $tmpdir/patched
180diff -q $DATA_DIR/new.file $tmpdir/patched || fail
181
182testname "reapply bsdiff patch"
183run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
184$ADB pull $WORK_DIR/old.file $tmpdir/patched
185diff -q $DATA_DIR/new.file $tmpdir/patched || fail
186
187
188# --------------- apply patch in new location ----------------------
189
190$ADB push $DATA_DIR/old.file $WORK_DIR
191$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
192
193# Check that the partition has enough space to apply the patch without
194# copying.  If it doesn't, we'll be testing the low-space condition
195# when we intend to test the not-low-space condition.
196testname "apply patch to new location (with enough space)"
197free_kb=$(free_space $WORK_FS)
198echo "${free_kb}kb free on /$WORK_FS."
199if (( free_kb * 1024 < NEW_SIZE * 3 / 2 )); then
200  echo "Not enough space on /$WORK_FS to patch test file."
201  echo
202  echo "This doesn't mean that applypatch is necessarily broken;"
203  echo "just that /$WORK_FS doesn't have enough free space to"
204  echo "properly run this test."
205  exit 1
206fi
207
208run_command rm $WORK_DIR/new.file
209run_command rm $CACHE_TEMP_SOURCE
210
211testname "apply bsdiff patch to new location"
212run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
213$ADB pull $WORK_DIR/new.file $tmpdir/patched
214diff -q $DATA_DIR/new.file $tmpdir/patched || fail
215
216testname "reapply bsdiff patch to new location"
217run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
218$ADB pull $WORK_DIR/new.file $tmpdir/patched
219diff -q $DATA_DIR/new.file $tmpdir/patched || fail
220
221$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE
222# put some junk in the old file
223run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail
224
225testname "apply bsdiff patch to new location with corrupted source"
226run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo || fail
227$ADB pull $WORK_DIR/new.file $tmpdir/patched
228diff -q $DATA_DIR/new.file $tmpdir/patched || fail
229
230# put some junk in the cache copy, too
231run_command dd if=/dev/urandom of=$CACHE_TEMP_SOURCE count=100 bs=1024 || fail
232
233run_command rm $WORK_DIR/new.file
234testname "apply bsdiff patch to new location with corrupted source and copy (no new file)"
235run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo && fail
236
237# put some junk in the new file
238run_command dd if=/dev/urandom of=$WORK_DIR/new.file count=100 bs=1024 || fail
239
240testname "apply bsdiff patch to new location with corrupted source and copy (bad new file)"
241run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo && fail
242
243# --------------- apply patch with low space on /system ----------------------
244
245$ADB push $DATA_DIR/old.file $WORK_DIR
246$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
247
248free_kb=$(free_space $WORK_FS)
249echo "${free_kb}kb free on /$WORK_FS; we'll soon fix that."
250echo run_command dd if=/dev/zero of=$WORK_DIR/bloat.dat count=$((free_kb-512)) bs=1024 || fail
251run_command dd if=/dev/zero of=$WORK_DIR/bloat.dat count=$((free_kb-512)) bs=1024 || fail
252free_kb=$(free_space $WORK_FS)
253echo "${free_kb}kb free on /$WORK_FS now."
254
255testname "apply bsdiff patch with low space"
256run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
257$ADB pull $WORK_DIR/old.file $tmpdir/patched
258diff -q $DATA_DIR/new.file $tmpdir/patched || fail
259
260testname "reapply bsdiff patch with low space"
261run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
262$ADB pull $WORK_DIR/old.file $tmpdir/patched
263diff -q $DATA_DIR/new.file $tmpdir/patched || fail
264
265# --------------- apply patch with low space on /system and /cache ----------------------
266
267$ADB push $DATA_DIR/old.file $WORK_DIR
268$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
269
270free_kb=$(free_space $WORK_FS)
271echo "${free_kb}kb free on /$WORK_FS"
272
273run_command mkdir /cache/subdir
274run_command 'echo > /cache/subdir/a.file'
275run_command 'echo > /cache/a.file'
276run_command mkdir /cache/recovery /cache/recovery/otatest
277run_command 'echo > /cache/recovery/otatest/b.file'
278run_command "echo > $CACHE_TEMP_SOURCE"
279free_kb=$(free_space cache)
280echo "${free_kb}kb free on /cache; we'll soon fix that."
281run_command dd if=/dev/zero of=/cache/bloat_small.dat count=128 bs=1024 || fail
282run_command dd if=/dev/zero of=/cache/bloat_large.dat count=$((free_kb-640)) bs=1024 || fail
283free_kb=$(free_space cache)
284echo "${free_kb}kb free on /cache now."
285
286testname "apply bsdiff patch with low space, full cache, can't delete enough"
287$ADB shell 'cat >> /cache/bloat_large.dat' & open_pid=$!
288echo "open_pid is $open_pid"
289
290# size check should fail even though it deletes some stuff
291run_command $WORK_DIR/applypatch -s $NEW_SIZE && fail
292run_command ls /cache/bloat_small.dat && fail          # was deleted
293run_command ls /cache/a.file && fail                   # was deleted
294run_command ls /cache/recovery/otatest/b.file && fail  # was deleted
295run_command ls /cache/bloat_large.dat || fail          # wasn't deleted because it was open
296run_command ls /cache/subdir/a.file || fail            # wasn't deleted because it's in a subdir
297run_command ls $CACHE_TEMP_SOURCE || fail              # wasn't deleted because it's the source file copy
298
299# should fail; not enough files can be deleted
300run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff && fail
301run_command ls /cache/bloat_large.dat || fail   # wasn't deleted because it was open
302run_command ls /cache/subdir/a.file || fail     # wasn't deleted because it's in a subdir
303run_command ls $CACHE_TEMP_SOURCE || fail       # wasn't deleted because it's the source file copy
304
305kill $open_pid   # /cache/bloat_large.dat is no longer open
306
307testname "apply bsdiff patch with low space, full cache, can delete enough"
308
309# should succeed after deleting /cache/bloat_large.dat
310run_command $WORK_DIR/applypatch -s $NEW_SIZE || fail
311run_command ls /cache/bloat_large.dat && fail   # was deleted
312run_command ls /cache/subdir/a.file || fail     # still wasn't deleted because it's in a subdir
313run_command ls $CACHE_TEMP_SOURCE || fail       # wasn't deleted because it's the source file copy
314
315# should succeed
316run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
317$ADB pull $WORK_DIR/old.file $tmpdir/patched
318diff -q $DATA_DIR/new.file $tmpdir/patched || fail
319run_command ls /cache/subdir/a.file || fail     # still wasn't deleted because it's in a subdir
320run_command ls $CACHE_TEMP_SOURCE && fail       # was deleted because patching overwrote it, then deleted it
321
322# --------------- apply patch from cache ----------------------
323
324$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE
325# put some junk in the old file
326run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail
327
328testname "apply bsdiff patch from cache (corrupted source) with low space"
329run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
330$ADB pull $WORK_DIR/old.file $tmpdir/patched
331diff -q $DATA_DIR/new.file $tmpdir/patched || fail
332
333$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE
334# remove the old file entirely
335run_command rm $WORK_DIR/old.file
336
337testname "apply bsdiff patch from cache (missing source) with low space"
338run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
339$ADB pull $WORK_DIR/old.file $tmpdir/patched
340diff -q $DATA_DIR/new.file $tmpdir/patched || fail
341
342
343# --------------- cleanup ----------------------
344
345cleanup
346
347echo
348echo PASS
349echo
350
351