diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 034392e..0000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM mcr.microsoft.com/devcontainers/base:debian - -# Install needed packages -RUN apt-get update && apt-get install -y curl git unzip xz-utils zip - -USER 1000:1000 - -ARG FLUTTER_VERSION=3.22.1 -RUN git clone -b $FLUTTER_VERSION https://github.com/flutter/flutter.git /home/vscode/flutter - -ENV PATH /home/vscode/flutter/bin:/home/vscode/.pub-cache/bin:$PATH - -RUN flutter precache - -USER root diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index d2721df..0000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,33 +0,0 @@ -// For format details, see https://aka.ms/devcontainer.json. For config options, see the -// README at: https://github.com/devcontainers/templates/tree/main/src/debian -{ - "name": "Debian", - - // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - "build": { - // Path is relative to the devcontainer.json file. - "dockerfile": "Dockerfile" - }, - - // Features to add to the dev container. More info: https://containers.dev/features. - // "features": {}, - - // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], - - // Configure tool-specific properties. - // "customizations": {}, - - "customizations": { - "vscode": { - "extensions": [ - "Dart-Code.flutter" - ] - } - }, - - // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. - // "remoteUser": "root" - - "postStartCommand": "flutter pub global activate -spath /workspaces/flutterpi_tool" -} diff --git a/.github/workflows/build-app.yml b/.github/workflows/build-app.yml index 9603939..ab6becb 100644 --- a/.github/workflows/build-app.yml +++ b/.github/workflows/build-app.yml @@ -2,19 +2,22 @@ name: Build Test App on: push: - branches: [ "main" ] + branches: ["main"] pull_request: - branches: [ "main" ] + branches: ["main"] -permissions: - contents: read +env: + FLUTTER_VERSION: 3.38.4 jobs: build: - name: Build Flutter-Pi Bundle (${{ matrix.arch }}, ${{ matrix.cpu}}) - runs-on: ubuntu-latest + name: Build Flutter-Pi Bundle (${{ matrix.runner}}, ${{ matrix.arch }}, ${{ matrix.cpu}}) + runs-on: ${{ matrix.runner }} strategy: matrix: + runner: + - windows-latest + - ubuntu-latest arch: - arm - arm64 @@ -22,24 +25,42 @@ jobs: - riscv64 cpu: - generic + + # TODO: Maybe find a better way to do this. include: - - arch: arm + - runner: windows-latest + arch: arm + cpu: pi3 + - runner: ubuntu-latest + arch: arm cpu: pi3 - - arch: arm + - runner: windows-latest + arch: arm cpu: pi4 - - arch: arm64 + - runner: ubuntu-latest + arch: arm + cpu: pi4 + - runner: windows-latest + arch: arm64 + cpu: pi3 + - runner: ubuntu-latest + arch: arm64 cpu: pi3 - - arch: arm64 + - runner: windows-latest + arch: arm64 + cpu: pi4 + - runner: ubuntu-latest + arch: arm64 cpu: pi4 steps: - uses: actions/checkout@v4 - + - uses: subosito/flutter-action@v2 with: cache: true channel: stable - flutter-version: 3.38.4 - + flutter-version: ${{ env.FLUTTER_VERSION }} + - name: Install dependencies & Activate as global executable run: | flutter pub get @@ -51,7 +72,7 @@ jobs: - name: Run flutterpi_tool build working-directory: test_app env: - GITHUB_TOKEN: ${{ secrets.TOKEN }} + GITHUB_TOKEN: ${{ secrets.PUBLIC_ONLY_PAT }} run: | echo '::group::flutterpi_tool build ... --debug-unoptimized' flutterpi_tool build --arch=${{ matrix.arch }} --cpu=${{ matrix.cpu }} --debug-unoptimized --debug-symbols --verbose @@ -60,7 +81,7 @@ jobs: echo '::group::flutterpi_tool build ... --debug' flutterpi_tool build --arch=${{ matrix.arch }} --cpu=${{ matrix.cpu }} --debug --debug-symbols --verbose echo '::endgroup::' - + echo '::group::flutterpi_tool build ... --profile' flutterpi_tool build --arch=${{ matrix.arch }} --cpu=${{ matrix.cpu }} --profile --debug-symbols --verbose echo '::endgroup::' diff --git a/.github/workflows/flutter.yml b/.github/workflows/flutter.yml index cabccc1..79f9543 100644 --- a/.github/workflows/flutter.yml +++ b/.github/workflows/flutter.yml @@ -7,39 +7,105 @@ name: Flutter on: push: - branches: [ "main" ] + branches: ["main"] pull_request: - branches: [ "main" ] + branches: ["main"] permissions: contents: read +env: + FLUTTER_VERSION: 3.38.4 + jobs: - build: + analyze: runs-on: ubuntu-latest env: GITHUB_TOKEN: ${{ secrets.TOKEN }} steps: - uses: actions/checkout@v4 - + - uses: subosito/flutter-action@v2 with: cache: true channel: stable - flutter-version: 3.38.4 - + flutter-version: ${{ env.FLUTTER_VERSION }} + - name: Install dependencies - run: flutter pub get - + run: dart pub get + - name: Verify formatting run: dart format --output=none --set-exit-if-changed . - # Consider passing '--fatal-infos' for slightly stricter analysis. - - name: Analyze project source - run: flutter analyze + - name: Analyze + run: dart analyze + + - name: Analyze e2e/hooks_test_package + working-directory: e2e/hooks_test_package + run: | + echo '::group::dart pub get' + dart pub get --enforce-lockfile + echo '::endgroup::' + + echo '::group::dart format' + dart format --output=none --set-exit-if-changed . + echo '::endgroup::' + + echo '::group::dart analyze' + dart analyze + echo '::endgroup::' + + test: + runs-on: ${{ matrix.runner }} + env: + GITHUB_TOKEN: ${{ secrets.token }} + strategy: + matrix: + runner: + - windows-latest + - ubuntu-latest + - macos-latest + steps: + - uses: actions/checkout@v4 + + - uses: subosito/flutter-action@v2 + with: + cache: true + channel: stable + flutter-version: ${{ env.FLUTTER_VERSION }} + + - name: Install dependencies + run: flutter pub get - # Your project will need to have tests in test/ and a dependency on - # package:test for this step to succeed. Note that Flutter projects will - # want to change this to 'flutter test'. - name: Run tests run: flutter test + + e2e: + name: E2E Tests (${{ matrix.runner }}) + runs-on: ${{ matrix.runner }} + env: + GITHUB_TOKEN: ${{ secrets.PUBLIC_ONLY_PAT }} + strategy: + matrix: + runner: + - windows-latest + - ubuntu-latest + - macos-latest + fail-fast: false + steps: + - uses: actions/checkout@v4 + + - uses: subosito/flutter-action@v2 + with: + cache: true + channel: stable + flutter-version: ${{ env.FLUTTER_VERSION }} + + - name: Install dependencies + run: flutter pub get + + - name: Activate flutterpi_tool globally + run: flutter pub global activate -spath . + + - name: Run e2e tests + run: dart test e2e diff --git a/.gitignore b/.gitignore index 84683cf..a513d72 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,9 @@ .dart_tool/ .packages build/ + # If you're building an application, you may want to check-in your pubspec.lock -pubspec.lock +/pubspec.lock # Directory created by dartdoc # If you don't generate documentation locally you can remove this line. @@ -52,4 +53,4 @@ Icon .AppleDesktop Network Trash Folder Temporary Items -.apdisk \ No newline at end of file +.apdisk diff --git a/analysis_options.yaml b/analysis_options.yaml index 0c09e66..7d871df 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -21,6 +21,9 @@ analyzer: exclude: - lib/src/archive + # separate package with additional dependencies not contained + # in the root pubspec + - e2e/hooks_test_package # For more information about the core and recommended set of lints, see # https://dart.dev/go/core-lints diff --git a/e2e/hooks_test.dart b/e2e/hooks_test.dart new file mode 100644 index 0000000..1ddb083 --- /dev/null +++ b/e2e/hooks_test.dart @@ -0,0 +1,193 @@ +import 'dart:io'; + +import 'package:test/test.dart'; + +/// Custom matcher that checks if a ProcessResult succeeded +/// and provides helpful error messages with stdout/stderr on failure. +class _ProcessExitedSuccessfully extends Matcher { + _ProcessExitedSuccessfully({ + Object? exitCode, + Object? stdout, + Object? stderr, + }) : exitCode = wrapMatcher(exitCode ?? equals(0)), + stdout = wrapMatcher(stdout ?? anything), + stderr = wrapMatcher(stderr ?? anything); + + final Matcher exitCode; + final Matcher stdout; + final Matcher stderr; + + @override + bool matches(dynamic item, Map matchState) { + if (item is! ProcessResult) return false; + + final exitCode = this.exitCode; + if (!exitCode.matches(item.exitCode, matchState)) { + matchState['exitCodeMatcher'] = exitCode; + matchState['field'] = 'exitCode'; + return false; + } + + if (!stdout.matches(item.stdout, matchState)) { + matchState['stdoutMatcher'] = stdout; + matchState['field'] = 'stdout'; + return false; + } + + if (!stderr.matches(item.stderr, matchState)) { + matchState['stderrMatcher'] = stderr; + matchState['field'] = 'stderr'; + return false; + } + + return true; + } + + @override + Description describe(Description description) { + description.add('process to succeed'); + + description.add(' with exit code '); + exitCode.describe(description); + + description.add(' and stdout '); + stdout.describe(description); + + description.add(' and stderr '); + stderr.describe(description); + + return description; + } + + @override + Description describeMismatch( + dynamic item, + Description mismatchDescription, + Map matchState, + bool verbose, + ) { + if (item is! ProcessResult) { + return mismatchDescription.add('is not a ProcessResult'); + } + + final field = matchState['field'] as String?; + + if (field == 'exitCode') { + final matcher = matchState['exitCodeMatcher'] as Matcher; + mismatchDescription.add('exit code '); + matcher.describeMismatch( + item.exitCode, + mismatchDescription, + matchState, + verbose, + ); + } else if (field == 'stdout') { + final matcher = matchState['stdoutMatcher'] as Matcher; + mismatchDescription.add('stdout '); + matcher.describeMismatch( + item.stdout, + mismatchDescription, + matchState, + verbose, + ); + } else if (field == 'stderr') { + final matcher = matchState['stderrMatcher'] as Matcher; + mismatchDescription.add('stderr '); + matcher.describeMismatch( + item.stderr, + mismatchDescription, + matchState, + verbose, + ); + } + + // Always include the full process output for context + return mismatchDescription + .add('\n\nProcess output:') + .add('\nexit code: ${item.exitCode}') + .add('\nstdout: ${item.stdout}') + .add('\nstderr: ${item.stderr}'); + } +} + +Matcher exitedSuccessfully({ + Matcher? exitCode, + Matcher? stdout, + Matcher? stderr, +}) => + _ProcessExitedSuccessfully( + exitCode: exitCode, + stdout: stdout, + stderr: stderr, + ); + +Matcher buildExitedSuccessfully() => _ProcessExitedSuccessfully( + stdout: isNot(contains('Failed to build bundle.')), + stderr: isNot(contains('Failed to build bundle.')), + ); + +void main() { + // Only tested on linux right now, see testOn below. + // On windows and macOS it's hard to get a working linux + // cross compiler. Best thing we can do maybe is just + // verify we provide an understandable error message. + group( + 'hooks_test native assets e2e', + () { + const exampleDir = 'e2e/hooks_test_package/example'; + + setUpAll(() async { + // Ensure dependencies are installed + final pubGetResult = await Process.run( + 'flutter', + ['pub', 'get'], + workingDirectory: exampleDir, + ); + expect(pubGetResult, exitedSuccessfully()); + }); + + test( + 'builds successfully in debug mode', + () async { + final result = await Process.run( + 'flutterpi_tool', + ['build', '--debug'], + workingDirectory: exampleDir, + ); + + expect(result, buildExitedSuccessfully()); + }, + timeout: const Timeout(Duration(minutes: 5)), + ); + + test( + 'builds successfully in profile mode', + () async { + final result = await Process.run( + 'flutterpi_tool', + ['build', '--profile'], + workingDirectory: exampleDir, + ); + + expect(result, buildExitedSuccessfully()); + }, + timeout: const Timeout(Duration(minutes: 5)), + ); + + test( + 'builds successfully in release mode', + () async { + final result = await Process.run( + 'flutterpi_tool', + ['build', '--release'], + workingDirectory: exampleDir, + ); + + expect(result, buildExitedSuccessfully()); + }, + timeout: const Timeout(Duration(minutes: 5)), + ); + }, + testOn: 'linux', + ); +} diff --git a/e2e/hooks_test_package/.gitignore b/e2e/hooks_test_package/.gitignore new file mode 100644 index 0000000..cd5fa12 --- /dev/null +++ b/e2e/hooks_test_package/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins-dependencies +/build/ +/coverage/ diff --git a/e2e/hooks_test_package/.metadata b/e2e/hooks_test_package/.metadata new file mode 100644 index 0000000..c422502 --- /dev/null +++ b/e2e/hooks_test_package/.metadata @@ -0,0 +1,27 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "3b62efc2a3da49882f43c372e0bc53daef7295a6" + channel: "stable" + +project_type: package_ffi + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 3b62efc2a3da49882f43c372e0bc53daef7295a6 + base_revision: 3b62efc2a3da49882f43c372e0bc53daef7295a6 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/e2e/hooks_test_package/example/.gitignore b/e2e/hooks_test_package/example/.gitignore new file mode 100644 index 0000000..3820a95 --- /dev/null +++ b/e2e/hooks_test_package/example/.gitignore @@ -0,0 +1,45 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ +/coverage/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/e2e/hooks_test_package/example/README.md b/e2e/hooks_test_package/example/README.md new file mode 100644 index 0000000..a7bde44 --- /dev/null +++ b/e2e/hooks_test_package/example/README.md @@ -0,0 +1,16 @@ +# hooks_test_example + +Demonstrates how to use the hooks_test package. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/e2e/hooks_test_package/example/lib/main.dart b/e2e/hooks_test_package/example/lib/main.dart new file mode 100644 index 0000000..db923ca --- /dev/null +++ b/e2e/hooks_test_package/example/lib/main.dart @@ -0,0 +1,13 @@ +import 'package:integration_test/integration_test.dart'; +import 'package:hooks_test_package/hooks_test_package.dart' as hooks_test; +import 'package:test/test.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + test('native hooks sum works', () async { + expect(hooks_test.sum(2, 1), equals(3)); + + await expectLater(hooks_test.sumAsync(2, 1), equals(3)); + }); +} diff --git a/e2e/hooks_test_package/example/pubspec.lock b/e2e/hooks_test_package/example/pubspec.lock new file mode 100644 index 0000000..b6c0de3 --- /dev/null +++ b/e2e/hooks_test_package/example/pubspec.lock @@ -0,0 +1,571 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d + url: "https://pub.dev" + source: hosted + version: "91.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: f51c8499b35f9b26820cfe914828a6a98a94efd5cc78b37bb7d03debae3a1d08 + url: "https://pub.dev" + source: hosted + version: "8.4.1" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + cli_config: + dependency: transitive + description: + name: cli_config + sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec + url: "https://pub.dev" + source: hosted + version: "0.2.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + code_assets: + dependency: transitive + description: + name: code_assets + sha256: ae0db647e668cbb295a3527f0938e4039e004c80099dce2f964102373f5ce0b5 + url: "https://pub.dev" + source: hosted + version: "0.19.10" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + coverage: + dependency: transitive + description: + name: coverage + sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d" + url: "https://pub.dev" + source: hosted + version: "1.15.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + url: "https://pub.dev" + source: hosted + version: "3.0.7" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" + source: hosted + version: "1.0.8" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_driver: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" + url: "https://pub.dev" + source: hosted + version: "6.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + fuchsia_remote_debug_protocol: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + url: "https://pub.dev" + source: hosted + version: "2.1.3" + hooks: + dependency: transitive + description: + name: hooks + sha256: "5410b9f4f6c9f01e8ff0eb81c9801ea13a3c3d39f8f0b1613cda08e27eab3c18" + url: "https://pub.dev" + source: hosted + version: "0.20.5" + hooks_test_package: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "0.0.1" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + url: "https://pub.dev" + source: hosted + version: "3.2.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + integration_test: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + io: + dependency: transitive + description: + name: io + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + url: "https://pub.dev" + source: hosted + version: "1.0.5" + js: + dependency: transitive + description: + name: js + sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" + url: "https://pub.dev" + source: hosted + version: "0.7.2" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + url: "https://pub.dev" + source: hosted + version: "11.0.2" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + url: "https://pub.dev" + source: hosted + version: "3.0.10" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + lints: + dependency: transitive + description: + name: lints + sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" + url: "https://pub.dev" + source: hosted + version: "6.1.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + url: "https://pub.dev" + source: hosted + version: "1.17.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + native_toolchain_c: + dependency: transitive + description: + name: native_toolchain_c + sha256: f8872ea6c7a50ce08db9ae280ca2b8efdd973157ce462826c82f3c3051d154ce + url: "https://pub.dev" + source: hosted + version: "0.17.2" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.dev" + source: hosted + version: "2.2.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + pool: + dependency: transitive + description: + name: pool + sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" + url: "https://pub.dev" + source: hosted + version: "1.5.2" + process: + dependency: transitive + description: + name: process + sha256: c6248e4526673988586e8c00bb22a49210c258dc91df5227d5da9748ecf79744 + url: "https://pub.dev" + source: hosted + version: "5.0.5" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shelf: + dependency: transitive + description: + name: shelf + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + url: "https://pub.dev" + source: hosted + version: "1.4.2" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + url: "https://pub.dev" + source: hosted + version: "1.1.3" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" + url: "https://pub.dev" + source: hosted + version: "0.10.13" + source_span: + dependency: transitive + description: + name: source_span + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" + url: "https://pub.dev" + source: hosted + version: "1.10.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + sync_http: + dependency: transitive + description: + name: sync_http + sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test: + dependency: "direct main" + description: + name: test + sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7" + url: "https://pub.dev" + source: hosted + version: "1.26.3" + test_api: + dependency: transitive + description: + name: test_api + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 + url: "https://pub.dev" + source: hosted + version: "0.7.7" + test_core: + dependency: transitive + description: + name: test_core + sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0" + url: "https://pub.dev" + source: hosted + version: "0.6.12" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + url: "https://pub.dev" + source: hosted + version: "2.2.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" + url: "https://pub.dev" + source: hosted + version: "15.0.2" + watcher: + dependency: transitive + description: + name: watcher + sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + webdriver: + dependency: transitive + description: + name: webdriver + sha256: "2f3a14ca026957870cfd9c635b83507e0e51d8091568e90129fbf805aba7cade" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" +sdks: + dart: ">=3.10.3 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/e2e/hooks_test_package/example/pubspec.yaml b/e2e/hooks_test_package/example/pubspec.yaml new file mode 100644 index 0000000..7f51b01 --- /dev/null +++ b/e2e/hooks_test_package/example/pubspec.yaml @@ -0,0 +1,99 @@ +name: hooks_test_example +description: "Demonstrates how to use the hooks_test package." +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: "none" # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: ^3.10.3 + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + integration_test: + sdk: flutter + + hooks_test_package: + # When depending on this package from a real application you should use: + # hooks_test: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.8 + test: ^1.26.3 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^6.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/to/asset-from-package + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/to/font-from-package diff --git a/e2e/hooks_test_package/ffigen.yaml b/e2e/hooks_test_package/ffigen.yaml new file mode 100644 index 0000000..31f33bb --- /dev/null +++ b/e2e/hooks_test_package/ffigen.yaml @@ -0,0 +1,20 @@ +# Run with `dart run ffigen --config ffigen.yaml`. +name: HooksTestBindings +description: | + Bindings for `src/hooks_test.h`. + + Regenerate bindings with `dart run ffigen --config ffigen.yaml`. +output: 'lib/hooks_test_bindings_generated.dart' +headers: + entry-points: + - 'src/hooks_test.h' + include-directives: + - 'src/hooks_test.h' +ffi-native: +preamble: | + // ignore_for_file: always_specify_types + // ignore_for_file: camel_case_types + // ignore_for_file: non_constant_identifier_names +comments: + style: any + length: full diff --git a/e2e/hooks_test_package/hook/build.dart b/e2e/hooks_test_package/hook/build.dart new file mode 100644 index 0000000..3c82887 --- /dev/null +++ b/e2e/hooks_test_package/hook/build.dart @@ -0,0 +1,21 @@ +import 'package:native_toolchain_c/native_toolchain_c.dart'; +import 'package:logging/logging.dart'; +import 'package:hooks/hooks.dart'; + +void main(List args) async { + await build(args, (input, output) async { + final packageName = input.packageName; + final cbuilder = CBuilder.library( + name: packageName, + assetName: 'hooks_test_bindings_generated.dart', + sources: ['src/hooks_test.c'], + ); + await cbuilder.run( + input: input, + output: output, + logger: Logger('') + ..level = Level.ALL + ..onRecord.listen((record) => print(record.message)), + ); + }); +} diff --git a/e2e/hooks_test_package/lib/hooks_test_bindings_generated.dart b/e2e/hooks_test_package/lib/hooks_test_bindings_generated.dart new file mode 100644 index 0000000..d61c422 --- /dev/null +++ b/e2e/hooks_test_package/lib/hooks_test_bindings_generated.dart @@ -0,0 +1,24 @@ +// ignore_for_file: always_specify_types +// ignore_for_file: camel_case_types +// ignore_for_file: non_constant_identifier_names + +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +import 'dart:ffi' as ffi; + +/// A very short-lived native function. +/// +/// For very short-lived functions, it is fine to call them on the main isolate. +/// They will block the Dart execution while running the native function, so +/// only do this for native functions which are guaranteed to be short-lived. +@ffi.Native() +external int sum(int a, int b); + +/// A longer lived native function, which occupies the thread calling it. +/// +/// Do not call these kind of native functions in the main isolate. They will +/// block Dart execution. This will cause dropped frames in Flutter applications. +/// Instead, call these native functions on a separate isolate. +@ffi.Native() +external int sum_long_running(int a, int b); diff --git a/e2e/hooks_test_package/lib/hooks_test_package.dart b/e2e/hooks_test_package/lib/hooks_test_package.dart new file mode 100644 index 0000000..326d32c --- /dev/null +++ b/e2e/hooks_test_package/lib/hooks_test_package.dart @@ -0,0 +1,108 @@ +import 'dart:async'; +import 'dart:isolate'; + +import 'hooks_test_bindings_generated.dart' as bindings; + +/// A very short-lived native function. +/// +/// For very short-lived functions, it is fine to call them on the main isolate. +/// They will block the Dart execution while running the native function, so +/// only do this for native functions which are guaranteed to be short-lived. +int sum(int a, int b) => bindings.sum(a, b); + +/// A longer lived native function, which occupies the thread calling it. +/// +/// Do not call these kind of native functions in the main isolate. They will +/// block Dart execution. This will cause dropped frames in Flutter applications. +/// Instead, call these native functions on a separate isolate. +/// +/// Modify this to suit your own use case. Example use cases: +/// +/// 1. Reuse a single isolate for various different kinds of requests. +/// 2. Use multiple helper isolates for parallel execution. +Future sumAsync(int a, int b) async { + final SendPort helperIsolateSendPort = await _helperIsolateSendPort; + final int requestId = _nextSumRequestId++; + final _SumRequest request = _SumRequest(requestId, a, b); + final Completer completer = Completer(); + _sumRequests[requestId] = completer; + helperIsolateSendPort.send(request); + return completer.future; +} + +/// A request to compute `sum`. +/// +/// Typically sent from one isolate to another. +class _SumRequest { + final int id; + final int a; + final int b; + + const _SumRequest(this.id, this.a, this.b); +} + +/// A response with the result of `sum`. +/// +/// Typically sent from one isolate to another. +class _SumResponse { + final int id; + final int result; + + const _SumResponse(this.id, this.result); +} + +/// Counter to identify [_SumRequest]s and [_SumResponse]s. +int _nextSumRequestId = 0; + +/// Mapping from [_SumRequest] `id`s to the completers corresponding to the correct future of the pending request. +final Map> _sumRequests = >{}; + +/// The SendPort belonging to the helper isolate. +Future _helperIsolateSendPort = () async { + // The helper isolate is going to send us back a SendPort, which we want to + // wait for. + final Completer completer = Completer(); + + // Receive port on the main isolate to receive messages from the helper. + // We receive two types of messages: + // 1. A port to send messages on. + // 2. Responses to requests we sent. + final ReceivePort receivePort = ReceivePort() + ..listen((dynamic data) { + if (data is SendPort) { + // The helper isolate sent us the port on which we can sent it requests. + completer.complete(data); + return; + } + if (data is _SumResponse) { + // The helper isolate sent us a response to a request we sent. + final Completer completer = _sumRequests[data.id]!; + _sumRequests.remove(data.id); + completer.complete(data.result); + return; + } + throw UnsupportedError('Unsupported message type: ${data.runtimeType}'); + }); + + // Start the helper isolate. + await Isolate.spawn((SendPort sendPort) async { + final ReceivePort helperReceivePort = ReceivePort() + ..listen((dynamic data) { + // On the helper isolate listen to requests and respond to them. + if (data is _SumRequest) { + final int result = bindings.sum_long_running(data.a, data.b); + final _SumResponse response = _SumResponse(data.id, result); + sendPort.send(response); + return; + } + throw UnsupportedError('Unsupported message type: ${data.runtimeType}'); + }); + + // Send the port to the main isolate on which we can receive requests. + sendPort.send(helperReceivePort.sendPort); + }, receivePort.sendPort); + + // Wait until the helper isolate has sent us back the SendPort on which we + // can start sending requests. + return completer.future; +}(); diff --git a/e2e/hooks_test_package/pubspec.lock b/e2e/hooks_test_package/pubspec.lock new file mode 100644 index 0000000..87753b6 --- /dev/null +++ b/e2e/hooks_test_package/pubspec.lock @@ -0,0 +1,461 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "796d97d925add7ffcdf5595f33a2066a6e3cee97971e6dbef09b76b7880fd760" + url: "https://pub.dev" + source: hosted + version: "94.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "9c8ebb304d72c0a0c8764344627529d9503fc83d7d73e43ed727dc532f822e4b" + url: "https://pub.dev" + source: hosted + version: "10.0.2" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + cli_config: + dependency: transitive + description: + name: cli_config + sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec + url: "https://pub.dev" + source: hosted + version: "0.2.0" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c + url: "https://pub.dev" + source: hosted + version: "0.4.2" + code_assets: + dependency: "direct main" + description: + name: code_assets + sha256: ae0db647e668cbb295a3527f0938e4039e004c80099dce2f964102373f5ce0b5 + url: "https://pub.dev" + source: hosted + version: "0.19.10" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + coverage: + dependency: transitive + description: + name: coverage + sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d" + url: "https://pub.dev" + source: hosted + version: "1.15.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + url: "https://pub.dev" + source: hosted + version: "3.0.7" + ffi: + dependency: "direct dev" + description: + name: ffi + sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c + url: "https://pub.dev" + source: hosted + version: "2.1.5" + ffigen: + dependency: "direct dev" + description: + name: ffigen + sha256: "10cb41647d73e0204f8d35138a3f20eb52418cce96599ad49167b1111e59a557" + url: "https://pub.dev" + source: hosted + version: "13.0.0" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" + url: "https://pub.dev" + source: hosted + version: "6.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + url: "https://pub.dev" + source: hosted + version: "2.1.3" + hooks: + dependency: "direct main" + description: + name: hooks + sha256: "5410b9f4f6c9f01e8ff0eb81c9801ea13a3c3d39f8f0b1613cda08e27eab3c18" + url: "https://pub.dev" + source: hosted + version: "0.20.5" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + url: "https://pub.dev" + source: hosted + version: "3.2.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + io: + dependency: transitive + description: + name: io + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + url: "https://pub.dev" + source: hosted + version: "1.0.5" + lints: + dependency: transitive + description: + name: lints + sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" + url: "https://pub.dev" + source: hosted + version: "6.1.0" + logging: + dependency: "direct main" + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" + url: "https://pub.dev" + source: hosted + version: "0.12.18" + meta: + dependency: transitive + description: + name: meta + sha256: "9f29b9bcc8ee287b1a31e0d01be0eae99a930dbffdaecf04b3f3d82a969f296f" + url: "https://pub.dev" + source: hosted + version: "1.18.1" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + native_toolchain_c: + dependency: "direct main" + description: + name: native_toolchain_c + sha256: f8872ea6c7a50ce08db9ae280ca2b8efdd973157ce462826c82f3c3051d154ce + url: "https://pub.dev" + source: hosted + version: "0.17.2" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.dev" + source: hosted + version: "2.2.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + pool: + dependency: transitive + description: + name: pool + sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" + url: "https://pub.dev" + source: hosted + version: "1.5.2" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + quiver: + dependency: transitive + description: + name: quiver + sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 + url: "https://pub.dev" + source: hosted + version: "3.2.2" + shelf: + dependency: transitive + description: + name: shelf + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + url: "https://pub.dev" + source: hosted + version: "1.4.2" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + url: "https://pub.dev" + source: hosted + version: "1.1.3" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" + url: "https://pub.dev" + source: hosted + version: "0.10.13" + source_span: + dependency: transitive + description: + name: source_span + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" + url: "https://pub.dev" + source: hosted + version: "1.10.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test: + dependency: "direct dev" + description: + name: test + sha256: "54c516bbb7cee2754d327ad4fca637f78abfc3cbcc5ace83b3eda117e42cd71a" + url: "https://pub.dev" + source: hosted + version: "1.29.0" + test_api: + dependency: transitive + description: + name: test_api + sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" + url: "https://pub.dev" + source: hosted + version: "0.7.9" + test_core: + dependency: transitive + description: + name: test_core + sha256: "394f07d21f0f2255ec9e3989f21e54d3c7dc0e6e9dbce160e5a9c1a6be0e2943" + url: "https://pub.dev" + source: hosted + version: "0.6.15" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" + url: "https://pub.dev" + source: hosted + version: "15.0.2" + watcher: + dependency: transitive + description: + name: watcher + sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" + yaml_edit: + dependency: transitive + description: + name: yaml_edit + sha256: ec709065bb2c911b336853b67f3732dd13e0336bd065cc2f1061d7610ddf45e3 + url: "https://pub.dev" + source: hosted + version: "2.2.3" +sdks: + dart: ">=3.10.3 <4.0.0" diff --git a/e2e/hooks_test_package/pubspec.yaml b/e2e/hooks_test_package/pubspec.yaml new file mode 100644 index 0000000..6de7b3f --- /dev/null +++ b/e2e/hooks_test_package/pubspec.yaml @@ -0,0 +1,18 @@ +name: hooks_test_package +description: "A new Dart FFI package project." +version: 0.0.1 + +environment: + sdk: ^3.10.3 + +dependencies: + code_assets: ^0.19.7 + hooks: ^0.20.1 + logging: ^1.3.0 + native_toolchain_c: ^0.17.1 + +dev_dependencies: + ffi: ^2.1.3 + ffigen: ^13.0.0 + flutter_lints: ^6.0.0 + test: ^1.25.8 diff --git a/e2e/hooks_test_package/src/hooks_test.c b/e2e/hooks_test_package/src/hooks_test.c new file mode 100644 index 0000000..41b4c2f --- /dev/null +++ b/e2e/hooks_test_package/src/hooks_test.c @@ -0,0 +1,29 @@ +#include "hooks_test.h" + +// A very short-lived native function. +// +// For very short-lived functions, it is fine to call them on the main isolate. +// They will block the Dart execution while running the native function, so +// only do this for native functions which are guaranteed to be short-lived. +FFI_PLUGIN_EXPORT intptr_t sum(intptr_t a, intptr_t b) { +#ifdef DEBUG + return a + b + 1000; +#else + return a + b; +#endif +} + +// A longer-lived native function, which occupies the thread calling it. +// +// Do not call these kind of native functions in the main isolate. They will +// block Dart execution. This will cause dropped frames in Flutter applications. +// Instead, call these native functions on a separate isolate. +FFI_PLUGIN_EXPORT intptr_t sum_long_running(intptr_t a, intptr_t b) { + // Simulate work. +#if _WIN32 + Sleep(5000); +#else + usleep(5000 * 1000); +#endif + return a + b; +} diff --git a/e2e/hooks_test_package/src/hooks_test.h b/e2e/hooks_test_package/src/hooks_test.h new file mode 100644 index 0000000..084c642 --- /dev/null +++ b/e2e/hooks_test_package/src/hooks_test.h @@ -0,0 +1,30 @@ +#include +#include +#include + +#if _WIN32 +#include +#else +#include +#include +#endif + +#if _WIN32 +#define FFI_PLUGIN_EXPORT __declspec(dllexport) +#else +#define FFI_PLUGIN_EXPORT +#endif + +// A very short-lived native function. +// +// For very short-lived functions, it is fine to call them on the main isolate. +// They will block the Dart execution while running the native function, so +// only do this for native functions which are guaranteed to be short-lived. +FFI_PLUGIN_EXPORT intptr_t sum(intptr_t a, intptr_t b); + +// A longer lived native function, which occupies the thread calling it. +// +// Do not call these kind of native functions in the main isolate. They will +// block Dart execution. This will cause dropped frames in Flutter applications. +// Instead, call these native functions on a separate isolate. +FFI_PLUGIN_EXPORT intptr_t sum_long_running(intptr_t a, intptr_t b); diff --git a/lib/src/context.dart b/lib/src/context.dart index 10560fe..b0eee14 100644 --- a/lib/src/context.dart +++ b/lib/src/context.dart @@ -7,6 +7,7 @@ import 'package:flutterpi_tool/src/build_system/build_app.dart'; import 'package:flutterpi_tool/src/config.dart'; import 'package:flutterpi_tool/src/devices/device_manager.dart'; import 'package:flutterpi_tool/src/devices/flutterpi_ssh/ssh_utils.dart'; +import 'package:github/github.dart' as gh; import 'package:unified_analytics/unified_analytics.dart'; import 'package:http/io_client.dart' as http; @@ -28,20 +29,28 @@ Future runInContext( overrides: { Analytics: () => const NoOpAnalytics(), fl.TemplateRenderer: () => const fl.MustacheTemplateRenderer(), - fl.Cache: () => FlutterpiCache( - hooks: globals.shutdownHooks, - logger: globals.logger, - fileSystem: globals.fs, - platform: globals.platform, - osUtils: globals.os as MoreOperatingSystemUtils, - projectFactory: globals.projectFactory, - processManager: globals.processManager, - github: MyGithub.caching( - httpClient: http.IOClient( - globals.httpClientFactory?.call() ?? io.HttpClient(), - ), + fl.Cache: () { + final auth = switch (globals.platform.environment['GITHUB_TOKEN']) { + final token? => gh.Authentication.bearerToken(token), + _ => null, + }; + + return FlutterpiCache( + hooks: globals.shutdownHooks, + logger: globals.logger, + fileSystem: globals.fs, + platform: globals.platform, + osUtils: globals.os as MoreOperatingSystemUtils, + projectFactory: globals.projectFactory, + processManager: globals.processManager, + github: MyGithub.caching( + httpClient: http.IOClient( + globals.httpClientFactory?.call() ?? io.HttpClient(), ), + auth: auth, ), + ); + }, fl.OperatingSystemUtils: () => MoreOperatingSystemUtils( fileSystem: globals.fs, logger: globals.logger, diff --git a/test/app_builder_test.dart b/test/app_builder_test.dart index 609194d..83bf118 100644 --- a/test/app_builder_test.dart +++ b/test/app_builder_test.dart @@ -114,33 +114,66 @@ void main() { expect(buildWasCalled, isTrue); }); - test('passes target path correctly', () async { - var buildWasCalled = false; - buildSystem.buildFn = ( - fl.Target target, - fl.Environment environment, { - fl.BuildSystemConfig buildSystemConfig = const fl.BuildSystemConfig(), - }) async { - expect( - environment.defines[fl.kTargetFile], - equals('lib/main_flutterpi.dart'), + group('passes target path correctly', () { + test('posix', () async { + var buildWasCalled = false; + buildSystem.buildFn = ( + fl.Target target, + fl.Environment environment, { + fl.BuildSystemConfig buildSystemConfig = const fl.BuildSystemConfig(), + }) async { + expect( + environment.defines[fl.kTargetFile], + equals(p.posix.join('lib', 'main_flutterpi.dart')), + ); + + buildWasCalled = true; + return fl.BuildResult(success: true); + }; + + await _runInTestContext( + () async => await appBuilder.build( + host: FlutterpiHostPlatform.linuxRV64, + target: FlutterpiTargetPlatform.genericRiscv64, + buildInfo: fl.BuildInfo.debug, + fsLayout: FilesystemLayout.flutterPi, + mainPath: p.posix.join('lib', 'main_flutterpi.dart'), + ), ); - buildWasCalled = true; - return fl.BuildResult(success: true); - }; + expect(buildWasCalled, isTrue); + }); - await _runInTestContext( - () async => await appBuilder.build( - host: FlutterpiHostPlatform.linuxRV64, - target: FlutterpiTargetPlatform.genericRiscv64, - buildInfo: fl.BuildInfo.debug, - fsLayout: FilesystemLayout.flutterPi, - mainPath: 'lib/main_flutterpi.dart', - ), - ); + test('windows', () async { + fs = MemoryFileSystem.test(style: FileSystemStyle.windows); - expect(buildWasCalled, isTrue); + var buildWasCalled = false; + buildSystem.buildFn = ( + fl.Target target, + fl.Environment environment, { + fl.BuildSystemConfig buildSystemConfig = const fl.BuildSystemConfig(), + }) async { + expect( + environment.defines[fl.kTargetFile], + equals(p.windows.join('lib', 'main_flutterpi.dart')), + ); + + buildWasCalled = true; + return fl.BuildResult(success: true); + }; + + await _runInTestContext( + () async => await appBuilder.build( + host: FlutterpiHostPlatform.linuxRV64, + target: FlutterpiTargetPlatform.genericRiscv64, + buildInfo: fl.BuildInfo.debug, + fsLayout: FilesystemLayout.flutterPi, + mainPath: p.windows.join('lib', 'main_flutterpi.dart'), + ), + ); + + expect(buildWasCalled, isTrue); + }); }); group('--fs-layout', () { @@ -259,34 +292,82 @@ void main() { expect(bundle.includesFlutterpiBinary, isTrue); }); - test('default output directory is build/-meta-flutter', () async { - var buildWasCalled = false; - buildSystem.buildFn = ( - fl.Target target, - fl.Environment environment, { - fl.BuildSystemConfig buildSystemConfig = const fl.BuildSystemConfig(), - }) async { - expect( - environment.outputDir.path, - equals('build/flutter-pi/meta-flutter-riscv64-generic'), + group('default output directory is build/-meta-flutter', () { + test('posix', () async { + var buildWasCalled = false; + buildSystem.buildFn = ( + fl.Target target, + fl.Environment environment, { + fl.BuildSystemConfig buildSystemConfig = + const fl.BuildSystemConfig(), + }) async { + expect( + environment.outputDir.path, + equals( + p.posix.join( + 'build', + 'flutter-pi', + 'meta-flutter-riscv64-generic', + ), + ), + ); + + buildWasCalled = true; + return fl.BuildResult(success: true); + }; + + await _runInTestContext( + () async => await appBuilder.buildBundle( + id: 'test-id', + host: FlutterpiHostPlatform.linuxRV64, + target: FlutterpiTargetPlatform.genericRiscv64, + buildInfo: fl.BuildInfo.debug, + fsLayout: FilesystemLayout.metaFlutter, + forceBundleFlutterpi: true, + ), ); - buildWasCalled = true; - return fl.BuildResult(success: true); - }; - - await _runInTestContext( - () async => await appBuilder.buildBundle( - id: 'test-id', - host: FlutterpiHostPlatform.linuxRV64, - target: FlutterpiTargetPlatform.genericRiscv64, - buildInfo: fl.BuildInfo.debug, - fsLayout: FilesystemLayout.metaFlutter, - forceBundleFlutterpi: true, - ), - ); + expect(buildWasCalled, isTrue); + }); + + test('windows', () async { + fs = MemoryFileSystem.test(style: FileSystemStyle.windows); + + var buildWasCalled = false; + buildSystem.buildFn = ( + fl.Target target, + fl.Environment environment, { + fl.BuildSystemConfig buildSystemConfig = + const fl.BuildSystemConfig(), + }) async { + expect( + environment.outputDir.path, + equals( + p.windows.join( + 'build', + 'flutter-pi', + 'meta-flutter-riscv64-generic', + ), + ), + ); + + buildWasCalled = true; + return fl.BuildResult(success: true); + }; + + await _runInTestContext( + () async => await appBuilder.buildBundle( + id: 'test-id', + host: FlutterpiHostPlatform.linuxRV64, + target: FlutterpiTargetPlatform.genericRiscv64, + buildInfo: fl.BuildInfo.debug, + fsLayout: FilesystemLayout.metaFlutter, + forceBundleFlutterpi: true, + ), + ); - expect(buildWasCalled, isTrue); + expect(buildWasCalled, isTrue); + }); }); }); @@ -366,34 +447,74 @@ void main() { expect(buildWasCalled, isTrue); }); - test('default output directory is build/', () async { - var buildWasCalled = false; - buildSystem.buildFn = ( - fl.Target target, - fl.Environment environment, { - fl.BuildSystemConfig buildSystemConfig = const fl.BuildSystemConfig(), - }) async { - expect( - environment.outputDir.path, - equals('build/flutter-pi/riscv64-generic'), + group('default output directory is build/', () { + test('posix', () async { + var buildWasCalled = false; + buildSystem.buildFn = ( + fl.Target target, + fl.Environment environment, { + fl.BuildSystemConfig buildSystemConfig = + const fl.BuildSystemConfig(), + }) async { + expect( + environment.outputDir.path, + equals( + p.posix.join('build', 'flutter-pi', 'riscv64-generic'), + ), + ); + + buildWasCalled = true; + return fl.BuildResult(success: true); + }; + + await _runInTestContext( + () async => await appBuilder.buildBundle( + id: 'test-id', + host: FlutterpiHostPlatform.linuxRV64, + target: FlutterpiTargetPlatform.genericRiscv64, + buildInfo: fl.BuildInfo.debug, + fsLayout: FilesystemLayout.flutterPi, + forceBundleFlutterpi: true, + ), ); - buildWasCalled = true; - return fl.BuildResult(success: true); - }; - - await _runInTestContext( - () async => await appBuilder.buildBundle( - id: 'test-id', - host: FlutterpiHostPlatform.linuxRV64, - target: FlutterpiTargetPlatform.genericRiscv64, - buildInfo: fl.BuildInfo.debug, - fsLayout: FilesystemLayout.flutterPi, - forceBundleFlutterpi: true, - ), - ); + expect(buildWasCalled, isTrue); + }); + + test('windows', () async { + fs = MemoryFileSystem.test(style: FileSystemStyle.windows); + + var buildWasCalled = false; + buildSystem.buildFn = ( + fl.Target target, + fl.Environment environment, { + fl.BuildSystemConfig buildSystemConfig = + const fl.BuildSystemConfig(), + }) async { + expect( + environment.outputDir.path, + equals( + p.windows.join('build', 'flutter-pi', 'riscv64-generic'), + ), + ); + + buildWasCalled = true; + return fl.BuildResult(success: true); + }; + + await _runInTestContext( + () async => await appBuilder.buildBundle( + id: 'test-id', + host: FlutterpiHostPlatform.linuxRV64, + target: FlutterpiTargetPlatform.genericRiscv64, + buildInfo: fl.BuildInfo.debug, + fsLayout: FilesystemLayout.flutterPi, + forceBundleFlutterpi: true, + ), + ); - expect(buildWasCalled, isTrue); + expect(buildWasCalled, isTrue); + }); }); }); }); @@ -469,176 +590,379 @@ void main() { }); group('bundle binaries', () { - test('binary paths for --fs-layout=flutter-pi', () async { - buildSystem.buildFn = ( - fl.Target target, - fl.Environment environment, { - fl.BuildSystemConfig buildSystemConfig = const fl.BuildSystemConfig(), - }) async { - return fl.BuildResult(success: true); - }; + group('binary paths for --fs-layout=flutter-pi', () { + test('posix', () async { + buildSystem.buildFn = ( + fl.Target target, + fl.Environment environment, { + fl.BuildSystemConfig buildSystemConfig = const fl.BuildSystemConfig(), + }) async { + return fl.BuildResult(success: true); + }; - final bundle = await _runInTestContext( - () async => await appBuilder.buildBundle( - id: 'test-id', - host: FlutterpiHostPlatform.linuxRV64, - target: FlutterpiTargetPlatform.genericRiscv64, - buildInfo: fl.BuildInfo.debug, - fsLayout: FilesystemLayout.flutterPi, - forceBundleFlutterpi: false, - ), - ) as PrebuiltFlutterpiAppBundle; + final bundle = await _runInTestContext( + () async => await appBuilder.buildBundle( + id: 'test-id', + host: FlutterpiHostPlatform.linuxRV64, + target: FlutterpiTargetPlatform.genericRiscv64, + buildInfo: fl.BuildInfo.debug, + fsLayout: FilesystemLayout.flutterPi, + forceBundleFlutterpi: false, + ), + ) as PrebuiltFlutterpiAppBundle; - expect( - bundle.binaries.map( - (file) => - p.relative(file.path, from: 'build/flutter-pi/riscv64-generic'), - ), - unorderedEquals([ - 'flutter-pi', - 'libflutter_engine.so', - ]), - ); + expect( + bundle.binaries.map( + (file) => p.posix.relative( + file.path, + from: p.posix.join('build', 'flutter-pi', 'riscv64-generic'), + ), + ), + unorderedEquals([ + 'flutter-pi', + 'libflutter_engine.so', + ]), + ); + }); + + test('windows', () async { + fs = MemoryFileSystem.test(style: FileSystemStyle.windows); + + buildSystem.buildFn = ( + fl.Target target, + fl.Environment environment, { + fl.BuildSystemConfig buildSystemConfig = const fl.BuildSystemConfig(), + }) async { + return fl.BuildResult(success: true); + }; + + final bundle = await _runInTestContext( + () async => await appBuilder.buildBundle( + id: 'test-id', + host: FlutterpiHostPlatform.linuxRV64, + target: FlutterpiTargetPlatform.genericRiscv64, + buildInfo: fl.BuildInfo.debug, + fsLayout: FilesystemLayout.flutterPi, + forceBundleFlutterpi: false, + ), + ) as PrebuiltFlutterpiAppBundle; + + expect( + bundle.binaries.map( + (file) => p.windows.relative( + file.path, + from: p.windows.join('build', 'flutter-pi', 'riscv64-generic'), + ), + ), + unorderedEquals([ + 'flutter-pi', + 'libflutter_engine.so', + ]), + ); + }); }); - test('binary paths for --fs-layout=flutter-pi and include debug symbols', - () async { - buildSystem.buildFn = ( - fl.Target target, - fl.Environment environment, { - fl.BuildSystemConfig buildSystemConfig = const fl.BuildSystemConfig(), - }) async { - return fl.BuildResult(success: true); - }; + group('binary paths for --fs-layout=flutter-pi and include debug symbols', + () { + test('posix', () async { + buildSystem.buildFn = ( + fl.Target target, + fl.Environment environment, { + fl.BuildSystemConfig buildSystemConfig = const fl.BuildSystemConfig(), + }) async { + return fl.BuildResult(success: true); + }; - final bundle = await _runInTestContext( - () async => await appBuilder.buildBundle( - id: 'test-id', - host: FlutterpiHostPlatform.linuxRV64, - target: FlutterpiTargetPlatform.genericRiscv64, - buildInfo: fl.BuildInfo.debug, - fsLayout: FilesystemLayout.flutterPi, - includeDebugSymbols: true, - forceBundleFlutterpi: false, - ), - ) as PrebuiltFlutterpiAppBundle; + final bundle = await _runInTestContext( + () async => await appBuilder.buildBundle( + id: 'test-id', + host: FlutterpiHostPlatform.linuxRV64, + target: FlutterpiTargetPlatform.genericRiscv64, + buildInfo: fl.BuildInfo.debug, + fsLayout: FilesystemLayout.flutterPi, + includeDebugSymbols: true, + forceBundleFlutterpi: false, + ), + ) as PrebuiltFlutterpiAppBundle; - expect( - bundle.binaries.map( - (file) => - p.relative(file.path, from: 'build/flutter-pi/riscv64-generic'), - ), - unorderedEquals([ - 'flutter-pi', - 'libflutter_engine.dbgsyms', - 'libflutter_engine.so', - ]), - ); + expect( + bundle.binaries.map( + (file) => p.posix.relative( + file.path, + from: p.posix.join('build', 'flutter-pi', 'riscv64-generic'), + ), + ), + unorderedEquals([ + 'flutter-pi', + 'libflutter_engine.dbgsyms', + 'libflutter_engine.so', + ]), + ); + }); + + test('windows', () async { + fs = MemoryFileSystem.test(style: FileSystemStyle.windows); + + buildSystem.buildFn = ( + fl.Target target, + fl.Environment environment, { + fl.BuildSystemConfig buildSystemConfig = const fl.BuildSystemConfig(), + }) async { + return fl.BuildResult(success: true); + }; + + final bundle = await _runInTestContext( + () async => await appBuilder.buildBundle( + id: 'test-id', + host: FlutterpiHostPlatform.linuxRV64, + target: FlutterpiTargetPlatform.genericRiscv64, + buildInfo: fl.BuildInfo.debug, + fsLayout: FilesystemLayout.flutterPi, + includeDebugSymbols: true, + forceBundleFlutterpi: false, + ), + ) as PrebuiltFlutterpiAppBundle; + + expect( + bundle.binaries.map( + (file) => p.windows.relative( + file.path, + from: p.windows.join('build', 'flutter-pi', 'riscv64-generic'), + ), + ), + unorderedEquals([ + 'flutter-pi', + 'libflutter_engine.dbgsyms', + 'libflutter_engine.so', + ]), + ); + }); }); - test('binary paths for --fs-layout=meta-flutter', () async { - buildSystem.buildFn = ( - fl.Target target, - fl.Environment environment, { - fl.BuildSystemConfig buildSystemConfig = const fl.BuildSystemConfig(), - }) async { - return fl.BuildResult(success: true); - }; + group('binary paths for --fs-layout=meta-flutter', () { + test('posix', () async { + buildSystem.buildFn = ( + fl.Target target, + fl.Environment environment, { + fl.BuildSystemConfig buildSystemConfig = const fl.BuildSystemConfig(), + }) async { + return fl.BuildResult(success: true); + }; - final bundle = await _runInTestContext( - () async => await appBuilder.buildBundle( - id: 'test-id', - host: FlutterpiHostPlatform.linuxRV64, - target: FlutterpiTargetPlatform.genericRiscv64, - buildInfo: fl.BuildInfo.debug, - fsLayout: FilesystemLayout.metaFlutter, - forceBundleFlutterpi: false, - ), - ) as PrebuiltFlutterpiAppBundle; + final bundle = await _runInTestContext( + () async => await appBuilder.buildBundle( + id: 'test-id', + host: FlutterpiHostPlatform.linuxRV64, + target: FlutterpiTargetPlatform.genericRiscv64, + buildInfo: fl.BuildInfo.debug, + fsLayout: FilesystemLayout.metaFlutter, + forceBundleFlutterpi: false, + ), + ) as PrebuiltFlutterpiAppBundle; - expect( - bundle.binaries.map( - (file) => p.relative( - file.path, - from: 'build/flutter-pi/meta-flutter-riscv64-generic', + expect( + bundle.binaries.map( + (file) => p.posix.relative( + file.path, + from: p.posix + .join('build', 'flutter-pi', 'meta-flutter-riscv64-generic'), + ), ), - ), - unorderedEquals([ - 'lib/libflutter_engine.so', - ]), - ); + unorderedEquals([ + p.posix.join('lib', 'libflutter_engine.so'), + ]), + ); + }); + + test('windows', () async { + fs = MemoryFileSystem.test(style: FileSystemStyle.windows); + + buildSystem.buildFn = ( + fl.Target target, + fl.Environment environment, { + fl.BuildSystemConfig buildSystemConfig = const fl.BuildSystemConfig(), + }) async { + return fl.BuildResult(success: true); + }; + + final bundle = await _runInTestContext( + () async => await appBuilder.buildBundle( + id: 'test-id', + host: FlutterpiHostPlatform.linuxRV64, + target: FlutterpiTargetPlatform.genericRiscv64, + buildInfo: fl.BuildInfo.debug, + fsLayout: FilesystemLayout.metaFlutter, + forceBundleFlutterpi: false, + ), + ) as PrebuiltFlutterpiAppBundle; + + expect( + bundle.binaries.map( + (file) => p.windows.relative( + file.path, + from: p.windows + .join('build', 'flutter-pi', 'meta-flutter-riscv64-generic'), + ), + ), + unorderedEquals([ + p.windows.join('lib', 'libflutter_engine.so'), + ]), + ); + }); }); - test( + group( 'binary paths for --fs-layout=meta-flutter with force bundle flutterpi', - () async { - buildSystem.buildFn = ( - fl.Target target, - fl.Environment environment, { - fl.BuildSystemConfig buildSystemConfig = const fl.BuildSystemConfig(), - }) async { - return fl.BuildResult(success: true); - }; + () { + test('posix', () async { + buildSystem.buildFn = ( + fl.Target target, + fl.Environment environment, { + fl.BuildSystemConfig buildSystemConfig = const fl.BuildSystemConfig(), + }) async { + return fl.BuildResult(success: true); + }; - final bundle = await _runInTestContext( - () async => await appBuilder.buildBundle( - id: 'test-id', - host: FlutterpiHostPlatform.linuxRV64, - target: FlutterpiTargetPlatform.genericRiscv64, - buildInfo: fl.BuildInfo.debug, - fsLayout: FilesystemLayout.metaFlutter, - forceBundleFlutterpi: true, - ), - ) as PrebuiltFlutterpiAppBundle; + final bundle = await _runInTestContext( + () async => await appBuilder.buildBundle( + id: 'test-id', + host: FlutterpiHostPlatform.linuxRV64, + target: FlutterpiTargetPlatform.genericRiscv64, + buildInfo: fl.BuildInfo.debug, + fsLayout: FilesystemLayout.metaFlutter, + forceBundleFlutterpi: true, + ), + ) as PrebuiltFlutterpiAppBundle; - expect( - bundle.binaries.map( - (file) => p.relative( - file.path, - from: 'build/flutter-pi/meta-flutter-riscv64-generic', + expect( + bundle.binaries.map( + (file) => p.posix.relative( + file.path, + from: p.posix + .join('build', 'flutter-pi', 'meta-flutter-riscv64-generic'), + ), ), - ), - unorderedEquals([ - 'bin/flutter-pi', - 'lib/libflutter_engine.so', - ]), - ); + unorderedEquals([ + p.posix.join('bin', 'flutter-pi'), + p.posix.join('lib', 'libflutter_engine.so'), + ]), + ); + }); + + test('windows', () async { + fs = MemoryFileSystem.test(style: FileSystemStyle.windows); + + buildSystem.buildFn = ( + fl.Target target, + fl.Environment environment, { + fl.BuildSystemConfig buildSystemConfig = const fl.BuildSystemConfig(), + }) async { + return fl.BuildResult(success: true); + }; + + final bundle = await _runInTestContext( + () async => await appBuilder.buildBundle( + id: 'test-id', + host: FlutterpiHostPlatform.linuxRV64, + target: FlutterpiTargetPlatform.genericRiscv64, + buildInfo: fl.BuildInfo.debug, + fsLayout: FilesystemLayout.metaFlutter, + forceBundleFlutterpi: true, + ), + ) as PrebuiltFlutterpiAppBundle; + + expect( + bundle.binaries.map( + (file) => p.windows.relative( + file.path, + from: p.windows + .join('build', 'flutter-pi', 'meta-flutter-riscv64-generic'), + ), + ), + unorderedEquals([ + p.windows.join('bin', 'flutter-pi'), + p.windows.join('lib', 'libflutter_engine.so'), + ]), + ); + }); }); - test('binary paths for --fs-layout=meta-flutter with include debug symbols', - () async { - buildSystem.buildFn = ( - fl.Target target, - fl.Environment environment, { - fl.BuildSystemConfig buildSystemConfig = const fl.BuildSystemConfig(), - }) async { - return fl.BuildResult(success: true); - }; + group( + 'binary paths for --fs-layout=meta-flutter with include debug symbols', + () { + test('posix', () async { + buildSystem.buildFn = ( + fl.Target target, + fl.Environment environment, { + fl.BuildSystemConfig buildSystemConfig = const fl.BuildSystemConfig(), + }) async { + return fl.BuildResult(success: true); + }; - final bundle = await _runInTestContext( - () async => await appBuilder.buildBundle( - id: 'test-id', - host: FlutterpiHostPlatform.linuxRV64, - target: FlutterpiTargetPlatform.genericRiscv64, - buildInfo: fl.BuildInfo.debug, - fsLayout: FilesystemLayout.metaFlutter, - includeDebugSymbols: true, - forceBundleFlutterpi: false, - ), - ) as PrebuiltFlutterpiAppBundle; + final bundle = await _runInTestContext( + () async => await appBuilder.buildBundle( + id: 'test-id', + host: FlutterpiHostPlatform.linuxRV64, + target: FlutterpiTargetPlatform.genericRiscv64, + buildInfo: fl.BuildInfo.debug, + fsLayout: FilesystemLayout.metaFlutter, + includeDebugSymbols: true, + forceBundleFlutterpi: false, + ), + ) as PrebuiltFlutterpiAppBundle; - expect( - bundle.binaries.map( - (file) => p.relative( - file.path, - from: 'build/flutter-pi/meta-flutter-riscv64-generic', + expect( + bundle.binaries.map( + (file) => p.posix.relative( + file.path, + from: p.posix + .join('build', 'flutter-pi', 'meta-flutter-riscv64-generic'), + ), ), - ), - unorderedEquals([ - 'lib/libflutter_engine.dbgsyms', - 'lib/libflutter_engine.so', - ]), - ); + unorderedEquals([ + p.posix.join('lib', 'libflutter_engine.dbgsyms'), + p.posix.join('lib', 'libflutter_engine.so'), + ]), + ); + }); + + test('windows', () async { + fs = MemoryFileSystem.test(style: FileSystemStyle.windows); + + buildSystem.buildFn = ( + fl.Target target, + fl.Environment environment, { + fl.BuildSystemConfig buildSystemConfig = const fl.BuildSystemConfig(), + }) async { + return fl.BuildResult(success: true); + }; + + final bundle = await _runInTestContext( + () async => await appBuilder.buildBundle( + id: 'test-id', + host: FlutterpiHostPlatform.linuxRV64, + target: FlutterpiTargetPlatform.genericRiscv64, + buildInfo: fl.BuildInfo.debug, + fsLayout: FilesystemLayout.metaFlutter, + includeDebugSymbols: true, + forceBundleFlutterpi: false, + ), + ) as PrebuiltFlutterpiAppBundle; + + expect( + bundle.binaries.map( + (file) => p.windows.relative( + file.path, + from: p.windows + .join('build', 'flutter-pi', 'meta-flutter-riscv64-generic'), + ), + ), + unorderedEquals([ + p.windows.join('lib', 'libflutter_engine.dbgsyms'), + p.windows.join('lib', 'libflutter_engine.so'), + ]), + ); + }); }); }); }