1#!/bin/sh
2##
3##  Copyright (c) 2014 The WebM project authors. All Rights Reserved.
4##
5##  Use of this source code is governed by a BSD-style license
6##  that can be found in the LICENSE file in the root of the source
7##  tree. An additional intellectual property rights grant can be found
8##  in the file PATENTS.  All contributing project authors may
9##  be found in the AUTHORS file in the root of the source tree.
10##
11##  This file contains shell code shared by test scripts for libvpx tools.
12
13# Use $VPX_TEST_TOOLS_COMMON_SH as a pseudo include guard.
14if [ -z "${VPX_TEST_TOOLS_COMMON_SH}" ]; then
15VPX_TEST_TOOLS_COMMON_SH=included
16
17set -e
18devnull='> /dev/null 2>&1'
19VPX_TEST_PREFIX=""
20
21elog() {
22  echo "$@" 1>&2
23}
24
25vlog() {
26  if [ "${VPX_TEST_VERBOSE_OUTPUT}" = "yes" ]; then
27    echo "$@"
28  fi
29}
30
31# Sets $VPX_TOOL_TEST to the name specified by positional parameter one.
32test_begin() {
33  VPX_TOOL_TEST="${1}"
34}
35
36# Clears the VPX_TOOL_TEST variable after confirming that $VPX_TOOL_TEST matches
37# positional parameter one.
38test_end() {
39  if [ "$1" != "${VPX_TOOL_TEST}" ]; then
40    echo "FAIL completed test mismatch!."
41    echo "  completed test: ${1}"
42    echo "  active test: ${VPX_TOOL_TEST}."
43    return 1
44  fi
45  VPX_TOOL_TEST='<unset>'
46}
47
48# Echoes the target configuration being tested.
49test_configuration_target() {
50  vpx_config_mk="${LIBVPX_CONFIG_PATH}/config.mk"
51  # Find the TOOLCHAIN line, split it using ':=' as the field separator, and
52  # print the last field to get the value. Then pipe the value to tr to consume
53  # any leading/trailing spaces while allowing tr to echo the output to stdout.
54  awk -F ':=' '/TOOLCHAIN/ { print $NF }' "${vpx_config_mk}" | tr -d ' '
55}
56
57# Trap function used for failure reports and tool output directory removal.
58# When the contents of $VPX_TOOL_TEST do not match the string '<unset>', reports
59# failure of test stored in $VPX_TOOL_TEST.
60cleanup() {
61  if [ -n "${VPX_TOOL_TEST}" ] && [ "${VPX_TOOL_TEST}" != '<unset>' ]; then
62    echo "FAIL: $VPX_TOOL_TEST"
63  fi
64  if [ -n "${VPX_TEST_OUTPUT_DIR}" ] && [ -d "${VPX_TEST_OUTPUT_DIR}" ]; then
65    rm -rf "${VPX_TEST_OUTPUT_DIR}"
66  fi
67}
68
69# Echoes the git hash portion of the VERSION_STRING variable defined in
70# $LIBVPX_CONFIG_PATH/config.mk to stdout, or the version number string when
71# no git hash is contained in VERSION_STRING.
72config_hash() {
73  vpx_config_mk="${LIBVPX_CONFIG_PATH}/config.mk"
74  # Find VERSION_STRING line, split it with "-g" and print the last field to
75  # output the git hash to stdout.
76  vpx_version=$(awk -F -g '/VERSION_STRING/ {print $NF}' "${vpx_config_mk}")
77  # Handle two situations here:
78  # 1. The default case: $vpx_version is a git hash, so echo it unchanged.
79  # 2. When being run a non-dev tree, the -g portion is not present in the
80  #    version string: It's only the version number.
81  #    In this case $vpx_version is something like 'VERSION_STRING=v1.3.0', so
82  #    we echo only what is after the '='.
83  echo "${vpx_version##*=}"
84}
85
86# Echoes the short form of the current git hash.
87current_hash() {
88  if git --version > /dev/null 2>&1; then
89    (cd "$(dirname "${0}")"
90    git rev-parse --short HEAD)
91  else
92    # Return the config hash if git is unavailable: Fail silently, git hashes
93    # are used only for warnings.
94    config_hash
95  fi
96}
97
98# Echoes warnings to stdout when git hash in vpx_config.h does not match the
99# current git hash.
100check_git_hashes() {
101  hash_at_configure_time=$(config_hash)
102  hash_now=$(current_hash)
103
104  if [ "${hash_at_configure_time}" != "${hash_now}" ]; then
105    echo "Warning: git hash has changed since last configure."
106  fi
107}
108
109# This script requires that the LIBVPX_BIN_PATH, LIBVPX_CONFIG_PATH, and
110# LIBVPX_TEST_DATA_PATH variables are in the environment: Confirm that
111# the variables are set and that they all evaluate to directory paths.
112verify_vpx_test_environment() {
113  if [ ! -d "${LIBVPX_BIN_PATH}" ]; then
114    echo "The LIBVPX_BIN_PATH environment variable must be set."
115    return 1
116  fi
117  if [ ! -d "${LIBVPX_CONFIG_PATH}" ]; then
118    echo "The LIBVPX_CONFIG_PATH environment variable must be set."
119    return 1
120  fi
121  if [ ! -d "${LIBVPX_TEST_DATA_PATH}" ]; then
122    echo "The LIBVPX_TEST_DATA_PATH environment variable must be set."
123    return 1
124  fi
125}
126
127# Greps vpx_config.h in LIBVPX_CONFIG_PATH for positional parameter one, which
128# should be a LIBVPX preprocessor flag. Echoes yes to stdout when the feature
129# is available.
130vpx_config_option_enabled() {
131  vpx_config_option="${1}"
132  vpx_config_file="${LIBVPX_CONFIG_PATH}/vpx_config.h"
133  config_line=$(grep "${vpx_config_option}" "${vpx_config_file}")
134  if echo "${config_line}" | egrep -q '1$'; then
135    echo yes
136  fi
137}
138
139# Echoes yes when output of test_configuration_target() contains win32 or win64.
140is_windows_target() {
141  if test_configuration_target \
142     | grep -q -e win32 -e win64 > /dev/null 2>&1; then
143    echo yes
144  fi
145}
146
147# Echoes path to $1 when it's executable and exists in ${LIBVPX_BIN_PATH}, or an
148# empty string. Caller is responsible for testing the string once the function
149# returns.
150vpx_tool_path() {
151  local readonly tool_name="$1"
152  local tool_path="${LIBVPX_BIN_PATH}/${tool_name}${VPX_TEST_EXE_SUFFIX}"
153  if [ ! -x "${tool_path}" ]; then
154    # Try one directory up: when running via examples.sh the tool could be in
155    # the parent directory of $LIBVPX_BIN_PATH.
156    tool_path="${LIBVPX_BIN_PATH}/../${tool_name}${VPX_TEST_EXE_SUFFIX}"
157  fi
158
159  if [ ! -x "${tool_path}" ]; then
160    tool_path=""
161  fi
162  echo "${tool_path}"
163}
164
165# Echoes yes to stdout when the file named by positional parameter one exists
166# in LIBVPX_BIN_PATH, and is executable.
167vpx_tool_available() {
168  local tool_name="$1"
169  local tool="${LIBVPX_BIN_PATH}/${tool_name}${VPX_TEST_EXE_SUFFIX}"
170  [ -x "${tool}" ] && echo yes
171}
172
173# Echoes yes to stdout when vpx_config_option_enabled() reports yes for
174# CONFIG_VP8_DECODER.
175vp8_decode_available() {
176  [ "$(vpx_config_option_enabled CONFIG_VP8_DECODER)" = "yes" ] && echo yes
177}
178
179# Echoes yes to stdout when vpx_config_option_enabled() reports yes for
180# CONFIG_VP8_ENCODER.
181vp8_encode_available() {
182  [ "$(vpx_config_option_enabled CONFIG_VP8_ENCODER)" = "yes" ] && echo yes
183}
184
185# Echoes yes to stdout when vpx_config_option_enabled() reports yes for
186# CONFIG_VP9_DECODER.
187vp9_decode_available() {
188  [ "$(vpx_config_option_enabled CONFIG_VP9_DECODER)" = "yes" ] && echo yes
189}
190
191# Echoes yes to stdout when vpx_config_option_enabled() reports yes for
192# CONFIG_VP9_ENCODER.
193vp9_encode_available() {
194  [ "$(vpx_config_option_enabled CONFIG_VP9_ENCODER)" = "yes" ] && echo yes
195}
196
197# Echoes yes to stdout when vpx_config_option_enabled() reports yes for
198# CONFIG_WEBM_IO.
199webm_io_available() {
200  [ "$(vpx_config_option_enabled CONFIG_WEBM_IO)" = "yes" ] && echo yes
201}
202
203# Filters strings from $1 using the filter specified by $2. Filter behavior
204# depends on the presence of $3. When $3 is present, strings that match the
205# filter are excluded. When $3 is omitted, strings matching the filter are
206# included.
207# The filtered result is echoed to stdout.
208filter_strings() {
209  strings=${1}
210  filter=${2}
211  exclude=${3}
212
213  if [ -n "${exclude}" ]; then
214    # When positional parameter three exists the caller wants to remove strings.
215    # Tell grep to invert matches using the -v argument.
216    exclude='-v'
217  else
218    unset exclude
219  fi
220
221  if [ -n "${filter}" ]; then
222    for s in ${strings}; do
223      if echo "${s}" | egrep -q ${exclude} "${filter}" > /dev/null 2>&1; then
224        filtered_strings="${filtered_strings} ${s}"
225      fi
226    done
227  else
228    filtered_strings="${strings}"
229  fi
230  echo "${filtered_strings}"
231}
232
233# Runs user test functions passed via positional parameters one and two.
234# Functions in positional parameter one are treated as environment verification
235# functions and are run unconditionally. Functions in positional parameter two
236# are run according to the rules specified in vpx_test_usage().
237run_tests() {
238  local env_tests="verify_vpx_test_environment $1"
239  local tests_to_filter="$2"
240  local test_name="${VPX_TEST_NAME}"
241
242  if [ -z "${test_name}" ]; then
243    test_name="$(basename "${0%.*}")"
244  fi
245
246  if [ "${VPX_TEST_RUN_DISABLED_TESTS}" != "yes" ]; then
247    # Filter out DISABLED tests.
248    tests_to_filter=$(filter_strings "${tests_to_filter}" ^DISABLED exclude)
249  fi
250
251  if [ -n "${VPX_TEST_FILTER}" ]; then
252    # Remove tests not matching the user's filter.
253    tests_to_filter=$(filter_strings "${tests_to_filter}" ${VPX_TEST_FILTER})
254  fi
255
256  # User requested test listing: Dump test names and return.
257  if [ "${VPX_TEST_LIST_TESTS}" = "yes" ]; then
258    for test_name in $tests_to_filter; do
259      echo ${test_name}
260    done
261    return
262  fi
263
264  # Combine environment and actual tests.
265  local tests_to_run="${env_tests} ${tests_to_filter}"
266
267  check_git_hashes
268
269  # Run tests.
270  for test in ${tests_to_run}; do
271    test_begin "${test}"
272    vlog "  RUN  ${test}"
273    "${test}"
274    vlog "  PASS ${test}"
275    test_end "${test}"
276  done
277
278  local tested_config="$(test_configuration_target) @ $(current_hash)"
279  echo "${test_name}: Done, all tests pass for ${tested_config}."
280}
281
282vpx_test_usage() {
283cat << EOF
284  Usage: ${0##*/} [arguments]
285    --bin-path <path to libvpx binaries directory>
286    --config-path <path to libvpx config directory>
287    --filter <filter>: User test filter. Only tests matching filter are run.
288    --run-disabled-tests: Run disabled tests.
289    --help: Display this message and exit.
290    --test-data-path <path to libvpx test data directory>
291    --show-program-output: Shows output from all programs being tested.
292    --prefix: Allows for a user specified prefix to be inserted before all test
293              programs. Grants the ability, for example, to run test programs
294              within valgrind.
295    --list-tests: List all test names and exit without actually running tests.
296    --verbose: Verbose output.
297
298    When the --bin-path option is not specified the script attempts to use
299    \$LIBVPX_BIN_PATH and then the current directory.
300
301    When the --config-path option is not specified the script attempts to use
302    \$LIBVPX_CONFIG_PATH and then the current directory.
303
304    When the -test-data-path option is not specified the script attempts to use
305    \$LIBVPX_TEST_DATA_PATH and then the current directory.
306EOF
307}
308
309# Returns non-zero (failure) when required environment variables are empty
310# strings.
311vpx_test_check_environment() {
312  if [ -z "${LIBVPX_BIN_PATH}" ] || \
313     [ -z "${LIBVPX_CONFIG_PATH}" ] || \
314     [ -z "${LIBVPX_TEST_DATA_PATH}" ]; then
315    return 1
316  fi
317}
318
319# Parse the command line.
320while [ -n "$1" ]; do
321  case "$1" in
322    --bin-path)
323      LIBVPX_BIN_PATH="$2"
324      shift
325      ;;
326    --config-path)
327      LIBVPX_CONFIG_PATH="$2"
328      shift
329      ;;
330    --filter)
331      VPX_TEST_FILTER="$2"
332      shift
333      ;;
334    --run-disabled-tests)
335      VPX_TEST_RUN_DISABLED_TESTS=yes
336      ;;
337    --help)
338      vpx_test_usage
339      exit
340      ;;
341    --test-data-path)
342      LIBVPX_TEST_DATA_PATH="$2"
343      shift
344      ;;
345    --prefix)
346      VPX_TEST_PREFIX="$2"
347      shift
348      ;;
349    --verbose)
350      VPX_TEST_VERBOSE_OUTPUT=yes
351      ;;
352    --show-program-output)
353      devnull=
354      ;;
355    --list-tests)
356      VPX_TEST_LIST_TESTS=yes
357      ;;
358    *)
359      vpx_test_usage
360      exit 1
361      ;;
362  esac
363  shift
364done
365
366# Handle running the tests from a build directory without arguments when running
367# the tests on *nix/macosx.
368LIBVPX_BIN_PATH="${LIBVPX_BIN_PATH:-.}"
369LIBVPX_CONFIG_PATH="${LIBVPX_CONFIG_PATH:-.}"
370LIBVPX_TEST_DATA_PATH="${LIBVPX_TEST_DATA_PATH:-.}"
371
372# Create a temporary directory for output files, and a trap to clean it up.
373if [ -n "${TMPDIR}" ]; then
374  VPX_TEST_TEMP_ROOT="${TMPDIR}"
375elif [ -n "${TEMPDIR}" ]; then
376  VPX_TEST_TEMP_ROOT="${TEMPDIR}"
377else
378  VPX_TEST_TEMP_ROOT=/tmp
379fi
380
381VPX_TEST_RAND=$(awk 'BEGIN { srand(); printf "%d\n",(rand() * 32768)}')
382VPX_TEST_OUTPUT_DIR="${VPX_TEST_TEMP_ROOT}/vpx_test_${VPX_TEST_RAND}"
383
384if ! mkdir -p "${VPX_TEST_OUTPUT_DIR}" || \
385   [ ! -d "${VPX_TEST_OUTPUT_DIR}" ]; then
386  echo "${0##*/}: Cannot create output directory, giving up."
387  echo "${0##*/}:   VPX_TEST_OUTPUT_DIR=${VPX_TEST_OUTPUT_DIR}"
388  exit 1
389fi
390
391if [ "$(is_windows_target)" = "yes" ]; then
392  VPX_TEST_EXE_SUFFIX=".exe"
393fi
394
395# Variables shared by tests.
396VP8_IVF_FILE="${LIBVPX_TEST_DATA_PATH}/vp80-00-comprehensive-001.ivf"
397VP9_IVF_FILE="${LIBVPX_TEST_DATA_PATH}/vp90-2-09-subpixel-00.ivf"
398
399VP9_WEBM_FILE="${LIBVPX_TEST_DATA_PATH}/vp90-2-00-quantizer-00.webm"
400
401YUV_RAW_INPUT="${LIBVPX_TEST_DATA_PATH}/hantro_collage_w352h288.yuv"
402YUV_RAW_INPUT_WIDTH=352
403YUV_RAW_INPUT_HEIGHT=288
404
405# Setup a trap function to clean up after tests complete.
406trap cleanup EXIT
407
408vlog "$(basename "${0%.*}") test configuration:
409  LIBVPX_BIN_PATH=${LIBVPX_BIN_PATH}
410  LIBVPX_CONFIG_PATH=${LIBVPX_CONFIG_PATH}
411  LIBVPX_TEST_DATA_PATH=${LIBVPX_TEST_DATA_PATH}
412  VP8_IVF_FILE=${VP8_IVF_FILE}
413  VP9_IVF_FILE=${VP9_IVF_FILE}
414  VP9_WEBM_FILE=${VP9_WEBM_FILE}
415  VPX_TEST_EXE_SUFFIX=${VPX_TEST_EXE_SUFFIX}
416  VPX_TEST_FILTER=${VPX_TEST_FILTER}
417  VPX_TEST_LIST_TESTS=${VPX_TEST_LIST_TESTS}
418  VPX_TEST_OUTPUT_DIR=${VPX_TEST_OUTPUT_DIR}
419  VPX_TEST_PREFIX=${VPX_TEST_PREFIX}
420  VPX_TEST_RAND=${VPX_TEST_RAND}
421  VPX_TEST_RUN_DISABLED_TESTS=${VPX_TEST_RUN_DISABLED_TESTS}
422  VPX_TEST_SHOW_PROGRAM_OUTPUT=${VPX_TEST_SHOW_PROGRAM_OUTPUT}
423  VPX_TEST_TEMP_ROOT=${VPX_TEST_TEMP_ROOT}
424  VPX_TEST_VERBOSE_OUTPUT=${VPX_TEST_VERBOSE_OUTPUT}
425  YUV_RAW_INPUT=${YUV_RAW_INPUT}
426  YUV_RAW_INPUT_WIDTH=${YUV_RAW_INPUT_WIDTH}
427  YUV_RAW_INPUT_HEIGHT=${YUV_RAW_INPUT_HEIGHT}"
428
429fi  # End $VPX_TEST_TOOLS_COMMON_SH pseudo include guard.
430