mirror of
https://github.com/immich-app/isar.git
synced 2026-09-30 13:23:02 +08:00
Tests for more platforms (#879)
This commit is contained in:
@@ -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' \;
|
||||
@@ -3,7 +3,7 @@ name: Docs
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -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
|
||||
|
||||
+181
-71
@@ -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
|
||||
|
||||
|
||||
+8
-7
@@ -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.
|
||||
|
||||
|
||||
+19
-19
@@ -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:
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<String, Isar> _instances = <String, Isar>{};
|
||||
@@ -87,6 +89,7 @@ abstract class Isar {
|
||||
List<CollectionSchema<dynamic>> 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<CollectionSchema<dynamic>> 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,
|
||||
);
|
||||
|
||||
@@ -3,7 +3,11 @@ part of isar;
|
||||
/// Normal keys consist of a single object, composite keys multiple.
|
||||
typedef IndexKey = List<Object?>;
|
||||
|
||||
/// 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<OBJ>()` or
|
||||
/// by using the generated `isar.yourCollections` getter.
|
||||
abstract class IsarCollection<OBJ> {
|
||||
/// The corresponding Isar instance.
|
||||
Isar get isar;
|
||||
@@ -14,150 +18,227 @@ abstract class IsarCollection<OBJ> {
|
||||
/// 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<OBJ?> get(Id id) {
|
||||
return getAll([id]).then((List<OBJ?> 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<List<OBJ?>> getAll(List<Id> ids);
|
||||
|
||||
/// Get a list of objects by their [ids] or `null` if an object does not
|
||||
/// exist.
|
||||
/// {@macro col_get_all}
|
||||
List<OBJ?> getAllSync(List<Id> 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<OBJ?> getByIndex(String indexName, IndexKey key) {
|
||||
return getAllByIndex(indexName, [key])
|
||||
.then((List<OBJ?> 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<List<OBJ?>> getAllByIndex(String indexName, List<IndexKey> keys);
|
||||
|
||||
/// @nodoc
|
||||
@protected
|
||||
/// {@macro col_get_all_by_index}'
|
||||
@experimental
|
||||
List<OBJ?> getAllByIndexSync(String indexName, List<IndexKey> 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<Id> put(OBJ object) {
|
||||
return putAll([object]).then((List<Id> 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<List<Id>> putAll(List<OBJ> objects);
|
||||
|
||||
/// Insert or update a list of [objects] and returns the list of assigned ids.
|
||||
/// {@macro col_put_all}
|
||||
List<Id> putAllSync(List<OBJ> 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<Id> putByIndex(String indexName, OBJ object) {
|
||||
return putAllByIndex(indexName, [object]).then((List<Id> 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<List<Id>> putAllByIndex(String indexName, List<OBJ> objects);
|
||||
|
||||
/// @nodoc
|
||||
@protected
|
||||
/// {@macro col_put_all_by_index}
|
||||
@experimental
|
||||
List<Id> putAllByIndexSync(
|
||||
String indexName,
|
||||
List<OBJ> 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<bool> 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<int> deleteAll(List<Id> 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<Id> 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<bool> 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<int> deleteAllByIndex(String indexName, List<IndexKey> keys);
|
||||
|
||||
/// @nodoc
|
||||
@protected
|
||||
/// {@macro col_delete_all_by_index}
|
||||
@experimental
|
||||
int deleteAllByIndexSync(String indexName, List<IndexKey> keys);
|
||||
|
||||
/// {@template col_clear}
|
||||
/// Remove all data in this collection and reset the auto increment value.
|
||||
/// {@endtemplate}
|
||||
Future<void> 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<void> 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<void> importJson(List<Map<String, dynamic>> json);
|
||||
|
||||
/// Import a list of json objects.
|
||||
/// {@macro col_import_json}
|
||||
void importJsonSync(List<Map<String, dynamic>> 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<OBJ, OBJ, QWhere> where({
|
||||
bool distinct = false,
|
||||
Sort sort = Sort.asc,
|
||||
@@ -172,10 +253,10 @@ abstract class IsarCollection<OBJ> {
|
||||
|
||||
/// 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<OBJ, OBJ, QFilterCondition> 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<OBJ> {
|
||||
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<int> 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<int> 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<OBJ> {
|
||||
/// If [fireImmediately] is `true`, an event will be fired immediately.
|
||||
Stream<void> 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<OBJ?> 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<void> watchObjectLazy(Id id, {bool fireImmediately = false});
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
part of isar;
|
||||
|
||||
/// @nodoc
|
||||
@protected
|
||||
@sealed
|
||||
abstract class IsarLinkBase<OBJ> {
|
||||
/// Is the containing object managed by Isar?
|
||||
bool get isAttached;
|
||||
@@ -12,32 +12,33 @@ abstract class IsarLinkBase<OBJ> {
|
||||
/// Has this link been loaded?
|
||||
bool get isLoaded;
|
||||
|
||||
/// {@template link_load}
|
||||
/// Loads the linked object(s) from the database
|
||||
/// {@endtemplate}
|
||||
Future<void> 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<void> 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<void> 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<OBJ> implements IsarLinkBase<OBJ>, Set<OBJ> {
|
||||
@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<void> update({
|
||||
Iterable<OBJ> link = const [],
|
||||
Iterable<OBJ> 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<OBJ> link = const [],
|
||||
Iterable<OBJ> unlink = const [],
|
||||
@@ -100,15 +100,14 @@ abstract class IsarLinks<OBJ> implements IsarLinkBase<OBJ>, Set<OBJ> {
|
||||
/// Starts a query for linked objects.
|
||||
QueryBuilder<OBJ, OBJ, QAfterFilterCondition> 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<int> 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();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
part of isar;
|
||||
|
||||
/// @nodoc
|
||||
@protected
|
||||
abstract class IsarReader {
|
||||
bool readBool(int offset);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
part of isar;
|
||||
|
||||
/// @nodoc
|
||||
@protected
|
||||
abstract class IsarWriter {
|
||||
void writeBool(int offset, bool? value);
|
||||
|
||||
@@ -1285,6 +1285,7 @@ class IsarCoreBindings {
|
||||
ffi.Pointer<ffi.Char> name,
|
||||
ffi.Pointer<ffi.Char> path,
|
||||
ffi.Pointer<ffi.Char> 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.Char>,
|
||||
ffi.Pointer<ffi.Char>,
|
||||
ffi.Pointer<ffi.Char>,
|
||||
ffi.Int64,
|
||||
ffi.Bool,
|
||||
ffi.Uint32,
|
||||
ffi.Uint32,
|
||||
@@ -1319,6 +1322,7 @@ class IsarCoreBindings {
|
||||
ffi.Pointer<ffi.Char>,
|
||||
ffi.Pointer<ffi.Char>,
|
||||
ffi.Pointer<ffi.Char>,
|
||||
int,
|
||||
bool,
|
||||
int,
|
||||
int,
|
||||
@@ -1329,6 +1333,7 @@ class IsarCoreBindings {
|
||||
ffi.Pointer<ffi.Char> name,
|
||||
ffi.Pointer<ffi.Char> path,
|
||||
ffi.Pointer<ffi.Char> 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.Char>,
|
||||
ffi.Pointer<ffi.Char>,
|
||||
ffi.Pointer<ffi.Char>,
|
||||
ffi.Int64,
|
||||
ffi.Bool,
|
||||
ffi.Uint32,
|
||||
ffi.Uint32,
|
||||
@@ -1367,6 +1374,7 @@ class IsarCoreBindings {
|
||||
ffi.Pointer<ffi.Char>,
|
||||
ffi.Pointer<ffi.Char>,
|
||||
ffi.Pointer<ffi.Char>,
|
||||
int,
|
||||
bool,
|
||||
int,
|
||||
int,
|
||||
|
||||
@@ -79,6 +79,7 @@ Future<Isar> openIsar({
|
||||
required List<CollectionSchema<dynamic>> schemas,
|
||||
String? directory,
|
||||
required String name,
|
||||
required int maxSizeMiB,
|
||||
required bool relaxedDurability,
|
||||
CompactCondition? compactOnLaunch,
|
||||
}) async {
|
||||
@@ -105,6 +106,7 @@ Future<Isar> openIsar({
|
||||
namePtr,
|
||||
dirPtr,
|
||||
schemaStrPtr,
|
||||
maxSizeMiB,
|
||||
relaxedDurability,
|
||||
compactMinFileSize ?? 0,
|
||||
compactMinBytes ?? 0,
|
||||
@@ -122,8 +124,9 @@ Future<Isar> openIsar({
|
||||
Isar openIsarSync({
|
||||
required List<CollectionSchema<dynamic>> 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,
|
||||
|
||||
@@ -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._();
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ impl IsarInstance {
|
||||
name: &str,
|
||||
dir: Option<&str>,
|
||||
mut schema: Schema,
|
||||
max_size_mib: usize,
|
||||
relaxed_durability: bool,
|
||||
compact_condition: Option<CompactCondition>,
|
||||
) -> Result<Arc<Self>> {
|
||||
@@ -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<CompactCondition>,
|
||||
) -> Result<Self> {
|
||||
@@ -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)
|
||||
|
||||
@@ -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<Env> {
|
||||
pub fn create(
|
||||
path: &str,
|
||||
max_dbs: u64,
|
||||
max_size_mib: usize,
|
||||
relaxed_durability: bool,
|
||||
) -> Result<Env> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
+12
@@ -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<MainActivity> rule = new ActivityTestRule<>(MainActivity.class, true, false);
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
+10
-20
@@ -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<List<String>>();
|
||||
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<List<String>> completer) {
|
||||
final completer = Completer<void>();
|
||||
group('Integration test', () {
|
||||
setUpAll(() async {
|
||||
if (!kIsWeb) {
|
||||
@@ -34,11 +20,15 @@ void executeTests(Completer<List<String>> 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);
|
||||
});
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import 'package:integration_test/integration_test_driver.dart';
|
||||
|
||||
void main() => integrationDriver();
|
||||
@@ -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';
|
||||
|
||||
@@ -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 = <String>[];
|
||||
int testCount = 0;
|
||||
@@ -111,6 +111,7 @@ Future<Isar> openTempIsar(
|
||||
List<CollectionSchema<dynamic>> schemas, {
|
||||
String? name,
|
||||
String? directory,
|
||||
int maxSizeMiB = Isar.defaultMaxSizeMiB,
|
||||
CompactCondition? compactOnLaunch,
|
||||
bool closeAutomatically = true,
|
||||
}) async {
|
||||
@@ -124,6 +125,7 @@ Future<Isar> openTempIsar(
|
||||
final isar = await tOpen(
|
||||
schemas: schemas,
|
||||
name: name ?? getRandomName(),
|
||||
maxSizeMiB: maxSizeMiB,
|
||||
directory: directory ?? testTempPath,
|
||||
compactOnLaunch: compactOnLaunch,
|
||||
);
|
||||
|
||||
@@ -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<void> 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,
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,17 @@ Matcher throwsIsarError([String? contains]) {
|
||||
return throwsA(isIsarError(contains));
|
||||
}
|
||||
|
||||
final Matcher throwsAssertionError = throwsA(isA<AssertionError>());
|
||||
Matcher get throwsAssertionError {
|
||||
var matcher = anything;
|
||||
assert(
|
||||
() {
|
||||
matcher = throwsA(isA<AssertionError>());
|
||||
return true;
|
||||
}(),
|
||||
'only in debug mode',
|
||||
);
|
||||
return matcher;
|
||||
}
|
||||
|
||||
bool listEquals<T>(List<T>? a, List<T>? b) {
|
||||
if (a == 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()}');
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ Future<Isar> tOpen({
|
||||
required List<CollectionSchema<dynamic>> schemas,
|
||||
String? directory,
|
||||
String name = Isar.defaultName,
|
||||
int maxSizeMiB = Isar.defaultMaxSizeMiB,
|
||||
bool relaxedDurability = true,
|
||||
CompactCondition? compactOnLaunch,
|
||||
}) {
|
||||
@@ -18,6 +19,7 @@ Future<Isar> tOpen({
|
||||
schemas,
|
||||
directory: directory,
|
||||
name: name,
|
||||
maxSizeMiB: maxSizeMiB,
|
||||
relaxedDurability: relaxedDurability,
|
||||
compactOnLaunch: compactOnLaunch,
|
||||
inspector: false,
|
||||
@@ -28,6 +30,7 @@ Future<Isar> tOpen({
|
||||
schemas,
|
||||
directory: directory,
|
||||
name: name,
|
||||
maxSizeMiB: maxSizeMiB,
|
||||
relaxedDurability: relaxedDurability,
|
||||
compactOnLaunch: compactOnLaunch,
|
||||
inspector: false,
|
||||
|
||||
+5
-6
@@ -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)
|
||||
+2
-3
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+2
-4
@@ -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', () {
|
||||
@@ -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()));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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 = <StringModel>[
|
||||
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 = <StringModel>[
|
||||
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 = <StringModel>[
|
||||
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 = <StringModel>[
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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<List<Tweet>> downloadTweets(String dir, int index) async {
|
||||
final i = index < 10 ? '0$index' : '$index';
|
||||
final file = File('$dir/tweets$i.json');
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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''';",
|
||||
);*/
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user