268 lines
11 KiB
Dart
268 lines
11 KiB
Dart
import 'package:test/test.dart';
|
|
import 'package:sqlite3/sqlite3.dart';
|
|
import 'package:xp_nix/src/monitors/productivity_monitor.dart';
|
|
import 'package:xp_nix/src/testing/mock_idle_monitor.dart';
|
|
import 'package:xp_nix/src/testing/mock_activity_detector.dart';
|
|
import 'package:xp_nix/src/testing/mock_time_provider.dart';
|
|
import 'package:xp_nix/src/testing/mock_desktop_enhancer.dart';
|
|
import 'package:xp_nix/src/config/config_manager.dart';
|
|
|
|
void main() {
|
|
group('Work Day Simulation 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 monitor with mocked dependencies
|
|
monitor = ProductivityMonitor(
|
|
db: db,
|
|
idleMonitor: mockIdleMonitor,
|
|
timeProvider: mockTimeProvider,
|
|
desktopEnhancer: mockDesktopEnhancer,
|
|
activityDetector: mockActivityDetector,
|
|
);
|
|
});
|
|
|
|
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('simulates a full productive work day', () async {
|
|
// Start the monitor
|
|
monitor.start();
|
|
|
|
// Wait a moment for initialization
|
|
await Future.delayed(Duration(milliseconds: 10));
|
|
|
|
// === MORNING CODING SESSION (9:00 - 10:30) ===
|
|
print('\n🌅 Starting morning coding session...');
|
|
mockActivityDetector.simulateActivity('vscode', 'main.dart - ProductivityApp');
|
|
mockTimeProvider.advanceTime(Duration(minutes: 90));
|
|
monitor.flushCurrentActivity(); // Save the activity
|
|
|
|
// Simulate switching to a different file
|
|
mockActivityDetector.simulateActivity('vscode', 'database_manager.dart - ProductivityApp');
|
|
mockTimeProvider.advanceTime(Duration(minutes: 30));
|
|
monitor.flushCurrentActivity(); // Save the activity
|
|
|
|
// Check stats after morning coding
|
|
var stats = monitor.getTodayStats();
|
|
expect(stats['xp'], greaterThan(0), reason: 'Should have earned XP from coding');
|
|
expect(stats['focus_time'], greaterThan(0), reason: 'Should have focus time from coding');
|
|
print('📊 After morning coding: ${stats['xp']} XP, ${(stats['focus_time'] / 3600).toStringAsFixed(1)}h focus');
|
|
|
|
// === BRIEF BREAK - CHECK SLACK (10:30 - 10:45) ===
|
|
print('\n💬 Quick Slack check...');
|
|
mockActivityDetector.simulateActivity('slack', 'General Channel');
|
|
mockTimeProvider.advanceTime(Duration(minutes: 15));
|
|
monitor.flushCurrentActivity();
|
|
|
|
// === RESEARCH SESSION (10:45 - 11:30) ===
|
|
print('\n🔍 Research session...');
|
|
mockActivityDetector.simulateActivity('firefox', 'Dart Documentation - dart.dev');
|
|
mockTimeProvider.advanceTime(Duration(minutes: 45));
|
|
monitor.flushCurrentActivity();
|
|
|
|
// === TEAM MEETING (11:30 - 12:30) ===
|
|
print('\n📅 Team meeting...');
|
|
mockActivityDetector.simulateActivity('zoom', 'Weekly Team Standup');
|
|
mockTimeProvider.advanceTime(Duration(minutes: 60));
|
|
monitor.flushCurrentActivity();
|
|
|
|
// Check stats after meeting
|
|
stats = monitor.getTodayStats();
|
|
expect(stats['meeting_time'], greaterThan(0), reason: 'Should have meeting time');
|
|
print('📊 After meeting: ${stats['xp']} XP, ${(stats['meeting_time'] / 3600).toStringAsFixed(1)}h meetings');
|
|
|
|
// === LUNCH BREAK - GO IDLE (12:30 - 13:30) ===
|
|
print('\n🍽️ Lunch break - going idle...');
|
|
mockIdleMonitor.simulateIdle();
|
|
mockTimeProvider.advanceTime(Duration(minutes: 60));
|
|
|
|
// Come back from lunch
|
|
print('\n🔄 Back from lunch...');
|
|
mockIdleMonitor.simulateActive();
|
|
|
|
// Should get focus session bonus for the morning work
|
|
stats = monitor.getTodayStats();
|
|
expect(stats['focus_sessions'], greaterThan(0), reason: 'Should have focus sessions from idle recovery');
|
|
print('📊 After lunch: ${stats['focus_sessions']} focus sessions completed');
|
|
|
|
// === AFTERNOON CODING SPRINT (13:30 - 15:30) ===
|
|
print('\n⚡ Afternoon coding sprint...');
|
|
mockActivityDetector.simulateActivity('vscode', 'test_suite.dart - ProductivityApp');
|
|
mockTimeProvider.advanceTime(Duration(minutes: 120));
|
|
monitor.flushCurrentActivity();
|
|
|
|
// === DOCUMENTATION WORK (15:30 - 16:30) ===
|
|
print('\n📝 Documentation work...');
|
|
mockActivityDetector.simulateActivity('vscode', 'README.md - ProductivityApp');
|
|
mockTimeProvider.advanceTime(Duration(minutes: 60));
|
|
monitor.flushCurrentActivity();
|
|
|
|
// === END OF DAY WRAP-UP (16:30 - 17:00) ===
|
|
print('\n📋 End of day wrap-up...');
|
|
mockActivityDetector.simulateActivity('slack', 'Daily Summary');
|
|
mockTimeProvider.advanceTime(Duration(minutes: 30));
|
|
monitor.flushCurrentActivity();
|
|
|
|
// === FINAL STATS VERIFICATION ===
|
|
print('\n📈 Final day statistics:');
|
|
stats = monitor.getTodayStats();
|
|
monitor.printDetailedStats();
|
|
|
|
// Verify XP calculations
|
|
expect(stats['xp'], greaterThan(1000), reason: 'Should have substantial XP from full work day');
|
|
expect(stats['level'], greaterThanOrEqualTo(2), reason: 'Should have leveled up');
|
|
|
|
// Verify focus time (coding + research)
|
|
final focusHours = stats['focus_time'] / 3600;
|
|
expect(focusHours, greaterThan(4), reason: 'Should have 4+ hours of focus time');
|
|
expect(focusHours, lessThan(6), reason: 'Focus time should be reasonable');
|
|
|
|
// Verify meeting time
|
|
final meetingHours = stats['meeting_time'] / 3600;
|
|
expect(meetingHours, greaterThan(0.9), reason: 'Should have ~1 hour of meeting time');
|
|
expect(meetingHours, lessThan(1.1), reason: 'Meeting time should be accurate');
|
|
|
|
// Verify focus sessions
|
|
expect(stats['focus_sessions'], greaterThanOrEqualTo(1), reason: 'Should have at least 1 focus session');
|
|
|
|
// Test specific XP calculations
|
|
_verifyXPCalculations(stats);
|
|
|
|
print('\n✅ Work day simulation completed successfully!');
|
|
print(
|
|
'📊 Final Stats: Level ${stats['level']}, ${stats['xp']} XP, ${focusHours.toStringAsFixed(1)}h focus, ${stats['focus_sessions']} sessions',
|
|
);
|
|
});
|
|
|
|
test('simulates rapid context switching day', () async {
|
|
monitor.start();
|
|
await Future.delayed(Duration(milliseconds: 10));
|
|
|
|
print('\n🔄 Simulating rapid context switching...');
|
|
|
|
// Simulate a day with lots of short activities
|
|
final activities = [
|
|
('vscode', 'main.dart', 15), // 15 min coding
|
|
('slack', 'General', 5), // 5 min slack
|
|
('firefox', 'Stack Overflow', 10), // 10 min research
|
|
('vscode', 'test.dart', 20), // 20 min coding
|
|
('zoom', 'Quick sync', 15), // 15 min meeting
|
|
('vscode', 'bug_fix.dart', 25), // 25 min coding
|
|
('slack', 'Code review', 10), // 10 min collaboration
|
|
];
|
|
|
|
for (final (app, title, minutes) in activities) {
|
|
mockActivityDetector.simulateActivity(app, title);
|
|
mockTimeProvider.advanceTime(Duration(minutes: minutes));
|
|
monitor.flushCurrentActivity(); // Flush each activity
|
|
}
|
|
|
|
final stats = monitor.getTodayStats();
|
|
|
|
// Should still accumulate reasonable XP despite context switching
|
|
expect(stats['xp'], greaterThan(200), reason: 'Should earn XP from varied activities');
|
|
expect(stats['focus_time'], greaterThan(0), reason: 'Should have some focus time');
|
|
|
|
print('📊 Context switching day: ${stats['xp']} XP, ${(stats['focus_time'] / 3600).toStringAsFixed(1)}h focus');
|
|
});
|
|
|
|
test('simulates achievement unlocking', () async {
|
|
monitor.start();
|
|
await Future.delayed(Duration(milliseconds: 10));
|
|
|
|
print('\n🏆 Testing achievement unlocking...');
|
|
|
|
// Simulate extended coding to trigger achievements
|
|
mockActivityDetector.simulateActivity('vscode', 'epic_feature.dart');
|
|
|
|
// Advance time to accumulate enough XP for level 5 (need ~500 XP)
|
|
// 30 min * 10 XP/min = 300 XP per flush, need at least 2 flushes
|
|
for (int i = 0; i < 3; i++) {
|
|
mockTimeProvider.advanceTime(Duration(minutes: 60)); // 1 hour each
|
|
monitor.flushCurrentActivity();
|
|
await Future.delayed(Duration(milliseconds: 1)); // Allow level check
|
|
}
|
|
|
|
final stats = monitor.getTodayStats();
|
|
expect(stats['level'], greaterThanOrEqualTo(2), reason: 'Should reach at least level 2');
|
|
expect(stats['xp'], greaterThan(500), reason: 'Should have substantial XP');
|
|
|
|
print('📊 Achievement test: Level ${stats['level']}, ${stats['xp']} XP');
|
|
});
|
|
|
|
test('verifies idle time handling', () async {
|
|
monitor.start();
|
|
await Future.delayed(Duration(milliseconds: 10));
|
|
|
|
print('\n😴 Testing idle time handling...');
|
|
|
|
// Start with some activity and make sure user is active
|
|
mockIdleMonitor.simulateActive(); // Ensure we start active
|
|
mockActivityDetector.simulateActivity('vscode', 'work.dart');
|
|
mockTimeProvider.advanceTime(Duration(minutes: 30));
|
|
monitor.flushCurrentActivity();
|
|
|
|
// Go idle for extended period
|
|
mockIdleMonitor.simulateIdle();
|
|
mockTimeProvider.advanceTime(Duration(minutes: 90)); // 1.5 hours idle
|
|
|
|
// Come back active - this should trigger focus session calculation
|
|
mockIdleMonitor.simulateActive();
|
|
|
|
// Should get focus session bonus
|
|
final stats = monitor.getTodayStats();
|
|
expect(stats['focus_sessions'], greaterThan(0), reason: 'Should award focus session after idle period');
|
|
|
|
print('📊 Idle handling: ${stats['focus_sessions']} focus sessions awarded');
|
|
});
|
|
});
|
|
}
|
|
|
|
/// Verify that XP calculations are working correctly
|
|
void _verifyXPCalculations(Map<String, dynamic> stats) {
|
|
print('\n🧮 Verifying XP calculations...');
|
|
|
|
// Expected XP breakdown (approximate):
|
|
// - Coding: ~4 hours * 60 min * 10 XP/min = ~2400 XP
|
|
// - Research: ~45 min * 6 XP/min = ~270 XP
|
|
// - Meetings: ~60 min * 3 XP/min = ~180 XP
|
|
// - Collaboration: ~45 min * 7 XP/min = ~315 XP
|
|
// - Focus session bonuses: varies
|
|
|
|
final totalXP = stats['xp'] as int;
|
|
expect(totalXP, greaterThan(2000), reason: 'Should have substantial XP from full day');
|
|
expect(totalXP, lessThan(5000), reason: 'XP should be reasonable, not inflated');
|
|
|
|
print('✅ XP calculations appear correct: $totalXP total XP');
|
|
}
|