- Updated `ai.md` to clarify the mechanics of hunger restoration and the importance of lunch breaks in the school schedule. - Revised `schedule.md` to detail the new lunch break structure, allowing for separate sittings for different grade levels. - Enhanced `Decision.cs` and `DecisionPlanner.cs` to incorporate logic for lunch breaks, ensuring that students only leave lessons during their designated lunch windows. - Updated `DayFrameDef` and related classes to support multiple lunch breaks and validate their configurations. - Adjusted tests to validate the new decision-making logic regarding lunch breaks and hunger management, ensuring robust functionality. - Improved localization strings to reflect changes in the school day structure and lunch functionalities.
100 lines
3.9 KiB
C#
100 lines
3.9 KiB
C#
namespace HSchool.Content.Tests;
|
|
|
|
/// <summary>
|
|
/// Two sittings instead of one crowded canteen: juniors eat after the third lesson, seniors after
|
|
/// the fourth. The list also decides which breaks are long, because fifteen minutes of lunch do
|
|
/// not fit into ten.
|
|
/// </summary>
|
|
public class LunchBreakTests
|
|
{
|
|
private readonly CatalogLoader _loader = new();
|
|
|
|
[Fact]
|
|
public void VanillaCore_FeedsJuniorsAndSeniorsAtDifferentBreaks()
|
|
{
|
|
var frame = LoadVanilla().DayFrame!;
|
|
|
|
Assert.Equal(2, frame.LunchBreaks.Count);
|
|
Assert.Equal(3, SchoolDay.LunchBreakAfter(frame, year: 1));
|
|
Assert.Equal(3, SchoolDay.LunchBreakAfter(frame, year: 5));
|
|
Assert.Equal(4, SchoolDay.LunchBreakAfter(frame, year: 6));
|
|
Assert.Equal(4, SchoolDay.LunchBreakAfter(frame, year: 11));
|
|
}
|
|
|
|
[Fact]
|
|
public void EveryBreakSomebodyEatsIn_IsALongOne()
|
|
{
|
|
var frame = LoadVanilla().DayFrame!;
|
|
|
|
Assert.True(SchoolDay.IsLongBreakAfter(frame, 3));
|
|
Assert.True(SchoolDay.IsLongBreakAfter(frame, 4));
|
|
Assert.False(SchoolDay.IsLongBreakAfter(frame, 2));
|
|
|
|
// The fourth break is twenty minutes now, so the fifth lesson starts at half past noon
|
|
// rather than twenty past: the second sitting has to fit into the day.
|
|
Assert.Equal(new TimeOnly(12, 30), SchoolDay.PeriodStart(frame, 5));
|
|
}
|
|
|
|
[Fact]
|
|
public void LunchWindow_IsOpenOnlyAtTheSittingOfThatParallel()
|
|
{
|
|
var frame = LoadVanilla().DayFrame!;
|
|
|
|
Assert.True(SchoolDay.IsLunchWindow(frame, DaySlot.BreakAfter(3), year: 4));
|
|
Assert.False(SchoolDay.IsLunchWindow(frame, DaySlot.BreakAfter(4), year: 4));
|
|
Assert.False(SchoolDay.IsLunchWindow(frame, DaySlot.BreakAfter(3), year: 9));
|
|
Assert.True(SchoolDay.IsLunchWindow(frame, DaySlot.BreakAfter(4), year: 9));
|
|
|
|
// Staff belong to no parallel and can eat at either sitting; a lesson is never a window.
|
|
Assert.True(SchoolDay.IsLunchWindow(frame, DaySlot.BreakAfter(3), year: null));
|
|
Assert.True(SchoolDay.IsLunchWindow(frame, DaySlot.BreakAfter(4), year: null));
|
|
Assert.False(SchoolDay.IsLunchWindow(frame, DaySlot.Lesson(3), year: 4));
|
|
}
|
|
|
|
[Fact]
|
|
public void ASittingAfterTheLastLesson_FailsTheCatalog()
|
|
{
|
|
var error = Assert.Throws<ContentLoadException>(() => LoadWithSittings(
|
|
"""{ "afterLesson": 7, "gradeMin": 1, "gradeMax": 11 }"""));
|
|
|
|
Assert.Contains("lunch break", error.Message, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
[Fact]
|
|
public void TwoSittingsForTheSameParallel_FailTheCatalog()
|
|
{
|
|
var error = Assert.Throws<ContentLoadException>(() => LoadWithSittings(
|
|
"""{ "afterLesson": 3, "gradeMin": 1, "gradeMax": 5 }, { "afterLesson": 4, "gradeMin": 5, "gradeMax": 11 }"""));
|
|
|
|
Assert.Contains("two lunch breaks", error.Message, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
private DefCatalog LoadWithSittings(string sittings) =>
|
|
_loader.Load(
|
|
[CatalogLoader.CorePackId],
|
|
[
|
|
PackDocuments.Def(
|
|
CatalogLoader.CorePackId,
|
|
"dayframe",
|
|
"day",
|
|
$$"""
|
|
{
|
|
"defName": "Day",
|
|
"firstLesson": "08:30",
|
|
"lessonCount": 7,
|
|
"lessonMinutes": 45,
|
|
"breakMinutes": 10,
|
|
"longBreakAfter": 3,
|
|
"longBreakMinutes": 20,
|
|
"lunchBreaks": [{{sittings}}]
|
|
}
|
|
"""),
|
|
]);
|
|
|
|
private DefCatalog LoadVanilla()
|
|
{
|
|
var root = Path.Combine(AppContext.BaseDirectory, "vanilla");
|
|
return _loader.Load([CatalogLoader.CorePackId], PackDocuments.FromDirectory(CatalogLoader.CorePackId, root));
|
|
}
|
|
}
|