diff --git a/.github/actions/prepare-build/action.yml b/.github/actions/prepare-build/action.yml new file mode 100644 index 0000000..8d99a37 --- /dev/null +++ b/.github/actions/prepare-build/action.yml @@ -0,0 +1,22 @@ +name: "Prepare Build" +description: "Prepares the build for Isar Core" +runs: + using: "composite" + steps: + - name: Install LLVM and Clang + if: runner.os == 'Windows' + uses: KyleMayes/install-llvm-action@v1 + with: + version: "11.0" + directory: ${{ runner.temp }}/llvm + - name: Set LIBCLANG_PATH + if: runner.os == 'Windows' + shell: bash + run: echo "LIBCLANG_PATH=$((gcm clang).source -replace "clang.exe")" >> $env:GITHUB_ENV + # See https://github.com/godot-rust/godot-rust/pull/920 + - name: "Workaround Android NDK due to Rust bug" + if: runner.os == 'Linux' || runner.os == 'macOS' + shell: bash + run: > + find -L $ANDROID_SDK_ROOT/ndk/$ANDROID_NDK_VERSION -name libunwind.a + -execdir sh -c 'echo "INPUT(-lunwind)" > libgcc.a' \; diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index dba621e..42e12a8 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -3,7 +3,7 @@ name: Docs on: push: branches: - - main + - main permissions: contents: read @@ -37,4 +37,4 @@ jobs: path: docs/docs/.vuepress/dist - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v1 \ No newline at end of file + uses: actions/deploy-pages@v1 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index bdc6e89..4730489 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -3,20 +3,20 @@ name: Isar release on: push: tags: - - '*' + - "*" jobs: verify_version: name: Verify version matches release runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: dart-lang/setup-dart@v1 - - name: Verify release version - run: | - dart pub get - dart tool/verify_release_version.dart ${{ github.ref_name }} - working-directory: packages/isar + - uses: actions/checkout@v3 + - uses: dart-lang/setup-dart@v1 + - name: Verify release version + run: | + dart pub get + dart tool/verify_release_version.dart ${{ github.ref_name }} + working-directory: packages/isar build_binaries: name: Build Binaries @@ -54,33 +54,20 @@ jobs: script: build_windows.sh x64 runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 - - name: Install LLVM and Clang - uses: KyleMayes/install-llvm-action@v1 - if: matrix.os == 'windows-latest' - with: - version: "11.0" - directory: ${{ runner.temp }}/llvm - - name: Set LIBCLANG_PATH - run: echo "LIBCLANG_PATH=$((gcm clang).source -replace "clang.exe")" >> $env:GITHUB_ENV - if: matrix.os == 'windows-latest' - # See https://github.com/godot-rust/godot-rust/pull/920 - - name: "Workaround Android NDK due to Rust bug" - if: matrix.os == 'ubuntu-latest' - run: > - find -L $ANDROID_SDK_ROOT/ndk/$ANDROID_NDK_VERSION -name libunwind.a - -execdir sh -c 'echo "INPUT(-lunwind)" > libgcc.a' \; - - name: Set env - run: echo "ISAR_VERSION=${{ github.ref_name }}" >> $GITHUB_ENV - - name: Build binary - run: bash tool/${{ matrix.script }} - - name: Upload binary - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{ matrix.artifact_name }} - asset_name: ${{ matrix.artifact_name }} - tag: ${{ github.ref }} + - uses: actions/checkout@v3 + - name: Prepare Build + uses: ./.github/actions/prepare-build + - name: Set env + run: echo "ISAR_VERSION=${{ github.ref_name }}" >> $GITHUB_ENV + - name: Build binary + run: bash tool/${{ matrix.script }} + - name: Upload binary + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ${{ matrix.artifact_name }} + asset_name: ${{ matrix.artifact_name }} + tag: ${{ github.ref }} build_inspector: name: Build Inspector diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a0e4624..5316237 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,10 +3,10 @@ name: Dart CI on: push: branches: - - main + - main pull_request: branches: - - main + - main jobs: format: @@ -48,35 +48,65 @@ jobs: run: flutter analyze test: + name: Dart Test strategy: matrix: os: [macos-latest, ubuntu-latest] fail-fast: false runs-on: ${{ matrix.os }} steps: + - run: echo "$OSTYPE" - uses: actions/checkout@v3 - uses: subosito/flutter-action@v2 + - name: Prepare Build + uses: ./.github/actions/prepare-build + - name: Build Isar Core + run: sh tool/build.sh + - name: Prepare Tests + run: sh tool/prepare_tests.sh - name: Run Flutter Unit tests + run: flutter test -j 1 + working-directory: packages/isar_test + + valgrind: + name: Valgrind + runs-on: ubuntu-latest + if: ${{ false }} + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + - name: Prepare Build + uses: ./.github/actions/prepare-build + - name: Install valgrind and llvm + run: sudo apt update && sudo apt install -y valgrind libclang-dev + - name: Build Isar Core + run: sh tool/build.sh + - name: Prepare Tests + run: sh tool/prepare_tests.sh + - name: Run Valgrind run: | - flutter pub get - dart tool/generate_long_double_test.dart - dart tool/generate_prepare_test.dart - flutter pub run build_runner build - flutter test -j 1 + dart compile exe integration_test/all_tests.dart + valgrind \ + --leak-check=full \ + --error-exitcode=1 \ + --show-mismatched-frees=no \ + --show-possibly-lost=no \ + --errors-for-leak-kinds=definite \ + integration_test/all_tests.exe working-directory: packages/isar_test coverage: + name: Code Coverage runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: subosito/flutter-action@v2 - - name: Prepare Unit tests - run: | - flutter pub get - dart tool/generate_long_double_test.dart - dart tool/generate_prepare_test.dart - flutter pub run build_runner build - working-directory: packages/isar_test + - name: Prepare Build + uses: ./.github/actions/prepare-build + - name: Build Isar Core + run: sh tool/build.sh + - name: Prepare Tests + run: sh tool/prepare_tests.sh - name: Add packages run: | flutter pub add json_annotation @@ -101,6 +131,7 @@ jobs: files: packages/isar/lcov_isar_test.info test_generator: + name: Generator Test runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -111,99 +142,178 @@ jobs: dart test working-directory: packages/isar_generator - drive_ios: - strategy: - matrix: - device: - - "iPhone 8" - - "iPhone 11 Pro Max" - fail-fast: false + integration_test_ios: + name: Integration Test iOS runs-on: macos-latest steps: - uses: actions/checkout@v3 - name: Start simulator uses: futureware-tech/simulator-action@v2 with: - model: ${{ matrix.device }} + model: iPhone 13 - uses: subosito/flutter-action@v2 + - name: Prepare Build + uses: ./.github/actions/prepare-build - name: Build Isar Core run: | bash tool/build_ios.sh unzip isar_ios.xcframework.zip -d packages/isar_flutter_libs/ios - - name: Run Flutter Driver tests - run: | - flutter pub get - dart tool/generate_long_double_test.dart - flutter pub run build_runner build - dart tool/generate_all_tests.dart - flutter drive --driver=isar_driver.dart --target=isar_driver_target.dart + - name: Prepare Tests + run: sh tool/prepare_tests.sh + - name: Run Flutter Integration tests + run: flutter test integration_test/integration_test.dart --dart-define STRESS=true working-directory: packages/isar_test - drive_android: + integration_test_android: + name: Integration Test Android runs-on: macos-latest - if: ${{ false }} - strategy: - matrix: - api-level: [29] #[21, 29] - target: [default] - fail-fast: false steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: - java-version: '11' - distribution: 'zulu' + java-version: "11" + distribution: "zulu" - uses: subosito/flutter-action@v2 + - name: Prepare Build + uses: ./.github/actions/prepare-build + - name: Setup old NDK + run: | + echo y | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager "ndk;21.4.7075529" + ln -sfn $ANDROID_HOME/ndk/21.4.7075529 $ANDROID_HOME/ndk-bundle + - name: Update ANDROID_NDK_HOME + run: echo "ANDROID_NDK_HOME=$ANDROID_HOME/ndk/21.4.7075529" >> $GITHUB_ENV - name: Build Isar Core run: | bash tool/build_android.sh x64 + mkdir -p packages/isar_flutter_libs/android/src/main/jniLibs/x86_64 mv libisar_android_x64.so packages/isar_flutter_libs/android/src/main/jniLibs/x86_64/libisar.so - - name: Run isar_generator - run: | - flutter pub get - dart tool/generate_long_double_test.dart - flutter pub run build_runner build - dart tool/generate_all_tests.dart - working-directory: packages/isar_test - - name: Run Flutter Driver tests - uses: reactivecircus/android-emulator-runner@v2 + - name: Prepare Tests + run: sh tool/prepare_tests.sh + - name: Run Flutter Integration tests + continue-on-error: true + timeout-minutes: ${{ inputs.timeout_minutes }} + uses: Wandalen/wretry.action@v1.0.25 with: - api-level: ${{ matrix.api-level }} - target: ${{ matrix.target }} - arch: x86_64 - profile: Nexus 6 - working-directory: packages/isar_test - script: "flutter drive --driver=isar_driver.dart --target=isar_driver_target.dart" + action: reactivecircus/android-emulator-runner@v2 + with: | + api-level: 29 + arch: x86_64 + profile: pixel + working-directory: packages/isar_test + script: flutter test integration_test/integration_test.dart --dart-define STRESS=true - drive_desktop: - strategy: - matrix: - os: [macos-latest] - fail-fast: false - runs-on: ${{ matrix.os }} + firebase_testlab_android: + name: Firebase Testlab Android + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: subosito/flutter-action@v2 - - name: Install Linux requirements - run: sudo apt-get install clang cmake ninja-build pkg-config libgtk-3-dev - if: matrix.os == 'ubuntu-latest' - - name: Build Isar Core macOS - if: matrix.os == 'macos-latest' + - name: Prepare Build + uses: ./.github/actions/prepare-build + - name: Build Isar Core arm64 + run: | + bash tool/build_android.sh arm64 + mkdir -p packages/isar_flutter_libs/android/src/main/jniLibs/arm64-v8a + mv libisar_android_arm64.so packages/isar_flutter_libs/android/src/main/jniLibs/arm64-v8a/libisar.so + - name: Build Isar Core armv7 + run: | + bash tool/build_android.sh armv7 + mkdir -p packages/isar_flutter_libs/android/src/main/jniLibs/armeabi-v7a + mv libisar_android_armv7.so packages/isar_flutter_libs/android/src/main/jniLibs/armeabi-v7a/libisar.so + - name: Prepare Tests + run: sh tool/prepare_tests.sh + - name: Build dummy APK + run: flutter build apk integration_test/integration_test.dart + working-directory: packages/isar_test + - name: Build APKs + run: | + ./gradlew app:assembleAndroidTest + ./gradlew app:assembleDebug -Ptarget=integration_test/integration_test.dart + working-directory: packages/isar_test/android + - name: Login to Google Cloud + uses: "google-github-actions/auth@v1" + with: + credentials_json: "${{ secrets.FIREBASE_JSON }}" + - name: Run tests + run: | + gcloud firebase test android run \ + --project honeyy \ + --type instrumentation \ + --device model=hammerhead,version=23 \ + --device model=starqlteue,version=26 \ + --device model=cheetah,version=33 \ + --device model=1610,version=23 \ + --device model=griffin,version=24 \ + --device model=harpia,version=23 \ + --device model=sailfish,version=25 \ + --app build/app/outputs/apk/debug/app-debug.apk \ + --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk\ + working-directory: packages/isar_test + + integration_test_macos: + name: Integration Test macOS + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + - name: Prepare Build + uses: ./.github/actions/prepare-build + - name: Build Isar Core run: | bash tool/build_macos.sh install_name_tool -id @rpath/libisar.dylib libisar_macos.dylib mv libisar_macos.dylib packages/isar_flutter_libs/macos/libisar.dylib + - name: Prepare Tests + run: sh tool/prepare_tests.sh - name: Run Flutter Driver tests run: | - flutter pub get - dart tool/generate_long_double_test.dart - flutter pub run build_runner build - dart tool/generate_all_tests.dart - flutter config --no-enable-web flutter config --enable-macos-desktop + flutter test -d macos integration_test/integration_test.dart --dart-define STRESS=true + working-directory: packages/isar_test + + integration_test_linux: + name: Integration Test Linux + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + - name: Prepare Build + uses: ./.github/actions/prepare-build + - name: Install Linux requirements + run: sudo apt-get install clang cmake ninja-build pkg-config libgtk-3-dev + - name: Setup headless display + uses: pyvista/setup-headless-display-action@v1 + - name: Build Isar Core + run: | + bash tool/build_linux.sh x64 + mv libisar_linux_x64.so packages/isar_flutter_libs/linux/libisar.so + - name: Prepare Tests + run: sh tool/prepare_tests.sh + - name: Run Flutter Driver tests + run: | flutter config --enable-linux-desktop + flutter test -d linux integration_test/integration_test.dart --dart-define STRESS=true + working-directory: packages/isar_test + + integration_test_windows: + name: Integration Test Windows + runs-on: windows-2019 + if: ${{ false }} + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + - name: Prepare Build + uses: ./.github/actions/prepare-build + - name: Build Isar Core + run: | + bash tool/build_windows.sh x64 + mv isar_windows_x64.dll packages/isar_flutter_libs/windows/libisar.dll + - name: Prepare Tests + run: sh tool/prepare_tests.sh + - name: Run Flutter Driver tests + run: | flutter config --enable-windows-desktop - flutter drive --driver=isar_driver.dart --target=isar_driver_target.dart + flutter test -d windows integration_test/integration_test.dart --dart-define STRESS=true working-directory: packages/isar_test drive_chrome: @@ -220,8 +330,8 @@ jobs: run: | flutter pub get dart tool/generate_long_double_test.dart - flutter pub run build_runner build dart tool/generate_all_tests.dart + flutter pub run build_runner build flutter drive --driver=isar_driver.dart --target=isar_driver_target.dart -d web-server --browser-name chrome working-directory: packages/isar_test diff --git a/docs/docs/crud.md b/docs/docs/crud.md index a090c8a..bdf21f2 100644 --- a/docs/docs/crud.md +++ b/docs/docs/crud.md @@ -18,13 +18,14 @@ final isar = await Isar.open([RecipeSchema]); You can use the default config or provide some of the following parameters: -| Config | Description | -| -------| -------------| -| `name` | Open multiple instances with distinct names. By default, `"default"` is used. | -| `directory` | The storage location for this instance. By default, `NSDocumentDirectory` is used for iOS and `getDataDirectory` for Android. Not required for web. | -| `relaxedDurability` | Relaxes the durability guarantee to increase write performance. In case of a system crash (not app crash), it is possible to lose the last committed transaction. Corruption is not possible | -| `compactOnLaunch` | Conditions to check whether the database should be compacted when the instance is opened. | -| `inspector` | Enabled the Inspector for debug builds. For profile and release builds this option is ignored. | +| Config | Description | +| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | Open multiple instances with distinct names. By default, `"default"` is used. | +| `directory` | The storage location for this instance. By default, `NSDocumentDirectory` is used for iOS and `getDataDirectory` for Android. Not required for web. | +| `maxSizeMib` | The maximum size of the database file in MiB. Isar uses virtual memory which is not an endless resource so be mindful with the value here. If you open multiple instances they share the available virtual memory so each instance should have a smaller `maxSizeMib` . The default is 2048. | +| `relaxedDurability` | Relaxes the durability guarantee to increase write performance. In case of a system crash (not app crash), it is possible to lose the last committed transaction. Corruption is not possible | +| `compactOnLaunch` | Conditions to check whether the database should be compacted when the instance is opened. | +| `inspector` | Enabled the Inspector for debug builds. For profile and release builds this option is ignored. | If an instance is already open, calling `Isar.open()` will yield the existing instance regardless of the specified parameters. That's useful for using Isar in an isolate. diff --git a/docs/docs/indexes.md b/docs/docs/indexes.md index 172b22b..a409a7b 100644 --- a/docs/docs/indexes.md +++ b/docs/docs/indexes.md @@ -185,7 +185,7 @@ final user1 = User() ..age = 25; // user does not exist so this is the same as put() -await isar.users.putByUsername(user1); +await isar.users.putByUsername(user1); await isar.user.where().findAll(); // -> [{id: 1, username: 'user1', age: 25}] final user2 = User() diff --git a/docs/docs/queries.md b/docs/docs/queries.md index 5d000e1..b9c8910 100644 --- a/docs/docs/queries.md +++ b/docs/docs/queries.md @@ -39,15 +39,15 @@ class Shoe { Depending on the type of field, there are different conditions available. -| Condition | Description | -| ----------| ------------| -| `.equalTo(value)` | Matches values that are equal to the specified `value`. | -| `.between(lower, upper)` | Matches values that are between `lower` and `upper`. | -| `.greaterThan(bound)` | Matches values that are greater than `bound`. | -| `.lessThan(bound)` | Matches values that are less than `bound`. `null` values will be included by default because `null` is considered smaller than any other value. | -| `.isNull()` | Matches values that are `null`.| -| `.isNotNull()` | Matches values that are not `null`.| -| `.length()` | List, String and link length queries filter objects based on the number of elements in a list or link. | +| Condition | Description | +| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| `.equalTo(value)` | Matches values that are equal to the specified `value`. | +| `.between(lower, upper)` | Matches values that are between `lower` and `upper`. | +| `.greaterThan(bound)` | Matches values that are greater than `bound`. | +| `.lessThan(bound)` | Matches values that are less than `bound`. `null` values will be included by default because `null` is considered smaller than any other value. | +| `.isNull()` | Matches values that are `null`. | +| `.isNotNull()` | Matches values that are not `null`. | +| `.length()` | List, String and link length queries filter objects based on the number of elements in a list or link. | Let's assume the database contains four shoes with sizes 39, 40, 46 and one with an un-set (`null`) size. Unless you perform sorting, the values will be returned sorted by id. @@ -71,13 +71,13 @@ isar.shoes.filter() You can composite predicates using the following logical operators: -| Operator | Description | -| ---------- | ----------- | +| Operator | Description | +| ---------- | ------------------------------------------------------------------------------------ | | `.and()` | Evaluates to `true` if both left-hand and right-hand expressions evaluate to `true`. | -| `.or()` | Evaluates to `true` if either expression evaluates to `true`. | -| `.xor()` | Evaluates to `true` if exactly one expression evaluates to `true`. | -| `.not()` | Negates the result of the following expression. | -| `.group()` | Group conditions and allow to specify order of evaluation. | +| `.or()` | Evaluates to `true` if either expression evaluates to `true`. | +| `.xor()` | Evaluates to `true` if exactly one expression evaluates to `true`. | +| `.not()` | Negates the result of the following expression. | +| `.group()` | Group conditions and allow to specify order of evaluation. | If you want to find all shoes in size 46, you can use the following query: @@ -152,11 +152,11 @@ A [wildcard string expression](https://en.wikipedia.org/wiki/Wildcard_character) Sometimes it is necessary to build a query based on some conditions or for different values. Isar has a very powerful tool for building conditional queries: -| Modifier | Description | -| --------------------- | ---------------------------------------------------- | +| Modifier | Description | +| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | `.optional(cond, qb)` | Extends the query only if the `condition` is `true`. This can be used almost anywhere in a query for example to conditionally sort or limit it. | -| `.anyOf(list, qb)` | Extends the query for each value in `values` and combines the conditions using logical **or**. | -| `.allOf(list, qb)` | Extends the query for each value in `values` and combines the conditions using logical **and**. | +| `.anyOf(list, qb)` | Extends the query for each value in `values` and combines the conditions using logical **or**. | +| `.allOf(list, qb)` | Extends the query for each value in `values` and combines the conditions using logical **and**. | In this example, we build a method that can find shoes with an optional filter: @@ -309,7 +309,7 @@ final result = await isar.students.filter() Link filters evaluate to `true` if at least one linked object matches the conditions. Let's search for all students that have no teachers: - + ```dart final result = await isar.students.filter().teachersLengthEqualTo(0).findAll(); ``` diff --git a/examples/pub/lib/provider.dart b/examples/pub/lib/provider.dart index ede5754..d3d6b9e 100644 --- a/examples/pub/lib/provider.dart +++ b/examples/pub/lib/provider.dart @@ -4,14 +4,16 @@ import 'dart:async'; import 'package:dio/dio.dart'; import 'package:isar/isar.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:pub_app/models/asset.dart'; import 'package:pub_app/models/package.dart'; import 'package:pub_app/package_manager.dart'; import 'package:pub_app/repository.dart'; import 'package:riverpod/riverpod.dart'; -final isarPod = FutureProvider((ref) { - return Isar.open([PackageSchema, AssetSchema]); +final isarPod = FutureProvider((ref) async { + final dir = await getApplicationDocumentsDirectory(); + return Isar.open([PackageSchema, AssetSchema], directory: dir.path); }); final repositoryPod = Provider((ref) { diff --git a/examples/pub/lib/ui/app_bar.dart b/examples/pub/lib/ui/app_bar.dart index da0f516..347d493 100644 --- a/examples/pub/lib/ui/app_bar.dart +++ b/examples/pub/lib/ui/app_bar.dart @@ -48,7 +48,7 @@ class PubAppBar extends ConsumerWidget implements PreferredSizeWidget { darkMode ? Icons.light_mode_rounded : Icons.dark_mode_rounded, ), onPressed: () { - ref.read(darkModePod.state).state = !darkMode; + ref.read(darkModePod.notifier).state = !darkMode; }, ), ], diff --git a/packages/isar/CHANGELOG.md b/packages/isar/CHANGELOG.md index 10f1300..1da1034 100644 --- a/packages/isar/CHANGELOG.md +++ b/packages/isar/CHANGELOG.md @@ -3,13 +3,18 @@ ### Enhancements - Improved performance for all operations +- Added `maxSizeMiB` option to `Isar.open()` to specify the maximum size of the database file - Significantly reduced native library size +- With the help of the community, the docs have been translated to a range of languages +- Improved API docs +- Added integration tests for more platforms to ensure high quality releases ### Fixes - Fixed a native port that was not closed correctly in some cases - Added swift version to podspec - Fixed crash on Windows +- Fixed "IndexNotFound" error ## 3.0.2 diff --git a/packages/isar/LICENSE b/packages/isar/LICENSE index 7a4a3ea..f0d4b83 100644 --- a/packages/isar/LICENSE +++ b/packages/isar/LICENSE @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2022 Simon Leier Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/isar/lib/src/isar.dart b/packages/isar/lib/src/isar.dart index ca7c770..b2fa504 100644 --- a/packages/isar/lib/src/isar.dart +++ b/packages/isar/lib/src/isar.dart @@ -29,8 +29,10 @@ abstract class Isar { /// The default Isar instance name. static const String defaultName = 'default'; + /// The default max Isar size. + static const int defaultMaxSizeMiB = 2048; + /// Placeholder for an auto-increment id. - // ignore: prefer_const_declarations static const Id autoIncrement = isarAutoIncrementId; static final Map _instances = {}; @@ -87,6 +89,7 @@ abstract class Isar { List> schemas, { String? directory, String name = defaultName, + int maxSizeMiB = Isar.defaultMaxSizeMiB, bool relaxedDurability = true, CompactCondition? compactOnLaunch, bool inspector = true, @@ -105,6 +108,7 @@ abstract class Isar { schemas: schemas, directory: directory, name: name, + maxSizeMiB: maxSizeMiB, relaxedDurability: relaxedDurability, compactOnLaunch: compactOnLaunch, ); @@ -115,6 +119,7 @@ abstract class Isar { List> schemas, { String? directory, String name = defaultName, + int maxSizeMiB = Isar.defaultMaxSizeMiB, bool relaxedDurability = true, CompactCondition? compactOnLaunch, bool inspector = true, @@ -133,6 +138,7 @@ abstract class Isar { schemas: schemas, directory: directory, name: name, + maxSizeMiB: maxSizeMiB, relaxedDurability: relaxedDurability, compactOnLaunch: compactOnLaunch, ); diff --git a/packages/isar/lib/src/isar_collection.dart b/packages/isar/lib/src/isar_collection.dart index 258ddbc..2d2b814 100644 --- a/packages/isar/lib/src/isar_collection.dart +++ b/packages/isar/lib/src/isar_collection.dart @@ -3,7 +3,11 @@ part of isar; /// Normal keys consist of a single object, composite keys multiple. typedef IndexKey = List; -/// Collections are used to store and retrieve your objects from Isar. +/// Use `IsarCollection` instances to find, query, and create new objects of a +/// given type in Isar. +/// +/// You can get an instance of `IsarCollection` by calling `isar.get()` or +/// by using the generated `isar.yourCollections` getter. abstract class IsarCollection { /// The corresponding Isar instance. Isar get isar; @@ -14,150 +18,227 @@ abstract class IsarCollection { /// The name of the collection. String get name => schema.name; + /// {@template col_get} /// Get a single object by its [id] or `null` if the object does not exist. + /// {@endtemplate} Future get(Id id) { return getAll([id]).then((List objects) => objects[0]); } - /// Get a single object by its [id] or `null` if the object does not exist. + /// {@macro col_get} OBJ? getSync(Id id) { return getAllSync([id])[0]; } + /// {@template col_get_all} /// Get a list of objects by their [ids] or `null` if an object does not /// exist. + /// {@endtemplate} Future> getAll(List ids); - /// Get a list of objects by their [ids] or `null` if an object does not - /// exist. + /// {@macro col_get_all} List getAllSync(List ids); - /// @nodoc - @protected + /// {@template col_get_by_index} + /// Get a single object by the unique index [indexName] and [key]. + /// + /// Returns `null` if the object does not exist. + /// + /// If possible, you should use the generated type-safe methods instead. + /// {@endtemplate} + @experimental Future getByIndex(String indexName, IndexKey key) { return getAllByIndex(indexName, [key]) .then((List objects) => objects[0]); } - /// @nodoc - @protected + /// {@macro col_get_by_index} + @experimental OBJ? getByIndexSync(String indexName, IndexKey key) { return getAllByIndexSync(indexName, [key])[0]; } - /// @nodoc - @protected + /// {@template col_get_all_by_index} + /// Get a list of objects by the unique index [indexName] and [keys]. + /// + /// Returns `null` if the object does not exist. + /// + /// If possible, you should use the generated type-safe methods instead. + /// {@endtemplate} + @experimental Future> getAllByIndex(String indexName, List keys); - /// @nodoc - @protected + /// {@macro col_get_all_by_index}' + @experimental List getAllByIndexSync(String indexName, List keys); - /// Insert or update an [object] and returns the assigned id. + /// {@template col_put} + /// Insert or update an [object]. Returns the id of the new or updated object. + /// + /// If the object has an non-final id property, it will be set to the assigned + /// id. Otherwise you should use the returned id to update the object. + /// {@endtemplate} Future put(OBJ object) { return putAll([object]).then((List ids) => ids[0]); } - /// Insert or update an [object] and returns the assigned id. + /// {@macro col_put} Id putSync(OBJ object, {bool saveLinks = true}) { return putAllSync([object], saveLinks: saveLinks)[0]; } - /// Insert or update a list of [objects] and returns the list of assigned ids. + /// {@template col_put_all} + /// Insert or update a list of [objects]. Returns the list of ids of the new + /// or updated objects. + /// + /// If the objects have an non-final id property, it will be set to the + /// assigned id. Otherwise you should use the returned ids to update the + /// objects. + /// {@endtemplate} Future> putAll(List objects); - /// Insert or update a list of [objects] and returns the list of assigned ids. + /// {@macro col_put_all} List putAllSync(List objects, {bool saveLinks = true}); - /// @nodoc - @protected + /// {@template col_put_by_index} + /// Insert or update the [object] by the unique index [indexName]. Returns the + /// id of the new or updated object. + /// + /// If there is already an object with the same index key, it will be + /// updated and all links will be preserved. Otherwise a new object will be + /// inserted. + /// + /// If the object has an non-final id property, it will be set to the assigned + /// id. Otherwise you should use the returned id to update the object. + /// + /// If possible, you should use the generated type-safe methods instead. + /// {@endtemplate} + @experimental Future putByIndex(String indexName, OBJ object) { return putAllByIndex(indexName, [object]).then((List ids) => ids[0]); } - /// @nodoc - @protected + /// {@macro col_put_by_index} + @experimental Id putByIndexSync(String indexName, OBJ object, {bool saveLinks = true}) { return putAllByIndexSync(indexName, [object])[0]; } - /// @nodoc - @protected + /// {@template col_put_all_by_index} + /// Insert or update a list of [objects] by the unique index [indexName]. + /// Returns the list of ids of the new or updated objects. + /// + /// If there is already an object with the same index key, it will be + /// updated and all links will be preserved. Otherwise a new object will be + /// inserted. + /// + /// If the objects have an non-final id property, it will be set to the + /// assigned id. Otherwise you should use the returned ids to update the + /// objects. + /// + /// If possible, you should use the generated type-safe methods instead. + /// {@endtemplate} + @experimental Future> putAllByIndex(String indexName, List objects); - /// @nodoc - @protected + /// {@macro col_put_all_by_index} + @experimental List putAllByIndexSync( String indexName, List objects, { bool saveLinks = true, }); + /// {@template col_delete} /// Delete a single object by its [id]. /// /// Returns whether the object has been deleted. Isar web always returns /// `true`. + /// {@endtemplate} Future delete(Id id) { return deleteAll([id]).then((int count) => count == 1); } - /// Delete a single object by its [id]. - /// - /// Returns whether the object has been deleted. + /// {@macro col_delete} bool deleteSync(Id id) { return deleteAllSync([id]) == 1; } + /// {@template col_delete_all} /// Delete a list of objects by their [ids]. /// /// Returns the number of objects that have been deleted. Isar web always /// returns `ids.length`. + /// {@endtemplate} Future deleteAll(List ids); - /// Delete a list of objects by their [ids]. - /// - /// Returns the number of objects that have been deleted. + /// {@macro col_delete_all} int deleteAllSync(List ids); - /// @nodoc - @protected + /// {@template col_delete_by_index} + /// Delete a single object by the unique index [indexName] and [key]. + /// + /// Returns whether the object has been deleted. Isar web always returns + /// `true`. + /// {@endtemplate} + @experimental Future deleteByIndex(String indexName, IndexKey key) { return deleteAllByIndex(indexName, [key]).then((int count) => count == 1); } - /// @nodoc - @protected + /// {@macro col_delete_by_index} + @experimental bool deleteByIndexSync(String indexName, IndexKey key) { return deleteAllByIndexSync(indexName, [key]) == 1; } - /// @nodoc - @protected + /// {@template col_delete_all_by_index} + /// Delete a list of objects by the unique index [indexName] and [keys]. + /// + /// Returns the number of objects that have been deleted. Isar web always + /// returns `keys.length`. + /// {@endtemplate} + @experimental Future deleteAllByIndex(String indexName, List keys); - /// @nodoc - @protected + /// {@macro col_delete_all_by_index} + @experimental int deleteAllByIndexSync(String indexName, List keys); + /// {@template col_clear} /// Remove all data in this collection and reset the auto increment value. + /// {@endtemplate} Future clear(); - /// Remove all data in this collection and reset the auto increment value. + /// {@macro col_clear} void clearSync(); - /// Import a list of json objects. + /// {@template col_import_json_raw} + /// Import a list of json objects encoded as a byte array. + /// + /// The json objects must have the same structure as the objects in this + /// collection. Otherwise an exception will be thrown. + /// {@endtemplate} Future importJsonRaw(Uint8List jsonBytes); - /// Import a list of json objects. + /// {@macro col_import_json_raw} void importJsonRawSync(Uint8List jsonBytes); + /// {@template col_import_json} /// Import a list of json objects. + /// + /// The json objects must have the same structure as the objects in this + /// collection. Otherwise an exception will be thrown. + /// {@endtemplate} Future importJson(List> json); - /// Import a list of json objects. + /// {@macro col_import_json} void importJsonSync(List> json); /// Start building a query using the [QueryBuilder]. + /// + /// You can use where clauses to only return [distinct] results. If you want + /// to reverse the order, set [sort] to [Sort.desc]. QueryBuilder where({ bool distinct = false, Sort sort = Sort.asc, @@ -172,10 +253,10 @@ abstract class IsarCollection { /// Start building a query using the [QueryBuilder]. /// - /// Shortcut if you don't want to use indexes + /// Shortcut if you don't want to use where clauses. QueryBuilder filter() => where().filter(); - /// Build a query dynamically. Can be used to build a custom query language. + /// Build a query dynamically for example to build a custom query language. /// /// It is highly discouraged to use this method. Only in very special cases /// should it be used. If you open an issue please always mention that you @@ -196,28 +277,26 @@ abstract class IsarCollection { String? property, }); + /// {@template col_count} /// Returns the total number of objects in this collection. /// /// For non-web apps, this method is extremely fast and independent of the /// number of objects in the collection. + /// {@endtemplate} Future count(); - /// Returns the total number of objects in this collection. + /// {@macro col_count} + int countSync(); + + /// {@template col_size} + /// Returns the size of the collection in bytes. Not supported on web. /// /// For non-web apps, this method is extremely fast and independent of the /// number of objects in the collection. - int countSync(); - - /// Returns the size of the collection in bytes. Not supported on web. - /// - /// This method is extremely fast and independent of the number of objects in - /// the collection. + /// {@endtemplate} Future getSize({bool includeIndexes = false, bool includeLinks = false}); - /// Returns the size of the collection in bytes. Not supported on web. - /// - /// This method is extremely fast and independent of the number of objects in - /// the collection. + /// {@macro col_size} int getSizeSync({bool includeIndexes = false, bool includeLinks = false}); /// Watch the collection for changes. @@ -225,13 +304,14 @@ abstract class IsarCollection { /// If [fireImmediately] is `true`, an event will be fired immediately. Stream watchLazy({bool fireImmediately = false}); - /// Watch an object with [id] for changes. + /// Watch the object with [id] for changes. If a change occurs, the new object + /// will be returned in the stream. /// /// Objects that don't exist (yet) can also be watched. If [fireImmediately] /// is `true`, the object will be sent to the consumer immediately. Stream watchObject(Id id, {bool fireImmediately = false}); - /// Watch an object with [id] for changes. + /// Watch the object with [id] for changes. /// /// If [fireImmediately] is `true`, an event will be fired immediately. Stream watchObjectLazy(Id id, {bool fireImmediately = false}); diff --git a/packages/isar/lib/src/isar_connect.dart b/packages/isar/lib/src/isar_connect.dart index 2603bdf..b6eee88 100644 --- a/packages/isar/lib/src/isar_connect.dart +++ b/packages/isar/lib/src/isar_connect.dart @@ -1,5 +1,5 @@ // coverage:ignore-file -// ignore_for_file: avoid_print, avoid_dynamic_calls +// ignore_for_file: avoid_print part of isar; @@ -243,11 +243,12 @@ abstract class _IsarConnect { if (objects.isNotEmpty) { dynamic object = objects.first; for (var i = 0; i < keys.length; i++) { - final key = object is List ? int.parse(keys[i]) : keys[i]; - if (i == keys.length - 1) { - object[key] = cEdit.value; - } else { - object = object[key]; + if (i == keys.length - 1 && object is Map) { + object[keys[i]] = cEdit.value; + } else if (object is Map) { + object = object[keys[i]]; + } else if (object is List) { + object = object[int.parse(keys[i])]; } } try { diff --git a/packages/isar/lib/src/isar_link.dart b/packages/isar/lib/src/isar_link.dart index b5ebe97..d589383 100644 --- a/packages/isar/lib/src/isar_link.dart +++ b/packages/isar/lib/src/isar_link.dart @@ -1,7 +1,7 @@ part of isar; /// @nodoc -@protected +@sealed abstract class IsarLinkBase { /// Is the containing object managed by Isar? bool get isAttached; @@ -12,32 +12,33 @@ abstract class IsarLinkBase { /// Has this link been loaded? bool get isLoaded; + /// {@template link_load} /// Loads the linked object(s) from the database + /// {@endtemplate} Future load(); - /// Loads the linked object(s) from the database + /// {@macro link_load} void loadSync(); + /// {@template link_save} /// Saves the linked object(s) to the database if there are changes. /// /// Also puts new objects into the database that have id set to `null` or /// `Isar.autoIncrement`. + /// {@endtemplate} Future save(); - /// Saves the linked object(s) to the database if there are changes. - /// - /// Also puts new objects into the database that have id set to `null` or - /// `Isar.autoIncrement`. + /// {@macro link_save} void saveSync(); + /// {@template link_reset} /// Unlinks all linked object(s). /// /// You can even call this method on links that have not been loaded yet. + /// {@endtemplate} Future reset(); - /// Unlinks all linked object(s). - /// - /// You can even call this method on links that have not been loaded yet. + /// {@macro link_reset} void resetSync(); /// @nodoc @@ -77,20 +78,19 @@ abstract class IsarLinks implements IsarLinkBase, Set { @override void loadSync({bool overrideChanges = true}); + /// {@template links_update} /// Creates and removes the specified links in the database. /// /// This operation does not alter the state of the local copy of this link /// and it can even be used without loading the link. + /// {@endtemplate} Future update({ Iterable link = const [], Iterable unlink = const [], bool reset = false, }); - /// Creates and removes the specified links in the database. - /// - /// This operation does not alter the state of the local copy of this link - /// and it can even be used without loading the link. + /// {@macro links_update} void updateSync({ Iterable link = const [], Iterable unlink = const [], @@ -100,15 +100,14 @@ abstract class IsarLinks implements IsarLinkBase, Set { /// Starts a query for linked objects. QueryBuilder filter(); + /// {@template links_count} /// Counts the linked objects in the database. /// /// It does not take the local state into account and can even be used /// without loading the link. + /// {@endtemplate} Future count() => filter().count(); - /// Counts the linked objects in the database. - /// - /// It does not take the local state into account and can even be used - /// without loading the link. + /// {@macro links_count} int countSync() => filter().countSync(); } diff --git a/packages/isar/lib/src/isar_reader.dart b/packages/isar/lib/src/isar_reader.dart index fe432e8..1601ed9 100644 --- a/packages/isar/lib/src/isar_reader.dart +++ b/packages/isar/lib/src/isar_reader.dart @@ -2,6 +2,7 @@ part of isar; +/// @nodoc @protected abstract class IsarReader { bool readBool(int offset); diff --git a/packages/isar/lib/src/isar_writer.dart b/packages/isar/lib/src/isar_writer.dart index f7c44ad..518802a 100644 --- a/packages/isar/lib/src/isar_writer.dart +++ b/packages/isar/lib/src/isar_writer.dart @@ -2,6 +2,7 @@ part of isar; +/// @nodoc @protected abstract class IsarWriter { void writeBool(int offset, bool? value); diff --git a/packages/isar/lib/src/native/bindings.dart b/packages/isar/lib/src/native/bindings.dart index 45cc81e..734bc9d 100644 --- a/packages/isar/lib/src/native/bindings.dart +++ b/packages/isar/lib/src/native/bindings.dart @@ -1285,6 +1285,7 @@ class IsarCoreBindings { ffi.Pointer name, ffi.Pointer path, ffi.Pointer schema_json, + int max_size_mib, bool relaxed_durability, int compact_min_file_size, int compact_min_bytes, @@ -1295,6 +1296,7 @@ class IsarCoreBindings { name, path, schema_json, + max_size_mib, relaxed_durability, compact_min_file_size, compact_min_bytes, @@ -1309,6 +1311,7 @@ class IsarCoreBindings { ffi.Pointer, ffi.Pointer, ffi.Pointer, + ffi.Int64, ffi.Bool, ffi.Uint32, ffi.Uint32, @@ -1319,6 +1322,7 @@ class IsarCoreBindings { ffi.Pointer, ffi.Pointer, ffi.Pointer, + int, bool, int, int, @@ -1329,6 +1333,7 @@ class IsarCoreBindings { ffi.Pointer name, ffi.Pointer path, ffi.Pointer schema_json, + int max_size_mib, bool relaxed_durability, int compact_min_file_size, int compact_min_bytes, @@ -1340,6 +1345,7 @@ class IsarCoreBindings { name, path, schema_json, + max_size_mib, relaxed_durability, compact_min_file_size, compact_min_bytes, @@ -1355,6 +1361,7 @@ class IsarCoreBindings { ffi.Pointer, ffi.Pointer, ffi.Pointer, + ffi.Int64, ffi.Bool, ffi.Uint32, ffi.Uint32, @@ -1367,6 +1374,7 @@ class IsarCoreBindings { ffi.Pointer, ffi.Pointer, ffi.Pointer, + int, bool, int, int, diff --git a/packages/isar/lib/src/native/open.dart b/packages/isar/lib/src/native/open.dart index 7698a7c..1b8fdec 100644 --- a/packages/isar/lib/src/native/open.dart +++ b/packages/isar/lib/src/native/open.dart @@ -79,6 +79,7 @@ Future openIsar({ required List> schemas, String? directory, required String name, + required int maxSizeMiB, required bool relaxedDurability, CompactCondition? compactOnLaunch, }) async { @@ -105,6 +106,7 @@ Future openIsar({ namePtr, dirPtr, schemaStrPtr, + maxSizeMiB, relaxedDurability, compactMinFileSize ?? 0, compactMinBytes ?? 0, @@ -122,8 +124,9 @@ Future openIsar({ Isar openIsarSync({ required List> schemas, String? directory, - String name = 'isar', - bool relaxedDurability = true, + required String name, + required int maxSizeMiB, + required bool relaxedDurability, CompactCondition? compactOnLaunch, }) { initializeCoreBinary(); @@ -146,6 +149,7 @@ Isar openIsarSync({ namePtr, dirPtr, schemaStrPtr, + maxSizeMiB, relaxedDurability, compactMinFileSize ?? 0, compactMinBytes ?? 0, diff --git a/packages/isar/lib/src/query_components.dart b/packages/isar/lib/src/query_components.dart index db19c27..a2bbfdb 100644 --- a/packages/isar/lib/src/query_components.dart +++ b/packages/isar/lib/src/query_components.dart @@ -1,5 +1,3 @@ -// ignore_for_file: prefer_initializing_formals - part of isar; /// A where clause to traverse an Isar index. @@ -20,20 +18,18 @@ class IdWhereClause extends WhereClause { /// Where clause that matches all id values greater than the given [lower] /// bound. const IdWhereClause.greaterThan({ - required Id lower, + required Id this.lower, this.includeLower = true, - }) : lower = lower, - upper = null, + }) : upper = null, includeUpper = true, super._(); /// Where clause that matches all id values less than the given [upper] /// bound. const IdWhereClause.lessThan({ - required Id upper, + required Id this.upper, this.includeUpper = true, - }) : upper = upper, - lower = null, + }) : lower = null, includeLower = true, super._(); @@ -86,11 +82,10 @@ class IndexWhereClause extends WhereClause { /// for equality. const IndexWhereClause.greaterThan({ required this.indexName, - required IndexKey lower, + required IndexKey this.lower, this.includeLower = true, this.epsilon = Query.epsilon, - }) : lower = lower, - upper = null, + }) : upper = null, includeUpper = true, super._(); @@ -101,11 +96,10 @@ class IndexWhereClause extends WhereClause { /// for equality. const IndexWhereClause.lessThan({ required this.indexName, - required IndexKey upper, + required IndexKey this.upper, this.includeUpper = true, this.epsilon = Query.epsilon, - }) : upper = upper, - lower = null, + }) : lower = null, includeLower = true, super._(); @@ -127,14 +121,12 @@ class IndexWhereClause extends WhereClause { /// are checked for equality. const IndexWhereClause.between({ required this.indexName, - required IndexKey lower, + required IndexKey this.lower, this.includeLower = true, - required IndexKey upper, + required IndexKey this.upper, this.includeUpper = true, this.epsilon = Query.epsilon, - }) : lower = lower, - upper = upper, - super._(); + }) : super._(); /// The Isar name of the index to be used. final String indexName; @@ -244,12 +236,11 @@ class FilterCondition extends FilterOperation { /// /// For lists, at least one of the values in the list has to match. const FilterCondition.equalTo({ - required String property, + required this.property, required Object? value, this.caseSensitive = true, this.epsilon = Query.epsilon, }) : type = FilterConditionType.equalTo, - property = property, value1 = value, include1 = true, value2 = null, @@ -578,20 +569,17 @@ class LinkFilter extends FilterOperation { /// Create a filter condition based on a link. const LinkFilter({ required this.linkName, - required FilterOperation filter, - }) : filter = filter, - lower = null, + required FilterOperation this.filter, + }) : lower = null, upper = null, super._(); /// Create a filter condition based on the number of linked objects. const LinkFilter.length({ required this.linkName, - required int lower, - required int upper, + required int this.lower, + required int this.upper, }) : filter = null, - lower = lower, - upper = upper, assert(lower >= 0 && upper >= 0, 'Link length must be positive.'), super._(); diff --git a/packages/isar_core/src/instance.rs b/packages/isar_core/src/instance.rs index cbd19b5..d0aef18 100644 --- a/packages/isar_core/src/instance.rs +++ b/packages/isar_core/src/instance.rs @@ -47,6 +47,7 @@ impl IsarInstance { name: &str, dir: Option<&str>, mut schema: Schema, + max_size_mib: usize, relaxed_durability: bool, compact_condition: Option, ) -> Result> { @@ -65,6 +66,7 @@ impl IsarInstance { dir, instance_id, schema, + max_size_mib, relaxed_durability, compact_condition, )?; @@ -107,6 +109,7 @@ impl IsarInstance { dir: &str, instance_id: u64, mut schema: Schema, + max_size_mib: usize, relaxed_durability: bool, compact_condition: Option, ) -> Result { @@ -115,8 +118,13 @@ impl IsarInstance { Self::move_old_database(name, dir, &isar_file); let db_count = schema.count_dbs() as u64 + 3; - let env = Env::create(&isar_file, db_count, relaxed_durability) - .map_err(|e| IsarError::EnvError { error: Box::new(e) })?; + let env = Env::create( + &isar_file, + db_count, + max_size_mib.max(1), + relaxed_durability, + ) + .map_err(|e| IsarError::EnvError { error: Box::new(e) })?; let txn = env.txn(true)?; let mut manager = SchemaManager::create(instance_id, &txn)?; @@ -154,7 +162,15 @@ impl IsarInstance { if let Some(instance) = instance { Ok(instance) } else { - Self::open_internal(name, dir, instance_id, schema, relaxed_durability, None) + Self::open_internal( + name, + dir, + instance_id, + schema, + max_size_mib, + relaxed_durability, + None, + ) } } else { Ok(instance) diff --git a/packages/isar_core/src/mdbx/env.rs b/packages/isar_core/src/mdbx/env.rs index 79f1281..91899f1 100644 --- a/packages/isar_core/src/mdbx/env.rs +++ b/packages/isar_core/src/mdbx/env.rs @@ -11,10 +11,15 @@ pub struct Env { unsafe impl Sync for Env {} unsafe impl Send for Env {} -const MB: isize = 1 << 20; +const MIB: isize = 1 << 20; impl Env { - pub fn create(path: &str, max_dbs: u64, relaxed_durability: bool) -> Result { + pub fn create( + path: &str, + max_dbs: u64, + max_size_mib: usize, + relaxed_durability: bool, + ) -> Result { let path = CString::new(path.as_bytes()).unwrap(); let mut env: *mut ffi::MDBX_env = ptr::null_mut(); unsafe { @@ -31,15 +36,17 @@ impl Env { flags |= ffi::MDBX_NOMETASYNC; } + let max_size = (max_size_mib as isize).saturating_mul(MIB); + let mut err_code = 0; for i in 0..9 { mdbx_result(ffi::mdbx_env_set_geometry( env, - MB, + MIB, 0, - (2000 - i * 200) * MB, - 5 * MB, - 20 * MB, + max_size - i * max_size / 10, + 5 * MIB, + 20 * MIB, -1, ))?; @@ -97,21 +104,3 @@ impl Drop for Env { } } } - -#[cfg(test)] -pub mod tests { - - use super::*; - - #[test] - fn test_create() { - get_env(); - } - - pub fn get_env() -> Env { - let mut dir = std::env::temp_dir(); - let r: u64 = rand::random(); - dir.push(&r.to_string()); - Env::create(dir.to_str().unwrap(), 50, false).unwrap() - } -} diff --git a/packages/isar_core/src/mdbx/mod.rs b/packages/isar_core/src/mdbx/mod.rs index d892310..c64ca09 100644 --- a/packages/isar_core/src/mdbx/mod.rs +++ b/packages/isar_core/src/mdbx/mod.rs @@ -47,7 +47,10 @@ pub fn mdbx_result(err_code: c_int) -> Result<()> { let err = CStr::from_ptr(err_raw); Err(IsarError::MdbxError { code: other, - message: err.to_str().unwrap().to_string(), + message: err + .to_str() + .unwrap_or("Cannot decode error message") + .to_string(), }) }, } diff --git a/packages/isar_core_ffi/src/instance.rs b/packages/isar_core_ffi/src/instance.rs index 46894c7..4e45886 100644 --- a/packages/isar_core_ffi/src/instance.rs +++ b/packages/isar_core_ffi/src/instance.rs @@ -30,6 +30,7 @@ pub unsafe extern "C" fn isar_instance_create( name: *const c_char, path: *const c_char, schema_json: *const c_char, + max_size_mib: i64, relaxed_durability: bool, compact_min_file_size: u32, compact_min_bytes: u32, @@ -51,8 +52,14 @@ pub unsafe extern "C" fn isar_instance_create( }) }; - let instance = - IsarInstance::open(name, path, schema, relaxed_durability, compact_condition)?; + let instance = IsarInstance::open( + name, + path, + schema, + max_size_mib as usize, + relaxed_durability, + compact_condition, + )?; isar.write(Arc::into_raw(instance)); Ok(()) }; @@ -66,6 +73,7 @@ pub unsafe extern "C" fn isar_instance_create_async( name: *const c_char, path: *const c_char, schema_json: *const c_char, + max_size_mib: i64, relaxed_durability: bool, compact_min_file_size: u32, compact_min_bytes: u32, @@ -86,6 +94,7 @@ pub unsafe extern "C" fn isar_instance_create_async( name.0, path.0, schema_json.0, + max_size_mib, relaxed_durability, compact_min_file_size, compact_min_bytes, diff --git a/packages/isar_flutter_libs/android/gradle/wrapper/gradle-wrapper.properties b/packages/isar_flutter_libs/android/gradle/wrapper/gradle-wrapper.properties index 30a56cd..0a42638 100644 --- a/packages/isar_flutter_libs/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/isar_flutter_libs/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Aug 27 10:16:05 CEST 2022 +#Sat Nov 12 16:30:49 CET 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/packages/isar_test/README.md b/packages/isar_test/README.md index 154479d..a5c8f3f 100644 --- a/packages/isar_test/README.md +++ b/packages/isar_test/README.md @@ -5,12 +5,15 @@ Use the following commands to run the tests on a connected device: ### Unit tests ``` -sh tool/setup_tests.sh +sh tool/prepare_tests.sh +sh tool/build.sh dart test ``` ### Integration tests ``` -flutter drive --driver=isar_driver.dart --target=isar_driver_target.dart +sh tool/prepare_tests.sh +sh tool/build.sh +flutter test integration_test/integration_test.dart ``` diff --git a/packages/isar_test/android/app/build.gradle b/packages/isar_test/android/app/build.gradle index 3b6f4bf..74efa64 100644 --- a/packages/isar_test/android/app/build.gradle +++ b/packages/isar_test/android/app/build.gradle @@ -26,25 +26,23 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 31 + compileSdkVersion 33 sourceSets { main.java.srcDirs += 'src/main/kotlin' } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "dev.isar.isar_test" minSdkVersion 16 - targetSdkVersion 31 + targetSdkVersion 33 versionCode flutterVersionCode.toInteger() versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.debug } } @@ -56,4 +54,7 @@ flutter { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } diff --git a/packages/isar_test/android/app/src/androidTest/java/dev/isar/isar_test/MainActivityTest.java b/packages/isar_test/android/app/src/androidTest/java/dev/isar/isar_test/MainActivityTest.java new file mode 100644 index 0000000..5ce0ad4 --- /dev/null +++ b/packages/isar_test/android/app/src/androidTest/java/dev/isar/isar_test/MainActivityTest.java @@ -0,0 +1,12 @@ +package dev.isar.isar_test; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.integration_test.FlutterTestRunner; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@RunWith(FlutterTestRunner.class) +public class MainActivityTest { + @Rule + public ActivityTestRule rule = new ActivityTestRule<>(MainActivity.class, true, false); +} \ No newline at end of file diff --git a/packages/isar_test/android/build.gradle b/packages/isar_test/android/build.gradle index 7fb1e31..090ffc6 100644 --- a/packages/isar_test/android/build.gradle +++ b/packages/isar_test/android/build.gradle @@ -2,7 +2,7 @@ buildscript { ext.kotlin_version = '1.7.20' repositories { google() - jcenter() + mavenCentral() } dependencies { @@ -14,7 +14,7 @@ buildscript { allprojects { repositories { google() - jcenter() + mavenCentral() } } diff --git a/packages/isar_test/android/gradle/wrapper/gradle-wrapper.properties b/packages/isar_test/android/gradle/wrapper/gradle-wrapper.properties index 595fb86..5bed38e 100644 --- a/packages/isar_test/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/isar_test/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Jun 23 08:50:38 CEST 2017 +#Wed Nov 09 23:26:39 CET 2022 distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip +zipStoreBase=GRADLE_USER_HOME diff --git a/packages/isar_test/isar_driver_target.dart b/packages/isar_test/integration_test/integration_test.dart similarity index 60% rename from packages/isar_test/isar_driver_target.dart rename to packages/isar_test/integration_test/integration_test.dart index 324333d..9469231 100644 --- a/packages/isar_test/isar_driver_target.dart +++ b/packages/isar_test/integration_test/integration_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: avoid_print +// ignore_for_file: avoid_print, depend_on_referenced_packages import 'dart:async'; @@ -12,21 +12,7 @@ import 'all_tests.dart' as tests; void main() async { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - final completer = Completer>(); - executeTests(completer); - - testWidgets('Isar', (WidgetTester tester) async { - await tester.pumpWidget(Container()); - final errors = await completer.future; - for (final err in errors) { - print(err); - } - expect(errors.isEmpty, true); - }); -} - -void executeTests(Completer> completer) { + final completer = Completer(); group('Integration test', () { setUpAll(() async { if (!kIsWeb) { @@ -34,11 +20,15 @@ void executeTests(Completer> completer) { testTempPath = dir.path; } }); - tearDownAll(() { - final result = testCount == 0 ? ['No tests were executed'] : testErrors; - completer.complete(result); - }); + tearDownAll(completer.complete); tests.main(); }); + + testWidgets('Isar', (WidgetTester tester) async { + await tester.pumpWidget(Container()); + await completer.future; + expect(testCount > 0, true); + expect(testErrors, isEmpty); + }); } diff --git a/packages/isar_test/isar_driver.dart b/packages/isar_test/isar_driver.dart deleted file mode 100644 index 7db05c0..0000000 --- a/packages/isar_test/isar_driver.dart +++ /dev/null @@ -1,3 +0,0 @@ -import 'package:integration_test/integration_test_driver.dart'; - -void main() => integrationDriver(); diff --git a/packages/isar_test/lib/isar_test.dart b/packages/isar_test/lib/isar_test.dart index b1686b4..df6c336 100644 --- a/packages/isar_test/lib/isar_test.dart +++ b/packages/isar_test/lib/isar_test.dart @@ -3,3 +3,10 @@ export 'src/listener.dart'; export 'src/matchers.dart'; export 'src/sync_async_helper.dart'; export 'src/sync_future.dart'; + +export 'src/twitter/entities.dart'; +export 'src/twitter/geo.dart'; +export 'src/twitter/media.dart'; +export 'src/twitter/tweet.dart'; +export 'src/twitter/user.dart'; +export 'src/twitter/util.dart'; diff --git a/packages/isar_test/lib/src/common.dart b/packages/isar_test/lib/src/common.dart index 1b1af0e..15aa6aa 100644 --- a/packages/isar_test/lib/src/common.dart +++ b/packages/isar_test/lib/src/common.dart @@ -13,7 +13,7 @@ import 'package:path/path.dart' as path; import 'package:test/test.dart'; import 'package:test_api/src/backend/invoker.dart'; -const bool kIsWeb = identical(0, 0.0); +const kIsWeb = identical(0, 0.0); final testErrors = []; int testCount = 0; @@ -111,6 +111,7 @@ Future openTempIsar( List> schemas, { String? name, String? directory, + int maxSizeMiB = Isar.defaultMaxSizeMiB, CompactCondition? compactOnLaunch, bool closeAutomatically = true, }) async { @@ -124,6 +125,7 @@ Future openTempIsar( final isar = await tOpen( schemas: schemas, name: name ?? getRandomName(), + maxSizeMiB: maxSizeMiB, directory: directory ?? testTempPath, compactOnLaunch: compactOnLaunch, ); diff --git a/packages/isar_test/lib/src/init_native.dart b/packages/isar_test/lib/src/init_native.dart index 9d3235e..e245429 100644 --- a/packages/isar_test/lib/src/init_native.dart +++ b/packages/isar_test/lib/src/init_native.dart @@ -2,27 +2,57 @@ import 'dart:ffi'; import 'dart:io'; import 'package:isar/isar.dart'; -import 'package:isar_test/src/rust_target.dart'; import 'package:path/path.dart' as path; -String getBinaryPath() { - final rootDir = path.dirname(path.dirname(Directory.current.path)); - final target = getRustTarget(); - final binaryName = Platform.isWindows - ? 'isar.dll' - : Platform.isMacOS - ? 'libisar.dylib' - : 'libisar.so'; - return path.join(rootDir, 'target', target, 'debug', binaryName); -} - Future init() async { if (Platform.isMacOS || Platform.isLinux || Platform.isWindows) { + final rootDir = path.dirname(path.dirname(Directory.current.path)); + final binaryName = Platform.isWindows + ? 'isar.dll' + : Platform.isMacOS + ? 'libisar.dylib' + : 'libisar.so'; try { await Isar.initializeIsarCore(); } catch (e) { await Isar.initializeIsarCore( - libraries: {Abi.current(): getBinaryPath()}, + libraries: { + Abi.macosArm64: path.join( + rootDir, + 'target', + 'aarch64-apple-darwin', + 'release', + binaryName, + ), + Abi.macosX64: path.join( + rootDir, + 'target', + 'x86_64-apple-darwin', + 'release', + binaryName, + ), + Abi.linuxArm64: path.join( + rootDir, + 'target', + 'aarch64-unknown-linux-gnu', + 'release', + binaryName, + ), + Abi.linuxX64: path.join( + rootDir, + 'target', + 'x86_64-unknown-linux-gnu', + 'release', + binaryName, + ), + Abi.windowsX64: path.join( + rootDir, + 'target', + 'x86_64-pc-windows-msvc', + 'release', + binaryName, + ), + }, ); } } diff --git a/packages/isar_test/lib/src/matchers.dart b/packages/isar_test/lib/src/matchers.dart index 50cefc4..f2cf45e 100644 --- a/packages/isar_test/lib/src/matchers.dart +++ b/packages/isar_test/lib/src/matchers.dart @@ -71,7 +71,17 @@ Matcher throwsIsarError([String? contains]) { return throwsA(isIsarError(contains)); } -final Matcher throwsAssertionError = throwsA(isA()); +Matcher get throwsAssertionError { + var matcher = anything; + assert( + () { + matcher = throwsA(isA()); + return true; + }(), + 'only in debug mode', + ); + return matcher; +} bool listEquals(List? a, List? b) { if (a == null) { diff --git a/packages/isar_test/lib/src/rust_target.dart b/packages/isar_test/lib/src/rust_target.dart deleted file mode 100644 index 05b654a..0000000 --- a/packages/isar_test/lib/src/rust_target.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'dart:ffi'; - -String getRustTarget() { - switch (Abi.current()) { - case Abi.macosArm64: - return 'aarch64-apple-darwin'; - case Abi.macosX64: - return 'x86_64-apple-darwin'; - case Abi.linuxArm64: - return 'aarch64-unknown-linux-gnu'; - case Abi.linuxX64: - return 'x86_64-unknown-linux-gnu'; - case Abi.windowsX64: - return 'x86_64-pc-windows-gnu'; - default: - throw UnsupportedError('Unsupported ABI: ${Abi.current()}'); - } -} diff --git a/packages/isar_test/lib/src/sync_async_helper.dart b/packages/isar_test/lib/src/sync_async_helper.dart index 122a486..9b043e5 100644 --- a/packages/isar_test/lib/src/sync_async_helper.dart +++ b/packages/isar_test/lib/src/sync_async_helper.dart @@ -10,6 +10,7 @@ Future tOpen({ required List> schemas, String? directory, String name = Isar.defaultName, + int maxSizeMiB = Isar.defaultMaxSizeMiB, bool relaxedDurability = true, CompactCondition? compactOnLaunch, }) { @@ -18,6 +19,7 @@ Future tOpen({ schemas, directory: directory, name: name, + maxSizeMiB: maxSizeMiB, relaxedDurability: relaxedDurability, compactOnLaunch: compactOnLaunch, inspector: false, @@ -28,6 +30,7 @@ Future tOpen({ schemas, directory: directory, name: name, + maxSizeMiB: maxSizeMiB, relaxedDurability: relaxedDurability, compactOnLaunch: compactOnLaunch, inspector: false, diff --git a/packages/isar_test/test/stress/twitter/entities.dart b/packages/isar_test/lib/src/twitter/entities.dart similarity index 100% rename from packages/isar_test/test/stress/twitter/entities.dart rename to packages/isar_test/lib/src/twitter/entities.dart diff --git a/packages/isar_test/test/stress/twitter/geo.dart b/packages/isar_test/lib/src/twitter/geo.dart similarity index 100% rename from packages/isar_test/test/stress/twitter/geo.dart rename to packages/isar_test/lib/src/twitter/geo.dart diff --git a/packages/isar_test/test/stress/twitter/media.dart b/packages/isar_test/lib/src/twitter/media.dart similarity index 100% rename from packages/isar_test/test/stress/twitter/media.dart rename to packages/isar_test/lib/src/twitter/media.dart diff --git a/packages/isar_test/test/stress/twitter/tweet.dart b/packages/isar_test/lib/src/twitter/tweet.dart similarity index 86% rename from packages/isar_test/test/stress/twitter/tweet.dart rename to packages/isar_test/lib/src/twitter/tweet.dart index 180d5d3..fbd7294 100644 --- a/packages/isar_test/test/stress/twitter/tweet.dart +++ b/packages/isar_test/lib/src/twitter/tweet.dart @@ -1,12 +1,11 @@ import 'package:isar/isar.dart'; +import 'package:isar_test/src/twitter/entities.dart'; +import 'package:isar_test/src/twitter/geo.dart'; +import 'package:isar_test/src/twitter/media.dart'; +import 'package:isar_test/src/twitter/user.dart'; +import 'package:isar_test/src/twitter/util.dart'; import 'package:json_annotation/json_annotation.dart'; -import 'entities.dart'; -import 'geo.dart'; -import 'media.dart'; -import 'user.dart'; -import 'util.dart'; - part 'tweet.g.dart'; @JsonSerializable(fieldRename: FieldRename.snake, createToJson: false) diff --git a/packages/isar_test/test/stress/twitter/user.dart b/packages/isar_test/lib/src/twitter/user.dart similarity index 92% rename from packages/isar_test/test/stress/twitter/user.dart rename to packages/isar_test/lib/src/twitter/user.dart index 8a240b5..dd59be9 100644 --- a/packages/isar_test/test/stress/twitter/user.dart +++ b/packages/isar_test/lib/src/twitter/user.dart @@ -1,9 +1,8 @@ import 'package:isar/isar.dart'; +import 'package:isar_test/src/twitter/entities.dart'; +import 'package:isar_test/src/twitter/util.dart'; import 'package:json_annotation/json_annotation.dart'; -import 'entities.dart'; -import 'util.dart'; - part 'user.g.dart'; @JsonSerializable(fieldRename: FieldRename.snake, createToJson: false) diff --git a/packages/isar_test/test/stress/twitter/util.dart b/packages/isar_test/lib/src/twitter/util.dart similarity index 100% rename from packages/isar_test/test/stress/twitter/util.dart rename to packages/isar_test/lib/src/twitter/util.dart diff --git a/packages/isar_test/pubspec.yaml b/packages/isar_test/pubspec.yaml index 7af6503..24da572 100644 --- a/packages/isar_test/pubspec.yaml +++ b/packages/isar_test/pubspec.yaml @@ -15,7 +15,7 @@ dependencies: intl: 0.17.0 isar: any isar_flutter_libs: any - json_annotation: ^4.2.0 + json_annotation: ^4.7.0 meta: ^1.8.0 path: ^1.8.2 path_provider: ^2.0.10 @@ -27,7 +27,7 @@ dev_dependencies: flutter_test: sdk: flutter isar_generator: any - json_serializable: ^6.3.1 + json_serializable: ^6.3.1 very_good_analysis: ^3.0.1 dependency_overrides: diff --git a/packages/isar_test/test/copy_to_file_test.dart b/packages/isar_test/test/copy_to_file_test.dart index 6894c32..bdbd3f1 100644 --- a/packages/isar_test/test/copy_to_file_test.dart +++ b/packages/isar_test/test/copy_to_file_test.dart @@ -54,10 +54,7 @@ void main() { isarTestVm('.copyToFile() should keep the same content', () async { final copiedDbFilename = getRandomName(); final copiedDbFile = File( - path.join( - isar.directory!, - '$copiedDbFilename.isar', - ), + path.join(isar.directory!, '$copiedDbFilename.isar'), ); await isar.copyToFile(copiedDbFile.path); @@ -73,7 +70,6 @@ void main() { copiedIsar.models.where(), originalObjs, ); - await copiedDbFile.delete(); }); isarTestVm('.copyToFile() should compact copied file', () async { diff --git a/packages/isar_test/test/stress/json_test.dart b/packages/isar_test/test/json_test.dart similarity index 98% rename from packages/isar_test/test/stress/json_test.dart rename to packages/isar_test/test/json_test.dart index 70dab2c..a1aa14d 100644 --- a/packages/isar_test/test/stress/json_test.dart +++ b/packages/isar_test/test/json_test.dart @@ -1,11 +1,9 @@ import 'dart:convert'; +import 'dart:typed_data'; -import 'package:flutter/foundation.dart'; -import 'package:flutter_test/flutter_test.dart'; import 'package:isar/isar.dart'; import 'package:isar_test/isar_test.dart'; - -import 'twitter/tweet.dart'; +import 'package:test/test.dart'; void main() { group('JSON', () { diff --git a/packages/isar_test/test/max_size_test.dart b/packages/isar_test/test/max_size_test.dart new file mode 100644 index 0000000..068fd4a --- /dev/null +++ b/packages/isar_test/test/max_size_test.dart @@ -0,0 +1,38 @@ +import 'package:isar/isar.dart'; +import 'package:isar_test/isar_test.dart'; +import 'package:test/test.dart'; + +part 'max_size_test.g.dart'; + +@collection +class Model { + final Id id = Isar.autoIncrement; + + final value = '123456789' * 1000; +} + +void main() { + group('Max Size', () { + test('default', () async { + final isar = await openTempIsar([ModelSchema]); + await isar.writeTxn(() async { + await isar.models.putAll(List.filled(10000, Model())); + }); + }); + + test('1MB', () async { + final isar = await openTempIsar([ModelSchema], maxSizeMiB: 1); + + expect( + isar.writeTxn(() async { + await isar.models.putAll(List.filled(100, Model())); + }), + throwsIsarError('The database is full'), + ); + + await isar.writeTxn(() async { + await isar.models.putAll(List.filled(50, Model())); + }); + }); + }); +} diff --git a/packages/isar_test/test/stress/long_string_test.dart b/packages/isar_test/test/stress/long_string_test.dart index 49ae907..45014ae 100644 --- a/packages/isar_test/test/stress/long_string_test.dart +++ b/packages/isar_test/test/stress/long_string_test.dart @@ -1,8 +1,8 @@ import 'dart:math'; -import 'package:flutter_test/flutter_test.dart'; import 'package:isar/isar.dart'; import 'package:isar_test/isar_test.dart'; +import 'package:test/test.dart'; part 'long_string_test.g.dart'; @@ -37,49 +37,51 @@ String _randomStr(int length) { } void main() { - late Isar isar; + group('Long String', () { + late Isar isar; - setUp(() async { - isar = await openTempIsar([StringModelSchema]); - }); - - isarTest('Long String', () async { - final models = [ - for (var i = 0; i < 100; i++) - StringModel( - string: '${_randomStr(50000)}test$i${_randomStr(50000)}', - ), - ]; - await isar.tWriteTxn(() async { - await isar.stringModels.tPutAll(models); + setUp(() async { + isar = await openTempIsar([StringModelSchema]); }); - await qEqual(isar.stringModels.where(), models); + isarTest('Single', () async { + final models = [ + for (var i = 0; i < 100; i++) + StringModel( + string: '${_randomStr(50000)}test$i${_randomStr(50000)}', + ), + ]; + await isar.tWriteTxn(() async { + await isar.stringModels.tPutAll(models); + }); - await qEqual( - isar.stringModels.filter().stringContains('test75'), - [models[75]], - ); - await qEqual( - isar.stringModels.filter().stringMatches('*test66*'), - [models[66]], - ); - }); + await qEqual(isar.stringModels.where(), models); - isarTest('Long String List', () async { - final models = [ - for (var i = 0; i < 10; i++) - StringModel( - stringList: [ - for (var j = 0; j < 100; j++) - '${_randomStr(10000)}test${i}_$j${_randomStr(10000)}', - ], - ), - ]; - await isar.tWriteTxn(() async { - await isar.stringModels.tPutAll(models); + await qEqual( + isar.stringModels.filter().stringContains('test75'), + [models[75]], + ); + await qEqual( + isar.stringModels.filter().stringMatches('*test66*'), + [models[66]], + ); }); - await qEqual(isar.stringModels.where(), models); + isarTest('List', () async { + final models = [ + for (var i = 0; i < 10; i++) + StringModel( + stringList: [ + for (var j = 0; j < 100; j++) + '${_randomStr(10000)}test${i}_$j${_randomStr(10000)}', + ], + ), + ]; + await isar.tWriteTxn(() async { + await isar.stringModels.tPutAll(models); + }); + + await qEqual(isar.stringModels.where(), models); + }); }); } diff --git a/packages/isar_test/test/stress/twitter_test.dart b/packages/isar_test/test/stress/twitter_test.dart index cbf8ae9..8093520 100644 --- a/packages/isar_test/test/stress/twitter_test.dart +++ b/packages/isar_test/test/stress/twitter_test.dart @@ -5,9 +5,6 @@ import 'package:isar/isar.dart'; import 'package:isar_test/isar_test.dart'; import 'package:test/test.dart'; -import 'twitter/entities.dart'; -import 'twitter/tweet.dart'; - Future> downloadTweets(String dir, int index) async { final i = index < 10 ? '0$index' : '$index'; final file = File('$dir/tweets$i.json'); diff --git a/packages/isar_test/tool/generate_all_tests.dart b/packages/isar_test/tool/generate_all_tests.dart index 41f81e0..568666a 100644 --- a/packages/isar_test/tool/generate_all_tests.dart +++ b/packages/isar_test/tool/generate_all_tests.dart @@ -9,12 +9,17 @@ void main() { .toList(); final imports = files.map((String e) { - return "import '$e' as ${e.split('.')[0].replaceAll(p.separator, '_')};"; + final dartPath = e.replaceAll(p.separator, '/'); + final name = e.split('.')[0].replaceAll(p.separator, '_'); + return "import '../$dartPath' as $name;"; }).join('\n'); final calls = files.map((String e) { final content = File(e).readAsStringSync(); - final call = "${e.split('.')[0].replaceAll(p.separator, '_')}.main();"; + var call = "${e.split('.')[0].replaceAll(p.separator, '_')}.main();"; + if (e.contains('stress')) { + call = 'if (stress) $call'; + } if (content.startsWith("@TestOn('vm')")) { return 'if (!kIsWeb) $call'; } else { @@ -29,9 +34,11 @@ void main() { $imports void main() { + const stress = bool.fromEnvironment('STRESS'); $calls } """; - File('all_tests.dart').writeAsStringSync(code); + Directory('integration_test').createSync(); + File('integration_test${p.separator}all_tests.dart').writeAsStringSync(code); } diff --git a/packages/isar_test/tool/generate_prepare_test.dart b/packages/isar_test/tool/generate_prepare_test.dart deleted file mode 100644 index e9f5035..0000000 --- a/packages/isar_test/tool/generate_prepare_test.dart +++ /dev/null @@ -1,53 +0,0 @@ -// ignore_for_file: avoid_print - -import 'dart:io'; - -import 'package:isar_test/src/rust_target.dart'; -import 'package:path/path.dart' as path; - -void main() { - print('Building Isar Core'); - final rootDir = path.dirname(path.dirname(Directory.current.path)); - final rustResult = Process.runSync( - 'cargo', - ['build', '--target', getRustTarget()], - runInShell: true, - workingDirectory: rootDir, - ); - if (rustResult.exitCode != 0) { - throw Exception('Cargo build failed: ${rustResult.stderr}'); - } - - /*print('Building Isar Web'); - final webDir = path.join(rootDir, 'packages', 'isar_web'); - final npmResult = Process.runSync( - 'npm', - ['run', 'build'], - runInShell: true, - workingDirectory: webDir, - ); - print(webDir); - if (npmResult.exitCode != 0) { - throw Exception('Npm build failed: ${npmResult.stderr}'); - } - - print('Packing Isar Web'); - final webpackResult = Process.runSync( - 'npx', - ['webpack', '--mode', 'development'], - runInShell: true, - workingDirectory: webDir, - ); - if (webpackResult.exitCode != 0) { - throw Exception('Webpack build failed: ${npmResult.stderr}'); - } - - print('Writing Isar Web in the test dir'); - final isarWebSrc = - File(path.join(webDir, 'dist', 'index.js')).readAsStringSync(); - final escaped = isarWebSrc.replaceAll(r'$', r'\$'); - File('lib/src/isar_web_src.dart').writeAsStringSync( - '// ignore_for_file: unnecessary_string_escapes\n' - "const isarWebSrc = '''\n$escaped''';", - );*/ -} diff --git a/packages/mdbx_sys/build.rs b/packages/mdbx_sys/build.rs index e016736..27abf95 100644 --- a/packages/mdbx_sys/build.rs +++ b/packages/mdbx_sys/build.rs @@ -50,7 +50,7 @@ impl ParseCallbacks for Callbacks { } const LIBMDBX_REPO: &str = "https://github.com/isar/libmdbx.git"; -const LIBMDBX_TAG: &str = "v0.12.2"; +const LIBMDBX_TAG: &str = "v0.11.2"; fn main() { println!("cargo:rerun-if-changed=build.rs"); @@ -61,13 +61,19 @@ fn main() { Command::new("git") .arg("clone") .arg(LIBMDBX_REPO) - .arg("--depth") - .arg("1") .arg("--branch") .arg(LIBMDBX_TAG) .output() .unwrap(); + /*Command::new("git") + .current_dir("libmdbx") + .arg("revert") + .arg("-n") + .arg("fe20de") + .output() + .unwrap();*/ + Command::new("make") .arg("release-assets") .current_dir("libmdbx") @@ -136,7 +142,8 @@ fn main() { .define("MDBX_LOCK_SUFFIX", "\".lock\"") .define("MDBX_TXN_CHECKOWNER", "0") .define("MDBX_OSX_SPEED_INSTEADOF_DURABILITY", "1") - .define("NDEBUG", "1") + .define("MDBX_HAVE_BUILTIN_CPU_SUPPORTS", "0") + //.define("MDX_DEBUG", "7") .file(mdbx.join("mdbx.c")) .compile("libmdbx.a"); } diff --git a/tool/build.sh b/tool/build.sh new file mode 100644 index 0000000..f93bac4 --- /dev/null +++ b/tool/build.sh @@ -0,0 +1,22 @@ +#!/bin/shuname + +arch=$(uname -m) + +if [ `uname` = "Linux" ] ; +then + if [ $arch = "x86_64" ] ; + then + cargo build --target x86_64-unknown-linux-gnu --release + else + cargo build --target aarch64-unknown-linux-gnu --release + fi +elif [ `uname` = "Darwin" ] ; +then + if [[ $arch == x86_64* ]]; then + cargo build --target x86_64-apple-darwin --release + else + cargo build --target aarch64-apple-darwin --release + fi +else + cargo build --target x86_64-pc-windows-msvc --release +fi \ No newline at end of file diff --git a/tool/build_android.sh b/tool/build_android.sh index 81e527c..6a3ca4d 100644 --- a/tool/build_android.sh +++ b/tool/build_android.sh @@ -45,8 +45,8 @@ elif [ "$1" = "x64" ]; then mv "target/x86_64-linux-android/release/libisar.so" "libisar_android_x64.so" elif [ "$1" = "armv7" ]; then rustup target add armv7-linux-androideabi - cargo build --target armv7-linux-androideabi --release - mv "target/armv7-linux-androideabi/release/libisar.so" "libisar_android_armv7.so" + cargo build --target armv7-linux-androideabi + mv "target/armv7-linux-androideabi/debug/libisar.so" "libisar_android_armv7.so" else rustup target add aarch64-linux-android cargo build --target aarch64-linux-android --release diff --git a/tool/prepare_tests.sh b/tool/prepare_tests.sh new file mode 100644 index 0000000..919d115 --- /dev/null +++ b/tool/prepare_tests.sh @@ -0,0 +1,6 @@ +cd packages/isar_test + +flutter pub get +dart tool/generate_long_double_test.dart +dart tool/generate_all_tests.dart +flutter pub run build_runner build \ No newline at end of file