201 lines
8.0 KiB
C#
201 lines
8.0 KiB
C#
using Arch.Core;
|
||
using HSchool.Content;
|
||
using HSchool.People;
|
||
|
||
namespace HSchool.Simulation.Tests;
|
||
|
||
public class PeopleInSchoolTests
|
||
{
|
||
private static readonly DateTime Start = new(2012, 4, 3, 6, 0, 0, DateTimeKind.Utc);
|
||
|
||
[Fact]
|
||
public void InstallPeople_FillsHomeroomsAndJobs()
|
||
{
|
||
var (catalog, map) = Vanilla();
|
||
var demand = SchoolDemand.From(catalog, map);
|
||
var roster = RosterGenerator.Generate(catalog, map, schoolSeed: 1, "Slavic", Start);
|
||
|
||
using var school = School.Create(1, "Полная", Start, catalog, map);
|
||
school.InstallPeople(roster, seed: 1);
|
||
school.Tick(1d / 20d, 5d);
|
||
|
||
Assert.Same(roster, school.Roster);
|
||
Assert.Equal(11, roster.Classes.Count);
|
||
Assert.Equal(demand.Seats.Count, roster.People.Count(person => person.IsStudent));
|
||
Assert.Equal(demand.Staff.Count, roster.People.Count(person => person.IsStaff));
|
||
Assert.True(RosterFit.Matches(roster, demand));
|
||
|
||
var peopleQuery = new QueryDescription().WithAll<PersonIdentity, PersonNeeds, PersonRoles>();
|
||
var classesQuery = new QueryDescription().WithAll<ClassIdentity>();
|
||
Assert.Equal(roster.People.Count, school.World.CountEntities(in peopleQuery));
|
||
Assert.Equal(11, school.World.CountEntities(in classesQuery));
|
||
}
|
||
|
||
[Fact]
|
||
public void CoreNeeds_DoNotMoveOnTick()
|
||
{
|
||
var (catalog, map) = Vanilla();
|
||
var roster = RosterGenerator.Generate(catalog, map, schoolSeed: 2, "Slavic", Start);
|
||
using var school = School.Create(2, "Нужды", Start, catalog, map);
|
||
school.InstallPeople(roster, seed: 2);
|
||
|
||
for (var i = 0; i < 20; i++)
|
||
{
|
||
school.Tick(1d / 20d, 5d);
|
||
}
|
||
|
||
var hunger = new List<float>();
|
||
var query = new QueryDescription().WithAll<PersonNeeds>();
|
||
school.World.Query(in query, (ref PersonNeeds needs) => hunger.Add(needs.Values["Hunger"]));
|
||
Assert.NotEmpty(hunger);
|
||
Assert.All(hunger, value => Assert.Equal(1f, value));
|
||
}
|
||
|
||
[Fact]
|
||
public void NeedDecay_DropsWhenTheDefSaysSo()
|
||
{
|
||
var catalog = new CatalogLoader().Load(
|
||
[CatalogLoader.CorePackId],
|
||
[
|
||
new ContentDocument(
|
||
CatalogLoader.CorePackId,
|
||
"defs/needs/hunger.jsonc",
|
||
"""{ "defName": "Hunger", "initial": 1, "decayPerHour": 1, "min": 0, "max": 1 }"""),
|
||
]);
|
||
|
||
var world = World.Create();
|
||
try
|
||
{
|
||
world.Create(new PersonNeeds(new Dictionary<string, float>(StringComparer.Ordinal) { ["Hunger"] = 1f }));
|
||
NeedDecay.Apply(world, catalog, gameMinutes: 60);
|
||
var query = new QueryDescription().WithAll<PersonNeeds>();
|
||
world.Query(in query, (ref PersonNeeds needs) => Assert.Equal(0f, needs.Values["Hunger"]));
|
||
}
|
||
finally
|
||
{
|
||
World.Destroy(world);
|
||
}
|
||
}
|
||
|
||
[Fact]
|
||
public void SameSeed_InstallsTheSameNames()
|
||
{
|
||
var (catalog, map) = Vanilla();
|
||
var first = RosterGenerator.Generate(catalog, map, schoolSeed: 9, "Slavic", Start);
|
||
var second = RosterGenerator.Generate(catalog, map, schoolSeed: 9, "Slavic", Start);
|
||
|
||
using var a = School.Create(9, "А", Start, catalog, map);
|
||
using var b = School.Create(9, "Б", Start, catalog, map);
|
||
a.InstallPeople(first, 9);
|
||
b.InstallPeople(second, 9);
|
||
|
||
Assert.Equal(
|
||
first.People.Select(person => $"{person.Name.Surname} {person.Name.Given} {person.Name.Patronymic}"),
|
||
second.People.Select(person => $"{person.Name.Surname} {person.Name.Given} {person.Name.Patronymic}"));
|
||
}
|
||
|
||
[Fact]
|
||
public void TickPastFirstSeptember_GraduatesTheOldestYearAndKeepsCapacity()
|
||
{
|
||
var catalog = Catalog();
|
||
var map = Classrooms(4);
|
||
var roster = RosterGenerator.Generate(catalog, map, schoolSeed: 1, "Slavic", Start);
|
||
var demand = SchoolDemand.From(catalog, map);
|
||
var oldest = roster.Classes.Max(schoolClass => schoolClass.Year);
|
||
var graduated = roster.Classes
|
||
.Where(schoolClass => schoolClass.Year == oldest)
|
||
.SelectMany(schoolClass => schoolClass.PupilIds)
|
||
.ToHashSet(StringComparer.Ordinal);
|
||
|
||
using var school = School.Create(1, "Четыре кабинета", Start, catalog, map);
|
||
school.InstallPeople(roster, seed: 1, "Slavic");
|
||
|
||
var sept = new DateTime(2012, 9, 1, 0, 0, 0, DateTimeKind.Utc);
|
||
var changed = school.Tick((sept - Start).TotalMinutes / 5d, 5d);
|
||
|
||
Assert.True(changed);
|
||
Assert.True(school.Clock.Time >= sept);
|
||
Assert.NotNull(school.Roster);
|
||
Assert.True(RosterFit.Matches(school.Roster, demand));
|
||
Assert.Equal(roster.People.Count(person => person.IsStudent), school.Roster.People.Count(person => person.IsStudent));
|
||
Assert.DoesNotContain(school.Roster.People, person => graduated.Contains(person.Id));
|
||
Assert.Contains(school.Roster.Classes, schoolClass => schoolClass.Year == 1);
|
||
|
||
var peopleQuery = new QueryDescription().WithAll<PersonIdentity>();
|
||
var classesQuery = new QueryDescription().WithAll<ClassIdentity>();
|
||
Assert.Equal(school.Roster.People.Count, school.World.CountEntities(in peopleQuery));
|
||
Assert.Equal(school.Roster.Classes.Count, school.World.CountEntities(in classesQuery));
|
||
}
|
||
|
||
[Fact]
|
||
public void TickOnCreateDateOfFirstSeptember_DoesNotIntake()
|
||
{
|
||
var start = new DateTime(2012, 9, 1, 6, 0, 0, DateTimeKind.Utc);
|
||
var catalog = Catalog();
|
||
var map = Classrooms(4);
|
||
var roster = RosterGenerator.Generate(catalog, map, schoolSeed: 3, "Slavic", start);
|
||
using var school = School.Create(3, "Первое сентября", start, catalog, map);
|
||
school.InstallPeople(roster, seed: 3, "Slavic");
|
||
|
||
var years = roster.Classes.Select(schoolClass => schoolClass.Year).OrderBy(year => year).ToArray();
|
||
var changed = school.Tick(1d / 20d, 5d);
|
||
|
||
Assert.False(changed);
|
||
Assert.Same(roster, school.Roster);
|
||
Assert.Equal(years, school.Roster!.Classes.Select(schoolClass => schoolClass.Year).OrderBy(year => year));
|
||
}
|
||
|
||
private static DefCatalog Catalog()
|
||
{
|
||
var root = Path.Combine(AppContext.BaseDirectory, "vanilla");
|
||
return new CatalogLoader().Load([CatalogLoader.CorePackId], PackDocumentsFrom(root));
|
||
}
|
||
|
||
private static MapLayout Classrooms(int count, int desks = 16)
|
||
{
|
||
var rooms = new RoomNode[count];
|
||
for (var i = 0; i < count; i++)
|
||
{
|
||
rooms[i] = new RoomNode
|
||
{
|
||
Id = $"classroom-{i:00}",
|
||
Def = "Classroom",
|
||
Building = "main",
|
||
Floor = "floor-1",
|
||
Label = $"{101 + i}",
|
||
Seats = desks,
|
||
};
|
||
}
|
||
|
||
return new MapLayout { Rooms = rooms };
|
||
}
|
||
|
||
private static (DefCatalog Catalog, MapLayout Map) Vanilla()
|
||
{
|
||
var root = Path.Combine(AppContext.BaseDirectory, "vanilla");
|
||
var documents = PackDocumentsFrom(root);
|
||
var catalog = new CatalogLoader().Load([CatalogLoader.CorePackId], documents);
|
||
var map = CatalogLoader.LastDefaultMap([CatalogLoader.CorePackId], documents);
|
||
Assert.NotNull(map);
|
||
return (catalog, map);
|
||
}
|
||
|
||
private static List<ContentDocument> PackDocumentsFrom(string packRoot)
|
||
{
|
||
var documents = new List<ContentDocument>();
|
||
foreach (var path in Directory.EnumerateFiles(packRoot, "*.*", SearchOption.AllDirectories))
|
||
{
|
||
if (!path.EndsWith(".jsonc", StringComparison.OrdinalIgnoreCase)
|
||
&& !path.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
var relative = Path.GetRelativePath(packRoot, path).Replace('\\', '/');
|
||
documents.Add(new ContentDocument(CatalogLoader.CorePackId, relative, File.ReadAllText(path)));
|
||
}
|
||
|
||
return documents;
|
||
}
|
||
}
|