升级 Gradle → 8.14、AGP → 8.11、Kotlin → 2.2.20 JVM 堆降到 -Xmx4G

This commit is contained in:
2026-06-04 13:35:10 +08:00
parent 250f21a2b8
commit 66435302b3
22 changed files with 755 additions and 291 deletions

View File

@@ -71,7 +71,7 @@ void main() {
});
group('iOS permission configuration', () {
test('Podfile enables camera and microphone permission macros', () {
test('Podfile enables camera, microphone and photos permission macros', () {
final podfile = File('ios/Podfile').readAsStringSync();
expect(
@@ -80,6 +80,8 @@ void main() {
);
expect(podfile, contains("'PERMISSION_CAMERA=1'"));
expect(podfile, contains("'PERMISSION_MICROPHONE=1'"));
expect(podfile, contains("'PERMISSION_PHOTOS=1'"));
expect(podfile, contains("'PERMISSION_PHOTOS_ADD_ONLY=1'"));
});
});
}

View File

@@ -17,9 +17,21 @@ void main() {
expect(model.startTimestamp, 1717334400);
expect(model.endTimestamp, 1717334400);
expect(model.address, '广州市番禺区·粤港澳大湾区青年人才双创小镇');
expect(model.filename, isNull);
expect(model.toJson(), clipboardJson);
});
test('parses optional filename from mini program JSON', () {
final json = {
...clipboardJson,
'filename': '选手名称_选手ID_赛事名称_赛项',
};
final model = ClipboardRecordingModel.fromJson(json);
expect(model.filename, '选手名称_选手ID_赛事名称_赛项');
expect(model.toJson(), json);
});
test('throws FormatException when required field is missing', () {
final json = Map<String, dynamic>.from(clipboardJson)..remove('title');

View File

@@ -0,0 +1,69 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:recording_tool/features/recording/recording_display_name.dart';
void main() {
group('sanitizeRecordingBaseName', () {
test('removes invalid path characters', () {
expect(
sanitizeRecordingBaseName(r'a/b:c*d?e"f<g>h|i'),
'a_b_c_d_e_f_g_h_i',
);
});
test('returns null for blank input', () {
expect(sanitizeRecordingBaseName(' '), isNull);
});
test('truncates overly long names', () {
final long = 'a' * 200;
expect(sanitizeRecordingBaseName(long)!.length, 120);
});
});
group('resolveRecordingDisplayName', () {
test('uses sanitized clipboard filename when present', () {
expect(
resolveRecordingDisplayName('选手名称_选手ID_赛事名称_赛项'),
'选手名称_选手ID_赛事名称_赛项',
);
});
test('falls back to REC_ prefix when clipboard filename is empty', () {
expect(resolveRecordingDisplayName(null), startsWith('REC_'));
expect(resolveRecordingDisplayName(''), startsWith('REC_'));
});
});
group('withVideoExtension', () {
test('appends mp4 on Android', () {
expect(
withVideoExtension('match', isIOS: false),
'match.mp4',
);
});
test('appends mov on iOS', () {
expect(
withVideoExtension('match', isIOS: true),
'match.mov',
);
});
test('keeps existing extension', () {
expect(withVideoExtension('a.mp4', isIOS: false), 'a.mp4');
expect(withVideoExtension('a.MOV', isIOS: true), 'a.MOV');
});
});
group('recordingFileNameForPlatform', () {
test('combines clipboard name with platform extension', () {
expect(
recordingFileNameForPlatform(
'选手名称_选手ID_赛事名称_赛项',
isIOS: false,
),
'选手名称_选手ID_赛事名称_赛项.mp4',
);
});
});
}

View File

@@ -8,7 +8,7 @@ void main() {
const defaultClipboardTitle = '';
const validClipboardText =
'{"title":"王东方 丨李想 空中格斗赛","startTimestamp":1717334400,"endTimestamp":1717334400,"address":"广州市番禺区·粤港澳大湾区青年人才双创小镇"}';
'{"title":"王东方 丨李想 空中格斗赛","startTimestamp":1717334400,"endTimestamp":1717334400,"filename":"选手名称_选手ID_赛事名称_赛项","address":"广州市番禺区·粤港澳大湾区青年人才双创小镇"}';
Future<void> setClipboardText(String? text) async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
@@ -33,116 +33,128 @@ void main() {
final container = ProviderContainer();
addTearDown(container.dispose);
await container
final result = await container
.read(recordingViewModelProvider.notifier)
.getClipboardContent();
final clipboardModel = container
.read(recordingViewModelProvider)
.clipboardRecordingModel;
expect(clipboardModel.title, '王东方 丨李想 空中格斗赛');
expect(clipboardModel.startTimestamp, 1717334400);
expect(clipboardModel.endTimestamp, 1717334400);
expect(clipboardModel.address, '广州市番禺区·粤港澳大湾区青年人才双创小镇');
expect(result, ClipboardReadResult.success);
final model = container.read(recordingViewModelProvider);
expect(model.hasValidClipboardInfo, isTrue);
expect(model.clipboardRecordingModel.title, '王东方 丨李想 空中格斗赛');
expect(model.clipboardRecordingModel.startTimestamp, 1717334400);
expect(model.clipboardRecordingModel.endTimestamp, 1717334400);
expect(
model.clipboardRecordingModel.address,
'广州市番禺区·粤港澳大湾区青年人才双创小镇',
);
expect(
model.clipboardRecordingModel.filename,
'选手名称_选手ID_赛事名称_赛项',
);
},
);
test('keeps default state when clipboard is empty', () async {
test('returns empty when clipboard is empty', () async {
await setClipboardText('');
final container = ProviderContainer();
addTearDown(container.dispose);
await container
final result = await container
.read(recordingViewModelProvider.notifier)
.getClipboardContent();
expect(
container
.read(recordingViewModelProvider)
.clipboardRecordingModel
.title,
defaultClipboardTitle,
);
expect(result, ClipboardReadResult.empty);
final model = container.read(recordingViewModelProvider);
expect(model.hasValidClipboardInfo, isFalse);
expect(model.clipboardRecordingModel.title, defaultClipboardTitle);
});
test('keeps default state when clipboard is not JSON', () async {
test('returns invalid when clipboard is not JSON', () async {
await setClipboardText('hello');
final container = ProviderContainer();
addTearDown(container.dispose);
await container
final result = await container
.read(recordingViewModelProvider.notifier)
.getClipboardContent();
expect(result, ClipboardReadResult.invalid);
expect(
container
.read(recordingViewModelProvider)
.clipboardRecordingModel
.title,
container.read(recordingViewModelProvider).clipboardRecordingModel.title,
defaultClipboardTitle,
);
expect(
container.read(recordingViewModelProvider).hasValidClipboardInfo,
isFalse,
);
});
test('keeps default state when clipboard JSON is not an object', () async {
test('returns invalid when clipboard JSON is not an object', () async {
await setClipboardText('[1,2,3]');
final container = ProviderContainer();
addTearDown(container.dispose);
await container
final result = await container
.read(recordingViewModelProvider.notifier)
.getClipboardContent();
expect(result, ClipboardReadResult.invalid);
expect(
container
.read(recordingViewModelProvider)
.clipboardRecordingModel
.title,
container.read(recordingViewModelProvider).clipboardRecordingModel.title,
defaultClipboardTitle,
);
});
test(
'keeps default state when clipboard JSON misses required fields',
() async {
await setClipboardText('{"title":"王东方 丨李想 空中格斗赛"}');
final container = ProviderContainer();
addTearDown(container.dispose);
test('returns invalid when clipboard JSON misses required fields', () async {
await setClipboardText('{"title":"王东方 丨李想 空中格斗赛"}');
final container = ProviderContainer();
addTearDown(container.dispose);
await container
.read(recordingViewModelProvider.notifier)
.getClipboardContent();
final result = await container
.read(recordingViewModelProvider.notifier)
.getClipboardContent();
expect(
container
.read(recordingViewModelProvider)
.clipboardRecordingModel
.title,
defaultClipboardTitle,
);
},
);
expect(result, ClipboardReadResult.invalid);
expect(
container.read(recordingViewModelProvider).clipboardRecordingModel.title,
defaultClipboardTitle,
);
});
test(
'keeps default state when clipboard JSON has wrong field type',
() async {
await setClipboardText(
'{"title":"王东方 丨李想 空中格斗赛","startTimestamp":"1717334400","endTimestamp":1717334400,"address":"广州市番禺区·粤港澳大湾区青年人才双创小镇"}',
);
final container = ProviderContainer();
addTearDown(container.dispose);
test('returns invalid when clipboard JSON has wrong field type', () async {
await setClipboardText(
'{"title":"王东方 丨李想 空中格斗赛","startTimestamp":"1717334400","endTimestamp":1717334400,"address":"广州市番禺区·粤港澳大湾区青年人才双创小镇"}',
);
final container = ProviderContainer();
addTearDown(container.dispose);
await container
.read(recordingViewModelProvider.notifier)
.getClipboardContent();
final result = await container
.read(recordingViewModelProvider.notifier)
.getClipboardContent();
expect(
container
.read(recordingViewModelProvider)
.clipboardRecordingModel
.title,
defaultClipboardTitle,
);
},
);
expect(result, ClipboardReadResult.invalid);
expect(
container.read(recordingViewModelProvider).clipboardRecordingModel.title,
defaultClipboardTitle,
);
});
test('returns invalid when title is blank', () async {
await setClipboardText(
'{"title":" ","startTimestamp":1717334400,"endTimestamp":1717334400,"address":"广州市"}',
);
final container = ProviderContainer();
addTearDown(container.dispose);
final result = await container
.read(recordingViewModelProvider.notifier)
.getClipboardContent();
expect(result, ClipboardReadResult.invalid);
expect(
container.read(recordingViewModelProvider).hasValidClipboardInfo,
isFalse,
);
});
});
}