Skip to content

Commit

Permalink
Merge branch 'main' into dependabot/github_actions/futureware-tech/si…
Browse files Browse the repository at this point in the history
…mulator-action-736249ceb5ed7916224ed6b6d3c3582fd8049deb
  • Loading branch information
nilsreichardt authored Jul 1, 2023
2 parents e131252 + 55cf126 commit 5f991aa
Show file tree
Hide file tree
Showing 11 changed files with 127 additions and 50 deletions.
112 changes: 67 additions & 45 deletions .github/workflows/integration_tests_app_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ jobs:
if: ${{ needs.changes.outputs.changesFound == 'true' }}
# Don't use less than 90 minutes. Often 40 minutes are enough but sometimes
# (~5% of the time) build takes longer and then is a long timeout needed.
defaults:
run:
working-directory: app
timeout-minutes: 90
steps:
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
Expand Down Expand Up @@ -110,58 +113,73 @@ jobs:
# flaky in terms of stability. With `flutter drive` ~ 30% the Android
# Emulator have difficulties to start properly.
- name: Build Android App
working-directory: ./app
env:
USER_1_EMAIL: ${{ secrets.INTEGRATION_TEST_USER_1_EMAIL }}
USER_1_PASSWORD: ${{ secrets.INTEGRATION_TEST_USER_1_PASSWORD }}
run: flutter build apk --target=integration_test/app_test.dart --flavor dev --dart-define USER_1_EMAIL=$USER_1_EMAIL --dart-define USER_1_PASSWORD=$USER_1_PASSWORD
run: flutter build apk --target=integration_test/app_test.dart --flavor prod --dart-define USER_1_EMAIL=$USER_1_EMAIL --dart-define USER_1_PASSWORD=$USER_1_PASSWORD

