Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions .github/workflows/flutter_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,14 @@ jobs:
avd-name: avd-x86_64-31
emulator-options: -no-snapshot-save -no-window -accel on -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
script: flutter test integration_test/all.dart --dart-define SENTRY_AUTH_TOKEN_E2E=$SENTRY_AUTH_TOKEN_E2E --verbose && flutter drive --driver=integration_test/test_driver/driver.dart --target=integration_test/sentry_widgets_flutter_binding_test.dart --profile -d emulator-5554
script: |
flutter test integration_test/all.dart --dart-define SENTRY_AUTH_TOKEN_E2E=$SENTRY_AUTH_TOKEN_E2E --verbose
flutter clean
flutter drive --driver=integration_test/test_driver/driver.dart --target=integration_test/sentry_widgets_flutter_binding_test.dart --profile -d emulator-5554

- name: build apk
working-directory: packages/flutter/example/android
run: flutter build apk --debug --target-platform=android-x64
run: flutter clean && flutter build apk --debug --target-platform=android-x64

cocoa:
name: '${{ matrix.target }} | ${{ matrix.sdk }}'
Expand Down Expand Up @@ -129,6 +132,18 @@ jobs:
run: |
flutter drive --driver=integration_test/test_driver/driver.dart --target=integration_test/sentry_widgets_flutter_binding_test.dart --profile -d "${{ steps.device.outputs.name }}"

- name: run native test
# We only have the native unit test package in the iOS xcodeproj at the moment.
# Should be OK because it will likely be removed after switching to FFI (see https://github.com/getsentry/sentry-dart/issues/1444).
if: ${{ matrix.target == 'ios' }}
working-directory: packages/flutter/example/${{ matrix.target }}
# For some reason running native unit tests directly after Flutter integration tests fails
# running flutter build ios before works: https://stackoverflow.com/a/77487525/22813624
run: |
flutter build ios --no-codesign
xcodebuild test -workspace Runner.xcworkspace -scheme Runner -configuration Debug -destination "platform=$DEVICE_PLATFORM" -allowProvisioningUpdates CODE_SIGNING_ALLOWED=NO
env:
DEVICE_PLATFORM: ${{ steps.device.outputs.platform }}
# TODO(buenaflor): fix the web integration test. The tests pass but the driver keeps hanging so the workflow cancels after 30minutes
# web:
# runs-on: ubuntu-latest
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Fixes

