#!/bin/bash

# Copyright 2022 The Centipede Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Run a short fuzzing session for one puzzle and check the outcome.
# This script is executed under the name run_S_PUZZLE_NAME, where S is a single
# digit representing the seed, so we get the seed and puzzle name from $0.
# Every puzzle must have one or more lines containing "RUN:<program-text>"
# This script will execute <program-text> directly in the current context.
# <program-text> can use the functions defined in this file, see USER_FUNCTIONS.

set -eu -o pipefail

ls -la "$(dirname "$0")"

source "$(dirname "$0")/../test_util.sh"

readonly centipede_dir="$(centipede::get_centipede_test_srcdir)"
centipede::maybe_set_var_to_executable_path centipede "${centipede_dir}/centipede_uninstrumented"
readonly centipede
centipede::maybe_set_var_to_executable_path llvm_symbolizer "$(centipede::get_llvm_symbolizer_path)"
readonly llvm_symbolizer
centipede::maybe_set_var_to_executable_path objdump "$(centipede::get_objdump_path)"
readonly objdump

readonly target_name="$(basename "$0")"
readonly seed_and_puzzle_name="${target_name#run_}"
readonly seed="${seed_and_puzzle_name:0:1}"
readonly puzzle_name="${seed_and_puzzle_name:2}"
readonly puzzle_source_name="${puzzle_name}.cc"
readonly puzzle_path="${centipede_dir}/puzzles/${puzzle_name}"
readonly puzzle_source_path="${centipede_dir}/puzzles/${puzzle_source_name}"

readonly workdir="${TEST_TMPDIR}/workdir"
readonly log="${TEST_TMPDIR}/log"
readonly script="${TEST_TMPDIR}/script"

# Read the configuration from the puzzle source.
grep 'RUN:' "${puzzle_source_path}" | sed 's/^.*RUN://' > "${script}"
echo "======== SCRIPT"
cat "${script}"
echo "======== END SCRIPT"

##################################### USER_FUNCTIONS

# Runs Centipede with additional parameters in $@, saves the result in log, cats
# the log. Expects Centipede to exit with failure.
function Run() {
  echo "======== Run $*"
  rm -rf "${workdir}"
  mkdir "${workdir}"
  if "${centipede}" \
    --workdir "${workdir}" \
    --binary "${puzzle_path}" \
    --symbolizer_path="${llvm_symbolizer}" \
    --objdump_path="${objdump}$" \
    --seed="${seed}" \
    --num_runs=2000000 \
    --timeout_per_input=10 \
    --exit_on_crash \
    "$@" \
    |& tee "${log}"
  then
    # Centipede must exit with failure.
    return 1
  fi
}

# Checks that $1 is the solution for the puzzle.
function SolutionIs() {
  echo "====== ${FUNCNAME[0]}: $1"
  centipede::assert_regex_in_file "Input bytes.*: $1" "${log}"
}

# Expects that Centipede found a per-input timeout.
function ExpectPerInputTimeout() {
  echo "======= ${FUNCNAME[0]}"
  centipede::assert_regex_in_file "Per-input timeout exceeded" "${log}"
  centipede::assert_regex_in_file "Failure.*: per-input-timeout-exceeded" "${log}"
}

# Expects that Centipede found a per-batch timeout.
function ExpectPerBatchTimeout() {
  echo "======= ${FUNCNAME[0]}"
  centipede::assert_regex_in_file "Per-batch timeout exceeded" "${log}"
  centipede::assert_regex_in_file "Failure.*: per-batch-timeout-exceeded" "${log}"
  centipede::assert_regex_in_file \
    "Failure applies to entire batch: not executing inputs one-by-one" "${log}"
  centipede::assert_regex_not_in_file \
    "Executing inputs one-by-one, trying to find the reproducer" "${log}"
}

# Expects that Centipede found a OOM.
function ExpectOOM() {
  echo "======= ${FUNCNAME[0]}"
  centipede::assert_regex_in_file "RSS limit exceeded" "${log}"
  centipede::assert_regex_in_file "Failure.*: rss-limit-exceeded" "${log}"
}

# Expects that $1 is found in the log.
function ExpectInLog() {
  echo "======= ${FUNCNAME[0]}: $1"
  centipede::assert_regex_in_file "$1" "${log}"
}

##################################### end USER_FUNCTIONS

# shellcheck disable=SC1090
source "${script}"

echo PASS
