@@ -41,11 +41,11 @@ jobs:
4141 gradle-version : " 8.14.3"
4242 add-job-summary : on-failure
4343 add-job-summary-as-pr-comment : on-failure
44- - name : Build for Testing
44+ - name : Build App for Testing
4545 if : success() || failure()
4646 run : |
4747 ./gradlew native:NativeSampleApps:AuthFlowTester:assembleDebug
48- - name : Build Tests
48+ - name : Build UI Tests
4949 run : |
5050 ./gradlew native:NativeSampleApps:AuthFlowTester:assembleAndroidTest
5151 - uses : ' google-github-actions/auth@v2'
@@ -54,84 +54,163 @@ jobs:
5454 credentials_json : ' ${{ secrets.GCLOUD_SERVICE_KEY }}'
5555 - uses : ' google-github-actions/setup-gcloud@v2'
5656 if : success() || failure()
57- - name : Run Tests
57+ - name : Run PR Tests
5858 continue-on-error : true
59- if : success() || failure()
59+ if : ${{ inputs.is_pr }}
6060 env :
6161 # Most used according to https://gs.statcounter.com/android-version-market-share/mobile-tablet/worldwide
6262 PR_API_VERSION : " 35"
63+ run : |
64+ GCLOUD_RESULTS_DIR=authflowtester-pr-build-${{github.run_number}}
65+
66+ PR_TESTS="class com.salesforce.samples.authflowtester.BootConfigLoginTests#testCAOpaque_DefaultScopes_WebServerFlow, \
67+ class com.salesforce.samples.authflowtester.ECALoginTests#testECAOpaque_DefaultScopes, \
68+ class com.salesforce.samples.authflowtester.ECALoginTests#testECAJwt_AllScopes, \
69+ class com.salesforce.samples.authflowtester.TokenMigrationTest#testMigrate_ECA_AddMoreScopes, \
70+ class com.salesforce.samples.authflowtester.MultiUserLoginTests#testSameApp_SameScopes_uniqueTokens"
71+
72+ gcloud firebase test android run \
73+ --project mobile-apps-firebase-test \
74+ --type instrumentation \
75+ --use-orchestrator \
76+ --environment-variables clearPackageData=true \
77+ --app "native/NativeSampleApps/AuthFlowTester/build/outputs/apk/debug/AuthFlowTester-debug.apk" \
78+ --test "native/NativeSampleApps/AuthFlowTester/build/outputs/apk/androidTest/debug/AuthFlowTester-debug-androidTest.apk" \
79+ --device model=MediumPhone.arm,version="${PR_API_VERSION}",locale=en,orientation=portrait \
80+ --directories-to-pull=/sdcard \
81+ --results-dir="${GCLOUD_RESULTS_DIR}" \
82+ --results-history-name=AuthFlowTester \
83+ --no-performance-metrics \
84+ --test-targets="${PR_TESTS}" \
85+ --timeout=10m \
86+ --num-flaky-test-attempts=1
87+ - name : Run All Single User Tests
88+ continue-on-error : true
89+ if : ${{ ! inputs.is_pr }}
90+ env :
6391 FULL_API_RANGE : " 28 29 30 31 32 33 34 35 36"
64- IS_PR : ${{ inputs.is_pr }}
6592 run : |
66- LEVELS_TO_TEST=$FULL_API_RANGE
67- RETRIES=0
68- TEST_TARGETS=""
69-
70- if $IS_PR ; then
71- LEVELS_TO_TEST=$PR_API_VERSION
72- RETRIES=1
73- # Run only a handful of smoke tests.
74- TEST_TARGETS="--test-targets \"class com.salesforce.samples.authflowtester.LoginTest#testBasicLogin\""
75- TEST_TARGETS+=",\"class com.salesforce.samples.authflowtester.TokenMigrationTest#testMigrate_ECA_AddMoreScopes\""
76- fi
77-
78- mkdir firebase_results
79- for LEVEL in $LEVELS_TO_TEST
80- do
81- GCLOUD_RESULTS_DIR=authflowtester-api-${LEVEL}-build-${{github.run_number}}
82-
83- eval gcloud firebase test android run \
84- --project mobile-apps-firebase-test \
85- --type instrumentation \
86- --use-orchestrator \
87- --environment-variables clearPackageData=true \
88- --app "native/NativeSampleApps/AuthFlowTester/build/outputs/apk/debug/AuthFlowTester-debug.apk" \
89- --test "native/NativeSampleApps/AuthFlowTester/build/outputs/apk/androidTest/debug/AuthFlowTester-debug-androidTest.apk" \
90- --device model=MediumPhone.arm,version=${LEVEL},locale=en,orientation=portrait \
91- --directories-to-pull=/sdcard \
92- --results-dir=${GCLOUD_RESULTS_DIR} \
93- --results-history-name=AuthFlowTester \
94- --timeout=10m --no-performance-metrics \
95- $TEST_TARGETS \
96- --num-flaky-test-attempts=${RETRIES} || true
97- done
93+ GCLOUD_RESULTS_DIR=authflowtester-single-user-build-${{github.run_number}}
94+ DEVICE_ARGS=()
95+ for LEVEL in $FULL_API_RANGE; do
96+ DEVICE_ARGS+=(--device "model=MediumPhone.arm,version=${LEVEL},locale=en,orientation=portrait")
97+ done
98+
99+ gcloud firebase test android run \
100+ --project mobile-apps-firebase-test \
101+ --type instrumentation \
102+ --use-orchestrator \
103+ --environment-variables clearPackageData=true \
104+ --app "native/NativeSampleApps/AuthFlowTester/build/outputs/apk/debug/AuthFlowTester-debug.apk" \
105+ --test "native/NativeSampleApps/AuthFlowTester/build/outputs/apk/androidTest/debug/AuthFlowTester-debug-androidTest.apk" \
106+ --test-targets "notClass com.salesforce.samples.authflowtester.MultiUserLoginTests" \
107+ "${DEVICE_ARGS[@]}" \
108+ --directories-to-pull=/sdcard \
109+ --results-dir="${GCLOUD_RESULTS_DIR}" \
110+ --results-history-name=AuthFlowTester \
111+ --no-performance-metrics \
112+ --num-flaky-test-attempts=1 \
113+ --timeout=30m || true
114+ - name : Run All Multi User Tests
115+ continue-on-error : true
116+ if : ${{ ! inputs.is_pr }}
117+ env :
118+ FULL_API_RANGE : " 28 29 30 31 32 33 34 35 36"
119+ run : |
120+ GCLOUD_RESULTS_DIR=authflowtester-multi-user-build-${{github.run_number}}
121+ DEVICE_ARGS=()
122+ for LEVEL in $FULL_API_RANGE; do
123+ DEVICE_ARGS+=(--device "model=MediumPhone.arm,version=${LEVEL},locale=en,orientation=portrait")
124+ done
125+
126+ gcloud firebase test android run \
127+ --project mobile-apps-firebase-test \
128+ --type instrumentation \
129+ --use-orchestrator \
130+ --environment-variables clearPackageData=true \
131+ --app "native/NativeSampleApps/AuthFlowTester/build/outputs/apk/debug/AuthFlowTester-debug.apk" \
132+ --test "native/NativeSampleApps/AuthFlowTester/build/outputs/apk/androidTest/debug/AuthFlowTester-debug-androidTest.apk" \
133+ --test-targets "class com.salesforce.samples.authflowtester.MultiUserLoginTests" \
134+ "${DEVICE_ARGS[@]}" \
135+ --directories-to-pull=/sdcard \
136+ --results-dir="${GCLOUD_RESULTS_DIR}" \
137+ --results-history-name=AuthFlowTester \
138+ --no-performance-metrics \
139+ --num-flaky-test-attempts=1 \
140+ --timeout=15m || true
98141 - name : Copy Test Results
99142 continue-on-error : true
100143 if : success() || failure()
101144 env :
102- # Most used according to https://gs.statcounter.com/android-version-market-share/mobile-tablet/worldwide
103- PR_API_VERSION : " 35"
104- FULL_API_RANGE : " 28 29 30 31 32 33 34 35 36"
105145 IS_PR : ${{ inputs.is_pr }}
106146 run : |
107- LEVELS_TO_TEST=$FULL_API_RANGE
147+ mkdir -p firebase_results
148+ BUCKET="gs://test-lab-w87i9sz6q175u-kwp8ium6js0zw"
108149
109- if $IS_PR ; then
110- LEVELS_TO_TEST=$PR_API_VERSION
111- fi
150+ copy_results_by_api_level() {
151+ local BUCKET_PATH=$1
152+ local OUTPUT_PREFIX=$2
112153
113- for LEVEL in $LEVELS_TO_TEST
114- do
115- GCLOUD_RESULTS_DIR=authflowtester-api-${LEVEL}-build-${{github.run_number}}
116- BUCKET_PATH="gs://test-lab-w87i9sz6q175u-kwp8ium6js0zw/${GCLOUD_RESULTS_DIR}"
117-
118- gsutil ls ${BUCKET_PATH} > /dev/null 2>&1
119- if [ $? == 0 ] ; then
120- # Copy XML file for test reporting
121- if gsutil ls "${BUCKET_PATH}/*test_results_merged.xml" > /dev/null 2>&1; then
122- # Sharded runs produce test_results_merged.xml at top level
123- gsutil cp "${BUCKET_PATH}/*test_results_merged.xml" firebase_results/api_${LEVEL}_test_result.xml
124- else
125- gsutil cp "${BUCKET_PATH}/*/test_result_1.xml" firebase_results/api_${LEVEL}_test_result.xml
126- fi
127- fi
154+ # Pass 1: copy original (non-rerun) results
155+ for RESULT_FILE in $(gsutil ls "${BUCKET_PATH}/*/test_result_1.xml" 2>/dev/null | grep -v "rerun"); do
156+ DEVICE_DIR=$(echo "${RESULT_FILE}" | sed 's|.*/\([^/]*\)/test_result_1.xml|\1|')
157+ API_LEVEL=$(echo "${DEVICE_DIR}" | sed 's/.*-\([0-9]*\)-.*/\1/')
158+ gsutil cp "${RESULT_FILE}" "firebase_results/${OUTPUT_PREFIX}_api_${API_LEVEL}_test_result.xml"
159+ done
160+ # Pass 2: merge rerun testcases into originals so check_retries detects flaky tests
161+ for RESULT_FILE in $(gsutil ls "${BUCKET_PATH}/*/test_result_1.xml" 2>/dev/null | grep "rerun"); do
162+ DEVICE_DIR=$(echo "${RESULT_FILE}" | sed 's|.*/\([^/]*\)/test_result_1.xml|\1|')
163+ API_LEVEL=$(echo "${DEVICE_DIR}" | sed 's/.*-\([0-9]*\)-.*/\1/')
164+ RERUN_TMP="firebase_results/${OUTPUT_PREFIX}_api_${API_LEVEL}_rerun_tmp.xml"
165+ ORIG_FILE="firebase_results/${OUTPUT_PREFIX}_api_${API_LEVEL}_test_result.xml"
166+ gsutil cp "${RESULT_FILE}" "${RERUN_TMP}"
167+ python3 - "${ORIG_FILE}" "${RERUN_TMP}" "${ORIG_FILE}" << 'PYEOF'
168+ import sys, xml.etree.ElementTree as ET
169+ orig = ET.parse(sys.argv[1])
170+ rerun = ET.parse(sys.argv[2])
171+ def suite(t):
172+ r = t.getroot()
173+ return r if r.tag == 'testsuite' else r.find('testsuite')
174+ os_el, rs_el = suite(orig), suite(rerun)
175+ failed_keys = set()
176+ for tc in os_el.findall('testcase'):
177+ if tc.find('failure') is not None or tc.find('error') is not None:
178+ failed_keys.add(f"{tc.get('name','')}|{tc.get('classname','')}|{tc.get('file','')}")
179+ added = 0
180+ for tc in rs_el.findall('testcase'):
181+ if f"{tc.get('name','')}|{tc.get('classname','')}|{tc.get('file','')}" in failed_keys:
182+ os_el.append(tc)
183+ added += 1
184+ os_el.set('tests', str(int(os_el.get('tests','0')) + added))
185+ with open(sys.argv[3], 'w') as f:
186+ f.write(ET.tostring(orig.getroot(), encoding='unicode'))
187+ PYEOF
188+ rm "${RERUN_TMP}"
128189 done
190+ }
191+
192+ if $IS_PR ; then
193+ BUCKET_PATH="${BUCKET}/authflowtester-pr-build-${{github.run_number}}"
194+ if gsutil ls "${BUCKET_PATH}" > /dev/null 2>&1; then
195+ copy_results_by_api_level "${BUCKET_PATH}" "pr"
196+ fi
197+ else
198+ SINGLE_PATH="${BUCKET}/authflowtester-single-user-build-${{github.run_number}}"
199+ if gsutil ls "${SINGLE_PATH}" > /dev/null 2>&1; then
200+ copy_results_by_api_level "${SINGLE_PATH}" "single-user"
201+ fi
202+
203+ MULTI_PATH="${BUCKET}/authflowtester-multi-user-build-${{github.run_number}}"
204+ if gsutil ls "${MULTI_PATH}" > /dev/null 2>&1; then
205+ copy_results_by_api_level "${MULTI_PATH}" "multi-user"
206+ fi
207+ fi
129208 - name : Test Report
130209 uses : mikepenz/action-junit-report@v6
131210 if : success() || failure()
132211 with :
133- check_name : ${{ inputs.lib }} Test Results
134- job_name : ${{ inputs.lib }} Test Results
212+ check_name : AuthFlowTester Test Results
213+ job_name : AuthFlowTester Test Results
135214 require_tests : true
136215 check_retries : true
137216 flaky_summary : true
@@ -140,4 +219,16 @@ jobs:
140219 include_passed : true
141220 include_empty_in_summary : false
142221 simplified_summary : true
143- report_paths : ' firebase_results/**.xml'
222+ report_paths : ' firebase_results/**.xml'
223+ - name : Archive APK
224+ if : success() || failure()
225+ uses : actions/upload-artifact@v4
226+ with :
227+ name : AuthFlowTester-debug-${{ github.run_number }}
228+ path : native/NativeSampleApps/AuthFlowTester/build/outputs/apk/debug/AuthFlowTester-debug.apk
229+ - name : Archive Test Results
230+ uses : actions/upload-artifact@v4
231+ if : success() || failure()
232+ with :
233+ name : ui-test-results
234+ path : ' firebase_results/**.xml'
0 commit comments