[verified] Publish ReelBox source under GPLv3
This commit is contained in:
commit
db2b505ca3
445 files changed
+105274
No files matched your search
@@ -0,0 +1,269 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:reelbox/media/native_player.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
const channel = MethodChannel('org.reelbox/media');
|
||||
setUp(() => debugDefaultTargetPlatformOverride = TargetPlatform.android);
|
||||
tearDown(() {
|
||||
debugDefaultTargetPlatformOverride = null;
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(channel, null);
|
||||
});
|
||||
|
||||
test('awaits exit and sends exact URL, title and checkpoint', () async {
|
||||
final exit = Completer<Map<String, Object>>();
|
||||
MethodCall? received;
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(channel, (call) {
|
||||
received = call;
|
||||
return exit.future;
|
||||
});
|
||||
var returned = false;
|
||||
const url = 'https://media.example.org/a%2Fb.m3u8?sig=a%2Bz&x=1';
|
||||
final pending =
|
||||
NativePlayerBridge.open(
|
||||
url: url,
|
||||
title: 'عنوان — episode',
|
||||
position: const Duration(milliseconds: 12345),
|
||||
autoplay: false,
|
||||
externalPackage: 'vlc',
|
||||
orientation: 'landscape',
|
||||
videoFit: 'cover',
|
||||
speed: 1.25,
|
||||
subtitleEnabled: false,
|
||||
).then((result) {
|
||||
returned = true;
|
||||
return result;
|
||||
});
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
expect(returned, isFalse);
|
||||
expect(received!.method, 'openNativePlayer');
|
||||
expect(received!.arguments, containsPair('url', url));
|
||||
expect(received!.arguments, containsPair('positionMs', 12345));
|
||||
expect(received!.arguments, containsPair('autoplay', false));
|
||||
expect(received!.arguments, containsPair('externalPackage', 'vlc'));
|
||||
expect(received!.arguments, containsPair('subtitleEnabled', false));
|
||||
expect(received!.arguments, containsPair('orientation', 'landscape'));
|
||||
expect(received!.arguments, containsPair('videoFit', 'cover'));
|
||||
expect(received!.arguments, containsPair('speed', 1.25));
|
||||
exit.complete({
|
||||
'positionMs': 42000,
|
||||
'durationMs': 90000,
|
||||
'opened': true,
|
||||
'completed': false,
|
||||
});
|
||||
final result = await pending;
|
||||
expect(result.position.inMilliseconds, 42000);
|
||||
expect(result.duration.inMilliseconds, 90000);
|
||||
expect(result.opened, isTrue);
|
||||
expect(result.completed, isFalse);
|
||||
});
|
||||
|
||||
test(
|
||||
'local and loopback sources and optional subtitles are unchanged',
|
||||
() async {
|
||||
final calls = <MethodCall>[];
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(channel, (call) async {
|
||||
calls.add(call);
|
||||
return {
|
||||
'positionMs': 0,
|
||||
'durationMs': 0,
|
||||
'opened': false,
|
||||
'completed': false,
|
||||
};
|
||||
});
|
||||
for (final url in [
|
||||
'/data/user/0/org.reelbox.app/files/فیلم.mkv',
|
||||
'http://127.0.0.1:49123/media/lease/index.m3u8?token=a%2Bz',
|
||||
]) {
|
||||
final result = await NativePlayerBridge.open(
|
||||
url: url,
|
||||
title: 'Film',
|
||||
subtitleUrl: 'https://media.example.org/sub.vtt?token=a%2Fz',
|
||||
hardwareAcceleration: false,
|
||||
pipEnabled: false,
|
||||
);
|
||||
expect(calls.last.arguments, containsPair('url', url));
|
||||
expect(
|
||||
calls.last.arguments,
|
||||
containsPair(
|
||||
'subtitleUrl',
|
||||
'https://media.example.org/sub.vtt?token=a%2Fz',
|
||||
),
|
||||
);
|
||||
expect(
|
||||
calls.last.arguments,
|
||||
containsPair('hardwareAcceleration', false),
|
||||
);
|
||||
expect(calls.last.arguments, containsPair('pipEnabled', false));
|
||||
expect(result.opened, isFalse);
|
||||
}
|
||||
expect(calls, hasLength(2));
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'rejects malformed exit maps instead of manufacturing progress',
|
||||
() async {
|
||||
for (final response in [
|
||||
null,
|
||||
{
|
||||
'positionMs': -1,
|
||||
'durationMs': 10,
|
||||
'opened': true,
|
||||
'completed': false,
|
||||
},
|
||||
{
|
||||
'positionMs': double.nan,
|
||||
'durationMs': 10,
|
||||
'opened': true,
|
||||
'completed': false,
|
||||
},
|
||||
{
|
||||
'positionMs': 0x7fffffffffffffff,
|
||||
'durationMs': 0,
|
||||
'opened': true,
|
||||
'completed': false,
|
||||
},
|
||||
{
|
||||
'positionMs': 1.5,
|
||||
'durationMs': 10,
|
||||
'opened': true,
|
||||
'completed': false,
|
||||
},
|
||||
{
|
||||
'positionMs': 10,
|
||||
'durationMs': -1,
|
||||
'opened': true,
|
||||
'completed': false,
|
||||
},
|
||||
{
|
||||
'positionMs': 10,
|
||||
'durationMs': 20,
|
||||
'opened': 'true',
|
||||
'completed': true,
|
||||
},
|
||||
]) {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(channel, (_) async => response);
|
||||
await expectLater(
|
||||
NativePlayerBridge.open(
|
||||
url: 'https://media.example.org/video.mp4',
|
||||
title: 'Film',
|
||||
),
|
||||
throwsFormatException,
|
||||
);
|
||||
}
|
||||
final bounded = NativePlaybackResult.fromMap({
|
||||
'positionMs': 1000,
|
||||
'durationMs': 100,
|
||||
'opened': false,
|
||||
'completed': true,
|
||||
});
|
||||
expect(bounded.position.inMilliseconds, 100);
|
||||
expect(bounded.completed, isFalse);
|
||||
},
|
||||
);
|
||||
|
||||
test('completion requires a credible near-end checkpoint, not just EOF', () {
|
||||
for (final sample in <Map<String, Object>>[
|
||||
{'positionMs': 180000, 'durationMs': 3600000},
|
||||
{'positionMs': 180000, 'durationMs': 0},
|
||||
{'positionMs': 0, 'durationMs': 3600000},
|
||||
{'positionMs': 3600001, 'durationMs': 3600000},
|
||||
{'positionMs': 3597000, 'durationMs': 3600000},
|
||||
{'positionMs': 3600000, 'durationMs': 3600000, 'opened': false},
|
||||
{'positionMs': 3600000, 'durationMs': 3600000, 'completed': false},
|
||||
]) {
|
||||
final result = NativePlaybackResult.fromMap({
|
||||
'opened': true,
|
||||
'completed': true,
|
||||
...sample,
|
||||
});
|
||||
expect(result.completed, isFalse, reason: sample.toString());
|
||||
}
|
||||
for (final position in [3599500, 3600000]) {
|
||||
final result = NativePlaybackResult.fromMap({
|
||||
'positionMs': position,
|
||||
'durationMs': 3600000,
|
||||
'opened': true,
|
||||
'completed': true,
|
||||
});
|
||||
expect(result.completed, isTrue);
|
||||
}
|
||||
});
|
||||
|
||||
test('platform failures and busy results propagate without retry', () async {
|
||||
var calls = 0;
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(channel, (_) async {
|
||||
calls++;
|
||||
throw PlatformException(code: 'NATIVE_PLAYER_BUSY');
|
||||
});
|
||||
await expectLater(
|
||||
NativePlayerBridge.open(
|
||||
url: 'https://media.example.org/video.mp4',
|
||||
title: 'Film',
|
||||
),
|
||||
throwsA(isA<PlatformException>()),
|
||||
);
|
||||
expect(calls, 1);
|
||||
});
|
||||
|
||||
test('unsupported platforms do not call Android', () async {
|
||||
debugDefaultTargetPlatformOverride = TargetPlatform.linux;
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(
|
||||
channel,
|
||||
(_) async => fail('Android was invoked'),
|
||||
);
|
||||
await expectLater(
|
||||
NativePlayerBridge.open(
|
||||
url: 'https://media.example.org/video.mp4',
|
||||
title: 'Film',
|
||||
),
|
||||
throwsUnsupportedError,
|
||||
);
|
||||
});
|
||||
|
||||
test('invalid launch preferences fail before starting an activity', () async {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(channel, (_) async {
|
||||
fail('Invalid preferences reached Android');
|
||||
});
|
||||
await expectLater(
|
||||
NativePlayerBridge.open(
|
||||
url: 'https://media.example.org/video.mp4',
|
||||
title: 'Film',
|
||||
speed: double.nan,
|
||||
),
|
||||
throwsArgumentError,
|
||||
);
|
||||
await expectLater(
|
||||
NativePlayerBridge.open(
|
||||
url: 'https://media.example.org/video.mp4',
|
||||
title: 'Film',
|
||||
orientation: 'invalid',
|
||||
),
|
||||
throwsArgumentError,
|
||||
);
|
||||
await expectLater(
|
||||
NativePlayerBridge.open(
|
||||
url: 'https://media.example.org/video.mp4',
|
||||
title: 'Film',
|
||||
videoFit: 'invalid',
|
||||
),
|
||||
throwsArgumentError,
|
||||
);
|
||||
await expectLater(
|
||||
NativePlayerBridge.open(url: '', title: 'Film'),
|
||||
throwsArgumentError,
|
||||
);
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user