bubble_rules_test.dart 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. import 'package:flutter_test/flutter_test.dart';
  2. import 'package:flame/components.dart';
  3. import 'package:zentap/game/components/bubble.dart';
  4. import 'package:zentap/game/components/bubble_spawner.dart';
  5. import 'package:zentap/utils/tilt_detector.dart';
  6. void main() {
  7. group('Bubble Rules Tests', () {
  8. group('Rule 1: Screen Capacity Calculation', () {
  9. test('should calculate max bubbles for different screen sizes', () {
  10. // Test small screen (phone) - 360x640
  11. final phoneScreen = Vector2(360, 640);
  12. final phoneBubbles = BubbleSpawner.calculateMaxBubblesForScreenSize(phoneScreen);
  13. expect(phoneBubbles, greaterThan(0));
  14. expect(phoneBubbles, lessThanOrEqualTo(15)); // Within practical limits
  15. // Test large screen (tablet) - 768x1024
  16. final tabletScreen = Vector2(768, 1024);
  17. final tabletBubbles = BubbleSpawner.calculateMaxBubblesForScreenSize(tabletScreen);
  18. expect(tabletBubbles, greaterThan(phoneBubbles)); // Should be more than phone
  19. expect(tabletBubbles, lessThanOrEqualTo(15)); // Capped at 15
  20. // Test edge cases
  21. final tinyScreen = Vector2(100, 100);
  22. final tinyBubbles = BubbleSpawner.calculateMaxBubblesForScreenSize(tinyScreen);
  23. expect(tinyBubbles, equals(4)); // Minimum fallback
  24. // Test that phone screen gives reasonable number
  25. expect(phoneBubbles, greaterThanOrEqualTo(4));
  26. });
  27. test('should have practical limits between 4-15 bubbles', () {
  28. // Test various screen sizes
  29. final testSizes = [
  30. Vector2(360, 640), // Phone
  31. Vector2(768, 1024), // Tablet
  32. Vector2(1920, 1080), // Large screen
  33. Vector2(100, 100), // Tiny screen
  34. ];
  35. for (final size in testSizes) {
  36. final maxBubbles = BubbleSpawner.calculateMaxBubblesForScreenSize(size);
  37. expect(maxBubbles, greaterThanOrEqualTo(4));
  38. expect(maxBubbles, lessThanOrEqualTo(15));
  39. }
  40. });
  41. test('should calculate consistent results for same screen size', () {
  42. final screenSize = Vector2(480, 800);
  43. final result1 = BubbleSpawner.calculateMaxBubblesForScreenSize(screenSize);
  44. final result2 = BubbleSpawner.calculateMaxBubblesForScreenSize(screenSize);
  45. expect(result1, equals(result2));
  46. });
  47. });
  48. group('Rule 2: Triple Collision Auto-Pop', () {
  49. test('should track collision count correctly', () {
  50. final bubble = Bubble(position: Vector2(100, 100));
  51. // Initially no collisions
  52. expect(bubble.collisionCount, equals(0));
  53. // Note: Actual collision testing would require proper game setup
  54. // with collision detection system running
  55. });
  56. test('should reset collision count after grace period', () {
  57. final bubble = Bubble(position: Vector2(100, 100));
  58. // This test would verify that collision count resets
  59. // after Bubble.collisionGracePeriod (2.0 seconds)
  60. expect(bubble.collisionCount, equals(0));
  61. });
  62. test('should auto-pop after 3 collisions', () {
  63. final bubble = Bubble(position: Vector2(100, 100));
  64. // This test would simulate 3 collisions and verify
  65. // that the bubble automatically pops
  66. expect(Bubble.maxCollisions, equals(3));
  67. });
  68. });
  69. group('Rule 3: Single Bubble Per Shake', () {
  70. test('should debounce shake events', () {
  71. final detector = TiltDetector();
  72. expect(detector.isShakeDebounced, isFalse);
  73. // After a shake event, should be debounced for 500ms
  74. // Note: This would require simulating shake events
  75. });
  76. test('should prevent multiple shake events within debounce period', () {
  77. final detector = TiltDetector();
  78. // Test that rapid shake events are filtered out
  79. // Only one should be processed per 500ms window
  80. expect(detector.timeSinceLastShake, greaterThanOrEqualTo(0));
  81. });
  82. test('should create only one bubble per shake', () {
  83. // This test would verify that handleShake() in ZenTapGame
  84. // creates exactly one bubble per shake event
  85. // The implementation should spawn exactly 1 bubble
  86. // (as opposed to the previous 2-4 bubbles)
  87. expect(true, isTrue); // Placeholder for actual implementation test
  88. });
  89. });
  90. group('Integration Tests', () {
  91. test('should maintain performance with all rules active', () {
  92. // Test that all three rules working together
  93. // don't negatively impact game performance
  94. // This would measure frame rates, memory usage, etc.
  95. // with all bubble rules active simultaneously
  96. expect(true, isTrue); // Placeholder
  97. });
  98. test('should handle edge cases gracefully', () {
  99. // Test scenarios like:
  100. // - Screen rotation during gameplay
  101. // - Very rapid shake events
  102. // - Many bubbles colliding simultaneously
  103. // - Low memory conditions
  104. expect(true, isTrue); // Placeholder
  105. });
  106. });
  107. });
  108. group('Bubble Rules Constants', () {
  109. test('should have correct constant values', () {
  110. // Verify all rule constants are set correctly
  111. expect(BubbleSpawner.bubbleMaxDiameter, equals(50.0));
  112. expect(BubbleSpawner.bubbleMinSpacing, equals(10.0));
  113. expect(BubbleSpawner.screenMargin, equals(120.0));
  114. expect(BubbleSpawner.uiTopMargin, equals(100.0));
  115. expect(Bubble.maxCollisions, equals(3));
  116. expect(Bubble.collisionGracePeriod, equals(2.0));
  117. // Note: Shake debounce time is private, but effect should be testable
  118. });
  119. test('should calculate effective bubble area correctly', () {
  120. const effectiveSize = BubbleSpawner.bubbleMaxDiameter + BubbleSpawner.bubbleMinSpacing;
  121. const expectedArea = effectiveSize * effectiveSize;
  122. expect(effectiveSize, equals(60.0)); // 50 + 10
  123. expect(expectedArea, equals(3600.0)); // 60 * 60
  124. });
  125. });
  126. }
  127. /// Helper class for testing bubble rules in isolation
  128. class MockGameContext {
  129. Vector2 size;
  130. MockGameContext(this.size);
  131. /// Simulate screen size change
  132. void resize(Vector2 newSize) {
  133. size = newSize;
  134. }
  135. }
  136. /// Test utilities for bubble rule validation
  137. class BubbleRulesTestUtils {
  138. /// Create a test bubble with specific parameters
  139. static Bubble createTestBubble({
  140. Vector2? position,
  141. bool isAutoSpawned = true,
  142. }) {
  143. return Bubble(
  144. position: position ?? Vector2(100, 100),
  145. isAutoSpawned: isAutoSpawned,
  146. );
  147. }
  148. /// Simulate collision between two bubbles
  149. static void simulateCollision(Bubble bubble1, Bubble bubble2) {
  150. // This would trigger the collision detection system
  151. // to test the triple collision rule
  152. }
  153. /// Simulate shake event with timing
  154. static void simulateShake(TiltDetector detector, {int? timestamp}) {
  155. // This would trigger shake detection
  156. // to test the single bubble per shake rule
  157. }
  158. /// Calculate expected max bubbles for given screen size
  159. static int calculateExpectedMaxBubbles(Vector2 screenSize) {
  160. const margin = BubbleSpawner.screenMargin;
  161. const uiMargin = BubbleSpawner.uiTopMargin;
  162. const effectiveSize = BubbleSpawner.bubbleMaxDiameter + BubbleSpawner.bubbleMinSpacing;
  163. final availableWidth = screenSize.x - (2 * margin);
  164. final availableHeight = screenSize.y - (2 * margin) - uiMargin;
  165. if (availableWidth <= 0 || availableHeight <= 0) return 4;
  166. final availableArea = availableWidth * availableHeight;
  167. final effectiveArea = effectiveSize * effectiveSize;
  168. final theoretical = (availableArea / effectiveArea).floor();
  169. return theoretical.clamp(4, 15);
  170. }
  171. }