Files
fanuzu/app/test/shake_live_hits_test.dart
T

60 lines
2.2 KiB
Dart

import 'package:fancake/features/quest/quest_evidence.dart';
import 'package:flutter_test/flutter_test.dart';
Duration ms(int v) => Duration(milliseconds: v);
void main() {
test('Music Shake 실시간 판정 — 영상 시각 기준 · 창이 닫힌 박만 센다 · 콤보', () {
// 60 BPM = 박 1초 · 창 perfect ±0.2 · good ±0.5 박(임시 운영값).
final j = ShakeJudge(bpm: 60, segments: 4)
..add(ms(100)) // 박 0 · perfect
..add(ms(1400)) // 박 1 · good
..add(ms(3050)); // 박 3 · perfect, 박 2 miss
expect(j.live(ms(400)).judged, 0);
final mid = j.live(ms(1600));
expect(
(mid.judged, mid.perfect, mid.good, mid.combo, mid.last),
(2, 1, 1, 2, ShakeHit.good),
);
final after2 = j.live(ms(2600));
expect((after2.miss, after2.combo, after2.last), (1, 0, ShakeHit.miss));
expect(j.end, ms(3500));
expect(j.judgement(), {'perfect': 2, 'good': 1, 'miss': 1, 'maxCombo': 2});
});
test('흔들기는 가장 가까운 박 하나에만 붙고 · 오지 않은 박은 miss 다', () {
final j = ShakeJudge(bpm: 60, segments: 3)
..add(ms(500))
..add(ms(600));
// 0.5 는 박 1 쪽으로 반올림(창 경계 0.5 포함) · 0.6 도 박 1 — 박 1 good 하나.
expect(j.judgement(), {'perfect': 0, 'good': 1, 'miss': 2, 'maxCombo': 1});
});
test('판정 창은 운영값 config/policy.musicShake 가 덮는다', () {
final w = ShakeWindows.from({
'musicShake': {'perfectWindow': 0.1, 'goodWindow': 0.3},
});
expect((w.perfect, w.good), (0.1, 0.3));
final d = ShakeWindows.from(const {});
expect((d.perfect, d.good), (0.2, 0.5));
});
test('YouTube 영상 ID', () {
expect(
youtubeVideoId('https://www.youtube.com/watch?v=WMweEpGlu_U'),
'WMweEpGlu_U',
);
expect(youtubeVideoId('https://youtu.be/WMweEpGlu_U?si=x'), 'WMweEpGlu_U');
expect(
youtubeVideoId('https://music.youtube.com/watch?v=WMweEpGlu_U'),
'WMweEpGlu_U',
);
expect(
youtubeVideoId('https://youtube.com/shorts/WMweEpGlu_U'),
'WMweEpGlu_U',
);
expect(youtubeVideoId('https://vimeo.com/123'), isNull);
expect(youtubeVideoId('https://www.youtube.com/watch?v=short'), isNull);
});
}