Skip to content

Commit ce4a240

Browse files
committed
finish repo testing
1 parent 06b74d9 commit ce4a240

6 files changed

Lines changed: 665 additions & 28 deletions

File tree

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
using System.IO;
2+
using System.Linq;
3+
using PG.StarWarsGame.Engine.ErrorReporting;
4+
using Xunit;
5+
6+
namespace PG.StarWarsGame.Engine.Test.IO.Foc;
7+
8+
public partial class FocGameRepositoryTests
9+
{
10+
[Fact]
11+
public void Init_LoadsEawFallbackPatchMegs()
12+
{
13+
using var repo = CreateBuilder()
14+
.WithFallbackGame(f =>
15+
{
16+
f.WriteMeg("Data/Patch.meg", m => m.Add("Init/EawPatch.bin", "EawPatch"));
17+
f.WriteMeg("Data/Patch2.meg", m => m.Add("Init/EawPatch2.bin", "EawPatch2"));
18+
f.WriteMeg("Data/64Patch.meg", m => m.Add("Init/Eaw64Patch.bin", "Eaw64Patch"));
19+
})
20+
.Build();
21+
var gameRepo = CreateRepository(repo);
22+
23+
Assert.Equal("EawPatch", ReadAll(gameRepo.OpenFile("Init/EawPatch.bin", megFileOnly: true)));
24+
Assert.Equal("EawPatch2", ReadAll(gameRepo.OpenFile("Init/EawPatch2.bin", megFileOnly: true)));
25+
Assert.Equal("Eaw64Patch", ReadAll(gameRepo.OpenFile("Init/Eaw64Patch.bin", megFileOnly: true)));
26+
}
27+
28+
[Fact]
29+
public void Init_LoadsEawFallbackMegaFilesXmlMegs()
30+
{
31+
const string megaFilesXml = """
32+
<?xml version="1.0" encoding="utf-8"?>
33+
<MegaFiles>
34+
<File>Data/EawCustom.meg</File>
35+
</MegaFiles>
36+
""";
37+
using var repo = CreateBuilder()
38+
.WithFallbackGame(f =>
39+
{
40+
f.Write("Data/MegaFiles.xml", megaFilesXml);
41+
f.WriteMeg("Data/EawCustom.meg", m => m.Add("Init/InEawCustom.bin", "EawCustom"));
42+
})
43+
.Build();
44+
var gameRepo = CreateRepository(repo);
45+
46+
Assert.Equal("EawCustom", ReadAll(gameRepo.OpenFile("Init/InEawCustom.bin", megFileOnly: true)));
47+
}
48+
49+
[Fact]
50+
public void MasterMeg_EawPatch2OverridesEawPatch()
51+
{
52+
using var repo = CreateBuilder()
53+
.WithFallbackGame(f =>
54+
{
55+
f.WriteMeg("Data/Patch.meg", m => m.Add("Init/EawConflict.bin", "EawPatch"));
56+
f.WriteMeg("Data/Patch2.meg", m => m.Add("Init/EawConflict.bin", "EawPatch2"));
57+
})
58+
.Build();
59+
var gameRepo = CreateRepository(repo);
60+
61+
Assert.Equal("EawPatch2", ReadAll(gameRepo.OpenFile("Init/EawConflict.bin", megFileOnly: true)));
62+
}
63+
64+
[Fact]
65+
public void MasterMeg_FocPatchOverridesEaw64Patch()
66+
{
67+
// All four EaW slots are loaded before any FoC slot, so even the latest EaW (64Patch)
68+
// gets overridden by the earliest FoC (Patch).
69+
//
70+
// The empty FoC Patch2/64Patch are necessary: the FoC ctor probes "Data/Patch2.meg" and
71+
// "Data/64Patch.meg" via the full lookup chain (mods → game → master → fallback). Without
72+
// empty game-dir shadows, the foc 64Patch probe would fall through to the fallback's 64Patch
73+
// and re-load the EaW entry as the very last write, defeating the test.
74+
using var repo = CreateBuilder()
75+
.WithFallbackGame(f => f.WriteMeg("Data/64Patch.meg",
76+
m => m.Add("Init/CrossConflict.bin", "Eaw64Patch")))
77+
.ConfigureGame(g =>
78+
{
79+
g.WriteMeg("Data/Patch.meg", m => m.Add("Init/CrossConflict.bin", "FocPatch"));
80+
g.WriteMeg("Data/Patch2.meg", _ => { });
81+
g.WriteMeg("Data/64Patch.meg", _ => { });
82+
})
83+
.Build();
84+
var gameRepo = CreateRepository(repo);
85+
86+
Assert.Equal("FocPatch", ReadAll(gameRepo.OpenFile("Init/CrossConflict.bin", megFileOnly: true)));
87+
}
88+
89+
[Fact]
90+
public void MasterMeg_FocMegaFilesXmlOverridesEaw64Patch()
91+
{
92+
const string focMegaFilesXml = """
93+
<?xml version="1.0" encoding="utf-8"?>
94+
<MegaFiles>
95+
<File>Data/FocCustom.meg</File>
96+
</MegaFiles>
97+
""";
98+
using var repo = CreateBuilder()
99+
.WithFallbackGame(f => f.WriteMeg("Data/64Patch.meg",
100+
m => m.Add("Init/CrossConflict.bin", "Eaw64Patch")))
101+
.ConfigureGame(g =>
102+
{
103+
g.Write("Data/MegaFiles.xml", focMegaFilesXml);
104+
g.WriteMeg("Data/FocCustom.meg", m => m.Add("Init/CrossConflict.bin", "FocCustom"));
105+
g.WriteMeg("Data/Patch.meg", _ => { });
106+
g.WriteMeg("Data/Patch2.meg", _ => { });
107+
g.WriteMeg("Data/64Patch.meg", _ => { }); // shadow fallback's 64Patch
108+
})
109+
.Build();
110+
var gameRepo = CreateRepository(repo);
111+
112+
Assert.Equal("FocCustom", ReadAll(gameRepo.OpenFile("Init/CrossConflict.bin", megFileOnly: true)));
113+
}
114+
115+
[Fact]
116+
public void ErrorReporter_MissingEawPatches_AssertsFileNotFound_WhenFallbackConfigured()
117+
{
118+
using var repo = CreateBuilder()
119+
.WithFallbackGame(_ => { })
120+
.Build();
121+
var reporter = new RecordingErrorReporter();
122+
123+
_ = CreateRepository(repo, reporter);
124+
125+
var names = reporter.Asserts
126+
.Where(a => a.Kind == EngineAssertKind.FileNotFound)
127+
.Select(a => Path.GetFileName(a.Value))
128+
.ToList();
129+
// 6 missing: Patch / Patch2 / 64Patch in both the fallback and the FoC dir.
130+
Assert.Equal(2, names.Count(v => v == "Patch.meg"));
131+
Assert.Equal(2, names.Count(v => v == "Patch2.meg"));
132+
Assert.Equal(2, names.Count(v => v == "64Patch.meg"));
133+
}
134+
135+
[Fact]
136+
public void ErrorReporter_NoFallbackConfigured_NoEawAttempts()
137+
{
138+
using var repo = CreateBuilder().Build();
139+
var reporter = new RecordingErrorReporter();
140+
141+
_ = CreateRepository(repo, reporter);
142+
143+
var fileNotFound = reporter.Asserts
144+
.Where(a => a.Kind == EngineAssertKind.FileNotFound)
145+
.ToList();
146+
Assert.Equal(3, fileNotFound.Count);
147+
}
148+
}

