import 'package:flutter_test/flutter_test.dart'; import 'package:timezone/data/latest.dart' as tzdata; import 'package:hepan_everyday/domain/schedule_engine.dart'; Course course({ String id = 'math', int day = 3, int first = 1, int last = 1, Set weeks = const {1}, }) => Course( id: id, name: '矩阵论', teacher: '示例教师', room: '示例教室', weekday: day, startPeriod: first, endPeriod: last, weeks: weeks, source: CourseSource.file, ); void main() { setUpAll(tzdata.initializeTimeZones); final term = Semester(startDate: DateTime(2026, 9, 9), totalWeeks: 5); test( 'all eleven lessons produce the specified Shanghai start and end times', () { const starts = [ 510, 560, 620, 670, 870, 920, 980, 1030, 1170, 1220, 1270, ]; const ends = [555, 605, 665, 715, 915, 965, 1025, 1075, 1215, 1265, 1315]; for (var p = 1; p <= 11; p++) { final result = ScheduleEngine.occurrences([ course(first: p, last: p), ], term); expect(result, hasLength(1)); expect( result.single.start.hour * 60 + result.single.start.minute, starts[p - 1], ); expect( result.single.end.hour * 60 + result.single.end.minute, ends[p - 1], ); } }, ); test('term beginning Wednesday suppresses earlier Monday', () { expect( ScheduleEngine.occurrences([ course(day: 1, weeks: {1, 2}), ], term).map((e) => e.start.day).toList(), [14], ); expect(ScheduleEngine.weekNumber(term, DateTime(2026, 9, 9)), 1); expect(ScheduleEngine.weekNumber(term, DateTime(2026, 9, 14)), 2); expect(ScheduleEngine.weekNumber(term, DateTime(2026, 10, 12)), isNull); }); test('odd and nonconsecutive weeks create only matching dates', () { expect( ScheduleEngine.occurrences([ course(weeks: {1, 3, 5}), ], term).map((e) => '${e.start.month}/${e.start.day}').toList(), ['9/9', '9/23', '10/7'], ); }); test('Sunday and consecutive periods use one correct occurrence', () { final events = ScheduleEngine.occurrences([ course(day: 7, first: 2, last: 3), ], term); expect(events, hasLength(1)); expect(events.single.start.day, 13); expect(events.single.start.hour, 9); expect(events.single.start.minute, 20); expect(events.single.end.hour, 11); expect(events.single.end.minute, 5); }); test( 'school lesson time has a fixed UTC instant regardless of device zone', () { final events = ScheduleEngine.occurrences([course()], term); expect(events, hasLength(1)); expect(events.single.start.toUtc(), DateTime.utc(2026, 9, 9, 0, 30)); }, ); test('overlap keeps simultaneous courses but excludes adjacent courses', () { final events = ScheduleEngine.occurrences([ course(id: 'a', first: 1, last: 2), course(id: 'b', first: 2, last: 3), course(id: 'c', first: 4, last: 4), ], term); expect(events, hasLength(3)); expect(ScheduleEngine.overlaps(events[0], events[1]), isTrue); expect(ScheduleEngine.overlaps(events[1], events[2]), isFalse); }); test('invalid lesson, weekday and week values are rejected', () { expect(() => course(first: 0), throwsArgumentError); expect(() => course(last: 12), throwsArgumentError); expect(() => course(day: 8), throwsArgumentError); expect(() => course(weeks: {0}), throwsArgumentError); expect( () => Semester(startDate: DateTime(2026), totalWeeks: 31), throwsArgumentError, ); }); }