- name: Create and start emulator
# We use the installed SDK in the macOS image to create and start the
# emulator.
#
# When you are going to change something here (like a different target,
# arch, profile, channel, etc.), please check if these configurations
# are stable. You can do this by running this workflow multiple times.
# Use a matrix to run multiple tests in parallel (matrix needs to be
# copied under "android-integration-test"):
# strategy:
# fail-fast: false
# matrix:
# test1: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# test2: [1, 2, 3]
#
# When starting the emulator, we use some emulator options for the
# following reasons:
# * "-no-snapshot": Disables the quick boot feature. Therefore, the
# emulator does not load or save the emulator state. We want to have a
# fresh testing environment for every test run.
# * "-no-window": Disables the graphical windows, so no display is
# required.
# * "-no-boot-anim": Disables the boot animation for faster booting.
# * "-camera-back virtualscene": Shows this virtual room when opening
# the back camera where you can walk. Default option for emulators
# created by Android Studio. We use this so we have the same
# environment locally as when using this action. Is needed when
# testing things with the camera. It's also possible to inject images
# into the virtual scene to test things like qr code scanner.
# * "-camera-front emulated": Shows this green monster when opening the
# front camera. Default option for emulators created by Android
# Studio. We use this so we have the same environment locally as when
# using this action. Is needed when testing things with camera.
run: |
API_LEVEL=33
TARGET=google_apis
ARCH=x86_64
echo "Installing system image"
echo "y" | $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager "system-images;android-$API_LEVEL;$TARGET;$ARCH"
echo "Creating AVD"
echo "no" | $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/avdmanager create avd -n test_emulator -k "system-images;android-$API_LEVEL;$TARGET;$ARCH" --force
echo "Starting emulator"
$ANDROID_SDK_ROOT/emulator/emulator -avd test_emulator -no-snapshot -no-boot-anim -no-window -camera-back virtualscene -camera-front emulated &
adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done; input keyevent 82'
- name: Run integration tests
uses: reactivecircus/android-emulator-runner@d94c3fbe4fe6a29e4a5ba47c12fb47677c73656b
env:
USER_1_EMAIL: ${{ secrets.INTEGRATION_TEST_USER_1_EMAIL }}
USER_1_PASSWORD: ${{ secrets.INTEGRATION_TEST_USER_1_PASSWORD }}
with:
# When you are going to change something here (like a different
# target, arch, profile, channel, etc.), please check if these
# configurations are stable.
#
# You can do this by running this workflow multiple times. Use a
# matrix to run multiple tests in parallel (matrix needs to be copied
# to above the "with"):
# strategy:
# fail-fast: false
# matrix:
# test1: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# test2: [1, 2, 3]
api-level: 30
target: google_apis
arch: x86_64
profile: Nexus 5X
channel: canary
# We use some emulator options for the following reasons:
# * "-no-snapshot": Disables the quick boot feature. Therefore, the
# emulator does not load or save the emulator state. We want to have
# * "-no-window": Disables the graphical windows, so no display is
# required.
# a fresh testing environment for every test run.
# * "-no-boot-anim": Disables the boot animation for faster booting.
# * "-camera-back virtualscene": Shows this virtual room when opening
# the back camera where you can walk. Default option for emulators
# created by Android Studio. We use this so we have the same
# environment locally as when using this action. Is needed when
# testing things with the camera. It's also possible to inject
# images into the virtual scene to test things like qr code scanner.
# * "-camera-front": Shows this green monster when opening the front
# camera. Default option for emulators created by Android Studio. We
# use this so we have the same environment locally as when using
# this action. Is needed when testing things with camera.
emulator-options: -no-snapshot -no-window -no-boot-anim -camera-back virtualscene -camera-front emulated
working-directory: ./app
# We can not use a multiline command because the
# "android-emulator-runner" action doesn't support it. It just takes every
# line as separate command.
script: flutter test integration_test --flavor dev --dart-define USER_1_EMAIL=$USER_1_EMAIL --dart-define USER_1_PASSWORD=$USER_1_PASSWORD
run: |
# We need to run the integration tests with the prod flavor because
# using not the default flavor will cause an exception when
# uninstalling the app, see:
# https://github.com/flutter/flutter/issues/88690
flutter test \
integration_test \
--flavor prod \
--dart-define USER_1_EMAIL=$USER_1_EMAIL \
--dart-define USER_1_PASSWORD=$USER_1_PASSWORD
ios-integration-test:
needs: changes
Expand Down Expand Up @@ -205,10 +223,14 @@ jobs:
# set the timeout (the --timeout argument has not effect). Tracking
# issue: https://github.com/flutter/flutter/issues/105913
run: |
# We need to run the integration tests with the prod flavor because
# using not the default flavor will cause an exception when
# uninstalling the app, see:
# https://github.com/flutter/flutter/issues/88690
fvm flutter drive \
--driver=test_driver/integration_test.dart \
--target=integration_test/app_test.dart \
--flavor dev \
--flavor prod \
--dart-define=USER_1_EMAIL=$USER_1_EMAIL \
--dart-define=USER_1_PASSWORD=$USER_1_PASSWORD \
-d $SIMULATOR_UDID
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/licence.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
- uses: actions/setup-go@dd84a9531a6f8e72c321f2aa3b9048ed359670e4
- uses: actions/setup-go@992f0689006f912ae66668969f1fa6618fc53ebd
with:
go-version: '^1.13.1'
- run: go install github.com/google/addlicense@v1.1.1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pull_request_label.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ jobs:
runs-on: ubuntu-22.04
timeout-minutes: 10
steps:
- uses: actions/labeler@9471598e3b7ff22b2fa181bd79addf94cb3e0847
- uses: actions/labeler@a2124851476f3021a479b14e553878efba8cd0dc
2 changes: 1 addition & 1 deletion .github/workflows/unsafe_app_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ jobs:
--target=lib/main_dev.dart
- name: Deploy to Firebase Hosting (sharezone-debug)
uses: FirebaseExtended/action-hosting-deploy@4d0d0023f1d92b9b7d16dda64b3d7abd2c98974b
uses: FirebaseExtended/action-hosting-deploy@120e124148ab7016bec2374e5050f15051255ba2
with:
repoToken: ${{ secrets.GITHUB_TOKEN }}
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_SHAREZONE_DEBUG }}
Expand Down
9 changes: 8 additions & 1 deletion app/android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
android.enableJetifier=true
android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536M
extra-gen-snapshot-options=--obfuscate
android.enableR8=true

