Commit a40f3bf
Changed files (19)
.github
workflows
app
lib
model
test
features
statistics
.github/workflows/app-CI.yml
@@ -7,185 +7,81 @@ on:
paths:
- "app/**"
- "health_data_store/**"
- - ".github/workflows/app-CI.yml"
+ - "extendend-testing.yml"
workflow_dispatch:
-env:
- FLUTTER_CHANNEL: 'beta'
- DART_SDK: 'beta'
- JAVA_VERSION: '17'
- EMULATOR_VERSION: 'system-images;android-34;aosp_atd;x86_64'
-
jobs:
- unit-test:
- name: "🧩🧪 Run unit tests"
+ run-tests:
runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ channel:
+ - beta
+ - stable
permissions:
contents: write
pull-requests: write
steps:
- - name: Checkout code
- uses: actions/checkout@v4
- with:
- # ensures there are no unexpected directories needed
- sparse-checkout: |
- app
- health_data_store
- - name: Cache generated health data store
- id: cache-generated
- uses: actions/cache@v4
- with:
- path: health_data_store/lib
- key: builder-${{ hashFiles('health_data_store/pubspec.yaml', 'health_data_store/lib/*', 'health_data_store/lib/**/*dart') }}
- - name: Setup dart
- if: steps.cache-generated.outputs.cache-hit != 'true'
- uses: dart-lang/setup-dart@v1
- with:
- sdk: ${{ env.DART_SDK }}
- - name: Generate code
- if: steps.cache-generated.outputs.cache-hit != 'true'
- run: dart run build_runner build
- working-directory: health_data_store
- - name: Setup Flutter
- uses: subosito/flutter-action@v2
- with:
- channel: ${{ env.FLUTTER_CHANNEL }}
- cache: true
- - name: Disable analytics
- run: flutter config --no-analytics --suppress-analytics
- - name: Generate mock code
- run: |
- flutter pub get
- flutter pub run build_runner build
- working-directory: app
- - name: Run tests
- run: flutter test --coverage
- working-directory: app
- - name: Update goldens
- id: gold-upd
- if: failure()
- run: flutter test --update-goldens --fail-fast
- working-directory: app
- - name: PR golden changes
- # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions#example-of-failure-with-conditions
- if: ${{ failure() && steps.gold-upd.conclusion == 'success' }}
- run: |
- git config user.name "GitHub Action (update goldens)"
- git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
-
- git checkout -B action-update-goldens
- export STATUS=$(git status)
- git commit -am "Update goldens"
- git push --set-upstream origin action-update-goldens
-
- gh pr create \
- --base main \
- --head action-update-goldens \
- --title "Update goldens" \
- --body "$STATUS"
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
-# Disabled: Integration tests are disabled as emulator setup fails on
-# GH-actions. Actions images no longer provide enough disk space to create
-# the userdata partition.
-#
-# integration-test:
-# name: "🛠️🧪 Run integration tests"
-# runs-on: ubuntu-latest
-#
-# steps:
-# - name: Checkout code
-# uses: actions/checkout@v4
-# with:
-# # ensures there are no unexpected directories needed
-# sparse-checkout: |
-# app
-# health_data_store
-# - name: Enable KVM group perms
-# # see: https://github.com/actions/runner-images/discussions/7191
-# run: |
-# echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
-# sudo udevadm control --reload-rules
-# sudo udevadm trigger --name-match=kvm
-# - name: Setup Java
-# uses: actions/setup-java@v3
-# with:
-# distribution: 'zulu'
-# java-version: ${{ env.JAVA_VERSION }}
-#
-# - name: Download Android emulator image
-# run: |
-# export ANDROID_TOOLS="$ANDROID_HOME/cmdline-tools/latest/bin"
-# echo "y" | $ANDROID_TOOLS/sdkmanager --install "${{ env.EMULATOR_VERSION }}"
-# echo "no" | $ANDROID_TOOLS/avdmanager create avd --force --name emu -k '${{ env.EMULATOR_VERSION }}'
-# echo "Android emulator installed"
-# $ANDROID_HOME/emulator/emulator -list-avds
-# - name: Setup dart
-# uses: dart-lang/setup-dart@v1
-# with:
-# sdk: ${{ env.DART_SDK }}
-# - name: Generate code
-# run: dart run build_runner build
-# working-directory: health_data_store
-# - name: Setup Flutter
-# uses: subosito/flutter-action@v2
-# with:
-# channel: ${{ env.FLUTTER_CHANNEL }}
-# - name: Start Android emulator
-# timeout-minutes: 10
-# run: |
-# export ANDROID_TOOLS="$ANDROID_HOME/cmdline-tools/latest/bin"
-# echo "Starting emulator"
-# $ANDROID_TOOLS/sdkmanager "platform-tools" "${{ env.EMULATOR_VERSION }}"
-# nohup $ANDROID_HOME/emulator/emulator -avd emu -no-audio -no-snapshot -no-window &
-# $ANDROID_HOME/platform-tools/adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed | tr -d '\r') ]]; do sleep 1; done; input keyevent 82'
-# $ANDROID_HOME/platform-tools/adb devices
-# echo "Android emulator started"
-# - name: Run integration tests
-# run: flutter test integration_test --flavor github
-# working-directory: app
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ # ensures there are no unexpected directories needed
+ sparse-checkout: |
+ app
+ health_data_store
+ - name: Cache generated health data store
+ id: cache-generated
+ uses: actions/cache@v4
+ with:
+ path: health_data_store/lib
+ key: builder-${{ matrix.channel }}-${{ hashFiles('health_data_store/pubspec.yaml', 'health_data_store/lib/*', 'health_data_store/lib/**/*dart') }}
+ - name: Setup dart
+ if: steps.cache-generated.outputs.cache-hit != 'true'
+ uses: dart-lang/setup-dart@v1
+ with:
+ sdk: ${{ matrix.channel }}
+ - name: Generate code
+ if: steps.cache-generated.outputs.cache-hit != 'true'
+ run: dart run build_runner build
+ working-directory: health_data_store
+ - name: Setup Flutter
+ uses: subosito/flutter-action@v2
+ with:
+ channel: ${{ matrix.channel }}
+ cache: true
+ - name: Disable analytics
+ run:
+ flutter config --no-analytics --suppress-analytics
+ - name: Update app dependencies
+ run: flutter pub get
+ working-directory: app
+ - name: Generate app mock code # no efficient caching possible
+ run: flutter pub run build_runner build
+ working-directory: app
+ - name: Run tests
+ run: flutter test --coverage --dart-define="channel=${{ matrix.channel }}"
+ working-directory: app
+ - name: Update goldens
+ id: gold-upd
+ if: failure()
+ run: flutter test --update-goldens --fail-fast --dart-define="channel=${{ matrix.channel }}"
+ working-directory: app
+ - name: PR golden changes
+ # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions#example-of-failure-with-conditions
+ if: ${{ failure() && steps.gold-upd.conclusion == 'success' }}
+ run: |
+ git config user.name "GitHub Action (update goldens)"
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- build-android:
- name: "🛠️ Build Android"
- runs-on: ubuntu-latest
+ git checkout -B action-update-goldens
+ export STATUS=$(git status)
+ git commit -am "Update goldens"
+ git push --set-upstream origin action-update-goldens
- steps:
- - name: Checkout code
- uses: actions/checkout@v4
- with:
- # ensures there are no unexpected directories needed
- sparse-checkout: |
- app
- health_data_store
- - name: Cache generated health data store
- id: cache-generated
- uses: actions/cache@v4
- with:
- path: health_data_store/lib
- key: builder-${{ hashFiles('health_data_store/pubspec.yaml', 'health_data_store/lib/*', 'health_data_store/lib/**/*dart') }}
- - name: Setup dart
- if: steps.cache-generated.outputs.cache-hit != 'true'
- uses: dart-lang/setup-dart@v1
- with:
- sdk: ${{ env.DART_SDK }}
- - name: Generate code
- if: steps.cache-generated.outputs.cache-hit != 'true'
- run: dart run build_runner build
- working-directory: health_data_store
- - name: Setup Flutter
- uses: subosito/flutter-action@v2
- with:
- channel: ${{ env.FLUTTER_CHANNEL }}
- cache: true
- - name: Disable analytics
- run: flutter config --no-analytics --suppress-analytics
- - name: Setup java
- uses: actions/setup-java@v2
- with:
- java-version: "17"
- distribution: "temurin"
- cache: 'gradle'
- - name: Build apk
- run: flutter build apk --flavor github --debug
- working-directory: app
+ gh pr create \
+ --base main \
+ --head action-update-goldens \
+ --title "Update goldens" \
+ --body "$STATUS"
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
.github/workflows/extendend-testing.yml
@@ -0,0 +1,229 @@
+name: 'Extended testing'
+
+on:
+ workflow_dispatch:
+
+jobs:
+ setup-env:
+ name: "Setup environment"
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ branch:
+ - beta
+ - stable
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ # ensures there are no unexpected directories needed
+ sparse-checkout: |
+ app
+ health_data_store
+ - name: Cache generated health data store
+ id: cache-generated
+ uses: actions/cache@v4
+ with:
+ path: health_data_store/lib
+ key: builder-${{ matrix.branch }}-${{ hashFiles('health_data_store/pubspec.yaml', 'health_data_store/lib/*', 'health_data_store/lib/**/*dart') }}
+ - name: Setup dart
+ if: steps.cache-generated.outputs.cache-hit != 'true'
+ uses: dart-lang/setup-dart@v1
+ with:
+ sdk: ${{ matrix.branch }}
+ - name: Generate code
+ if: steps.cache-generated.outputs.cache-hit != 'true'
+ run: dart run build_runner build
+ working-directory: health_data_store
+ - name: Setup Flutter
+ uses: subosito/flutter-action@v2
+ with:
+ channel: ${{ matrix.branch }}
+ cache: true
+ - name: Disable analytics
+ run:
+ flutter config --no-analytics --suppress-analytics
+ - name: Update app dependencies
+ run: flutter pub get
+ working-directory: app
+ - name: Generate app mock code
+ run: flutter pub run build_runner build
+ working-directory: app
+ - name: Upload results
+ uses: actions/upload-artifact@v4
+ with:
+ name: src-${{ matrix.branch }}
+ path: ./
+
+ unit-test:
+ name: "🧩🧪 Run unit tests"
+ runs-on: ubuntu-latest
+ needs: setup-env
+ strategy:
+ matrix:
+ branch:
+ - beta
+ - stable
+ permissions:
+ contents: write
+ pull-requests: write
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ sparse-checkout: |
+ app
+ health_data_store
+ - name: Download src directory
+ uses: actions/download-artifact@v4
+ with:
+ name: src-${{ matrix.branch }}
+ - name: Setup Flutter
+ uses: subosito/flutter-action@v2
+ with:
+ channel: ${{ matrix.branch }}
+ cache: true
+ - name: Disable analytics
+ run: flutter config --no-analytics --suppress-analytics
+ - name: Run tests ignoring goldens
+ run: flutter test --coverage --update-goldens
+ working-directory: app
+
+# Disabled: Integration tests are disabled as emulator setup fails on
+# GH-actions. Actions images no longer provide enough disk space to create
+# the userdata partition.
+#
+# integration-test:
+# name: "🛠️🧪 Run integration tests"
+# runs-on: ubuntu-latest
+#
+# steps:
+# - name: Checkout code
+# uses: actions/checkout@v4
+# with:
+# # ensures there are no unexpected directories needed
+# sparse-checkout: |
+# app
+# health_data_store
+# - name: Enable KVM group perms
+# # see: https://github.com/actions/runner-images/discussions/7191
+# run: |
+# echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
+# sudo udevadm control --reload-rules
+# sudo udevadm trigger --name-match=kvm
+# - name: Setup Java
+# uses: actions/setup-java@v3
+# with:
+# distribution: 'zulu'
+# java-version: ${{ env.JAVA_VERSION }}
+#
+# - name: Download Android emulator image
+# run: |
+# export ANDROID_TOOLS="$ANDROID_HOME/cmdline-tools/latest/bin"
+# echo "y" | $ANDROID_TOOLS/sdkmanager --install "${{ env.EMULATOR_VERSION }}"
+# echo "no" | $ANDROID_TOOLS/avdmanager create avd --force --name emu -k '${{ env.EMULATOR_VERSION }}'
+# echo "Android emulator installed"
+# $ANDROID_HOME/emulator/emulator -list-avds
+# - name: Setup dart
+# uses: dart-lang/setup-dart@v1
+# with:
+# sdk: ${{ env.DART_SDK }}
+# - name: Generate code
+# run: dart run build_runner build
+# working-directory: health_data_store
+# - name: Setup Flutter
+# uses: subosito/flutter-action@v2
+# with:
+# channel: ${{ env.FLUTTER_CHANNEL }}
+# - name: Start Android emulator
+# timeout-minutes: 10
+# run: |
+# export ANDROID_TOOLS="$ANDROID_HOME/cmdline-tools/latest/bin"
+# echo "Starting emulator"
+# $ANDROID_TOOLS/sdkmanager "platform-tools" "${{ env.EMULATOR_VERSION }}"
+# nohup $ANDROID_HOME/emulator/emulator -avd emu -no-audio -no-snapshot -no-window &
+# $ANDROID_HOME/platform-tools/adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed | tr -d '\r') ]]; do sleep 1; done; input keyevent 82'
+# $ANDROID_HOME/platform-tools/adb devices
+# echo "Android emulator started"
+# - name: Run integration tests
+# run: flutter test integration_test --flavor github
+# working-directory: app
+
+ build-android:
+ name: "🛠️ Build Android"
+ strategy:
+ matrix:
+ java-version:
+ - 17
+ - 21
+ branch:
+ - beta
+ - stable
+ build:
+ - debug
+ - release
+
+ flavor:
+ - github
+ - fdroid
+ runs-on: ubuntu-latest
+ needs: setup-env
+ steps:
+ - name: Download src directory
+ uses: actions/download-artifact@v4
+ with:
+ name: src-${{ matrix.branch }}
+ - name: Setup Flutter
+ uses: subosito/flutter-action@v2
+ with:
+ channel: ${{ matrix.branch }}
+ cache: true
+ - name: Disable analytics
+ run: flutter config --no-analytics --suppress-analytics
+ - name: Setup java
+ uses: actions/setup-java@v2
+ with:
+ java-version: "17"
+ distribution: "temurin"
+ cache: 'gradle'
+ - name: Build apk
+ run: flutter build apk --flavor ${{ matrix.flavor }} --${{ matrix.build }}
+ working-directory: app
+ - name: Upload apks
+ uses: actions/upload-artifact@v4
+ with:
+ name: app-${{ matrix.build }}-java${{ matrix.java-version }}-flutter${{ matrix.branch }}
+ path: ./app/build/app/outputs/flutter-apk/*.apk
+ build-linux:
+ name: "🖥️ Build Desktop (linux)"
+ strategy:
+ matrix:
+ branch:
+ - beta
+ - stable
+ build:
+ - debug
+ - release
+
+ runs-on: ubuntu-latest
+ needs: setup-env
+ steps:
+ - name: Download src directory
+ uses: actions/download-artifact@v4
+ with:
+ name: src-${{ matrix.branch }}
+ - name: Setup Flutter
+ uses: subosito/flutter-action@v2
+ with:
+ channel: ${{ matrix.branch }}
+ cache: true
+ - name: Disable analytics
+ run: flutter config --no-analytics --suppress-analytics
+ - name: Build linux
+ run: flutter build linux --${{ matrix.build }}
+ working-directory: app
+ - name: Upload program
+ uses: actions/upload-artifact@v4
+ with:
+ name: linux-${{ matrix.build }}-flutter${{ matrix.branch }}-
+ path: ./app/build/linux/x64/release/bundle/blood_pressure_app/
+
app/lib/model/iso_lang_names.dart
@@ -19,7 +19,7 @@ String getDisplayLanguage(Locale l) => switch(l.toLanguageTag()) {
'hu' => 'Magyar (Magyarország)',
'et' => 'Eesti (Eesti)',
'nl' => 'Nederlands',
- 'cs' => 'čeština',
+ 'cs' => 'Čeština',
// Websites with names for expanding when new languages get added:
// - https://chronoplexsoftware.com/localisation/help/languagecodes.htm
// - https://localizely.com/locale-code/zh-Hans/
app/test/features/statistics/beta-ClockBpGraph-dark.png
Binary file
app/test/features/statistics/beta-ClockBpGraph-light.png
Binary file
app/test/features/statistics/beta-full_graph-years.png
Binary file
app/test/features/statistics/value-graph-end-warn.png → app/test/features/statistics/beta-value-graph-end-warn.png
File renamed without changes
app/test/features/statistics/value-graph-start-warn.png → app/test/features/statistics/beta-value-graph-start-warn.png
File renamed without changes
app/test/features/statistics/clock_bp_graph_test.dart
@@ -7,6 +7,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart';
import '../../model/analyzer_test.dart';
+import '../../util.dart';
void main() {
testWidgets("doesn't throw when empty" , (tester) async {
@@ -41,7 +42,7 @@ void main() {
),
),
));
- await expectLater(find.byType(ClockBpGraph), matchesGoldenFile('ClockBpGraph-light.png'));
+ await expectLater(find.byType(ClockBpGraph), myMatchesGoldenFile('ClockBpGraph-light.png'));
});
testWidgets('renders sample data like expected in dart mode', (tester) async {
final rng = Random(1234);
@@ -64,6 +65,6 @@ void main() {
),
),
));
- await expectLater(find.byType(ClockBpGraph), matchesGoldenFile('ClockBpGraph-dark.png'));
+ await expectLater(find.byType(ClockBpGraph), myMatchesGoldenFile('ClockBpGraph-dark.png'));
});
}
app/test/features/statistics/ClockBpGraph-dark.png
Binary file
app/test/features/statistics/ClockBpGraph-light.png
Binary file
app/test/features/statistics/full_graph-years.png
Binary file
app/test/features/statistics/stable-ClockBpGraph-dark.png
Binary file
app/test/features/statistics/stable-ClockBpGraph-light.png
Binary file
app/test/features/statistics/stable-full_graph-years.png
Binary file
app/test/features/statistics/stable-value-graph-end-warn.png
Binary file
app/test/features/statistics/stable-value-graph-start-warn.png
Binary file
app/test/features/statistics/value_graph_test.dart
@@ -100,7 +100,7 @@ void main() {
final localizations = await AppLocalizations.delegate.load(const Locale('en'));
expect(find.text(localizations.errNotEnoughDataToGraph), findsNothing);
- await expectLater(find.byType(BloodPressureValueGraph), matchesGoldenFile('full_graph-years.png'));
+ await expectLater(find.byType(BloodPressureValueGraph), myMatchesGoldenFile('full_graph-years.png'));
});
testWidgets('BloodPressureValueGraph is fine with enough values in sys category', (tester) async {
@@ -142,7 +142,7 @@ void main() {
final localizations = await AppLocalizations.delegate.load(const Locale('en'));
expect(find.text(localizations.errNotEnoughDataToGraph), findsNothing);
- await expectLater(find.byType(BloodPressureValueGraph), matchesGoldenFile('value-graph-start-warn.png'));
+ await expectLater(find.byType(BloodPressureValueGraph), myMatchesGoldenFile('value-graph-start-warn.png'));
});
testWidgets('graph renders area at end correctly', (tester) async {
await tester.pumpWidget(_buildGraph([
@@ -158,7 +158,7 @@ void main() {
final localizations = await AppLocalizations.delegate.load(const Locale('en'));
expect(find.text(localizations.errNotEnoughDataToGraph), findsNothing);
- await expectLater(find.byType(BloodPressureValueGraph), matchesGoldenFile('value-graph-end-warn.png'));
+ await expectLater(find.byType(BloodPressureValueGraph), myMatchesGoldenFile('value-graph-end-warn.png'));
});
}
app/test/util.dart
@@ -327,3 +327,8 @@ class MockMedicineIntakeRepository extends _MockRepo<MedicineIntake> implements
class MockMedicineRepository extends _MockRepo<Medicine> implements MedicineRepository {}
class MockNoteRepository extends _MockRepo<Note> implements NoteRepository {}
class MockBodyweightRepository extends _MockRepo<BodyweightRecord> implements BodyweightRepository {}
+
+dynamic myMatchesGoldenFile(String key) {
+ final channel = const String.fromEnvironment('channel');
+ return matchesGoldenFile('$channel-$key');
+}