src/PetroglyphTools/PG.StarWarsGame.Engine.Test/IO/GameRepositoryTests.FileLookup.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,4 +196,14 @@ public void FileExists_DataPathWithDotPrefix_FallbackHit()
196196
Assert.True(gameRepo.FileExists("./Data\\XML\\Foo.xml"));
197197
Assert.True(gameRepo.FileExists(".\\Data/XML\\Foo.xml"));
198198
}
199+
200+
[Fact]
201+
public void EmptyRepository_NoErrors_LookupReturnsFalse()
202+
{
203+
using var repo = CreateBuilder().Build();
204+
var gameRepo = CreateRepository(repo);
205+
206+
Assert.False(gameRepo.FileExists("anything.txt"));
207+
Assert.Null(gameRepo.TryOpenFile("anything.txt"));
208+
}
199209
}
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
using System.IO;
2+
using System.Linq;
3+
using PG.StarWarsGame.Engine.ErrorReporting;
4+
using Xunit;
5+
6+
namespace PG.StarWarsGame.Engine.Test.IO;
7+
8+
public abstract partial class GameRepositoryTests
9+
{
10+
// ----------------------- pre-load: which MEGs are loaded at construction time -----------------------
11+
12+
[Fact]
13+
public void Init_LoadsAllPatchMegs()
14+
{
15+
using var repo = CreateBuilder()
16+
.ConfigureGame(g =>
17+
{
18+
g.WriteMeg("Data/Patch.meg", m => m.Add("Init/InPatch.bin", "Patch"));
19+
g.WriteMeg("Data/Patch2.meg", m => m.Add("Init/InPatch2.bin", "Patch2"));
20+
g.WriteMeg("Data/64Patch.meg", m => m.Add("Init/In64Patch.bin", "64Patch"));
21+
})
22+
.Build();
23+
var gameRepo = CreateRepository(repo);
24+
25+
Assert.Equal("Patch", ReadAll(gameRepo.OpenFile("Init/InPatch.bin", megFileOnly: true)));
26+
Assert.Equal("Patch2", ReadAll(gameRepo.OpenFile("Init/InPatch2.bin", megFileOnly: true)));
27+
Assert.Equal("64Patch", ReadAll(gameRepo.OpenFile("Init/In64Patch.bin", megFileOnly: true)));
28+
}
29+
30+
[Fact]
31+
public void Init_LoadsMegsListedInMegaFilesXml()
32+
{
33+
const string megaFilesXml = """
34+
<?xml version="1.0" encoding="utf-8"?>
35+
<MegaFiles>
36+
<File>Data/First.meg</File>
37+
<File>Data/Second.meg</File>
38+
</MegaFiles>
39+
""";
40+
using var repo = CreateBuilder()
41+
.ConfigureGame(g =>
42+
{
43+
g.Write("Data/MegaFiles.xml", megaFilesXml);
44+
g.WriteMeg("Data/First.meg", m => m.Add("Init/InFirst.bin", "First"));
45+
g.WriteMeg("Data/Second.meg", m => m.Add("Init/InSecond.bin", "Second"));
46+
})
47+
.Build();
48+
var gameRepo = CreateRepository(repo);
49+
50+
Assert.Equal("First", ReadAll(gameRepo.OpenFile("Init/InFirst.bin", megFileOnly: true)));
51+
Assert.Equal("Second", ReadAll(gameRepo.OpenFile("Init/InSecond.bin", megFileOnly: true)));
52+
}
53+
54+
// ----------------------- master MEG ordering: later load wins -----------------------
55+
56+
[Fact]
57+
public void MasterMeg_Patch2OverridesPatch()
58+
{
59+
using var repo = CreateBuilder()
60+
.ConfigureGame(g =>
61+
{
62+
g.WriteMeg("Data/Patch.meg", m => m.Add("Init/Conflict.bin", "Patch"));
63+
g.WriteMeg("Data/Patch2.meg", m => m.Add("Init/Conflict.bin", "Patch2"));
64+
})
65+
.Build();
66+
var gameRepo = CreateRepository(repo);
67+
68+
Assert.Equal("Patch2", ReadAll(gameRepo.OpenFile("Init/Conflict.bin", megFileOnly: true)));
69+
}
70+
71+
[Fact]
72+
public void MasterMeg_64PatchOverridesPatch2()
73+
{
74+
using var repo = CreateBuilder()
75+
.ConfigureGame(g =>
76+
{
77+
g.WriteMeg("Data/Patch2.meg", m => m.Add("Init/Conflict.bin", "Patch2"));
78+
g.WriteMeg("Data/64Patch.meg", m => m.Add("Init/Conflict.bin", "64Patch"));
79+
})
80+
.Build();
81+
var gameRepo = CreateRepository(repo);
82+
83+
Assert.Equal("64Patch", ReadAll(gameRepo.OpenFile("Init/Conflict.bin", megFileOnly: true)));
84+
}
85+
86+
[Fact]
87+
public void MasterMeg_PatchOverridesMegaFilesXmlEntries()
88+
{
89+
const string megaFilesXml = """
90+
<?xml version="1.0" encoding="utf-8"?>
91+
<MegaFiles>
92+
<File>Data/Custom.meg</File>
93+
</MegaFiles>
94+
""";
95+
using var repo = CreateBuilder()
96+
.ConfigureGame(g =>
97+
{
98+
g.Write("Data/MegaFiles.xml", megaFilesXml);
99+
g.WriteMeg("Data/Custom.meg", m => m.Add("Init/Conflict.bin", "Custom"));
100+
g.WriteMeg("Data/Patch.meg", m => m.Add("Init/Conflict.bin", "Patch"));
101+
})
102+
.Build();
103+
var gameRepo = CreateRepository(repo);
104+
105+
Assert.Equal("Patch", ReadAll(gameRepo.OpenFile("Init/Conflict.bin", megFileOnly: true)));
106+
}
107+
108+
[Fact]
109+
public void MasterMeg_MegaFilesXml_LaterMegOverridesEarlier()
110+
{
111+
const string megaFilesXml = """
112+
<?xml version="1.0" encoding="utf-8"?>
113+
<MegaFiles>
114+
<File>Data/First.meg</File>
115+
<File>Data/Second.meg</File>
116+
</MegaFiles>
117+
""";
118+
using var repo = CreateBuilder()
119+
.ConfigureGame(g =>
120+
{
121+
g.Write("Data/MegaFiles.xml", megaFilesXml);
122+
g.WriteMeg("Data/First.meg", m => m.Add("Init/Conflict.bin", "First"));
123+
g.WriteMeg("Data/Second.meg", m => m.Add("Init/Conflict.bin", "Second"));
124+
})
125+
.Build();
126+
var gameRepo = CreateRepository(repo);
127+
128+
Assert.Equal("Second", ReadAll(gameRepo.OpenFile("Init/Conflict.bin", megFileOnly: true)));
129+
}
130+
131+
// ----------------------- error reporter signals at init -----------------------
132+
133+
[Fact]
134+
public void ErrorReporter_MissingPatchMegs_AssertsFileNotFound()
135+
{
136+
// Empty repo (no fallback): the ctor will probe each patch slot and miss all three.
137+
using var repo = CreateBuilder().Build();
138+
var reporter = new RecordingErrorReporter();
139+
140+
_ = CreateRepository(repo, reporter);
141+
142+
var fileNotFoundNames = reporter.Asserts
143+
.Where(a => a.Kind == EngineAssertKind.FileNotFound)
144+
.Select(a => Path.GetFileName(a.Value))
145+
.ToList();
146+
Assert.Contains("Patch.meg", fileNotFoundNames);
147+
Assert.Contains("Patch2.meg", fileNotFoundNames);
148+
Assert.Contains("64Patch.meg", fileNotFoundNames);
149+
}
150+
151+
[Fact]
152+
public void ErrorReporter_AllPatchesPresent_NoFileNotFoundForPatches()
153+
{
154+
using var repo = CreateBuilder()
155+
.ConfigureGame(g =>
156+
{
157+
g.WriteMeg("Data/Patch.meg", _ => { });
158+
g.WriteMeg("Data/Patch2.meg", _ => { });
159+
g.WriteMeg("Data/64Patch.meg", _ => { });
160+
})
161+
.Build();
162+
var reporter = new RecordingErrorReporter();
163+
164+
_ = CreateRepository(repo, reporter);
165+
166+
var fileNotFoundNames = reporter.Asserts
167+
.Where(a => a.Kind == EngineAssertKind.FileNotFound)
168+
.Select(a => Path.GetFileName(a.Value))
169+
.ToList();
170+
Assert.DoesNotContain("Patch.meg", fileNotFoundNames);
171+
Assert.DoesNotContain("Patch2.meg", fileNotFoundNames);
172+
Assert.DoesNotContain("64Patch.meg", fileNotFoundNames);
173+
}
174+
175+
[Fact]
176+
public void ErrorReporter_MegaFilesXmlReferencesMissingMeg_AssertsFileNotFound()
177+
{
178+
const string megaFilesXml = """
179+
<?xml version="1.0" encoding="utf-8"?>
180+
<MegaFiles>
181+
<File>Data/DoesNotExist.meg</File>
182+
</MegaFiles>
183+
""";
184+
using var repo = CreateBuilder()
185+
.ConfigureGame(g => g.Write("Data/MegaFiles.xml", megaFilesXml))
186+
.Build();
187+
var reporter = new RecordingErrorReporter();
188+
189+
_ = CreateRepository(repo, reporter);
190+
191+
Assert.Contains(reporter.Asserts, a =>
192+
a.Kind == EngineAssertKind.FileNotFound && Path.GetFileName(a.Value) == "DoesNotExist.meg");
193+
}
194+
195+
[Fact]
196+
public void ErrorReporter_MissingSpeechMeg_DoesNotAssert()
197+
{
198+
// Speech.meg paths are intentionally silent: missing speech files only emit a debug log.
199+
const string megaFilesXml = """
200+
<?xml version="1.0" encoding="utf-8"?>
201+
<MegaFiles>
202+
<File>Data/EnglishSpeech.meg</File>
203+
</MegaFiles>
204+
""";
205+
using var repo = CreateBuilder()
206+
.ConfigureGame(g => g.Write("Data/MegaFiles.xml", megaFilesXml))
207+
.Build();
208+
var reporter = new RecordingErrorReporter();
209+
210+
_ = CreateRepository(repo, reporter);
211+
212+
Assert.DoesNotContain(reporter.Asserts, a =>
213+
a.Kind == EngineAssertKind.FileNotFound && Path.GetFileName(a.Value) == "EnglishSpeech.meg");
214+
}
215+
}

0 commit comments

Comments
 (0)