From e2d918709d5d811c31e31af551cbc8380065332a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Wed, 29 May 2024 15:56:56 +0200 Subject: [PATCH 1/4] RDART-962: Use xcode 15.4 (#1548) * Use xcode 15.4 Update CHANGELOG Use Release instead of MinSIzeRel * bump ulimit on ios test * Check arch instead of inferring implicitly * Update flutter-desktop-tests.yml to match dart-desktop-tests.yml * Don't force XCode version on iOS tests (only on builds) * Run iOS test on macos-12 * Update CHANGELOG * PR feedback Co-authored-by: Nikola Irinchev * PR feedback 2 --------- Co-authored-by: Nikola Irinchev --- .github/workflows/binary-combine-android.yml | 2 + .github/workflows/build-native.yml | 8 +- .github/workflows/ci.yml | 101 ++++++++++++------- .github/workflows/dart-desktop-tests.yml | 46 +++++---- .github/workflows/flutter-desktop-tests.yml | 48 +++++---- CHANGELOG.md | 1 + packages/realm_dart/CMakePresets.json | 2 +- packages/realm_dart/scripts/build-macos.sh | 2 +- 8 files changed, 127 insertions(+), 83 deletions(-) diff --git a/.github/workflows/binary-combine-android.yml b/.github/workflows/binary-combine-android.yml index 6187c54f9..559fe51c9 100644 --- a/.github/workflows/binary-combine-android.yml +++ b/.github/workflows/binary-combine-android.yml @@ -14,11 +14,13 @@ jobs: with: name: librealm-android-x86_64 path: packages/realm_dart/binary/android + - name: Fetch armeabi-v7a build uses: actions/download-artifact@v4 with: name: librealm-android-armeabi-v7a path: packages/realm_dart/binary/android + - name: Fetch arm64-v8a build uses: actions/download-artifact@v4 with: diff --git a/.github/workflows/build-native.yml b/.github/workflows/build-native.yml index 67352f8ef..33daa68a1 100644 --- a/.github/workflows/build-native.yml +++ b/.github/workflows/build-native.yml @@ -51,9 +51,11 @@ jobs: if: startsWith(matrix.build, 'android') run: echo "ANDROID_NDK=$ANDROID_NDK_LATEST_HOME" >> $GITHUB_ENV - - name: Downgrade XCode for MacOS - if: matrix.build == 'macos' - run: sudo xcodes select 14.3.1 + - name: Select XCode for MacOS + if: runner.os == 'macOS' + run: | + xcodes installed + sudo xcodes select 15.4 - name: Build if: steps.check-cache.outputs.cache-hit != 'true' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dc27fc007..63d68845b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,9 +19,9 @@ jobs: name: Build Windows uses: ./.github/workflows/build-native.yml with: - runner: windows-latest - binary: windows - build: '["windows"]' + runner: windows-latest + binary: windows + build: '["windows"]' build-macos: name: Build MacOS @@ -35,23 +35,23 @@ jobs: name: Build Linux uses: ./.github/workflows/build-native.yml with: - runner: ubuntu-20.04 # Building on the lowest possible Linux (Ubuntu) version for compatibility - binary: linux - build: '["linux"]' + runner: ubuntu-20.04 # Building on the lowest possible Linux (Ubuntu) version for compatibility + binary: linux + build: '["linux"]' build-android: name: Build Android uses: ./.github/workflows/build-native.yml with: - runner: ubuntu-20.04 - binary: android - build: '["android-x86_64", "android-armeabi-v7a", "android-arm64-v8a"]' + runner: ubuntu-20.04 + binary: android + build: '["android-x86_64", "android-armeabi-v7a", "android-arm64-v8a"]' build-ios: name: Build IOS uses: ./.github/workflows/build-native.yml with: - runner: macos-12 + runner: macos-14 binary: ios build: '["ios-device", "ios-simulator"]' @@ -65,7 +65,7 @@ jobs: needs: build-ios uses: ./.github/workflows/binary-combine-ios.yml -# Dart jobs + # Dart jobs deploy-cluster-dart-windows: name: Deploy Cluster for Dart Windows @@ -82,7 +82,6 @@ jobs: - deploy-cluster-dart-windows secrets: inherit with: - os: windows runner: windows-latest differentiator: dw${{ github.run_id }}${{ github.run_attempt }} @@ -111,8 +110,7 @@ jobs: - deploy-cluster-dart-macos secrets: inherit with: - os: macos - runner: macos-latest + runner: macos-13 differentiator: dm${{ github.run_id }}${{ github.run_attempt }} cleanup-cluster-dart-macos: @@ -140,9 +138,8 @@ jobs: - deploy-cluster-dart-macos-arm secrets: inherit with: - os: macos runner: macos-14 - architecture: arm + arch: arm64 differentiator: dma${{ github.run_id }}${{ github.run_attempt }} cleanup-cluster-dart-macos-arm: @@ -170,7 +167,6 @@ jobs: - deploy-cluster-dart-linux secrets: inherit with: - os: linux runner: ubuntu-latest differentiator: dl${{ github.run_id }}${{ github.run_attempt }} @@ -184,7 +180,7 @@ jobs: with: differentiator: dl${{ github.run_id }}${{ github.run_attempt }} -# Flutter jobs + # Flutter jobs deploy-cluster-flutter-windows: name: Deploy Cluster for Flutter Windows uses: ./.github/workflows/deploy-baas.yml @@ -200,11 +196,9 @@ jobs: - deploy-cluster-flutter-windows secrets: inherit with: - os: windows runner: windows-latest differentiator: fw${{ github.run_id }}${{ github.run_attempt }} - cleanup-cluster-flutter-windows: name: Cleanup Cluster for Flutter Windows uses: ./.github/workflows/terminate-baas.yml @@ -230,8 +224,7 @@ jobs: - deploy-cluster-flutter-macos secrets: inherit with: - os: macos - runner: macos-latest + runner: macos-13 differentiator: fm${{ github.run_id }}${{ github.run_attempt }} cleanup-cluster-flutter-macos: @@ -244,6 +237,35 @@ jobs: with: differentiator: fm${{ github.run_id }}${{ github.run_attempt }} + deploy-cluster-flutter-macos-arm: + name: Deploy Cluster for Flutter MacOS Arm + uses: ./.github/workflows/deploy-baas.yml + secrets: inherit + with: + differentiator: fma${{ github.run_id }}${{ github.run_attempt }} + + flutter-tests-macos-arm: + name: Flutter Tests MacOS Arm + uses: ./.github/workflows/flutter-desktop-tests.yml + needs: + - build-macos + - deploy-cluster-flutter-macos-arm + secrets: inherit + with: + runner: macos-14 + arch: arm64 + differentiator: fma${{ github.run_id }}${{ github.run_attempt }} + + cleanup-cluster-flutter-macos-arm: + name: Cleanup Cluster for Flutter macOS Arm + uses: ./.github/workflows/terminate-baas.yml + if: always() + needs: + - flutter-tests-macos-arm + secrets: inherit + with: + differentiator: fma${{ github.run_id }}${{ github.run_attempt }} + deploy-cluster-flutter-linux: name: Deploy Cluster for Flutter Linux uses: ./.github/workflows/deploy-baas.yml @@ -259,7 +281,6 @@ jobs: - deploy-cluster-flutter-linux secrets: inherit with: - os: linux runner: ubuntu-latest differentiator: fl${{ github.run_id }}${{ github.run_attempt }} @@ -298,7 +319,12 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: - submodules: 'recursive' + submodules: "recursive" + + - name: Bump ulimit + run: | + ulimit -n + ulimit -n 10240 - name: Enable ccache run: echo "PATH=/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" >> $GITHUB_ENV @@ -312,7 +338,7 @@ jobs: - name: Setup Flutter uses: subosito/flutter-action@v2 with: - channel: 'stable' + channel: "stable" - name: Setup Melos run: | @@ -325,9 +351,9 @@ jobs: - name: Launch Simulator uses: futureware-tech/simulator-action@v3 with: - model: 'iPhone SE (3rd generation)' - os: 'iOS' - os_version: '>= 14.0' + model: "iPhone SE (3rd generation)" + os: "iOS" + os_version: ">= 14.0" - name: Run tests on iOS Simulator run: | @@ -382,7 +408,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: - submodules: 'recursive' + submodules: "recursive" - name: Enable KVM run: | @@ -396,7 +422,7 @@ jobs: - name: Setup Java uses: actions/setup-java@v4 with: - distribution: 'temurin' + distribution: "temurin" java-version: 11 - name: Fetch artifacts @@ -408,7 +434,7 @@ jobs: - name: Setup Flutter uses: subosito/flutter-action@v2 with: - channel: 'stable' + channel: "stable" - name: Setup Melos run: | @@ -419,7 +445,7 @@ jobs: run: dart pub get # Hack to free up space on the runner to ensure we have enough diskspace to run the emulator - - name: Remove unnecessary files (dotnet, etc.) + - name: Remove unnecessary files (dotnet, etc.) run: | sudo rm -rf /usr/share/dotnet @@ -468,7 +494,6 @@ jobs: only-summary: true working-directory: packages/realm/tests - cleanup-cluster-flutter-android: name: Cleanup Cluster for Flutter Android uses: ./.github/workflows/terminate-baas.yml @@ -479,7 +504,7 @@ jobs: with: differentiator: fa${{ github.run_id }}${{ github.run_attempt }} -# Generator jobs + # Generator jobs generator: strategy: @@ -499,12 +524,12 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: - submodules: 'recursive' + submodules: "recursive" - name: Setup Flutter uses: subosito/flutter-action@v2 with: - channel: 'stable' + channel: "stable" - name: Setup Melos run: | @@ -580,7 +605,7 @@ jobs: runs-on: ubuntu-latest if: always() && github.ref == 'refs/heads/main' steps: - # Run this action to set env.WORKFLOW_CONCLUSION + # Run this action to set env.WORKFLOW_CONCLUSION - uses: technote-space/workflow-conclusion-action@45ce8e0eb155657ab8ccf346ade734257fd196a5 - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f @@ -589,7 +614,7 @@ jobs: with: status: ${{ env.WORKFLOW_CONCLUSION }} webhook-url: ${{ secrets.SLACK_DART_WEBHOOK }} - channel: '#realm-github-dart' + channel: "#realm-github-dart" message: | ** <{{refUrl}}|`{{ref}}` - {{description}}> diff --git a/.github/workflows/dart-desktop-tests.yml b/.github/workflows/dart-desktop-tests.yml index 0f4d80087..1d65b3cca 100644 --- a/.github/workflows/dart-desktop-tests.yml +++ b/.github/workflows/dart-desktop-tests.yml @@ -3,22 +3,18 @@ name: Dart desktop tests on: workflow_call: inputs: - os: - description: OS to execute on. - required: true - type: string runner: description: GitHub runner image to execute on. required: true type: string - architecture: - description: Architecture to execute on. - required: false - type: string differentiator: description: Differentiator for the BaaS container. required: true type: string + arch: + description: Architecture to execute on. + default: x64 + type: string env: REALM_CI: true @@ -28,7 +24,7 @@ env: jobs: dart-tests: runs-on: ${{ inputs.runner }} - name: Dart tests on ${{inputs.os }} ${{ inputs.architecture }} + name: Dart tests on ${{ inputs.runner }}-${{ inputs.arch }} timeout-minutes: 45 steps: @@ -37,17 +33,27 @@ jobs: with: submodules: false + - name: Check runner.arch + if: ${{ inputs.arch != runner.arch }} + run: false # fail if arch is not as expected + shell: bash + + - id: runner_os_lowercase + # there is no such thing as ${{ tolower(runner.os) }}, hence this abomination ¯\_(ツ)_/¯ + # use with steps.runner_os_lowercase.outputs.os + run: echo ${{ runner.os }} | awk '{print "os=" tolower($0)}' >> $GITHUB_OUTPUT + shell: bash + - name: Fetch artifacts uses: actions/download-artifact@v4 with: - name: librealm-${{ inputs.os }} - path: packages/realm_dart/binary/${{ inputs.os }} + name: librealm-${{ steps.runner_os_lowercase.outputs.os }} + path: packages/realm_dart/binary/${{ steps.runner_os_lowercase.outputs.os }} - - name : Setup Dart SDK + - name: Setup Dart SDK uses: dart-lang/setup-dart@main with: sdk: stable - architecture: ${{ inputs.architecture == 'arm' && 'arm64' || 'x64'}} - name: Setup Melos run: | @@ -56,18 +62,20 @@ jobs: melos setup - name: Bump ulimit on macos - run: ulimit -n 10240 - if: ${{ contains(inputs.os, 'macos') }} + run: | + ulimit -n + ulimit -n 10240 + if: ${{ contains(runner.os, 'macos') }} - - name: Run tests - run: ${{ inputs.architecture == 'arm' && 'arch -arm64 ' || '' }}melos test:unit + - name: Run tests ${{ runner }} ${{ runner.arch }} + run: melos test:unit # TODO: Publish all reports - name: Publish Test Report uses: dorny/test-reporter@v1.8.0 if: success() || failure() with: - name: Test Results Dart ${{ inputs.os }} ${{ inputs.architecture }} + name: Test Results Dart ${{ runner }} ${{ runner.arch }} path: test-results.json reporter: dart-json only-summary: true @@ -89,4 +97,4 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} flag-name: realm_dart path-to-lcov: ./coverage/lcov.info - parallel: true \ No newline at end of file + parallel: true diff --git a/.github/workflows/flutter-desktop-tests.yml b/.github/workflows/flutter-desktop-tests.yml index 59df0e196..a5234499b 100644 --- a/.github/workflows/flutter-desktop-tests.yml +++ b/.github/workflows/flutter-desktop-tests.yml @@ -3,22 +3,18 @@ name: Flutter desktop tests on: workflow_call: inputs: - os: - description: OS to execute on. - required: true - type: string runner: description: GitHub runner image to execute on. required: true type: string - architecture: - description: Architecture to execute on. - required: false - type: string differentiator: description: Differentiator for the BaaS container. required: true type: string + arch: + description: Architecture to execute on. + default: x64 + type: string env: REALM_CI: true @@ -26,7 +22,7 @@ env: jobs: flutter-tests: runs-on: ${{ inputs.runner }} - name: Flutter tests on ${{inputs.os }}-${{ inputs.architecture }} + name: Flutter tests on ${{ inputs.runner }}-${{ inputs.arch }} timeout-minutes: 45 env: BAAS_BAASAAS_API_KEY: ${{ secrets.BAASAAS_API_KEY}} @@ -42,8 +38,19 @@ jobs: with: submodules: false + - name: Check runner.arch + if: ${{ inputs.arch != runner.arch }} + run: false # fail if arch is not as expected + shell: bash + + - id: runner_os_lowercase + # there is no such thing as ${{ tolower(runner.os) }}, hence this abomination ¯\_(ツ)_/¯ + # use with steps.runner_os_lowercase.outputs.os + run: echo ${{ runner.os }} | awk '{print "os=" tolower($0)}' >> $GITHUB_OUTPUT + shell: bash + - name: Setup GTK & Ninja on Linux - if: ${{ inputs.os == 'linux' }} + if: ${{ runner.os == 'linux' }} run: | sudo apt-get update -y sudo apt-get install -y libgtk-3-dev xvfb ninja-build @@ -51,17 +58,14 @@ jobs: - name: Fetch artifacts uses: actions/download-artifact@v4 with: - name: librealm-${{ inputs.os }} - path: packages/realm_dart/binary/${{ inputs.os }} + name: librealm-${{ steps.runner_os_lowercase.outputs.os }} + path: packages/realm_dart/binary/${{ steps.runner_os_lowercase.outputs.os }} - name: Setup Flutter uses: subosito/flutter-action@v2 with: channel: 'stable' - architecture: ${{ inputs.architecture == 'arm' && 'arm64' || 'x64'}} - - - name: Enable Flutter Desktop support - run: flutter config --enable-${{ inputs.os }}-desktop + architecture: ${{ inputs.arch }} - name: Setup Melos run: | @@ -72,16 +76,18 @@ jobs: run: dart pub get - name: Bump ulimit - run: ulimit -n 10240 - if: ${{ contains(inputs.os, 'macos') }} + run: | + ulimit -n + ulimit -n 10240 + if: ${{ contains(runner.os, 'macos') }} - name: Run tests run: | - ${{ inputs.os == 'linux' && 'xvfb-run' || '' }} \ + ${{ runner.os == 'linux' && 'xvfb-run' || '' }} \ flutter test integration_test/all_tests.dart \ --dart-define=BAAS_BAASAAS_API_KEY=$BAAS_BAASAAS_API_KEY \ --dart-define=BAAS_DIFFERENTIATOR=$BAAS_DIFFERENTIATOR \ - --device-id=${{ inputs.os }} \ + --device-id=${{ steps.runner_os_lowercase.outputs.os }} \ --file-reporter=json:test-results.json \ --suppress-analytics shell: bash @@ -91,7 +97,7 @@ jobs: uses: dorny/test-reporter@v1.8.0 if: success() || failure() with: - name: Test Results Flutter ${{ inputs.os }} ${{ inputs.architecture }} + name: Test Results Flutter ${{ runner.os }} ${{ runner.arch }} path: test-results.json reporter: dart-json only-summary: true diff --git a/CHANGELOG.md b/CHANGELOG.md index f1a9004d7..233abb1a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * Using Core 14.7.0. * Disabled codesigning of Apple binaries. (Issue [#1679](https://github.com/realm/realm-dart/issues/1679)) * Drop building xcframework for catalyst. (Issue [#1695](https://github.com/realm/realm-dart/issues/1695)) +* Using xcode 15.4 for native build. (Issue [#1547](https://github.com/realm/realm-dart/issues/1547)) ## 2.3.0 (2024-05-23) diff --git a/packages/realm_dart/CMakePresets.json b/packages/realm_dart/CMakePresets.json index 2c8948870..b802ce4be 100644 --- a/packages/realm_dart/CMakePresets.json +++ b/packages/realm_dart/CMakePresets.json @@ -154,7 +154,7 @@ "configurePreset": "macos", "nativeToolOptions": [ "-destination", - "platform=macOS" + "generic/platform=macOS" ], "configuration": "Debug" }, diff --git a/packages/realm_dart/scripts/build-macos.sh b/packages/realm_dart/scripts/build-macos.sh index dd1b98585..5c78eee8f 100755 --- a/packages/realm_dart/scripts/build-macos.sh +++ b/packages/realm_dart/scripts/build-macos.sh @@ -7,4 +7,4 @@ set -o pipefail cd "$(dirname "$0")/.." cmake --preset macos -cmake --build --preset macos --config MinSizeRel -- -destination "generic/platform=macOS" +cmake --build --preset macos --config Release -- -destination "generic/platform=macOS" From c57d96f0786ff53fa2141862406442962e44dc51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Thu, 30 May 2024 09:46:06 +0200 Subject: [PATCH 2/4] RDART-1045: Expose setrlimit ios (#1700) * Expose setrlimit on iOS * Use setrlimit when running test on iOS * Revert "Run iOS test on macos-12" This reverts commit 1e6a379b80efecfd38bdfeef13a6899fa8627888. * Throw and handle exceptions on error for good measure --- .github/workflows/ci.yml | 2 +- .../lib/src/native/realm_bindings.dart | 32 ++++++++++++++++ .../realm_dart/lib/src/native/realm_core.dart | 8 ++++ packages/realm_dart/src/ios/platform.mm | 38 +++++++++++++++++-- packages/realm_dart/src/realm_dart.h | 12 ++++++ packages/realm_dart/test/test.dart | 5 +++ 6 files changed, 93 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 63d68845b..4cf172d84 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -302,7 +302,7 @@ jobs: differentiator: fi${{ github.run_id }}${{ github.run_attempt }} flutter-tests-ios: - runs-on: macos-12 + runs-on: macos-14 name: Flutter Tests iOS timeout-minutes: 45 needs: diff --git a/packages/realm_dart/lib/src/native/realm_bindings.dart b/packages/realm_dart/lib/src/native/realm_bindings.dart index a7fdd06fe..721dbca85 100644 --- a/packages/realm_dart/lib/src/native/realm_bindings.dart +++ b/packages/realm_dart/lib/src/native/realm_bindings.dart @@ -3971,6 +3971,33 @@ class RealmLibrary { late final _realm_dart_scheduler_invoke = _realm_dart_scheduler_invokePtr .asFunction)>(); + /// implemented for iOS only (for now - valid for all posix) + /// /** + /// * Set the soft limit on number of open files + /// * @param limit The requested limit. If less than zero no attempt is made. + /// * @param[out] out_limit The actual limit set. + /// * + /// * @return true if no error occurred. + /// * + /// * @throws RLM_ERR_FILE_PERMISSION_DENIED if the operation was not permitted. + /// */ + bool realm_dart_set_and_get_rlimit( + int limit, + ffi.Pointer out_limit, + ) { + return _realm_dart_set_and_get_rlimit( + limit, + out_limit, + ); + } + + late final _realm_dart_set_and_get_rlimitPtr = _lookup< + ffi + .NativeFunction)>>( + 'realm_dart_set_and_get_rlimit'); + late final _realm_dart_set_and_get_rlimit = _realm_dart_set_and_get_rlimitPtr + .asFunction)>(); + bool realm_dart_sync_after_reset_handler_callback( ffi.Pointer userdata, ffi.Pointer before_realm, @@ -11833,6 +11860,11 @@ class _SymbolAddresses { .NativeFunction)>> get realm_dart_scheduler_invoke => _library._realm_dart_scheduler_invokePtr; + ffi.Pointer< + ffi + .NativeFunction)>> + get realm_dart_set_and_get_rlimit => + _library._realm_dart_set_and_get_rlimitPtr; ffi.Pointer< ffi.NativeFunction< ffi.Bool Function( diff --git a/packages/realm_dart/lib/src/native/realm_core.dart b/packages/realm_dart/lib/src/native/realm_core.dart index a05b4d1ab..2b2a4830e 100644 --- a/packages/realm_dart/lib/src/native/realm_core.dart +++ b/packages/realm_dart/lib/src/native/realm_core.dart @@ -188,4 +188,12 @@ class RealmCore { String _getFilesPath() { return realmLib.realm_dart_get_files_path().cast().toRealmDartString()!; } + + int setAndGetRLimit(int limit) { + return using((arena) { + final outLimit = arena(); + realmLib.realm_dart_set_and_get_rlimit(limit, outLimit).raiseLastErrorIfFalse(); + return outLimit.value; + }); + } } diff --git a/packages/realm_dart/src/ios/platform.mm b/packages/realm_dart/src/ios/platform.mm index 80a330ee6..bfe62f2e8 100644 --- a/packages/realm_dart/src/ios/platform.mm +++ b/packages/realm_dart/src/ios/platform.mm @@ -19,9 +19,12 @@ #import #import -#include #include "../realm_dart.h" -#import +#include +#include +#include +#include +#include static std::string filesDir; static std::string deviceModel; @@ -48,7 +51,6 @@ } } - RLM_API const char* realm_dart_get_files_path() { if (filesDir == "") { filesDir = default_realm_file_directory(); @@ -74,3 +76,33 @@ return deviceVersion.c_str(); } + +namespace { + using namespace realm::c_api; + + rlimit get_rlimit() { + rlimit rlim; + int status = getrlimit(RLIMIT_NOFILE, &rlim); + if (status < 0) + throw std::system_error(errno, std::system_category(), "getrlimit() failed"); + return rlim; + } + + long set_and_get_rlimit(long limit) { + if (limit > 0) { + auto rlim = get_rlimit(); + rlim.rlim_cur = limit; + int status = setrlimit(RLIMIT_NOFILE, &rlim); + if (status < 0) + throw std::system_error(errno, std::system_category(), "setrlimit() failed"); + } + return get_rlimit().rlim_cur; + } +} + +RLM_API bool realm_dart_set_and_get_rlimit(long limit, long* out_limit) { + return wrap_err([&]() { + *out_limit = set_and_get_rlimit(limit); + return true; + }); +} diff --git a/packages/realm_dart/src/realm_dart.h b/packages/realm_dart/src/realm_dart.h index 7108b677a..5726809a5 100644 --- a/packages/realm_dart/src/realm_dart.h +++ b/packages/realm_dart/src/realm_dart.h @@ -36,6 +36,18 @@ RLM_API const char* realm_dart_get_device_version(); // implemented for Android only RLM_API const char* realm_dart_get_bundle_id(); +// implemented for iOS only (for now - valid for all posix) +/** + * Set the soft limit on number of open files + * @param limit The requested limit. If less than zero no attempt is made. + * @param[out] out_limit The actual limit set. + * + * @return true if no error occurred. + * + * @throws RLM_ERR_FILE_PERMISSION_DENIED if the operation was not permitted. + */ +RLM_API bool realm_dart_set_and_get_rlimit(long limit, long* out_limit); + RLM_API const char* realm_get_library_cpu_arch(); typedef struct realm_dart_userdata_async* realm_dart_userdata_async_t; diff --git a/packages/realm_dart/test/test.dart b/packages/realm_dart/test/test.dart index 7dc8eae1c..f88e5d0ec 100644 --- a/packages/realm_dart/test/test.dart +++ b/packages/realm_dart/test/test.dart @@ -430,6 +430,11 @@ void setupTests() { printOnFailure('${record.category} ${record.level.name}: ${record.message}'); }); + if (Platform.isIOS) { + final maxFiles = realmCore.setAndGetRLimit(1024); + print('Max files: $maxFiles'); + } + // Enable this to print platform info, including current PID _printPlatformInfo(); }); From 2b476a74853d4479c964d06aac86b02f99cc831c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Thu, 30 May 2024 11:41:45 +0200 Subject: [PATCH 3/4] RDART-999: Fix flutter test dlopen (#1623) * Use lib in package directly during flutter test * Fix paths * Update CHANGELOG --- CHANGELOG.md | 1 + .../lib/src/cli/common/target_os_type.dart | 15 +-- .../lib/src/cli/install/install_command.dart | 72 +++++------ .../lib/src/cli/install/options.dart | 10 +- .../lib/src/cli/install/options.g.dart | 57 +++++--- packages/realm_dart/lib/src/init.dart | 122 ++++++++++++------ packages/realm_dart/lib/src/realm_dart.dart | 3 - .../realm_dart/lib/src/realm_flutter.dart | 3 - 8 files changed, 166 insertions(+), 117 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 233abb1a2..8550b9c42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Fixed * Fixed an issue that would cause macOS apps to be rejected with `Invalid Code Signing Entitlements` error. (Issue [#1679](https://github.com/realm/realm-dart/issues/1679)) +* Fixed a regression that makes it inconvenient to run unit tests using realm. (Issue [#1619](https://github.com/realm/realm-dart/issues/1619)) ### Compatibility * Realm Studio: 15.0.0 or later. diff --git a/packages/realm_dart/lib/src/cli/common/target_os_type.dart b/packages/realm_dart/lib/src/cli/common/target_os_type.dart index f87d958b6..6868ebfce 100644 --- a/packages/realm_dart/lib/src/cli/common/target_os_type.dart +++ b/packages/realm_dart/lib/src/cli/common/target_os_type.dart @@ -8,24 +8,19 @@ enum TargetOsType { ios, linux, macos, - windows, + windows; + + bool get isDesktop => [TargetOsType.linux, TargetOsType.macos, TargetOsType.windows].contains(this); } // Cannot use Dart 2.17 enhanced enums, due to an issue with build_cli :-/ enum Flavor { flutter, - dart, + dart; } extension FlavorEx on Flavor { - String get packageName { - switch (this) { - case Flavor.dart: - return 'realm_dart'; - case Flavor.flutter: - return 'realm'; - } - } + String get packageName => switch (this) { Flavor.dart => 'realm_dart', Flavor.flutter => 'realm' }; } extension StringEx on String { diff --git a/packages/realm_dart/lib/src/cli/install/install_command.dart b/packages/realm_dart/lib/src/cli/install/install_command.dart index 5d44ba5db..5f783f128 100644 --- a/packages/realm_dart/lib/src/cli/install/install_command.dart +++ b/packages/realm_dart/lib/src/cli/install/install_command.dart @@ -25,26 +25,34 @@ class InstallCommand extends Command { late Options options; - bool get debug => options.debug; + late Pubspec pubspec; + late final debug = options.debug; + late final flavor = options.flavor!; + late final force = options.force; // not used!?! + late final targetOsType = options.targetOsType!; InstallCommand() { - populateOptionsParser(argParser); + pubspec = parsePubspec(File('pubspec.yaml')); + final defaultFlavor = pubspec.dependencies['flutter'] == null ? Flavor.dart : Flavor.flutter; // default depends on project type + populateOptionsParser( + argParser, + targetOsTypeDefaultOverride: Platform.operatingSystem.asTargetOsType, + flavorDefaultOverride: defaultFlavor, + ); } - Directory getBinaryPath(Directory realmPackagePath, {required bool isFlutter}) { - if (isFlutter) { - final root = realmPackagePath.path; - return Directory(switch (options.targetOsType) { - TargetOsType.android => path.join(root, 'android', 'src', 'main', 'cpp', 'lib'), - TargetOsType.ios => path.join(root, 'ios'), - TargetOsType.macos => path.join(root, 'macos'), - TargetOsType.linux => path.join(root, 'linux', 'binary', 'linux'), - TargetOsType.windows => path.join(root, 'windows', 'binary', 'windows'), - _ => throw Exception('Unsupported target OS type for Flutter: ${options.targetOsType}') - }); - } - // TODO: Should binaries not go into package also for Dart? - return Directory(path.join(Directory.current.absolute.path, 'binary', options.targetOsType!.name)); + Directory getBinaryPath(Directory realmPackagePath, Flavor flavor) { + final root = realmPackagePath.path; + return switch (flavor) { + Flavor.dart => Directory(path.join(root, 'binary', targetOsType.name)), + Flavor.flutter => Directory(switch (targetOsType) { + TargetOsType.android => path.join(root, 'android', 'src', 'main', 'cpp', 'lib'), + TargetOsType.ios => path.join(root, 'ios'), + TargetOsType.macos => path.join(root, 'macos'), + TargetOsType.linux => path.join(root, 'linux', 'binary', 'linux'), + TargetOsType.windows => path.join(root, 'windows', 'binary', 'windows'), + }), + }; } Future shouldSkipDownload(String binariesPath, String expectedVersion) async { @@ -105,31 +113,29 @@ class InstallCommand extends Command { if (packageConfig == null) { abort('Run `dart pub get`'); } - final package = packageConfig.packages.where((p) => p.name == name).singleOrNull; + final package = packageConfig[name]; if (package == null) { abort('$name package not found in dependencies. Add $name package to your pubspec.yaml'); } return Directory.fromUri(package.root); } - Future parsePubspec(File file) async { + Pubspec parsePubspec(File file) { try { - return Pubspec.parse(await file.readAsString(), sourceUrl: file.uri); - } on Exception catch (e) { - throw Exception('Error parsing package pubspec at ${file.parent}. Error $e'); + return Pubspec.parse(file.readAsStringSync(), sourceUrl: file.uri); + } catch (e) { + abort('Error parsing package pubspec at ${file.parent}. Error $e'); } } @override FutureOr run() async { - final pubspec = await parsePubspec(File('pubspec.yaml')); - final flavor = pubspec.dependencies['flutter'] == null ? Flavor.dart : Flavor.flutter; - options = parseOptionsResult(argResults!); - validateOptions(flavor); final flavorName = flavor.packageName; final realmDependency = pubspec.dependencyOverrides[flavorName] ?? pubspec.dependencies[flavorName]; + print(pubspec.dependencyOverrides.values.join('\n')); + print(realmDependency); if (realmDependency is PathDependency) { print('Path dependency for $flavorName found. Skipping install of native lib (assuming local development)'); return; @@ -140,26 +146,16 @@ class InstallCommand extends Command { } final realmPackagePath = await getPackagePath(flavorName); - final realmPubspec = await parsePubspec(File(path.join(realmPackagePath.path, "pubspec.yaml"))); + final realmPubspec = parsePubspec(File(path.join(realmPackagePath.path, "pubspec.yaml"))); - final binaryPath = getBinaryPath(realmPackagePath, isFlutter: flavor == Flavor.flutter); + final binaryPath = getBinaryPath(realmPackagePath, flavor); print(binaryPath); - final archiveName = '${options.targetOsType!.name}.tar.gz'; + final archiveName = '${targetOsType.name}.tar.gz'; await downloadAndExtractBinaries(binaryPath, realmPubspec.version!, archiveName); print('Realm install command finished.'); } - void validateOptions(Flavor flavor) { - final targetOs = flavor == Flavor.dart ? getTargetOS() : options.targetOsType; - if (targetOs == null) { - abort('Target OS not specified'); - } - options.targetOsType = targetOs; - } - - TargetOsType getTargetOS() => Platform.operatingSystem.asTargetOsType ?? (throw UnsupportedError('Unsupported platform ${Platform.operatingSystem}')); - Never abort(String error) { print(error); print(usage); diff --git a/packages/realm_dart/lib/src/cli/install/options.dart b/packages/realm_dart/lib/src/cli/install/options.dart index f9c1440b6..59b35e96f 100644 --- a/packages/realm_dart/lib/src/cli/install/options.dart +++ b/packages/realm_dart/lib/src/cli/install/options.dart @@ -8,7 +8,10 @@ part 'options.g.dart'; @CliOptions() class Options { - @CliOption(help: 'The target OS to install binaries for.', abbr: 't') + @CliOption(help: 'The flavor to install binaries for.', abbr: 'f', provideDefaultToOverride: true) + Flavor? flavor; + + @CliOption(help: 'The target OS to install binaries for.', abbr: 't', provideDefaultToOverride: true) TargetOsType? targetOsType; // use to debug install command @@ -23,6 +26,9 @@ class Options { String get usage => _$parserForOptions.usage; -ArgParser populateOptionsParser(ArgParser p) => _$populateOptionsParser(p); +ArgParser populateOptionsParser(ArgParser parser, { + TargetOsType? targetOsTypeDefaultOverride, + Flavor? flavorDefaultOverride, +}) => _$populateOptionsParser(parser, targetOsTypeDefaultOverride: targetOsTypeDefaultOverride, flavorDefaultOverride: flavorDefaultOverride); Options parseOptionsResult(ArgResults results) => _$parseOptionsResult(results); diff --git a/packages/realm_dart/lib/src/cli/install/options.g.dart b/packages/realm_dart/lib/src/cli/install/options.g.dart index f0a4e6e05..d6a8b6f7f 100644 --- a/packages/realm_dart/lib/src/cli/install/options.g.dart +++ b/packages/realm_dart/lib/src/cli/install/options.g.dart @@ -30,7 +30,15 @@ Options _$parseOptionsResult(ArgResults result) => Options( ), force: result['force'] as bool, debug: result['debug'] as bool, - ); + )..flavor = _$nullableEnumValueHelperNullable( + _$FlavorEnumMapBuildCli, + result['flavor'] as String?, + ); + +const _$FlavorEnumMapBuildCli = { + Flavor.flutter: 'flutter', + Flavor.dart: 'dart' +}; const _$TargetOsTypeEnumMapBuildCli = { TargetOsType.android: 'android', @@ -40,23 +48,36 @@ const _$TargetOsTypeEnumMapBuildCli = { TargetOsType.windows: 'windows' }; -ArgParser _$populateOptionsParser(ArgParser parser) => parser - ..addOption( - 'target-os-type', - abbr: 't', - help: 'The target OS to install binaries for.', - allowed: ['android', 'ios', 'linux', 'macos', 'windows'], - ) - ..addFlag( - 'debug', - help: 'Download binary from http://localhost:8000/.', - hide: true, - ) - ..addFlag( - 'force', - help: 'Force install, even if we would normally skip it.', - hide: true, - ); +ArgParser _$populateOptionsParser( + ArgParser parser, { + Flavor? flavorDefaultOverride, + TargetOsType? targetOsTypeDefaultOverride, +}) => + parser + ..addOption( + 'flavor', + abbr: 'f', + help: 'The flavor to install binaries for.', + defaultsTo: _$FlavorEnumMapBuildCli[flavorDefaultOverride], + allowed: ['flutter', 'dart'], + ) + ..addOption( + 'target-os-type', + abbr: 't', + help: 'The target OS to install binaries for.', + defaultsTo: _$TargetOsTypeEnumMapBuildCli[targetOsTypeDefaultOverride], + allowed: ['android', 'ios', 'linux', 'macos', 'windows'], + ) + ..addFlag( + 'debug', + help: 'Download binary from http://localhost:8000/.', + hide: true, + ) + ..addFlag( + 'force', + help: 'Force install, even if we would normally skip it.', + hide: true, + ); final _$parserForOptions = _$populateOptionsParser(ArgParser()); diff --git a/packages/realm_dart/lib/src/init.dart b/packages/realm_dart/lib/src/init.dart index 21de3e93a..075ca4764 100644 --- a/packages/realm_dart/lib/src/init.dart +++ b/packages/realm_dart/lib/src/init.dart @@ -4,45 +4,61 @@ import 'dart:ffi'; import 'dart:io'; +import 'package:package_config/package_config.dart'; import 'package:path/path.dart' as p; import '../realm.dart' as realm show isFlutterPlatform; -import '../realm.dart' show realmBinaryName; import 'cli/common/target_os_type.dart'; import 'cli/metrics/metrics_command.dart'; import 'cli/metrics/options.dart'; import 'realm_class.dart'; +const realmBinaryName = 'realm_dart'; +final targetOsType = Platform.operatingSystem.asTargetOsType ?? _platformNotSupported(); +final nativeLibraryName = _getLibName(realmBinaryName); + DynamicLibrary? _library; -String _getPluginPath(String libName) { - if (Platform.isAndroid) { - return libName; - } - if (Platform.isLinux) { - return '$_exeDirName/lib/$libName'; - } - if (Platform.isMacOS) { - return '$_exeDirName/../Frameworks/$libName'; - } - if (Platform.isIOS) { - return '$_exeDirName/Frameworks/realm_dart.framework/$libName'; - } - if (Platform.isWindows) { - return libName; +String _getLibPathFlutter() { + final root = _exeDirName; + return switch (targetOsType) { + TargetOsType.android => nativeLibraryName, + TargetOsType.ios => p.join(root, 'Frameworks', 'realm_dart.framework', nativeLibraryName), + TargetOsType.linux => p.join(root, 'lib', nativeLibraryName), + TargetOsType.macos => p.join(p.dirname(root), 'Frameworks', nativeLibraryName), + TargetOsType.windows => nativeLibraryName, + }; +} + +String _getLibPathFlutterTest(Package realmPackage) { + assert(realmPackage.name == 'realm'); + final root = p.join(realmPackage.root.toFilePath(), targetOsType.name); + return switch (targetOsType) { + TargetOsType.linux => p.join(root, 'binary', 'linux', nativeLibraryName), + TargetOsType.macos => p.join(root, nativeLibraryName), + TargetOsType.windows => p.join(root, 'binary', 'windows', nativeLibraryName), + _ => _platformNotSupported(), + }; +} + +String _getLibPathDart(Package realmDartPackage) { + assert(realmDartPackage.name == 'realm_dart'); + final root = p.join(realmDartPackage.root.toFilePath(), 'binary', targetOsType.name); + if (targetOsType.isDesktop) { + return p.join(root, nativeLibraryName); } _platformNotSupported(); } bool get isFlutterPlatform => realm.isFlutterPlatform; -String _getLibName(String stem) { - if (Platform.isMacOS) return 'lib$stem.dylib'; - if (Platform.isIOS) return stem; - if (Platform.isWindows) return '$stem.dll'; - if (Platform.isAndroid || Platform.isLinux) return 'lib$stem.so'; - _platformNotSupported(); // we don't support Fuchsia yet -} +String _getLibName(String stem) => switch (targetOsType) { + TargetOsType.android => 'lib$stem.so', + TargetOsType.ios => stem, // xcframeworks are a directory + TargetOsType.linux => 'lib$stem.so', + TargetOsType.macos => 'lib$stem.dylib', + TargetOsType.windows => '$stem.dll', + }; String? _getNearestProjectRoot(String dir) { while (dir != p.dirname(dir)) { @@ -52,17 +68,24 @@ String? _getNearestProjectRoot(String dir) { return null; } +File _getPackageConfigJson(Directory d) { + final root = _getNearestProjectRoot(d.path); + if (root != null) { + final file = File(p.join(root, '.dart_tool', 'package_config.json')); + if (file.existsSync()) return file; + } + throw StateError('Could not find package_config.json'); +} + Never _platformNotSupported() => throw UnsupportedError('Platform ${Platform.operatingSystem} is not supported'); String get _exeDirName => p.dirname(Platform.resolvedExecutable); DynamicLibrary _openRealmLib() { - final libName = _getLibName(realmBinaryName); - DynamicLibrary? tryOpen(String path) { try { return DynamicLibrary.open(path); - } on Error catch (_) { + } catch (_) { return null; } } @@ -70,7 +93,7 @@ DynamicLibrary _openRealmLib() { Never throwError(Iterable candidatePaths) { throw RealmError( [ - 'Could not open $libName. Tried:', + 'Could not open $nativeLibraryName. Tried:', candidatePaths.map((p) => '- "$p"').join('\n'), isFlutterPlatform // ? 'Hint: Did you forget to add a dependency on the realm package?' @@ -79,23 +102,36 @@ DynamicLibrary _openRealmLib() { ); } - if (isFlutterPlatform) { - final path = _getPluginPath(libName); - return tryOpen(path) ?? throwError([path]); - } else { - final root = _getNearestProjectRoot(Platform.script.path) ?? _getNearestProjectRoot(p.current); - final candidatePaths = [ - libName, // just ask OS.. - p.join(_exeDirName, libName), // try finding it next to the executable - if (root != null) p.join(root, 'binary', Platform.operatingSystem, libName), // try finding it relative to project - ]; - DynamicLibrary? lib; - for (final path in candidatePaths) { - lib = tryOpen(path); - if (lib != null) return lib; - } - throwError(candidatePaths); + DynamicLibrary open(String path) => tryOpen(path) ?? throwError([path]); + + final isFlutterTest = Platform.environment.containsKey('FLUTTER_TEST'); + if (isFlutterPlatform && !isFlutterTest) { + return open(_getLibPathFlutter()); + } + + // NOTE: This needs to be sync, so we cannot use findPackageConfig + final packageConfigFile = _getPackageConfigJson(Directory.current); + final packageConfig = PackageConfig.parseBytes(packageConfigFile.readAsBytesSync(), packageConfigFile.uri); + + if (isFlutterTest) { + final realmPackage = packageConfig['realm']!; + return open(_getLibPathFlutterTest(realmPackage)); + } + + final realmDartPackage = packageConfig['realm_dart']!; + + // else plain dart + final candidatePaths = [ + nativeLibraryName, // just ask OS.. + p.join(_exeDirName, nativeLibraryName), // try finding it next to the executable + _getLibPathDart(realmDartPackage), // try finding it in the package + ]; + DynamicLibrary? lib; + for (final path in candidatePaths) { + lib = tryOpen(path); + if (lib != null) return lib; } + throwError(candidatePaths); } /// @nodoc diff --git a/packages/realm_dart/lib/src/realm_dart.dart b/packages/realm_dart/lib/src/realm_dart.dart index 9de3c2278..5c3c9c7c4 100644 --- a/packages/realm_dart/lib/src/realm_dart.dart +++ b/packages/realm_dart/lib/src/realm_dart.dart @@ -6,6 +6,3 @@ export 'realm_class.dart' hide RealmInternal; /// @nodoc // is Realm loaded in Flutter or Dart const bool isFlutterPlatform = false; - -/// @nodoc -const String realmBinaryName = 'realm_dart'; diff --git a/packages/realm_dart/lib/src/realm_flutter.dart b/packages/realm_dart/lib/src/realm_flutter.dart index cb7b7e9d6..63202c442 100644 --- a/packages/realm_dart/lib/src/realm_flutter.dart +++ b/packages/realm_dart/lib/src/realm_flutter.dart @@ -6,6 +6,3 @@ export 'realm_class.dart' hide RealmInternal; /// @nodoc // is Realm loaded in Flutter or Dart const bool isFlutterPlatform = true; - -/// @nodoc -const String realmBinaryName = 'realm_dart'; From 641956d46d512c92234991c45e8288d433e0a6bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Thu, 30 May 2024 17:08:34 +0200 Subject: [PATCH 4/4] RDART-1020: Fix writeAsync behaviour (#1666) * Add test that illustrates problem with async callback to writeAsync * Fix async callback issue * Update CHANGELOG * Add assert that callback to write is not async * Handle Never, which is a subtype of any type, but not a future * Disallow async callbacks even on writeAsync * Drop new async callback tests again --- CHANGELOG.md | 1 + packages/realm_dart/lib/src/realm_class.dart | 6 +++++- packages/realm_dart/test/realm_test.dart | 12 +++++++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8550b9c42..29e462d70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * None ### Fixed +* `Realm.writeAsync` did not handle async callbacks (`Future Function()`) correctly. (Issue [#1667](https://github.com/realm/realm-dart/issues/1667)) * Fixed an issue that would cause macOS apps to be rejected with `Invalid Code Signing Entitlements` error. (Issue [#1679](https://github.com/realm/realm-dart/issues/1679)) * Fixed a regression that makes it inconvenient to run unit tests using realm. (Issue [#1619](https://github.com/realm/realm-dart/issues/1619)) diff --git a/packages/realm_dart/lib/src/realm_class.dart b/packages/realm_dart/lib/src/realm_class.dart index ad4c26862..5cc18c3fc 100644 --- a/packages/realm_dart/lib/src/realm_class.dart +++ b/packages/realm_dart/lib/src/realm_class.dart @@ -355,11 +355,15 @@ class Realm { /// Checks whether the `Realm` is in write transaction. bool get isInTransaction => handle.isWritable; + bool _isSubtype() => [] is List; + bool _isFuture() => T != Never && _isSubtype(); + /// Synchronously calls the provided callback inside a write transaction. /// /// If no exception is thrown from within the callback, the transaction will be committed. /// It is more efficient to update several properties or even create multiple objects in a single write transaction. T write(T Function() writeCallback) { + assert(!_isFuture(), 'writeCallback must be synchronous'); final transaction = beginWrite(); try { @@ -388,8 +392,8 @@ class Realm { /// Executes the provided [writeCallback] in a temporary write transaction. Both acquiring the write /// lock and committing the transaction will be done asynchronously. Future writeAsync(T Function() writeCallback, [CancellationToken? cancellationToken]) async { + assert(!_isFuture(), 'writeCallback must be synchronous'); final transaction = await beginWriteAsync(cancellationToken); - try { T result = writeCallback(); await transaction.commitAsync(cancellationToken); diff --git a/packages/realm_dart/test/realm_test.dart b/packages/realm_dart/test/realm_test.dart index d8c946499..cb7c094de 100644 --- a/packages/realm_dart/test/realm_test.dart +++ b/packages/realm_dart/test/realm_test.dart @@ -1071,7 +1071,7 @@ void main() { expect(realm2.all().length, 0); }); - test("Realm.writeAsync with multiple transactions doesnt't deadlock", () async { + test("Realm.writeAsync with multiple transactions doesn't deadlock", () async { final realm = getRealm(Configuration.local([Person.schema])); final t1 = await realm.beginWriteAsync(); realm.add(Person('Marco')); @@ -1200,6 +1200,16 @@ void main() { expect(realm.isInTransaction, false); }); + test('Realm.writeAsync with async callback fails with assert', () async { + final realm = getRealm(Configuration.local([Person.schema])); + await expectLater(realm.writeAsync(() async {}), throwsA(isA())); + }); + + test('Realm.write with async callback', () { + final realm = getRealm(Configuration.local([Person.schema])); + expect(() => realm.write(() async {}), throwsA(isA())); + }); + test('Transaction.commitAsync with a canceled token throws', () async { final realm = getRealm(Configuration.local([Person.schema]));