import 'dart:async'; import 'package:fancake/core/data/planet_visual_state.dart'; import 'package:fancake/features/galaxy/galaxy_data.dart'; import 'package:fancake/features/galaxy/galaxy_motion.dart'; import 'package:fancake/features/galaxy/galaxy_screen.dart'; import 'package:fancake/features/galaxy/widgets/galaxy_field.dart'; import 'package:fancake/features/planet/planet_data.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'eclipse_stubs.dart'; void main() { test('상태별 표류는 점진적으로 변하고 사용자 드래그는 유지된다', () { final clock = GalaxyClock(); addTearDown(clock.dispose); for (final scale in [0.25, 0.5, 1.5, 1.0]) { final before = clock.velocity; clock.idleSpeedScale = scale; clock.advance(1 / 60); final target = GalaxyMotion.idleSpin * scale; expect( clock.velocity, inExclusiveRange( before < target ? before : target, before > target ? before : target, ), ); for (var i = 0; i < 600; i++) { clock.advance(1 / 60); } expect(clock.velocity, closeTo(target, 1e-6)); } clock.idleSpeedScale = 0.25; clock.drag(10); clock.advance(1 / 60); expect(clock.velocity, closeTo(10 * 60 * GalaxyMotion.dragSpinGain, 1e-9)); }); testWidgets('선택 행성의 서버 상태만 주변 궤도에 적용하고 Reduce Motion은 정지한다', (tester) async { final streams = >{}; Stream watch(String id) => streams .putIfAbsent(id, () => StreamController.broadcast()) .stream; await tester.pumpWidget( MaterialApp( home: GalaxyScreen( repository: const SampleGalaxyRepository(), planetRepository: samplePlanetRepository, eclipse: const NoEclipseRepository(), watchPlanet: watch, ), ), ); await tester.pump(); await tester.pump(); final clock = tester .widgetList(find.byType(AnimatedBuilder)) .map((w) => w.listenable) .whereType() .first; GalaxyField field() => tester.widget(find.byType(GalaxyField)); final mine = field().planets.firstWhere((p) => p.isMine); for (final entry in [ (const PlanetVisualState(isHeroTime: true), 1.5), (const PlanetVisualState(isHeroTime: true, isEclipsed: true), 0.5), (const PlanetVisualState(isHeroTime: true, isHibernating: true), 0.25), (const PlanetVisualState(), 1.0), ]) { streams[mine.id]!.add(entry.$1); await tester.pump(); expect(clock.idleSpeedScale, entry.$2); } final other = field().planets.firstWhere((p) => !p.isMine); field().onSelect(other); await tester.pump(); streams[mine.id]!.add(const PlanetVisualState(isHibernating: true)); streams[other.id]!.add(const PlanetVisualState(isHeroTime: true)); await tester.pump(); expect(clock.idleSpeedScale, 1.5); tester.platformDispatcher.accessibilityFeaturesTestValue = const FakeAccessibilityFeatures(disableAnimations: true); addTearDown(tester.platformDispatcher.clearAccessibilityFeaturesTestValue); await tester.pump(); final spin = clock.spin; await tester.pump(const Duration(seconds: 1)); expect(clock.spin, spin); await tester.pumpWidget(const SizedBox()); for (final stream in streams.values) { expect(stream.hasListener, isFalse); await stream.close(); } }); }