之前只要 confidence>=0.6 且 validation_count>=2 就升级 verified,
两次读到不同结果也会被当成"验证通过",错误标签一旦写入很难纠正。
新增 MetadataStability(纯函数,可单测):
- 首次见到 → pending(first_seen),次数 1
- 与上次记录标题/艺术家冲突 → pending(conflict),次数重置为 1
- 一致且次数 >= 2、置信度达标 → verified(stable)
- 置信度不足 → pending(low_confidence),次数继续累计
- 字段缺失不算冲突;比较时忽略大小写与首尾空格
metadata_service 改为调用该状态机,并打印判定原因:
🧭 [MetadataService] 状态机: stable → verified(validations=2)
测试:新增 test/metadata_stability_test.dart 7 个用例,
连同原有 9 个共 16 个,flutter test 全绿。
已构建并安装 release 验证。
125 lines
3.5 KiB
Dart
125 lines
3.5 KiB
Dart
// test/metadata_stability_test.dart
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:qingting_player/metadata/metadata_model.dart';
|
|
import 'package:qingting_player/metadata/metadata_stability.dart';
|
|
|
|
void main() {
|
|
CandidateMetadata candidate({double confidence = 0.8}) => CandidateMetadata(
|
|
metadata: const NormalizedMetadata(title: '夜曲', artist: '周杰伦'),
|
|
confidence: confidence,
|
|
);
|
|
|
|
group('MetadataStability', () {
|
|
test('首次见到 → pending,次数 1', () {
|
|
final d = MetadataStability.decide(
|
|
previous: null,
|
|
candidate: candidate(),
|
|
title: '夜曲',
|
|
artist: '周杰伦',
|
|
);
|
|
expect(d.status, 'pending');
|
|
expect(d.reason, 'first_seen');
|
|
expect(d.validationCount, 1);
|
|
});
|
|
|
|
test('第二次结果一致且置信度达标 → verified', () {
|
|
final d = MetadataStability.decide(
|
|
previous: {
|
|
'title': '夜曲',
|
|
'artist': '周杰伦',
|
|
'confidence': 0.8,
|
|
'validation_count': 1,
|
|
},
|
|
candidate: candidate(),
|
|
title: '夜曲',
|
|
artist: '周杰伦',
|
|
);
|
|
expect(d.status, 'verified');
|
|
expect(d.reason, 'stable');
|
|
expect(d.validationCount, 2);
|
|
});
|
|
|
|
test('标题或艺术家冲突 → 次数重置为 1,保持 pending', () {
|
|
final d = MetadataStability.decide(
|
|
previous: {
|
|
'title': '夜曲',
|
|
'artist': '周杰伦',
|
|
'confidence': 0.9,
|
|
'validation_count': 5,
|
|
},
|
|
candidate: candidate(),
|
|
title: '完全不同的歌',
|
|
artist: '别的歌手',
|
|
);
|
|
expect(d.status, 'pending');
|
|
expect(d.reason, 'conflict');
|
|
expect(d.validationCount, 1);
|
|
});
|
|
|
|
test('置信度不足 → pending,但次数继续累计', () {
|
|
final d = MetadataStability.decide(
|
|
previous: {
|
|
'title': '夜曲',
|
|
'artist': '周杰伦',
|
|
'confidence': 0.4,
|
|
'validation_count': 3,
|
|
},
|
|
candidate: candidate(confidence: 0.5),
|
|
title: '夜曲',
|
|
artist: '周杰伦',
|
|
);
|
|
expect(d.status, 'pending');
|
|
expect(d.reason, 'low_confidence');
|
|
expect(d.validationCount, 4);
|
|
});
|
|
|
|
test('一致但只验证过一次 → pending', () {
|
|
final d = MetadataStability.decide(
|
|
previous: {
|
|
'title': '夜曲',
|
|
'artist': '周杰伦',
|
|
'confidence': 0.8,
|
|
'validation_count': 0,
|
|
},
|
|
candidate: candidate(),
|
|
title: '夜曲',
|
|
artist: '周杰伦',
|
|
);
|
|
expect(d.status, 'pending');
|
|
expect(d.reason, 'need_second_validation');
|
|
expect(d.validationCount, 1);
|
|
});
|
|
|
|
test('字段缺失不算冲突(旧记录没有艺术家)', () {
|
|
final d = MetadataStability.decide(
|
|
previous: {
|
|
'title': '夜曲',
|
|
'artist': '',
|
|
'confidence': 0.7,
|
|
'validation_count': 1,
|
|
},
|
|
candidate: candidate(),
|
|
title: '夜曲',
|
|
artist: '周杰伦',
|
|
);
|
|
expect(d.status, 'verified');
|
|
expect(d.validationCount, 2);
|
|
});
|
|
|
|
test('大小写与首尾空格不影响一致性判断', () {
|
|
final d = MetadataStability.decide(
|
|
previous: {
|
|
'title': ' Night Song ',
|
|
'artist': 'JAY',
|
|
'confidence': 0.8,
|
|
'validation_count': 1,
|
|
},
|
|
candidate: candidate(),
|
|
title: 'night song',
|
|
artist: 'jay',
|
|
);
|
|
expect(d.status, 'verified');
|
|
});
|
|
});
|
|
}
|