1#!/bin/bash
2
3# Copyright 2013 The Chromium Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7# Stress test and size measurement for courgette patches.
8
9error() {
10  echo "error: ${@}" >&2
11}
12
13outdir_prefix="stress_test_"
14
15if [ $# -lt 2 ]; then
16  cat <<EOF
17
18USAGE: $(basename ${0}) [-s] dir1 dir2 [outdir]
19
20  -s only test files supported by courgette
21
22Stress test courgette by generating and applying patches for two given
23directories, dir1 and dir2.  The test will use files with the same
24name and relative path in the two directories, which makes it easy to
25compare two extracted ChromeOS images.  It also compares the unzipped
26and bzipped patches against the likewise bsdiff patches.  If outdir is
27not specified, the name will be "${outdir_prefix}" concatenated with
28the current date and time.
29
30EOF
31  exit 1
32fi
33
34if [ "${1}" == "-s" ]; then
35  test_supported_only=true
36  shift
37else
38  test_supported_only=
39fi
40
41dir1="${1}"
42if [ ! -e "${dir1}" ]; then
43  error "\"${dir1}\" not found"
44  exit 1
45fi
46
47dir2="${2}"
48if [ ! -e "${dir2}" ]; then
49  error "\"${dir2}\" not found"
50  exit 1
51fi
52
53out_dir="${3:-${outdir_prefix}$(date +%Y%m%d_%H%M%S)}"
54if [ -e "${out_dir}" ]; then
55  error "\"${out_dir}\" already exists"
56  exit 1
57fi
58
59mkdir -p "${out_dir}" || exit 1
60
61patches_dir="${out_dir}/patches"
62applied_dir="${out_dir}/applied"
63dis_dir="${out_dir}/dis"
64bsdiff="${out_dir}/bsdiff"
65log="${out_dir}/log"
66results="${out_dir}/results"
67
68echo "${0} ${@}" > "${log}"
69date >> "${log}"
70
71run_test() {
72  if [[ ! -z "${1}" && ! -z "${2}" ]]; then
73    local file1="${1}"
74    local file2="${2}"
75    local patch="${patches_dir}/${file1}.patch"
76    local apply="${applied_dir}/${file2}.applied"
77    local dis="${dis_dir}/${file1}.dis"
78    local asm="${dis_dir}/${file1}.asm"
79    mkdir -p "$(dirname "${dis}")"
80    if [ ! $test_supported_only ]; then
81      courgette -supported "${file1}" >/dev/null
82      if [ "${?}" -eq 0 ]; then
83        courgette -dis "${file1}" "${dis}"
84        courgette -asm "${dis}" "${asm}"
85        cmp -s "${file1}" "${asm}"
86        if [ "${?}" -ne 0 ]; then
87          echo "FAIL_DISASSEMBLE ${file1}"
88        fi
89      fi
90    fi
91    mkdir -p "$(dirname "${patch}")"
92    mkdir -p "$(dirname "${apply}")"
93    courgette -gen "${file1}" "${file2}" "${patch}"
94    courgette -apply "${file1}" "${patch}" "${apply}"
95    cmp -s "${file2}" "${apply}"
96    if [ "${?}" -ne 0 ]; then
97      echo "FAIL_COURGETTE ${file1}"
98    else
99      echo "PASS_COURGETTE ${file1}"
100      local bsdiff_patch="${patches_dir}/${file1}.bsdiff_patch"
101      local bsdiff_apply="${applied_dir}/${file2}.bsdiff_applied"
102      bsdiff "${file1}" "${file2}" "${bsdiff_patch}"
103      bspatch "${file1}" "${bsdiff_apply}" "${bsdiff_patch}"
104      cmp -s "${file2}" "${bsdiff_apply}"
105      if [ "${?}" -ne 0 ]; then
106        echo "FAIL_BSDIFF ${file1}"
107      else
108        echo "PASS_BSDIFF ${file1}"
109        bzip2 -k -9 "${patch}"
110        local patch_size="$(du -b "${patch}.bz2" | cut -f1)"
111        local bsdiff_patch_size="$(du -b "${bsdiff_patch}" | cut -f1)"
112        echo "SIZE courgette=${patch_size} bsdiff=${bsdiff_patch_size} ${file1}"
113        if [ "${patch_size}" -eq "${bsdiff_patch_size}" ]; then
114          echo "BEST_TIE ${patch_size} ${file1}"
115        elif [ "${patch_size}" -lt "${bsdiff_patch_size}" ]; then
116          echo "BEST_COURGETTE ${patch_size} ${file1}"
117        elif [ "${patch_size}" -gt "${bsdiff_patch_size}" ]; then
118          echo "BEST_BSDIFF ${bsdiff_patch_size} ${file1}"
119        fi
120      fi
121    fi
122  fi
123}
124
125# Use diff to find the files that appear in both directories.
126time diff -qr "${dir1}" "${dir2}" 2>/dev/null \
127  | grep "^Files" \
128  | awk '{print $2,$4}' \
129  | while read file; do
130  # Use awk to separate the two filenames.  May break if filenames
131  # contain spaces.
132  file1="$(echo "${file}" | awk '{print $1}')"
133  file2="$(echo "${file}" | awk '{print $2}')"
134  if [ $test_supported_only ]; then
135    courgette -supported "${file1}" >/dev/null
136    if [ "${?}" -ne 0 ]; then
137      continue;
138    fi
139  fi
140  run_test "${file1}" "${file2}"
141done 2>&1 | tee -a "${log}"
142
143date >> "${log}"
144
145count_result() {
146  if [ ! -z "${1}" ]; then
147    echo $(cat "${log}" | grep "^${1} " | wc -l)    
148  else
149    echo 0
150  fi
151}
152
153cat <<EOF | tee -a "${log}"
154$(count_result "PASS_COURGETTE") successful courgette patches
155$(count_result "FAIL_COURGETTE") failed courgette patches (search log for \
156"^FAIL_COURGETTE")
157$(count_result "FAIL_DISASSEMBLE") failed to disassemble/assemble (search log \
158for "^FAIL_DISASSEMBLE")
159$(count_result "PASS_BSDIFF") succesful bsdiff patches
160$(count_result "FAIL_BSDIFF") failed bsdiff patches
161$(count_result "BEST_COURGETTE") patch(es) where courgette is smaller
162$(count_result "BEST_BSDIFF") patch(es) where bsdiff is smaller
163$(count_result "BEST_TIE") patch(es) where both are the same size
164EOF
165