Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

can not send a mock object to an isolate #249

Open
newbie-eng opened this issue Oct 14, 2024 · 0 comments
Open

can not send a mock object to an isolate #249

newbie-eng opened this issue Oct 14, 2024 · 0 comments

Comments

@newbie-eng
Copy link

newbie-eng commented Oct 14, 2024

Describe the bug
I have a piece of code that downloads some resources from internet and saves it local disk. Download operation is performed on a separate isolate. I wrote some tests and wanted to test the case when network drops while downloading. In order to do that, I used some mock objects. When I tried to send a mock object to isolate, I got an error. See below please.

In order to reproduce the problem, I created a brand new project. I am attaching the pubspec.yaml and source files.

To Reproduce
/pubspec.yaml


name: mock_in_isolate
description: A sample command-line application.
version: 1.0.0
# repository: https://github.com/my_org/my_repo

environment:
  sdk: ^3.3.1

# Add regular dependencies here.
dependencies:
  http: ^1.2.2
  mocktail: ^1.0.4
  # path: ^1.8.0

dev_dependencies:
  lints: ^4.0.0
  test: ^1.24.0

/bin/mock_in_isolate.dart

import 'package:mock_in_isolate/isolated_downloader.dart' as iso_down;
import 'package:mock_in_isolate/mock_in_isolate.dart' as mock_in_isolate;

void main(List<String> arguments) {
  print('Hello world: ${mock_in_isolate.calculate()}!');
  iso_down.IsolatedDownloader dwn = iso_down.IsolatedDownloader();
  dwn.startDownload();
}

/lib/isolated_downlaod.dart

import 'dart:isolate';

import 'package:http/http.dart' as im_http;
import 'package:mocktail/mocktail.dart' as im_mock;

class MockHttpClient extends im_mock.Mock implements im_http.Client {}

class MockStreamedResponse extends im_mock.Mock
    implements im_http.StreamedResponse {}

class FakeBaseRequest extends im_mock.Fake implements im_http.BaseRequest {}

Future<void> downloadInIsolate(SendPort senderPOrtOnIsolate) async {
  final receiverOnChild = ReceivePort();
  senderPOrtOnIsolate.send(receiverOnChild.sendPort);

  receiverOnChild.listen((Object? msg) async {
    if (msg is im_http.Client) {
      im_http.Client client = msg;
      im_http.Response response = await client.get(Uri.http('google.com'));
      senderPOrtOnIsolate.send(response.statusCode);
    }
  });
}

class IsolatedDownloader {
  final receiverPortOnMain = ReceivePort();
  SendPort? sendPortOnMain;
  Isolate? downloadIsolate;

  Future<void> startDownload() async {
    if (downloadIsolate != null) return;

    MockHttpClient client = MockHttpClient();
    im_http.StreamedResponse streamedResponse = MockStreamedResponse();

    im_mock.registerFallbackValue(FakeBaseRequest());
    im_mock
        .when(() => client.send(im_mock.any()))
        .thenAnswer((invocation) async => streamedResponse);

    im_mock.when(() => streamedResponse.statusCode).thenReturn(
        await Future<int>.delayed(const Duration(seconds: 2), () => 502));

    downloadIsolate = await Isolate.spawn<SendPort>(
        downloadInIsolate, receiverPortOnMain.sendPort);

    receiverPortOnMain.listen((Object? msgFromIsolate) async {
      print('Message from Child: $msgFromIsolate');
      if (msgFromIsolate is int) {
        print('response.statusCode: $msgFromIsolate');
        dispose();
      } else if (msgFromIsolate is SendPort) {
        sendPortOnMain = msgFromIsolate;
        sendPortOnMain?.send(client);
      }
    });
  }

  void dispose() {
    receiverPortOnMain.close();
    downloadIsolate?.kill();
    downloadIsolate = null;
  }
}

Future<void> main() async {
  final id = IsolatedDownloader();
  await id.startDownload();
}
  1. cd /tmp
  2. dart create mock_in_isolate
  3. cd mock_in_isolate
  4. dart pub add http
  5. dart pub add mocktail
  6. copy above files into proper folders
  7. dart run

Expected behavior
I should be able to send a mock object to an isolate (hopefully)

Screenshots
Not a GUI app, so no need for screenshots I guess.

**Logs **
dart analyze
Analyzing mock_in_isolate... 0.6s
No issues found!

flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.19.3, on Ubuntu 22.04.5 LTS 6.8.0-45-generic, locale
en_US.UTF-8)
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✓] Chrome - develop for the web
[✓] Linux toolchain - develop for Linux desktop
[✓] Android Studio (version 2022.3)
[✓] VS Code (version 1.94.2)
[✓] Connected device (2 available)
[✓] Network resources

Additional context
output of the program when I run it by dart run

building package executable... 
Built mock_in_isolate:mock_in_isolate.
Hello world: 42!
Message from Child: SendPort
Unhandled exception:
Invalid argument(s): Illegal argument in isolate message: object is unsendable - Library:'dart:isolate' Class: _ReceivePortImpl@1026248 (see restrictions listed at `SendPort.send()` documentation for more information)
 <- receiverPortOnMain in Instance of 'IsolatedDownloader' (from package:mock_in_isolate/isolated_downloader.dart)
 <- Context num_variables: 3 <- Closure: (Invocation) => Future<StreamedResponse> (from package:mock_in_isolate/isolated_downloader.dart)
 <- Instance of 'Expectation<Future<StreamedResponse>>' (from package:mocktail/src/mocktail.dart)
 <- _List len:3 (from dart:core)
 <- Instance(length:1) of '_GrowableList' (from dart:core)
 <- Instance of 'MockHttpClient' (from package:mock_in_isolate/isolated_downloader.dart)

#0      _SendPort._sendInternal (dart:isolate-patch/isolate_patch.dart:250:43)
#1      _SendPort.send (dart:isolate-patch/isolate_patch.dart:231:5)
#2      IsolatedDownloader.startDownload.<anonymous closure> (package:mock_in_isolate/isolated_downloader.dart:55:25)
#3      _RootZone.runUnaryGuarded (dart:async/zone.dart:1594:10)
#4      _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:339:11)
#5      _BufferingStreamSubscription._add (dart:async/stream_impl.dart:271:7)
#6      _SyncStreamControllerDispatch._sendData (dart:async/stream_controller.dart:784:19)
#7      _StreamController._add (dart:async/stream_controller.dart:658:7)
#8      _StreamController.add (dart:async/stream_controller.dart:606:5)
#9      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant