#!/bin/bash
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

# Script used to run iOS tests.
# If no arguments are passed to the script, it will only compile
# the RNTester.
# If the script is called with a single argument "test", we'll
# run the RNTester unit and integration tests
# ./objc-test.sh test

SCRIPTS=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
ROOT=$(dirname "$SCRIPTS")

SKIPPED_TESTS=()
# TODO: T60408036 This test crashes iOS 13 for bad access, please investigate
# and re-enable. See https://gist.github.com/0xced/56035d2f57254cf518b5.
SKIPPED_TESTS+=("-skip-testing:RNTesterUnitTests/RCTJSONTests/testNotUTF8Convertible")

# Create cleanup handler
cleanup() {
  EXIT=$?
  set +e

  if [ $EXIT -ne 0 ];
  then
    WATCHMAN_LOGS=/usr/local/Cellar/watchman/3.1/var/run/watchman/$USER.log
    [ -f "$WATCHMAN_LOGS" ] && cat "$WATCHMAN_LOGS"
  fi
  # kill whatever is occupying port 8081 (packager)
  lsof -i tcp:8081 | awk 'NR!=1 {print $2}' | xargs kill
  # kill whatever is occupying port 5555 (web socket server)
  lsof -i tcp:5555 | awk 'NR!=1 {print $2}' | xargs kill
}

# Wait for the package to start
waitForPackager() {
  local -i max_attempts=60
  local -i attempt_num=1

  until curl -s http://localhost:8081/status | grep "packager-status:running" -q; do
    if (( attempt_num == max_attempts )); then
      echo "Packager did not respond in time. No more attempts left."
      exit 1
    else
      (( attempt_num++ ))
      echo "Packager did not respond. Retrying for attempt number $attempt_num..."
      sleep 1
    fi
  done

  echo "Packager is ready!"
}

waitForWebSocketServer() {
  local -i max_attempts=60
  local -i attempt_num=1

  until curl -s http://localhost:5555 | grep "Upgrade Required" -q; do
    if (( attempt_num == max_attempts )); then
      echo "WebSocket Server did not respond in time. No more attempts left."
      exit 1
    else
      (( attempt_num++ ))
      echo "WebSocket Server did not respond. Retrying for attempt number $attempt_num..."
      sleep 1
    fi
  done

  echo "WebSocket Server is ready!"
}

runTests() {
  # shellcheck disable=SC1091
  source "$ROOT/scripts/.tests.env"
  xcodebuild build test \
    -workspace RNTesterPods.xcworkspace \
    -scheme RNTester \
    -sdk iphonesimulator \
    -destination "platform=iOS Simulator,name=$IOS_DEVICE,OS=$IOS_TARGET_OS" \
      "${SKIPPED_TESTS[@]}"
}

buildForTesting() {
  # shellcheck disable=SC1091
  source "$ROOT/scripts/.tests.env"
  xcodebuild build-for-testing \
    -workspace RNTesterPods.xcworkspace \
    -scheme RNTester \
    -sdk iphonesimulator
}

runTestsOnly() {
  # shellcheck disable=SC1091
  source "$ROOT/scripts/.tests.env"
  echo "[Testing] Running tests on $IOS_DEVICE for OS $IOS_TARGET_OS"
  xcodebuild test \
    -workspace RNTesterPods.xcworkspace \
    -scheme RNTester \
    -sdk iphonesimulator \
    -destination "platform=iOS Simulator,name=$IOS_DEVICE,OS=$IOS_TARGET_OS" \
      "${SKIPPED_TESTS[@]}"
}

buildProject() {
  xcodebuild build \
    -workspace RNTesterPods.xcworkspace \
    -scheme RNTester \
    -sdk iphonesimulator
}

xcbeautifyFormat() {
  if [ "$CI" ]; then
    # CI expects JUnit reports to be available here
    REPORTS_DIR="$HOME/react-native/reports/junit"
  else
    THIS_DIR=$(cd -P "$(dirname "$(realpath "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)

    # Write reports to the react-native root dir
    REPORTS_DIR="$THIS_DIR/../build/reports"
  fi

  xcbeautify --report junit --report-path "$REPORTS_DIR/ios/results.xml"
}

preloadBundlesRNIntegrationTests() {
  # Preload IntegrationTests bundles (packages/rn-tester/)
  curl -s 'http://localhost:8081/IntegrationTests/IntegrationTestsApp.bundle?platform=ios&dev=true' -o /dev/null
}

preloadBundlesRNTester() {
  # Preload RNTesterApp bundles (packages/rn-tester/)
  curl -s 'http://localhost:8081/js/RNTesterApp.ios.bundle?platform=ios&dev=true' -o /dev/null
  curl -s 'http://localhost:8081/js/RNTesterApp.ios.bundle?platform=ios&dev=true&minify=false' -o /dev/null
}

main() {
  cd "$ROOT/packages/rn-tester" || exit

  # If first argument is "test", actually start the packager and run tests.
  # Otherwise, just build RNTester and exit
  if [ "$1" = "test" ]; then

    # Start the WebSocket test server
    echo "Launch WebSocket Server"
    sh "./IntegrationTests/launchWebSocketServer.sh" &
    waitForWebSocketServer

    # Start the packager
    yarn start --max-workers=1 || echo "Can't start packager automatically" &
    waitForPackager
    preloadBundlesRNTester
    preloadBundlesRNIntegrationTests

    buildForTesting

    # Build and run tests.
    RESULT=-1
    MAX_RETRY=3
    for ((i=1; i<=MAX_RETRY; i++))
    do
      echo "Attempt #$i of running tests..."
      if [ -x "$(command -v xcbeautify)" ]; then
        runTests | xcbeautifyFormat && exit "${PIPESTATUS[0]}"
        RESULT="$?"
      else
        echo 'Warning: xcbeautify is not installed. Install xcbeautify to generate JUnit reports.'
        runTests
        RESULT="$?"
      fi

      if [[ "$RESULT" == 0 ]]; then
        # Successful tests!
        echo "Test completed successfully!"
        exit 0
      fi
    done

    echo "Test Failed with code $RESULT!"
    exit $RESULT

  else
    # Build without running tests.
    if [ -x "$(command -v xcbeautify)" ]; then
      buildProject | xcbeautifyFormat && exit "${PIPESTATUS[0]}"
    else
      buildProject
    fi
  fi
}

trap cleanup EXIT
main "$@"
