import 'dart:ui'; import 'package:flutter_test/flutter_test.dart'; import 'package:pyramid/features/call_ui/bubble_physics.dart'; /// Lässt die Simulation [seconds] lang laufen (60 Schritte pro Sekunde). bool _run(BubbleSim sim, double seconds) { var moving = true; for (var i = 0; i < (seconds * 60).round(); i++) { moving = sim.step(1 / 60); } return moving; } void main() { const phone = Size(390, 620); const desktop = Size(1200, 700); test('zwei Bubbles drücken sich leicht aneinander und kommen zur Ruhe', () { final sim = BubbleSim()..sync(['a', 'b'], {}, desktop); final moving = _run(sim, 6); expect(moving, isFalse, reason: 'Simulation kommt nicht zur Ruhe'); final a = sim.bodies['a']!, b = sim.bodies['b']!; final dist = (a.pos - b.pos).distance; // Sie berühren sich (überlappen leicht) statt frei zu schweben … expect(dist, lessThan(a.r + b.r)); // … liegen aber nicht ineinander. expect(dist, greaterThan((a.r + b.r) * 0.8)); // Querformat: nebeneinander statt übereinander expect((a.pos.dy - b.pos.dy).abs(), lessThan((a.pos.dx - b.pos.dx).abs())); }); test('wer spricht, wird größer', () { final sim = BubbleSim()..sync(['a', 'b', 'c'], {}, phone); _run(sim, 4); final before = sim.bodies['b']!.r; sim.sync(['a', 'b', 'c'], {'b'}, phone); _run(sim, 4); final b = sim.bodies['b']!.r; expect(b, greaterThan(before * 1.1)); expect(b, greaterThan(sim.bodies['a']!.r * 1.1)); }); test('viele Teilnehmer bleiben im Bild (Handy hochkant)', () { final ids = [for (var i = 0; i < 8; i++) 'p$i']; final sim = BubbleSim()..sync(ids, {}, phone); _run(sim, 8); for (final b in sim.bodies.values) { expect(b.pos.dx - b.r, greaterThan(-2), reason: '${b.id} links raus'); expect(b.pos.dx + b.r, lessThan(phone.width + 2), reason: '${b.id} rechts raus'); expect(b.pos.dy - b.r, greaterThan(-2), reason: '${b.id} oben raus'); expect(b.pos.dy + b.r, lessThan(phone.height + 2), reason: '${b.id} unten raus'); } }); test('Gehende schrumpfen weg und verschwinden', () { final sim = BubbleSim()..sync(['a', 'b'], {}, phone); _run(sim, 3); sim.sync(['a'], {}, phone); _run(sim, 3); expect(sim.bodies.keys, ['a']); }); test('Berührungsfläche ist abgeflacht, freier Rand bleibt rund', () { final sim = BubbleSim()..sync(['a', 'b'], {}, desktop); _run(sim, 6); final a = sim.bodies['a']!, b = sim.bodies['b']!; final bounds = sim.clipPath('a').getBounds(); final toB = (b.pos - a.pos); // In Richtung des Nachbarn endet die Bubble vor ihrem Kreisrand … if (toB.dx > 0) { expect(bounds.right, lessThan(a.pos.dx + a.r - 1)); // … auf der freien Seite reicht sie bis zum Kreisrand. expect(bounds.left, closeTo(a.pos.dx - a.r, 1)); } else { expect(bounds.left, greaterThan(a.pos.dx - a.r + 1)); expect(bounds.right, closeTo(a.pos.dx + a.r, 1)); } // Mittelpunkt liegt im Umriss, der Nachbar-Mittelpunkt nicht. expect(sim.clipPath('a').contains(a.pos), isTrue); expect(sim.clipPath('a').contains(b.pos), isFalse); }); test('einzelne Bubble: rund, mittig', () { final sim = BubbleSim()..sync(['solo'], {}, phone); _run(sim, 4); final s = sim.bodies['solo']!; expect(s.pos.dx, closeTo(phone.width / 2, 2)); expect(s.pos.dy, closeTo(phone.height / 2, 2)); final bounds = sim.clipPath('solo').getBounds(); expect(bounds.width, closeTo(s.r * 2, 1)); }); }