204 lines
7.8 KiB
Dart
204 lines
7.8 KiB
Dart
import 'package:test/test.dart';
|
|
import 'package:sqlite3/sqlite3.dart';
|
|
import 'package:xp_nix/src/monitors/productivity_monitor.dart';
|
|
import '../lib/src/testing/mock_idle_monitor.dart';
|
|
import '../lib/src/testing/mock_activity_detector.dart';
|
|
import '../lib/src/testing/mock_time_provider.dart';
|
|
import '../lib/src/testing/mock_desktop_enhancer.dart';
|
|
import '../lib/src/config/config_manager.dart';
|
|
|
|
void main() {
|
|
group('Deep Idle Event Tests', () {
|
|
late Database db;
|
|
late ProductivityMonitor monitor;
|
|
late MockIdleMonitor mockIdleMonitor;
|
|
late MockActivityDetector mockActivityDetector;
|
|
late MockTimeProvider mockTimeProvider;
|
|
late MockDesktopEnhancer mockDesktopEnhancer;
|
|
|
|
setUp(() async {
|
|
// Create in-memory database for testing
|
|
db = sqlite3.openInMemory();
|
|
|
|
// Reset and initialize ConfigManager with default config
|
|
ConfigManager.resetInstance();
|
|
await ConfigManager.instance.initialize('/tmp/test_config_${DateTime.now().millisecondsSinceEpoch}.json');
|
|
|
|
// Create mock dependencies
|
|
mockIdleMonitor = MockIdleMonitor();
|
|
mockActivityDetector = MockActivityDetector();
|
|
mockTimeProvider = MockTimeProvider();
|
|
mockDesktopEnhancer = MockDesktopEnhancer();
|
|
|
|
// Set up starting time (Monday 9 AM)
|
|
mockTimeProvider.setTime(DateTime(2024, 1, 1, 9, 0));
|
|
|
|
// Create testable monitor with mocked dependencies
|
|
monitor = ProductivityMonitor(
|
|
db: db,
|
|
idleMonitor: mockIdleMonitor,
|
|
activityDetector: mockActivityDetector,
|
|
timeProvider: mockTimeProvider,
|
|
desktopEnhancer: mockDesktopEnhancer,
|
|
);
|
|
});
|
|
|
|
tearDown(() async {
|
|
monitor.stop();
|
|
// Add a small delay to allow async operations to complete
|
|
await Future.delayed(Duration(milliseconds: 100));
|
|
try {
|
|
db.dispose();
|
|
} catch (e) {
|
|
// Database might already be closed, ignore the error
|
|
}
|
|
});
|
|
|
|
test('should end current activity and award XP when user goes deep idle', () async {
|
|
// Start the monitor
|
|
monitor.start();
|
|
await Future.delayed(Duration(milliseconds: 10));
|
|
|
|
print('\n🧪 Testing deep idle behavior...');
|
|
|
|
// Start a coding activity
|
|
mockActivityDetector.simulateActivity('vscode', 'main.dart - TestProject');
|
|
await Future.delayed(Duration(milliseconds: 10));
|
|
|
|
// Work for 30 minutes
|
|
mockTimeProvider.advanceTime(Duration(minutes: 30));
|
|
|
|
// Check that no activity has been saved yet (still in progress)
|
|
var stats = monitor.getTodayStats();
|
|
var initialXP = stats['xp'] as int;
|
|
print('📊 Initial XP before deep idle: $initialXP');
|
|
|
|
// User goes deep idle - this should trigger ending the current activity
|
|
print('😴 User goes deep idle...');
|
|
mockIdleMonitor.simulateDeepIdle();
|
|
await Future.delayed(Duration(milliseconds: 50)); // Allow event processing
|
|
|
|
// Check that the activity was saved and XP was awarded
|
|
stats = monitor.getTodayStats();
|
|
var finalXP = stats['xp'] as int;
|
|
var focusTime = stats['focus_time'] as int;
|
|
|
|
print('📊 Final XP after deep idle: $finalXP');
|
|
print('📊 Focus time: ${(focusTime / 60).toStringAsFixed(1)} minutes');
|
|
|
|
// Verify that XP was awarded for the 30-minute coding session
|
|
expect(finalXP, greaterThan(initialXP),
|
|
reason: 'Should have earned XP from the coding session when going deep idle');
|
|
|
|
// Expected: 30 minutes * 10 XP/min = 300 XP (base, before multipliers)
|
|
expect(finalXP, greaterThan(200),
|
|
reason: 'Should have earned substantial XP from 30 minutes of coding');
|
|
|
|
// Should have focus time from coding
|
|
expect(focusTime, greaterThan(25 * 60),
|
|
reason: 'Should have at least 25 minutes of focus time recorded');
|
|
|
|
print('✅ Deep idle activity ending test passed!');
|
|
});
|
|
|
|
test('should not duplicate XP when going from light idle to deep idle', () async {
|
|
// Start the monitor
|
|
monitor.start();
|
|
await Future.delayed(Duration(milliseconds: 10));
|
|
|
|
print('\n🧪 Testing light idle to deep idle transition...');
|
|
|
|
// Start a coding activity
|
|
mockActivityDetector.simulateActivity('vscode', 'feature.dart');
|
|
await Future.delayed(Duration(milliseconds: 10));
|
|
|
|
// Work for 20 minutes
|
|
mockTimeProvider.advanceTime(Duration(minutes: 20));
|
|
|
|
// User goes light idle first
|
|
print('😐 User goes light idle...');
|
|
mockIdleMonitor.simulateLightIdle();
|
|
await Future.delayed(Duration(milliseconds: 50));
|
|
|
|
var stats = monitor.getTodayStats();
|
|
var xpAfterLightIdle = stats['xp'] as int;
|
|
print('📊 XP after light idle: $xpAfterLightIdle');
|
|
|
|
// Then user goes deep idle
|
|
print('😴 User goes deep idle...');
|
|
mockIdleMonitor.simulateDeepIdle();
|
|
await Future.delayed(Duration(milliseconds: 50));
|
|
|
|
stats = monitor.getTodayStats();
|
|
var xpAfterDeepIdle = stats['xp'] as int;
|
|
print('📊 XP after deep idle: $xpAfterDeepIdle');
|
|
|
|
// XP should have been awarded when going deep idle, but not duplicated
|
|
expect(xpAfterDeepIdle, greaterThan(0),
|
|
reason: 'Should have earned XP from the coding session');
|
|
|
|
// The XP should be the same whether we went through light idle or not
|
|
// (since the activity should only be saved once when going deep idle)
|
|
expect(xpAfterDeepIdle, greaterThan(150),
|
|
reason: 'Should have earned XP for 20 minutes of coding');
|
|
|
|
print('✅ Light idle to deep idle transition test passed!');
|
|
});
|
|
|
|
test('should handle multiple activity sessions with deep idle interruptions', () async {
|
|
// Start the monitor
|
|
monitor.start();
|
|
await Future.delayed(Duration(milliseconds: 10));
|
|
|
|
print('\n🧪 Testing multiple sessions with deep idle interruptions...');
|
|
|
|
// First coding session
|
|
mockActivityDetector.simulateActivity('vscode', 'session1.dart');
|
|
await Future.delayed(Duration(milliseconds: 10));
|
|
mockTimeProvider.advanceTime(Duration(minutes: 25));
|
|
|
|
// Go deep idle (should end first session)
|
|
mockIdleMonitor.simulateDeepIdle();
|
|
await Future.delayed(Duration(milliseconds: 50));
|
|
|
|
var stats = monitor.getTodayStats();
|
|
var xpAfterFirstSession = stats['xp'] as int;
|
|
print('📊 XP after first session: $xpAfterFirstSession');
|
|
|
|
// User becomes active again
|
|
mockIdleMonitor.simulateActive();
|
|
await Future.delayed(Duration(milliseconds: 10));
|
|
|
|
// Second coding session
|
|
mockActivityDetector.simulateActivity('vscode', 'session2.dart');
|
|
await Future.delayed(Duration(milliseconds: 10));
|
|
mockTimeProvider.advanceTime(Duration(minutes: 35));
|
|
|
|
// Go deep idle again (should end second session)
|
|
mockIdleMonitor.simulateDeepIdle();
|
|
await Future.delayed(Duration(milliseconds: 50));
|
|
|
|
stats = monitor.getTodayStats();
|
|
var finalXP = stats['xp'] as int;
|
|
var totalFocusTime = stats['focus_time'] as int;
|
|
|
|
print('📊 Final XP after both sessions: $finalXP');
|
|
print('📊 Total focus time: ${(totalFocusTime / 60).toStringAsFixed(1)} minutes');
|
|
|
|
// Should have XP from both sessions
|
|
expect(finalXP, greaterThan(xpAfterFirstSession),
|
|
reason: 'Should have earned additional XP from second session');
|
|
|
|
// Should have substantial XP from 60 minutes total (25 + 35)
|
|
expect(finalXP, greaterThan(400),
|
|
reason: 'Should have earned substantial XP from both coding sessions');
|
|
|
|
// Should have focus time from both sessions (at least 55 minutes)
|
|
expect(totalFocusTime, greaterThan(55 * 60),
|
|
reason: 'Should have focus time from both sessions');
|
|
|
|
print('✅ Multiple sessions with deep idle test passed!');
|
|
});
|
|
});
|
|
}
|