- Revert FFI usage on iOS/macOS due to symbol stripping issues ([#3379](https://github.com/getsentry/sentry-dart/pull/3379))

## 9.9.0-beta.3

### Features
Expand Down
222 changes: 80 additions & 142 deletions packages/flutter/example/integration_test/integration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@ import 'package:package_info_plus/package_info_plus.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:sentry_flutter_example/main.dart';
import 'package:sentry_flutter/src/native/java/sentry_native_java.dart';
import 'package:sentry_flutter/src/native/cocoa/sentry_native_cocoa.dart';
import 'package:sentry_flutter/src/native/java/binding.dart' as jni;
import 'package:sentry_flutter/src/native/cocoa/binding.dart' as cocoa;
import 'package:objective_c/objective_c.dart';

import 'utils.dart';

Expand Down Expand Up @@ -169,13 +166,8 @@ void main() {
await transaction.finish();
});

testWidgets('init maps Dart options into native SDK options', (tester) async {
if (Platform.isIOS || Platform.isMacOS) {
// Since this is a static var previous test might have overridden this so
// we should set this back to the default (false).
cocoa.PrivateSentrySDKOnly.setAppStartMeasurementHybridSDKMode(false);
}

testWidgets('init maps Dart options into native SDK options on Android',
(tester) async {
await restoreFlutterOnErrorAfter(() async {
await setupSentryWithCustomInit(() async {
await tester.pumpWidget(
Expand Down Expand Up @@ -219,140 +211,86 @@ void main() {
options.replay.quality = SentryReplayQuality.high;
options.replay.sessionSampleRate = 0.4;
options.replay.onErrorSampleRate = 0.8;

// Cocoa-only
if (Platform.isIOS || Platform.isMacOS) {
options.recordHttpBreadcrumbs = false;
options.captureFailedRequests = false;
options.enableAppHangTracking = false;
options.appHangTimeoutInterval = const Duration(seconds: 1);
}
// Android-only
if (Platform.isAndroid) {
options.enableNdkScopeSync = true;
options.attachThreads = true;
options.anrEnabled = false;
options.anrTimeoutInterval = const Duration(seconds: 2);
options.connectionTimeout = const Duration(milliseconds: 1234);
options.readTimeout = const Duration(milliseconds: 2345);
}
options.enableNdkScopeSync = true;
options.attachThreads = true;
options.anrEnabled = false;
options.anrTimeoutInterval = const Duration(seconds: 2);
options.connectionTimeout = const Duration(milliseconds: 1234);
options.readTimeout = const Duration(milliseconds: 2345);
});
});

if (Platform.isIOS || Platform.isMacOS) {
final cocoaOptions = cocoa.PrivateSentrySDKOnly.getOptions();
expect(cocoaOptions, isNotNull);
if (Platform.isIOS) {
final nativeReplayOptions =
cocoa.SentryFlutterPlugin.getReplayOptions();
expect(nativeReplayOptions, isNotNull);
expect(nativeReplayOptions!.quality,
cocoa.SentryReplayQuality.SentryReplayQualityHigh);
// Can't use direct comparison because of floating point precision
expect(nativeReplayOptions.sessionSampleRate, closeTo(0.4, 0.001));
expect(nativeReplayOptions.onErrorSampleRate, closeTo(0.8, 0.001));
}
expect(cocoaOptions.dsn?.toDartString(), fakeDsn);
expect(cocoaOptions.debug, isTrue);
expect(cocoaOptions.diagnosticLevel.value, SentryLevel.error.ordinal);
expect(cocoaOptions.environment.toDartString(), 'init-test-env');
expect(cocoaOptions.releaseName?.toDartString(), '1.2.3+9');
expect(cocoaOptions.dist?.toDartString(), '42');
expect(cocoaOptions.sendDefaultPii, isTrue);
expect(cocoaOptions.attachStacktrace, isFalse);
expect(cocoaOptions.maxBreadcrumbs, 7);
expect(cocoaOptions.maxCacheItems, 77);
expect(cocoaOptions.maxAttachmentSize, 512);
expect(cocoaOptions.enableAutoSessionTracking, isFalse);
expect(cocoaOptions.sessionTrackingIntervalMillis, 5000);
expect(cocoaOptions.enableAutoBreadcrumbTracking, isFalse);
expect(cocoaOptions.enableNetworkBreadcrumbs, isFalse);
expect(cocoaOptions.enableCaptureFailedRequests, isFalse);
expect(cocoaOptions.enableAppHangTracking, isFalse);
expect(cocoaOptions.appHangTimeoutInterval, 1);
expect(cocoaOptions.enableSpotlight, isTrue);
expect(cocoaOptions.spotlightUrl.toDartString(),
Sentry.currentHub.options.spotlight.url);
expect(cocoaOptions.sendClientReports, isFalse);
expect(
cocoa.PrivateSentrySDKOnly.getSdkName().toDartString(), cocoaSdkName);
expect(cocoa.PrivateSentrySDKOnly.getAppStartMeasurementHybridSDKMode(),
isFalse);
// currently cannot assert the sdk package and integration since it's attached only
// to the event and we don't have a convenient way to access beforeSend
} else if (Platform.isAndroid) {
final ref = jni.ScopesAdapter.getInstance()?.getOptions().reference;
expect(ref, isNotNull);
final androidOptions = jni.SentryAndroidOptions.fromReference(ref!);

expect(androidOptions, isNotNull);
expect(androidOptions.getDsn()?.toDartString(), fakeDsn);
expect(androidOptions.isDebug(), isTrue);
final diagnostic = androidOptions.getDiagnosticLevel();
expect(
diagnostic,
jni.SentryLevel.ERROR,
);
expect(androidOptions.getEnvironment()?.toDartString(), 'init-test-env');
expect(androidOptions.getRelease()?.toDartString(), '1.2.3+9');
expect(androidOptions.getDist()?.toDartString(), '42');
expect(androidOptions.isSendDefaultPii(), isTrue);
expect(androidOptions.isAttachStacktrace(), isFalse);
expect(androidOptions.isAttachThreads(), isTrue);
expect(androidOptions.getMaxBreadcrumbs(), 7);
expect(androidOptions.getMaxCacheItems(), 77);
expect(androidOptions.getMaxAttachmentSize(), 512);
expect(androidOptions.isEnableScopeSync(), isTrue);
expect(androidOptions.isAnrEnabled(), isFalse);
expect(androidOptions.getAnrTimeoutIntervalMillis(), 2000);
expect(androidOptions.isEnableActivityLifecycleBreadcrumbs(), isFalse);
expect(androidOptions.isEnableAppLifecycleBreadcrumbs(), isFalse);
expect(androidOptions.isEnableSystemEventBreadcrumbs(), isFalse);
expect(androidOptions.isEnableAppComponentBreadcrumbs(), isFalse);
expect(androidOptions.isEnableUserInteractionBreadcrumbs(), isFalse);
expect(androidOptions.getConnectionTimeoutMillis(), 1234);
expect(androidOptions.getReadTimeoutMillis(), 2345);
expect(androidOptions.isEnableSpotlight(), isTrue);
expect(androidOptions.isSendClientReports(), isFalse);
expect(
androidOptions.getSpotlightConnectionUrl()?.toDartString(),
Sentry.currentHub.options.spotlight.url,
);
expect(androidOptions.getSentryClientName()?.toDartString(),
'$androidSdkName/${jni.BuildConfig.VERSION_NAME?.toDartString()}');
expect(androidOptions.getNativeSdkName()?.toDartString(), nativeSdkName);
expect(androidOptions.getSdkVersion()?.getName().toDartString(),
androidSdkName);
expect(androidOptions.getSdkVersion()?.getVersion().toDartString(),
jni.BuildConfig.VERSION_NAME?.toDartString());
final allPackages = androidOptions
.getSdkVersion()
?.getPackageSet()
.map((pkg) {
if (pkg == null) return null;
return SentryPackage(
pkg.getName().toDartString(), pkg.getVersion().toDartString());
})
.nonNulls
.toList();
for (final package in Sentry.currentHub.options.sdk.packages) {
final findMatchingPackage = allPackages?.firstWhere(
(p) => p.name == package.name && p.version == package.version);
expect(findMatchingPackage, isNotNull);
}
final androidProxy = androidOptions.getProxy();
expect(androidProxy, isNotNull);
expect(androidProxy!.getHost()?.toDartString(), 'proxy.local');
expect(androidProxy.getPort()?.toDartString(), '8084');
expect(androidProxy.getUser()?.toDartString(), 'u');
expect(androidProxy.getPass()?.toDartString(), 'p');
final r = androidOptions.getSessionReplay();
expect(r.getQuality(), jni.SentryReplayOptions$SentryReplayQuality.HIGH);
expect(r.getSessionSampleRate(), isNotNull);
expect(r.getOnErrorSampleRate(), isNotNull);
expect(r.isTrackConfiguration(), isFalse);
final ref = jni.ScopesAdapter.getInstance()?.getOptions().reference;
expect(ref, isNotNull);
final androidOptions = jni.SentryAndroidOptions.fromReference(ref!);

expect(androidOptions, isNotNull);
expect(androidOptions.getDsn()?.toDartString(), fakeDsn);
expect(androidOptions.isDebug(), isTrue);
final diagnostic = androidOptions.getDiagnosticLevel();
expect(
diagnostic,
jni.SentryLevel.ERROR,
);
expect(androidOptions.getEnvironment()?.toDartString(), 'init-test-env');
expect(androidOptions.getRelease()?.toDartString(), '1.2.3+9');
expect(androidOptions.getDist()?.toDartString(), '42');
expect(androidOptions.isSendDefaultPii(), isTrue);
expect(androidOptions.isAttachStacktrace(), isFalse);
expect(androidOptions.isAttachThreads(), isTrue);
expect(androidOptions.getMaxBreadcrumbs(), 7);
expect(androidOptions.getMaxCacheItems(), 77);
expect(androidOptions.getMaxAttachmentSize(), 512);
expect(androidOptions.isEnableScopeSync(), isTrue);
expect(androidOptions.isAnrEnabled(), isFalse);
expect(androidOptions.getAnrTimeoutIntervalMillis(), 2000);
expect(androidOptions.isEnableActivityLifecycleBreadcrumbs(), isFalse);
expect(androidOptions.isEnableAppLifecycleBreadcrumbs(), isFalse);
expect(androidOptions.isEnableSystemEventBreadcrumbs(), isFalse);
expect(androidOptions.isEnableAppComponentBreadcrumbs(), isFalse);
expect(androidOptions.isEnableUserInteractionBreadcrumbs(), isFalse);
expect(androidOptions.getConnectionTimeoutMillis(), 1234);
expect(androidOptions.getReadTimeoutMillis(), 2345);
expect(androidOptions.isEnableSpotlight(), isTrue);
expect(androidOptions.isSendClientReports(), isFalse);
expect(
androidOptions.getSpotlightConnectionUrl()?.toDartString(),
Sentry.currentHub.options.spotlight.url,
);
expect(androidOptions.getSentryClientName()?.toDartString(),
'$androidSdkName/${jni.BuildConfig.VERSION_NAME?.toDartString()}');
expect(androidOptions.getNativeSdkName()?.toDartString(), nativeSdkName);
expect(androidOptions.getSdkVersion()?.getName().toDartString(),
androidSdkName);
expect(androidOptions.getSdkVersion()?.getVersion().toDartString(),
jni.BuildConfig.VERSION_NAME?.toDartString());
final allPackages = androidOptions
.getSdkVersion()
?.getPackageSet()
.map((pkg) {
if (pkg == null) return null;
return SentryPackage(
pkg.getName().toDartString(), pkg.getVersion().toDartString());
})
.nonNulls
.toList();
for (final package in Sentry.currentHub.options.sdk.packages) {
final findMatchingPackage = allPackages?.firstWhere(
(p) => p.name == package.name && p.version == package.version);
expect(findMatchingPackage, isNotNull);
}
});
final androidProxy = androidOptions.getProxy();
expect(androidProxy, isNotNull);
expect(androidProxy!.getHost()?.toDartString(), 'proxy.local');
expect(androidProxy.getPort()?.toDartString(), '8084');
expect(androidProxy.getUser()?.toDartString(), 'u');
expect(androidProxy.getPass()?.toDartString(), 'p');
final r = androidOptions.getSessionReplay();
expect(r.getQuality(), jni.SentryReplayOptions$SentryReplayQuality.HIGH);
expect(r.getSessionSampleRate(), isNotNull);
expect(r.getOnErrorSampleRate(), isNotNull);
expect(r.isTrackConfiguration(), isFalse);
}, skip: !Platform.isAndroid);

testWidgets('loads native contexts through loadContexts', (tester) async {
await restoreFlutterOnErrorAfter(() async {
Expand Down Expand Up @@ -422,8 +360,8 @@ void main() {
expect(breadcrumbs, isA<List>());
if (breadcrumbs!.isNotEmpty) {
final firstCrumb = breadcrumbs.first;
expect(firstCrumb, isA<Map<String, dynamic>>());
final Map<String, dynamic> crumbMap = firstCrumb as Map<String, dynamic>;
expect(firstCrumb, isA<Map>());
final crumbMap = firstCrumb as Map;
expect(crumbMap.containsKey('timestamp'), isTrue,
reason: 'Breadcrumb timestamp missing');
expect(crumbMap['timestamp'], isA<String>());
Expand Down
21 changes: 13 additions & 8 deletions packages/flutter/example/integration_test/profiling_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ import 'dart:convert';
import 'dart:io';

import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:sentry_flutter_example/main.dart';
import '../../../dart/test/mocks/mock_transport.dart';

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();

final transport = MockTransport();

setUp(() async {
Future<void> setupSentryAndApp(WidgetTester tester) async {
await SentryFlutter.init((options) {
// ignore: invalid_use_of_internal_member
options.automatedTestMode = true;
Expand All @@ -17,20 +21,24 @@ void main() {
options.transport = transport;
options.tracesSampleRate = 1.0;
options.profilesSampleRate = 1.0;
}, appRunner: () async {
await tester.pumpWidget(const MyApp());
});
});
}

tearDown(() async {
await Sentry.close();
transport.reset();
});

test('native binding is initialized', () async {
testWidgets('native binding is initialized', (tester) async {
await setupSentryAndApp(tester);
// ignore: invalid_use_of_internal_member
expect(SentryFlutter.native, isNotNull);
});

test('profile is captured', () async {
testWidgets('profile is captured', (tester) async {
await setupSentryAndApp(tester);
final tx = Sentry.startTransaction("name", "op");
await Future.delayed(const Duration(milliseconds: 1000));
await tx.finish();
Expand Down Expand Up @@ -63,8 +71,5 @@ void main() {
expect(profileMap["profile"]["samples"], isNotEmpty);
expect(profileMap["profile"]["stacks"], isNotEmpty);
expect(profileMap["profile"]["frames"], isNotEmpty);
},
skip: (Platform.isMacOS || Platform.isIOS)
? false
: "Profiling is not supported on this platform");
}, skip: !(Platform.isMacOS || Platform.isIOS));
}
Loading
Loading