-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRuntimeModelTests.cs
More file actions
195 lines (161 loc) · 10.4 KB
/
Copy pathRuntimeModelTests.cs
File metadata and controls
195 lines (161 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
using BoplEight.Match;
using BoplEight.Protocol;
using BoplEight.Ui;
namespace BoplEight.Tests
{
internal static class RuntimeModelTests
{
public static void RosterStateAcceptsStrictlyNewerHostRosters()
{
var state = new RosterState();
string reason;
Assert.True(state.TryActivate(NewRoster(10), 1001, out reason), "The first owner roster should activate. Reason: " + reason);
Assert.True(state.TryActivate(NewRoster(11), 1001, out reason), "A newer owner roster should replace the active roster. Reason: " + reason);
Assert.Equal((ushort)11, state.ActiveRoster.Settings.SequenceNumber, "The newest roster should remain active.");
}
public static void RosterStateAcceptsSequenceWraparound()
{
var state = new RosterState();
string reason;
Assert.True(state.TryActivate(NewRoster(ushort.MaxValue), 1001, out reason), "The pre-wrap roster should activate. Reason: " + reason);
Assert.True(state.TryActivate(NewRoster(0), 1001, out reason), "Sequence zero should be newer after ushort wraparound. Reason: " + reason);
}
public static void RosterStateRejectsStaleAndForeignRosters()
{
var state = new RosterState();
string reason;
Assert.False(state.TryActivate(NewRoster(20), 9999, out reason), "A sender absent from the roster must be rejected.");
Assert.True(state.TryActivate(NewRoster(20), 1001, out reason), "A valid initial roster should activate. Reason: " + reason);
Assert.False(state.TryActivate(NewRoster(20), 1001, out reason), "A duplicate sequence must be rejected.");
Assert.False(state.TryActivate(NewRoster(19), 1001, out reason), "An older sequence must be rejected.");
}
public static void RosterFramesSupportSparseEightPlayerSlots()
{
var frame = new RosterFrame<int>(new byte[] { 0, 3, 7 });
frame.Set(0, 10);
frame.Set(3, 30);
Assert.False(frame.IsComplete, "A sparse frame should remain incomplete until every roster slot has input.");
frame.Set(7, 70);
Assert.True(frame.IsComplete, "Slots zero through seven must be representable without requiring contiguous players.");
Assert.Equal(70, frame.Get(7), "The eighth simulation slot should retain its input.");
Assert.Throws<System.ArgumentException>(delegate { frame.Set(6, 60); }, "Frames must reject inputs from slots outside their roster.");
}
public static void RosterLayoutPlacesEightPlayersInUniqueColumns()
{
const float left = -420f;
const float right = 420f;
float previous = RosterLayout.ColumnCenter(left, right, 0, ProtocolConstants.MaximumPlayers);
for (var slot = 1; slot < ProtocolConstants.MaximumPlayers; slot++)
{
float current = RosterLayout.ColumnCenter(left, right, slot, ProtocolConstants.MaximumPlayers);
Assert.True(current > previous, "Every player must occupy a distinct left-to-right column.");
previous = current;
}
}
public static void RosterLayoutKeepsPlayerFiveSeparateFromPlayerOne()
{
float playerOne = RosterLayout.ColumnCenter(-420f, 420f, 0, ProtocolConstants.MaximumPlayers);
float playerFive = RosterLayout.ColumnCenter(-420f, 420f, 4, ProtocolConstants.MaximumPlayers);
Assert.False(playerOne == playerFive, "Player five must not wrap onto player one's column.");
}
public static void RosterLayoutFitsEightAbilitySelectorsAcrossVanillaWidth()
{
const float vanillaSeparation = 200f;
float fitted = RosterLayout.FittedSeparation(vanillaSeparation, ProtocolConstants.MaximumPlayers);
Assert.Equal(RosterLayout.ScorePortraitSeparation, fitted, "Eight score portraits must use their measured non-overlapping card separation.");
}
public static void RosterLayoutPreservesPrefabScaleWhenFittingEightColumns()
{
const float originalButtonScale = 0.2843f;
const float unscaledButtonWidth = 64f;
const float originalFirstCenter = 3.5f;
const float originalLastCenter = 63.5f;
float fittedScale = RosterLayout.FittedScale(originalButtonScale);
float eightColumnSpacing = (originalLastCenter - originalFirstCenter) / 7f;
Assert.True(fittedScale < originalButtonScale, "Eight-column fitting must multiply the prefab scale instead of replacing it.");
Assert.True(unscaledButtonWidth * fittedScale < eightColumnSpacing, "Fitted Steam portrait hitboxes must not overlap adjacent columns.");
}
public static void RosterLayoutKeepsOffsetReadyCardOnVisualBaseline()
{
const float localRoot = -51f;
const float remoteVisualBaseline = -610f;
float fittedRoot = RosterLayout.FittedRootPosition(localRoot, remoteVisualBaseline);
float fittedChildOffset = (remoteVisualBaseline - localRoot) * RosterLayout.Scale;
Assert.Equal(remoteVisualBaseline, fittedRoot + fittedChildOffset, "Scaling the local selector root must keep its offset ready card aligned with remote cards.");
}
public static void RosterLayoutUsesStoredRestingPositionAfterAnimationAwake()
{
const float localRoot = -51f;
const float currentHiddenRemotePosition = 3000f;
const float storedRemoteRestingPosition = -610f;
float baseline = RosterLayout.AnimationRestingPosition(
currentHiddenRemotePosition,
new float[] { storedRemoteRestingPosition });
float fittedRoot = RosterLayout.FittedRootPosition(localRoot, baseline);
Assert.Equal(storedRemoteRestingPosition, baseline, "Layout must not use the remote card's animated hidden position as its visual baseline.");
Assert.True(System.Math.Abs(fittedRoot - -386.4f) < 0.01f, "The local card must remain on-screen after remote animations initialize.");
}
public static void RosterLayoutExpandsAnimationTravelForScaledCards()
{
float fitted = RosterLayout.FittedAnimationBoundary(3000f, 0f);
Assert.True(System.Math.Abs(fitted - 7500f) < 0.01f, "Scaled character cards must retain the original screen-space animation travel.");
}
public static void RosterLayoutStartsScaledControlsAtTheFittedBoundary()
{
float initialPosition = RosterLayout.InitialAnimationPosition(3000f, 0f);
Assert.True(System.Math.Abs(initialPosition - 7500f) < 0.01f, "Controls hidden on first lobby load must use the fitted off-screen boundary before their first animation.");
}
public static void RosterLayoutPreservesEnteringJoinControl()
{
Assert.False(
RosterLayout.ShouldSetInitialAnimationPosition(true),
"The auto-entering join control must not be reset off-screen.");
Assert.True(
RosterLayout.ShouldSetInitialAnimationPosition(false),
"Inactive template controls must begin at the fitted off-screen boundary.");
}
public static void RosterLayoutKeepsScorePortraitsCompact()
{
const float vanillaSeparation = 230f;
const float visualCardWidth = 107.17f;
float fivePlayerSeparation = RosterLayout.FittedSeparation(vanillaSeparation, 5);
float eightPlayerSeparation = RosterLayout.FittedSeparation(vanillaSeparation, 8);
Assert.True(fivePlayerSeparation >= visualCardWidth, "Five score portraits must not overlap.");
Assert.True(eightPlayerSeparation >= visualCardWidth, "Eight score portraits must not overlap.");
Assert.Equal(RosterLayout.ScorePortraitSeparation, fivePlayerSeparation, "Five score portraits must use the compact measured separation.");
Assert.Equal(RosterLayout.ScorePortraitSeparation, eightPlayerSeparation, "Eight score portraits must use the compact measured separation.");
}
public static void RosterLayoutPreservesRoundSummaryTiming()
{
const float vanillaSpacing = 0.3f;
const float vanillaNextLevelStagger = vanillaSpacing * 5f;
float fivePlayerStagger = RosterLayout.FittedRoundSummaryDelaySpacing(vanillaSpacing, 5) * 6f;
float eightPlayerStagger = RosterLayout.FittedRoundSummaryDelaySpacing(vanillaSpacing, 8) * 9f;
Assert.True(System.Math.Abs(fivePlayerStagger - vanillaNextLevelStagger) < 0.01f, "Five players must not delay the next-round control reveal.");
Assert.True(System.Math.Abs(eightPlayerStagger - vanillaNextLevelStagger) < 0.01f, "Eight players must retain the four-player next-round control timing.");
}
public static void SparseAvatarReadinessPreservesConnectionSlots()
{
var ready = new bool[] { true, false, false, true };
Assert.Equal(0, AvatarSlotMapping.ConnectionIndexForSquare(0, ready.Length, ready[0]), "The first ready avatar should remain in the first connection slot.");
Assert.Equal(-1, AvatarSlotMapping.ConnectionIndexForSquare(1, ready.Length, ready[1]), "An unfinished avatar must leave its own slot loading.");
Assert.Equal(3, AvatarSlotMapping.ConnectionIndexForSquare(3, ready.Length, ready[3]), "A later completed avatar must not disappear behind an unfinished earlier request.");
}
public static void RosterLayoutIdentifiesOnlyExpandedRemoteSlots()
{
Assert.True(RosterLayout.IsExpandedRemoteSlot("BoplEight Remote Slot 5"), "Expanded remote slots must be activated before initialization.");
Assert.False(RosterLayout.IsExpandedRemoteSlot("Remote Slot 5"), "Vanilla slots must retain their existing lifecycle.");
Assert.False(RosterLayout.IsExpandedRemoteSlot(null), "Missing slot names must not be treated as expanded slots.");
}
private static StartRoster NewRoster(ushort sequence)
{
return new StartRoster(
new MatchStartSettings(sequence, 123, 3, 4, 8, 3),
new PlayerDescriptor[]
{
ProtocolTests.NewPlayer(0, 1001, 0, 0, false, 1, 2, 3),
ProtocolTests.NewPlayer(7, 1008, 7, 7, true, 4, 5, 6)
});
}
}
}