Skip to content
This repository has been archived by the owner on Sep 22, 2024. It is now read-only.

Commit

Permalink
Support working on background #7
Browse files Browse the repository at this point in the history
complete implemention of isolatedreceiver & isolatedsender
  • Loading branch information
iamalper committed Dec 9, 2023
1 parent 75c7486 commit f573335
Show file tree
Hide file tree
Showing 10 changed files with 404 additions and 137 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ jobs:
- run: flutter config --enable-linux-desktop

- name: Running flutter tests
run: xvfb-run flutter test integration_test
run: xvfb-run flutter test integration_test/app_test.dart

- name: Start linux build
run: flutter build linux
Expand Down Expand Up @@ -166,7 +166,7 @@ jobs:
- run: flutter config --enable-windows-desktop

- name: Running flutter tests
run: flutter test integration_test
run: flutter test integration_test\app_test.dart

- name: Build for windows
run: flutter build windows
Expand Down
55 changes: 29 additions & 26 deletions integration_test/app_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ void main() {
var sendingFiles = <File>[];
var platformFiles = <PlatformFile>[];
late Directory subdir;
late Sender sender;
setUpAll(() async {
sender = Sender();
final tempDir = await getTemporaryDirectory();
subdir = tempDir.createTempSync("sending");
final testImageData = await rootBundle.load("assets/test_image.png");
Expand Down Expand Up @@ -47,28 +45,32 @@ void main() {
});
group('IO tests', () {
var downloadedFiles = <DbFile>[];
final recieve = Receiver(
saveToTemp: true,
useDb: false,
onAllFilesDownloaded: (files) => downloadedFiles = files);

testWidgets('Discover, send and receive files', (_) async {
final code = await recieve.listen();
var allDevices = <Device>[];
while (allDevices.isEmpty) {
allDevices = await Discover.discover();
}
final devices = allDevices.where((device) => device.code == code);
expect(devices, isNotEmpty, reason: "Expected to discover itself");
await sender.send(devices.first, platformFiles, useDb: false);
for (var i = 0; i < sendingFiles.length; i++) {
final gidenDosya = sendingFiles[i];
final gelenDosya = File(downloadedFiles[i].path);
expect(
gidenDosya.readAsBytesSync(), equals(gelenDosya.readAsBytesSync()),
reason: "All sent files expected to has same content as originals");
}
});
Receiver? receiver;
testWidgets(
'Discover, send and receive files',
(_) async {
receiver = Receiver(
saveToTemp: true,
useDb: false,
onAllFilesDownloaded: (files) => downloadedFiles = files);
final code = await receiver!.listen();
var allDevices = <Device>[];
while (allDevices.isEmpty) {
allDevices = await Discover.discover();
}
final devices = allDevices.where((device) => device.code == code);
expect(devices, isNotEmpty, reason: "Expected to discover itself");
await Sender().send(devices.first, platformFiles, useDb: false);
for (var i = 0; i < sendingFiles.length; i++) {
final gidenDosya = sendingFiles[i];
final gelenDosya = File(downloadedFiles[i].path);
expect(gidenDosya.readAsBytesSync(),
equals(gelenDosya.readAsBytesSync()),
reason:
"All sent files expected to has same content as originals");
}
},
);

tearDown(() {
for (var file in downloadedFiles) {
Expand All @@ -77,22 +79,23 @@ void main() {
downloadedFiles = [];
});
tearDownAll(() async {
await recieve.stopListening();
await receiver?.stopListening();
});
});

group('Error handling', () {
testWidgets("Handle no_receiver error", (_) async {
final rand1 = Random().nextInt(30);
final rand2 = Random().nextInt(30);
final sendFuture = sender.send(
final sendFuture = Sender().send(
Device(adress: "192.168.$rand1.$rand2", code: 1000, port: 2326),
platformFiles,
useDb: false);
await expectLater(sendFuture, throwsA(isA<FileDropException>()));
}, retry: 2);
testWidgets("Handle connection lost while reciving", (_) async {
FileDropException? throwedError;
final sender = Sender();
final code = await Receiver(
onDownloadError: (error) => throwedError = error,
useDb: false,
Expand Down
1 change: 0 additions & 1 deletion integration_test/fake_path_provider.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'dart:io';

import 'package:path/path.dart' as p;
import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
Expand Down
77 changes: 77 additions & 0 deletions integration_test/mobile_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:path/path.dart' as path;
import 'package:file_picker/file_picker.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:path_provider/path_provider.dart';
import 'package:weepy/classes/discover.dart';
import 'package:weepy/classes/workers/worker_interface.dart';
import 'package:weepy/models.dart';

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
var sendingFiles = <File>[];
var platformFiles = <PlatformFile>[];
var downloadedFiles = <DbFile>[];
IsolatedReceiver? receiver;
late Directory subdir;
setUpAll(() async {
final tempDir = await getTemporaryDirectory();
subdir = tempDir.createTempSync("sending");
final testImageData = await rootBundle.load("assets/test_image.png");
final testImageFile = File(path.join(subdir.path, "test_image.png"));
testImageFile.writeAsBytesSync(testImageData.buffer.asInt8List(),
mode: FileMode.writeOnly);
sendingFiles = [
File(path.join(subdir.path, "deneme 1.txt")),
File(path.join(subdir.path, "deneme 2.txt")),
testImageFile
];
for (var gidenDosya in sendingFiles) {
if (path.extension(gidenDosya.path) == ".txt") {
gidenDosya.writeAsStringSync("deneme gövdesi",
mode: FileMode.writeOnly);
}
}
platformFiles = List.generate(
sendingFiles.length,
(index) => PlatformFile(
//readStream: sendingFiles[index].openRead(),
size: sendingFiles[index].lengthSync(),
name: path.basename(sendingFiles[index].path),
path: sendingFiles[index].path));
});
testWidgets("Test IsolatedReceiver & IsolatedSender", (_) async {
receiver = IsolatedReceiver(
saveToTemp: true,
useDb: false,
onAllFilesDownloaded: (files) => downloadedFiles = files,
onDownloadError: (error) => throw error,
progressNotification: false);
final code = await receiver!.listen();
var allDevices = <Device>[];
while (allDevices.isEmpty) {
allDevices = await Discover.discover();
}
final devices = allDevices.where((device) => device.code == code);
expect(devices, isNotEmpty, reason: "Expected to discover itself");
await IsolatedSender(progressNotification: false)
.send(devices.first, platformFiles, useDb: false);
for (var i = 0; i < sendingFiles.length; i++) {
final gidenDosya = sendingFiles[i];
final gelenDosya = File(downloadedFiles[i].path);
expect(gidenDosya.readAsBytesSync(), equals(gelenDosya.readAsBytesSync()),
reason: "All sent files expected to has same content as originals");
}
});
tearDown(() {
for (var file in downloadedFiles) {
File(file.path).deleteSync();
}
downloadedFiles = [];
});
tearDownAll(() async {
await receiver?.stopListening();
});
}
47 changes: 29 additions & 18 deletions lib/classes/database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,40 @@ class DatabaseManager {
databaseFactory = databaseFactoryFfiNoIsolate;
}
_initalised = true;
return openDatabase(
"files.db",
version: 1,
onCreate: (db, version) async {
await db.execute(
"create table downloaded (ID integer primary key autoincrement, name text not null, path text not null, type text, time int not null)");
await db.execute(
"create table uploaded (ID integer primary key autoincrement, name text not null, path text not null, type text, time int not null)");
},
);
return openDatabase("files.db", version: 2, onCreate: (db, version) async {
await db.execute(
"create table downloaded (ID integer primary key autoincrement, name text not null, path text not null, type text, timeEpoch int not null)");
await db.execute(
"create table uploaded (ID integer primary key autoincrement, name text not null, path text not null, type text, timeEpoch int not null)");
}, onUpgrade: (db, oldVersion, newVersion) async {
if (oldVersion == 1 && newVersion == 2) {
await db
.execute("alter table downloaded rename column time to timeEpoch");
await db
.execute("alter table uploaded rename column time to timeEpoch");
} else {
throw UnsupportedError("Unsupported db version");
}
});
}

///Insert a uploaded or downloaded file information
Future<void> insert(DbFile file) async {
final db = await _db;
switch (file.fileStatus) {
case DbFileStatus.upload:
await db.insert("uploaded",
{"name": file.name, "time": file.timeEpoch, "path": file.path});
await db.insert("uploaded", {
"name": file.name,
"timeEpoch": file.timeEpoch,
"path": file.path
});
break;
case DbFileStatus.download:
await db.insert("downloaded",
{"name": file.name, "time": file.timeEpoch, "path": file.path});
await db.insert("downloaded", {
"name": file.name,
"timeEpoch": file.timeEpoch,
"path": file.path
});
break;
}
}
Expand All @@ -58,12 +69,12 @@ class DatabaseManager {
///Get all downloaded/uploaded file information as list.
Future<List<DbFile>> get files async {
final db = await _db;
final uploadedMaps =
await db.query("uploaded", columns: ["name", "type", "time", "path"]);
final uploadedMaps = await db
.query("uploaded", columns: ["name", "type", "timeEpoch", "path"]);
final uploadedFiles =
uploadedMaps.map((e) => DbFile.uploadedFromMap(e)).toList();
final downloadedMaps =
await db.query("downloaded", columns: ["name", "type", "time", "path"]);
final downloadedMaps = await db
.query("downloaded", columns: ["name", "type", "timeEpoch", "path"]);
final downloadedFiles =
downloadedMaps.map((e) => DbFile.downloadedFromMap(e)).toList();
List<DbFile> files = [];
Expand Down
56 changes: 33 additions & 23 deletions lib/classes/receiver.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ class Receiver {
final _files = <DbFile>[];
late MediaStore _ms;
final _tempDir = getTemporaryDirectory();
final int _code;
final int code;
HttpServer? _server;
bool _isBusy = false;
int get code => _code;

///If [useDb] is `true`, file informations will be saved to sqflite database.
///Don't needed to open the database manually.
Expand Down Expand Up @@ -63,7 +62,7 @@ class Receiver {
///[onDownloadError] will be called when error happened while saving file.
///
///When [onDownloadError] called, no other callback will be called,
///no exception thrown and server will wait new connection.
///no exception thrown and server will wait for new connection.
///
///See [FileDropException]
final void Function(FileDropException error)? onDownloadError;
Expand All @@ -84,16 +83,22 @@ class Receiver {
this.onAllFilesDownloaded,
this.onDownloadError,
int? code,
}) : _code = code ?? Random().nextInt(8888) + 1111;
}) : code = code ?? Random().nextInt(8888) + 1111;

///Get storage permission for Android and IOS
///
///For other platforms always returns [true]
Future<bool> checkPermission() async {
//These platforms needs storage permissions (only tested on Android)
if (Platform.isAndroid || Platform.isIOS) {
//These platforms needs storage permissions (only tested on Android)
final perm = await Permission.storage.request();
return perm.isGranted;
final newPermission =
await _ms.requestForAccess(initialRelativePath: null);
if (newPermission == null) {
final perm = await Permission.storage.request();
return perm.isGranted;
} else {
return true;
}
} else {
return true;
}
Expand All @@ -104,9 +109,9 @@ class Receiver {
///sends `400 Bad request` as response
///
///Returns the code generated for discovery. Other devices should select this code for
///connecting to this devices
///connecting to [Receiver]
Future<int> listen() async {
if (!saveToTemp && false) {
if (!saveToTemp) {
final permissionStatus = await checkPermission();
if (!permissionStatus) {
throw NoStoragePermissionException();
Expand All @@ -133,10 +138,10 @@ class Receiver {
}
}
if (_server != null) {
log("Listening for new file with port: ${_server!.port}, code: $_code",
log("Listening for new file with port: ${_server!.port}, code: $code",
name: "Receive");
}
return _code;
return code;
}

Future<Response> _requestMethod(Request request) async {
Expand All @@ -147,10 +152,10 @@ class Receiver {
}
if (request.method == "GET") {
//Response to discovery requests
log("Discovery request recieved, returned code $_code",
log("Discovery request recieved, returned code $code",
name: "Receive server");
return Response.ok(
jsonEncode({"message": Constants.meeting, "code": _code}));
jsonEncode({"message": Constants.meeting, "code": code}));
} else if (request.method == "POST") {
//Reciving file
log("Reciving file...", name: "Receive server");
Expand Down Expand Up @@ -224,8 +229,8 @@ class Receiver {
}
log("Recived file(s) $_files", name: "Receive server");
return Response.ok(null);
} catch (_) {
log("Download error", name: "Receiver");
} catch (e) {
log("Download error", name: "Receiver", error: e);
onDownloadError?.call(ConnectionLostException());
return Response.badRequest();
} finally {
Expand Down Expand Up @@ -262,14 +267,19 @@ class Receiver {
Map<String, dynamic> get map => {
"useDb": useDb,
"saveToTemp": saveToTemp,
if (port != null) "port": port,
"code": _code,
if (port != null) "port": port!,
"code": code,
};

Receiver.fromMap(Map<String, dynamic> map)
: this(
useDb: map["useDb"],
saveToTemp: map["saveToTemp"],
port: map["port"],
code: map["code"]);
Receiver.fromMap(Map<String, dynamic> map,
{this.onAllFilesDownloaded,
this.downloadAnimC,
this.onDownloadError,
this.onDownloadStart,
this.onFileDownloaded,
this.onDownloadUpdatePercent})
: useDb = map["useDb"],
saveToTemp = map["saveToTemp"],
port = map["port"],
code = map["code"];
}
Loading

0 comments on commit f573335

Please sign in to comment.