# Workaround for 'Unable to make field private final java.lang.String
# java.io.File.path accessible: module java.base does not "opens java.io" to
# unnamed module @2f76df44' using Java 17.
#
# Sourec:
# https://github.com/fluttercommunity/flutter_workmanager/issues/287#issuecomment-893476072
org.gradle.jvmargs=-Xmx1536M --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED
3 changes: 2 additions & 1 deletion app/integration_test/app_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ void main() {
_UserCredentials user1;

setUpAll(() async {
dependencies = await initializeDependencies(flavor: Flavor.dev);
dependencies = await initializeDependencies(flavor: Flavor.prod);
});

setUp(() async {
Expand All @@ -43,6 +43,7 @@ void main() {
blocDependencies: dependencies.blocDependencies,
dynamicLinkBloc: dependencies.dynamicLinkBloc,
flavor: Flavor.dev,
isIntegrationTest: true,
),
);
}
Expand Down
12 changes: 12 additions & 0 deletions app/lib/main/sharezone.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ import 'package:sharezone_utils/device_information_manager.dart';
import 'package:sharezone_utils/platform.dart';
import 'package:sharezone_widgets/sharezone_widgets.dart';

/// Defines if the app is running in integration test mode.
///
/// This is used to disable some features, which are not working for integration
/// tests. These features are:
/// * Firebase Messaging (throws SERVICE_NOT_AVAILABLE or AUTHENTICATION_FAILED
/// when running on device farm devices, see
/// https://github.com/SharezoneApp/sharezone-app/issues/420)
bool isIntegrationTest = false;

/// StreamBuilder "above" the Auth and SharezoneApp.
/// Reasoning is that if the user logged out,
/// he will always be in the log in screen.
Expand All @@ -41,13 +50,15 @@ class Sharezone extends StatefulWidget {
final DynamicLinkBloc dynamicLinkBloc;
final Stream<Beitrittsversuch> beitrittsversuche;
final Flavor flavor;
final bool isIntegrationTest;

const Sharezone({
Key key,
@required this.blocDependencies,
@required this.dynamicLinkBloc,
@required this.beitrittsversuche,
@required this.flavor,
this.isIntegrationTest = false,
}) : super(key: key);

static Analytics analytics = Analytics(getBackend());
Expand All @@ -63,6 +74,7 @@ class _SharezoneState extends State<Sharezone> with WidgetsBindingObserver {
void initState() {
super.initState();

isIntegrationTest = widget.isIntegrationTest;
signUpBloc = SignUpBloc();

// You have to wait a little moment (1000 milliseconds), to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'package:notifications/notifications.dart';
import 'package:overlay_support/overlay_support.dart';
import 'package:sharezone/blocs/application_bloc.dart';
import 'package:sharezone/logging/logging.dart';
import 'package:sharezone/main/sharezone.dart';
import 'package:sharezone/navigation/logic/navigation_bloc.dart';
import 'package:sharezone/notifications/notifications_permission.dart';
import 'package:sharezone/notifications/push_notification_action_handler_instrumentation_implementation.dart';
Expand Down Expand Up @@ -46,6 +47,11 @@ class FirebaseMessagingCallbackConfigurator {
});

Future<void> configureCallbacks(BuildContext context) async {
if (isIntegrationTest) {
// Firebase Messaging is not available in integration tests.
return;
}

await _requestPermissionIfNeeded(context);

final _logger = szLogger.makeChild('FirebaseMessagingCallbackConfigurator');
Expand Down
9 changes: 9 additions & 0 deletions app/lib/notifications/notifications_permission.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
//
// SPDX-License-Identifier: EUPL-1.2

import 'dart:developer';

import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:meta/meta.dart';
import 'package:sharezone/main/sharezone.dart';
import 'package:sharezone_utils/device_information_manager.dart';
import 'package:sharezone_utils/platform.dart';

Expand Down Expand Up @@ -43,6 +46,12 @@ class NotificationsPermission {
}

Future<void> requestPermission() async {
if (isIntegrationTest) {
// Firebase Messaging is not available in integration tests.
log('Skipping to request Firebase Messaging access because integration test is running.');
return;
}

await firebaseMessaging.requestPermission(
alert: true,
sound: true,
Expand Down
7 changes: 7 additions & 0 deletions app/lib/util/api/user_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// SPDX-License-Identifier: EUPL-1.2

import 'dart:async';
import 'dart:developer';

import 'package:app_functions/app_functions.dart';
import 'package:authentification_base/authentification.dart';
Expand All @@ -15,6 +16,7 @@ import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:rxdart/subjects.dart';
import 'package:sharezone/main/sharezone.dart';
import 'package:sharezone/util/api.dart';
import 'package:sharezone_common/api_errors.dart';
import 'package:sharezone_common/references.dart';
Expand Down Expand Up @@ -73,6 +75,11 @@ class UserGateway implements UserGatewayAuthentifcation {

Future<void> logOut() async {
if (PlatformCheck.isMobile) {
if (isIntegrationTest) {
// Firebase Messaging is not available in integration tests.
log('Skipping to remove Firebase Messaging token because integration test is running.');
return;
}
removeNotificationToken(await FirebaseMessaging.instance.getToken());
}
authUser.signOut();
Expand Down
13 changes: 13 additions & 0 deletions app/lib/util/notification_token_adder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import 'dart:developer';

import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:sharezone/main/sharezone.dart';
import 'package:sharezone/util/api/user_api.dart';
import 'package:user/user.dart';

Expand Down Expand Up @@ -52,10 +53,22 @@ class NotificationTokenAdderApi {
);

Future<String> getFCMToken() {
if (isIntegrationTest) {
// Firebase Messaging is not available in integration tests.
log('Skipping to get FCM token because integration test is running.');
return null;
}

return _firebaseMessaging.getToken(vapidKey: vapidKey);
}

Future<void> tryAddTokenToDatabase(String token) async {
if (isIntegrationTest) {
// Firebase Messaging is not available in integration tests.
log('Skipping to add token to the database because integration test is running.');
return;
}

try {
await _userApi.addNotificationToken(token);
} on Exception catch (e) {
Expand Down

0 comments on commit 5f991aa

Please sign in to comment.