1#!/bin/bash
2# Copyright (c) Meta Platforms, Inc. and affiliates.
3#
4# This source code is licensed under the MIT license found in the
5# LICENSE file in the root directory of this source tree.
6
7# Script used to run iOS tests.
8# If no arguments are passed to the script, it will only compile
9# the RNTester.
10# If the script is called with a single argument "test", we'll
11# run the RNTester unit and integration tests
12# ./objc-test.sh test
13
14SCRIPTS=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
15ROOT=$(dirname "$SCRIPTS")
16
17SKIPPED_TESTS=()
18SKIPPED_TESTS+=("-skip-testing:RNTesterIntegrationTests/RNTesterSnapshotTests")
19# TODO: T60408036 This test crashes iOS 13 for bad access, please investigate
20# and re-enable. See https://gist.github.com/0xced/56035d2f57254cf518b5.
21SKIPPED_TESTS+=("-skip-testing:RNTesterUnitTests/RCTJSONTests/testNotUTF8Convertible")
22
23# Create cleanup handler
24cleanup() {
25  EXIT=$?
26  set +e
27
28  if [ $EXIT -ne 0 ];
29  then
30    WATCHMAN_LOGS=/usr/local/Cellar/watchman/3.1/var/run/watchman/$USER.log
31    [ -f "$WATCHMAN_LOGS" ] && cat "$WATCHMAN_LOGS"
32  fi
33  # kill whatever is occupying port 8081 (packager)
34  lsof -i tcp:8081 | awk 'NR!=1 {print $2}' | xargs kill
35  # kill whatever is occupying port 5555 (web socket server)
36  lsof -i tcp:5555 | awk 'NR!=1 {print $2}' | xargs kill
37}
38
39# Wait for the package to start
40waitForPackager() {
41  local -i max_attempts=60
42  local -i attempt_num=1
43
44  until curl -s http://localhost:8081/status | grep "packager-status:running" -q; do
45    if (( attempt_num == max_attempts )); then
46      echo "Packager did not respond in time. No more attempts left."
47      exit 1
48    else
49      (( attempt_num++ ))
50      echo "Packager did not respond. Retrying for attempt number $attempt_num..."
51      sleep 1
52    fi
53  done
54
55  echo "Packager is ready!"
56}
57
58waitForWebSocketServer() {
59  local -i max_attempts=60
60  local -i attempt_num=1
61
62  until curl -s http://localhost:5555 | grep "Upgrade Required" -q; do
63    if (( attempt_num == max_attempts )); then
64      echo "WebSocket Server did not respond in time. No more attempts left."
65      exit 1
66    else
67      (( attempt_num++ ))
68      echo "WebSocket Server did not respond. Retrying for attempt number $attempt_num..."
69      sleep 1
70    fi
71  done
72
73  echo "WebSocket Server is ready!"
74}
75
76runTests() {
77  # shellcheck disable=SC1091
78  source "./scripts/.tests.env"
79  xcodebuild build test \
80    -workspace packages/rn-tester/RNTesterPods.xcworkspace \
81    -scheme RNTester \
82    -sdk iphonesimulator \
83    -destination "platform=iOS Simulator,name=$IOS_DEVICE,OS=$IOS_TARGET_OS" \
84      "${SKIPPED_TESTS[@]}"
85}
86
87buildProject() {
88  xcodebuild build \
89    -workspace packages/rn-tester/RNTesterPods.xcworkspace \
90    -scheme RNTester \
91    -sdk iphonesimulator
92}
93
94xcprettyFormat() {
95  if [ "$CI" ]; then
96    # Circle CI expects JUnit reports to be available here
97    REPORTS_DIR="$HOME/react-native/reports/junit"
98  else
99    THIS_DIR=$(cd -P "$(dirname "$(realpath "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
100
101    # Write reports to the react-native root dir
102    REPORTS_DIR="$THIS_DIR/../build/reports"
103  fi
104
105  xcpretty --report junit --output "$REPORTS_DIR/ios/results.xml"
106}
107
108preloadBundles() {
109  # Preload the RNTesterApp bundle for better performance in integration tests
110  curl -s 'http://localhost:8081/packages/rn-tester/js/RNTesterApp.ios.bundle?platform=ios&dev=true' -o /dev/null
111  curl -s 'http://localhost:8081/packages/rn-tester/js/RNTesterApp.ios.bundle?platform=ios&dev=true&minify=false' -o /dev/null
112  curl -s 'http://localhost:8081/IntegrationTests/IntegrationTestsApp.bundle?platform=ios&dev=true' -o /dev/null
113  curl -s 'http://localhost:8081/IntegrationTests/RCTRootViewIntegrationTestApp.bundle?platform=ios&dev=true' -o /dev/null
114}
115
116main() {
117  cd "$ROOT" || exit
118
119  # If first argument is "test", actually start the packager and run tests.
120  # Otherwise, just build RNTester and exit
121  if [ "$1" = "test" ]; then
122
123    # Start the WebSocket test server
124    echo "Launch WebSocket Server"
125    sh "./IntegrationTests/launchWebSocketServer.sh" &
126    waitForWebSocketServer
127
128    # Start the packager
129    yarn start --max-workers=1 || echo "Can't start packager automatically" &
130    waitForPackager
131    preloadBundles
132
133    # Build and run tests.
134    if [ -x "$(command -v xcpretty)" ]; then
135      runTests | xcprettyFormat && exit "${PIPESTATUS[0]}"
136    else
137      echo 'Warning: xcpretty is not installed. Install xcpretty to generate JUnit reports.'
138      runTests
139    fi
140  else
141    # Build without running tests.
142    if [ -x "$(command -v xcpretty)" ]; then
143      buildProject | xcprettyFormat && exit "${PIPESTATUS[0]}"
144    else
145      buildProject
146    fi
147  fi
148}
149
150trap cleanup EXIT
151main "$@"
152