revert: remove download size estimates

This commit is contained in:
zarzet
2026-09-06 22:17:23 +07:00
parent 174852b056
commit ed51b5d6ea
17 changed files with 10 additions and 806 deletions
-209
View File
@@ -1,209 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:spotiflac_android/l10n/app_localizations.dart';
import 'package:spotiflac_android/models/settings.dart';
import 'package:spotiflac_android/models/track.dart';
import 'package:spotiflac_android/providers/extension_provider.dart';
import 'package:spotiflac_android/providers/settings_provider.dart';
import 'package:spotiflac_android/widgets/download_service_picker.dart';
class _PickerExtensions extends ExtensionNotifier {
@override
ExtensionState build() => const ExtensionState(
extensions: [
Extension(
id: 'provider-a',
name: 'provider-a',
displayName: 'Audio A',
version: '1.0.0',
description: '',
enabled: true,
status: 'loaded',
hasDownloadProvider: true,
qualityOptions: [
QualityOption(id: 'LOSSLESS', label: 'Lossless'),
QualityOption(id: 'HI_RES', label: 'Hi-Res'),
QualityOption(id: 'HI_RES_LOSSLESS', label: 'Hi-Res'),
],
),
Extension(
id: 'provider-b',
name: 'provider-b',
displayName: 'Audio B',
version: '1.0.0',
description: '',
enabled: true,
status: 'loaded',
hasDownloadProvider: true,
qualityOptions: [
QualityOption(id: 'opus_256', label: 'Opus 256kbps'),
QualityOption(id: 'custom', label: 'Custom'),
],
),
],
);
}
class _PickerSettings extends SettingsNotifier {
final AppSettings _settings;
_PickerSettings(this._settings);
@override
AppSettings build() => _settings;
}
Future<void> _openPicker(
WidgetTester tester, {
Duration? duration,
String? audioQuality = '16bit/44.1kHz',
String? source = 'provider-a',
AppSettings settings = const AppSettings(),
Locale locale = const Locale('en'),
double textScale = 1,
void Function(String, String)? onSelect,
}) async {
await tester.pumpWidget(
ProviderScope(
overrides: [
extensionProvider.overrideWith(_PickerExtensions.new),
settingsProvider.overrideWith(() => _PickerSettings(settings)),
],
child: MaterialApp(
locale: locale,
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
builder: (context, child) => MediaQuery(
data: MediaQuery.of(
context,
).copyWith(textScaler: TextScaler.linear(textScale)),
child: child!,
),
home: Scaffold(
body: Builder(
builder: (context) => TextButton(
onPressed: () => DownloadServicePicker.show(
context,
trackName: 'Selected tracks',
tracks: [
Track(
id: 'sample',
name: 'Sample',
artistName: 'Artist',
albumName: 'Album',
duration: duration?.inSeconds ?? 0,
source: source,
audioQuality: audioQuality,
),
],
onSelect: onSelect ?? (_, _) {},
),
child: const Text('Open'),
),
),
),
),
),
);
await tester.tap(find.text('Open'));
await tester.pumpAndSettle();
}
void main() {
testWidgets('estimates change with the provider and selection stays intact', (
tester,
) async {
(String, String)? selected;
await _openPicker(
tester,
duration: const Duration(minutes: 4),
onSelect: (quality, service) => selected = (quality, service),
);
expect(find.text('≈ 26.2 MB'), findsNWidgets(3));
expect(find.textContaining('MB'), findsNothing);
await tester.tap(find.text('Audio B'));
await tester.pumpAndSettle();
expect(find.text('≈ 7.3 MB'), findsOneWidget);
expect(find.text('Size estimate unavailable'), findsOneWidget);
expect(find.text('≈ 26.2 MB'), findsNothing);
expect(find.textContaining('if 24-bit'), findsNothing);
await tester.tap(find.text('Opus 256kbps'));
await tester.pumpAndSettle();
expect(selected, ('opus_256', 'provider-b'));
expect(find.byType(DownloadServicePicker), findsNothing);
});
testWidgets(
'missing duration stays unknown and downloads remain selectable',
(tester) async {
await _openPicker(tester, locale: const Locale('id'));
expect(find.text('Estimasi ukuran belum tersedia'), findsNWidgets(3));
expect(find.textContaining(''), findsNothing);
await tester.tap(find.text('FLAC Lossless'));
await tester.pumpAndSettle();
expect(find.byType(DownloadServicePicker), findsNothing);
},
);
testWidgets('metadata caps both hi-res tiers to the available sample rate', (
tester,
) async {
await _openPicker(
tester,
duration: const Duration(minutes: 4),
audioQuality: '24bit/96kHz',
locale: const Locale('id'),
);
expect(find.text('≈ 85.7 MB'), findsNWidgets(2));
expect(find.text('≈ 26.2 MB'), findsOneWidget);
expect(find.textContaining('171.4'), findsNothing);
});
testWidgets('foreign or incomplete track quality stays unavailable', (
tester,
) async {
await _openPicker(
tester,
duration: const Duration(minutes: 4),
source: 'provider-b',
);
expect(find.text('Size estimate unavailable'), findsNWidgets(3));
await tester.pumpWidget(const SizedBox.shrink());
await tester.pumpAndSettle();
await _openPicker(
tester,
duration: const Duration(minutes: 4),
audioQuality: '24bit',
);
expect(find.text('Size estimate unavailable'), findsNWidgets(3));
});
testWidgets(
'conversion estimate is separate and fits narrow, enlarged text',
(tester) async {
tester.view.physicalSize = const Size(320, 720);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
await _openPicker(
tester,
duration: const Duration(minutes: 4),
textScale: 1.8,
settings: const AppSettings(
autoConvertDownloads: true,
autoConvertFormat: 'opus',
autoConvertBitrate: '256k',
),
);
expect(find.text('≈ 26.2 MB'), findsNWidgets(3));
final conversionNote = find.textContaining(
'After conversion to OPUS: ≈ 7.3 MB',
);
expect(conversionNote, findsOneWidget);
await tester.ensureVisible(conversionNote);
await tester.pumpAndSettle();
expect(tester.takeException(), isNull);
},
);
}
-211
View File
@@ -1,211 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:spotiflac_android/models/track.dart';
import 'package:spotiflac_android/providers/extension_provider.dart';
import 'package:spotiflac_android/utils/download_size_estimate.dart';
Track _track(
int duration, {
String? itemType,
String? audioQuality,
String? source = 'provider-a',
}) => Track(
id: 'track',
name: 'Track',
artistName: 'Artist',
albumName: 'Album',
duration: duration,
itemType: itemType,
audioQuality: audioQuality,
source: source,
);
void main() {
const duration = Duration(minutes: 4);
test('track metadata caps quality and sums each recording separately', () {
final cd = _track(240, audioQuality: '16bit/44.1kHz');
final hiRes = _track(120, audioQuality: '24-bit/96000Hz');
for (final id in ['LOSSLESS', 'HI_RES', 'HI_RES_LOSSLESS']) {
expect(
estimateDownloadSize(
duration: duration,
quality: QualityOption(id: id, label: ''),
tracks: [cd],
providerId: 'provider-a',
)!.bytes,
27518400,
);
}
final tracks = [cd, hiRes];
expect(
estimateDownloadSize(
duration: totalDownloadDuration(tracks),
quality: QualityOption.fromJson({
'id': 'best',
'label': 'Best',
'kind': 'lossless',
}),
tracks: tracks,
providerId: 'provider-a',
)!.bytes,
27518400 + 44928000,
);
});
test(
'lossless metadata must be complete and belong to selected provider',
() {
for (final track in [
_track(240),
_track(240, audioQuality: '24bit'),
_track(240, audioQuality: '16bit/44.1kHz', source: 'provider-b'),
]) {
expect(
estimateDownloadSize(
duration: duration,
quality: const QualityOption(id: 'LOSSLESS', label: ''),
tracks: [track],
providerId: 'provider-a',
),
isNull,
);
}
},
);
test('four minutes at 256 kbps is 7,680,000 bytes before overhead', () {
for (final quality in [
const QualityOption(id: 'opus_256', label: 'Opus'),
const QualityOption(id: 'custom', label: 'Opus 256kbps'),
QualityOption.fromJson({
'id': 'custom',
'label': 'Audio',
'sizeEstimate': {'bitrateKbps': 256},
}),
]) {
final estimate = estimateDownloadSize(
duration: duration,
quality: quality,
)!;
expect(estimate.bytes, 7680000);
}
});
test('lossless estimates compare each tier at its own quality', () {
const cd = QualityOption(id: 'LOSSLESS', label: 'Lossless');
final cdSize = estimateDownloadSize(duration: duration, quality: cd)!;
expect(cdSize.bytes, 27518400);
final batchSize = estimateDownloadSize(
duration: totalDownloadDuration([_track(240), _track(240)]),
quality: cd,
)!;
expect(batchSize.bytes, cdSize.bytes * 2);
for (final (id, bytes) in [
('HI_RES', 89856000),
('HI_RES_LOSSLESS', 179712000),
]) {
final size = estimateDownloadSize(
duration: duration,
quality: QualityOption(id: id, label: ''),
)!;
expect(size.bytes, bytes);
expect(size.bytes, greaterThan(cdSize.bytes));
}
});
test(
'explicit parameters override legacy assumptions and preserve channels',
() {
final quality = QualityOption.fromJson({
'id': 'LOSSLESS',
'label': 'Custom lossless',
'sizeEstimate': {'bitDepth': 24, 'sampleRate': 48000, 'channels': 1},
});
final estimate = estimateDownloadSize(
duration: duration,
quality: quality,
)!;
expect(estimate.bytes, 22464000);
final capped = estimateDownloadSize(
duration: duration,
quality: QualityOption.fromJson({
'id': 'best',
'label': 'Best',
'sizeEstimate': {
'bitDepth': 24,
'sampleRate': 192000,
'isMaximum': true,
},
}),
)!;
expect(capped.bytes, 179712000);
},
);
test(
'unknown tiers, incomplete or invalid parameters do not invent sizes',
() {
for (final quality in [
const QualityOption(id: 'best', label: 'Best'),
const QualityOption(id: 'HIGH', label: 'High'),
const QualityOption(id: 'DOLBY_ATMOS', label: 'Spatial'),
const QualityOption(id: 'opus', label: 'Opus'),
const QualityOption(
id: 'custom',
label: 'Best',
description: 'May fall back to Opus 256kbps',
),
const QualityOption(
id: 'LOSSLESS',
label: '',
sizeEstimate: QualitySizeEstimate(bitDepth: 24),
),
const QualityOption(
id: 'LOSSLESS',
label: '',
sizeEstimate: QualitySizeEstimate(bitDepth: 0, sampleRate: 48000),
),
const QualityOption(
id: 'opus_256',
label: '',
sizeEstimate: QualitySizeEstimate(bitrateKbps: -1),
),
const QualityOption(
id: 'custom',
label: '',
sizeEstimate: QualitySizeEstimate(bitrateKbps: 320, isMaximum: true),
),
]) {
expect(
estimateDownloadSize(duration: duration, quality: quality),
isNull,
);
}
for (final duration in [
null,
Duration.zero,
const Duration(seconds: -1),
]) {
expect(
estimateDownloadSize(
duration: duration,
quality: const QualityOption(id: 'mp3_320', label: 'MP3'),
),
isNull,
);
}
},
);
test('batch duration is unavailable when tracks are missing duration', () {
expect(totalDownloadDuration([]), isNull);
expect(totalDownloadDuration([_track(240), _track(0)]), isNull);
expect(totalDownloadDuration([_track(-1)]), isNull);
expect(totalDownloadDuration([_track(240, itemType: 'album')]), isNull);
expect(
totalDownloadDuration([_track(120), _track(180)]),
const Duration(minutes: 5),
);
});
}