Skip to content

Commit 40b8ef7

Browse files
committed
Add some tests for movie undo/redo actions.
1 parent b91524a commit 40b8ef7

File tree

4 files changed

+308
-0
lines changed

4 files changed

+308
-0
lines changed

src/BizHawk.Client.Common/movie/bk2/StringLogs.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ public static IStringLog MakeStringLog()
3737
}
3838
}
3939

40+
if (newLog.Count != currentLog.Count)
41+
return Math.Min(newLog.Count, currentLog.Count);
42+
4043
return null;
4144
}
4245

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System.Collections.Generic;
2+
using System.IO;
3+
4+
using BizHawk.Client.Common;
5+
using BizHawk.Emulation.Common;
6+
using BizHawk.Tests.Emulation.Common;
7+
8+
namespace BizHawk.Tests.Client.Common.Movie
9+
{
10+
internal class FakeMovieSession : IMovieSession
11+
{
12+
public IMovieConfig Settings { get; set; }
13+
14+
public required IMovie Movie { get; set; }
15+
16+
public bool ReadOnly { get => false; set { } }
17+
18+
public bool NewMovieQueued => throw new NotImplementedException();
19+
20+
public string QueuedSyncSettings => throw new NotImplementedException();
21+
22+
public string QueuedCoreName => throw new NotImplementedException();
23+
24+
public string QueuedSysID => throw new NotImplementedException();
25+
26+
public IDictionary<string, object> UserBag { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
27+
28+
29+
public IMovieController MovieController => FakeEmulator.Controller;
30+
31+
public IController StickySource { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
32+
public IController MovieIn { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
33+
34+
public IInputAdapter MovieOut => throw new NotImplementedException();
35+
36+
public string BackupDirectory { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
37+
38+
public FakeMovieSession()
39+
{
40+
Settings = new MovieConfig();
41+
}
42+
43+
public void AbortQueuedMovie() => throw new NotImplementedException();
44+
public bool CheckSavestateTimeline(TextReader reader) => throw new NotImplementedException();
45+
public IMovieController GenerateMovieController(ControllerDefinition? definition = null, string? logKey = null) => throw new NotImplementedException();
46+
public IMovie Get(string path, bool loadMovie = false) => throw new NotImplementedException();
47+
public void HandleFrameAfter(bool ignoreMovieEndAction) => throw new NotImplementedException();
48+
public void HandleFrameBefore() => throw new NotImplementedException();
49+
public bool HandleLoadState(TextReader reader) => throw new NotImplementedException();
50+
public void HandleSaveState(TextWriter writer) => throw new NotImplementedException();
51+
public void PopupMessage(string message) => throw new NotImplementedException();
52+
public void QueueNewMovie(IMovie movie, string systemId, string loadedRomHash, PathEntryCollection pathEntries, IDictionary<string, string> preferredCores) => throw new NotImplementedException();
53+
public void RunQueuedMovie(bool recordMode, IEmulator emulator) => throw new NotImplementedException();
54+
public void StopMovie(bool saveChanges = true) => Movie.Stop();
55+
}
56+
}
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
using BizHawk.Client.Common;
2+
using BizHawk.Tests.Emulation.Common;
3+
4+
namespace BizHawk.Tests.Client.Common.Movie
5+
{
6+
[TestClass]
7+
public class MovieUndoTests
8+
{
9+
private TasMovie MakeMovie(int numberOfFrames)
10+
{
11+
FakeMovieSession session = new() { Movie = null! };
12+
TasMovie movie = new(session, "/fake/path");
13+
session.Movie = movie;
14+
15+
movie.Attach(new FakeEmulator());
16+
movie.InsertEmptyFrame(0, numberOfFrames);
17+
18+
return movie;
19+
}
20+
21+
private void ValidateActionCanUndoAndRedo(TasMovie movie, Action action, int expectedUndoItems = 1)
22+
{
23+
IStringLog originalLog = movie.GetLogEntries().Clone();
24+
int originalUndoLength = movie.ChangeLog.UndoIndex;
25+
action();
26+
27+
IStringLog changedLog = movie.GetLogEntries().Clone();
28+
int changedUndoLength = movie.ChangeLog.UndoIndex;
29+
int firstEditedFrame = originalLog.DivergentPoint(changedLog) ?? movie.InputLogLength;
30+
31+
Assert.AreEqual(originalUndoLength + expectedUndoItems, changedUndoLength);
32+
33+
// undo
34+
int undoFrame = int.MaxValue;
35+
for (int i = 0; i < expectedUndoItems; i++)
36+
undoFrame = Math.Min(undoFrame, movie.ChangeLog.Undo());
37+
Assert.AreEqual(firstEditedFrame, undoFrame);
38+
Assert.IsNull(originalLog.DivergentPoint(movie.GetLogEntries()));
39+
40+
// redo
41+
int redoFrame = int.MaxValue;
42+
for (int i = 0; i < expectedUndoItems; i++)
43+
redoFrame = Math.Min(redoFrame, movie.ChangeLog.Redo());
44+
Assert.AreEqual(firstEditedFrame, redoFrame);
45+
Assert.IsNull(changedLog.DivergentPoint(movie.GetLogEntries()));
46+
}
47+
48+
[TestMethod]
49+
public void SetBool()
50+
{
51+
TasMovie movie = MakeMovie(5);
52+
53+
ValidateActionCanUndoAndRedo(movie, () =>
54+
{
55+
movie.SetBoolState(2, "A", true);
56+
});
57+
}
58+
59+
[TestMethod]
60+
public void SetAxis()
61+
{
62+
TasMovie movie = MakeMovie(5);
63+
64+
ValidateActionCanUndoAndRedo(movie, () =>
65+
{
66+
movie.SetAxisState(2, "Stick", 20);
67+
});
68+
}
69+
70+
[TestMethod]
71+
public void InsertFrame()
72+
{
73+
TasMovie movie = MakeMovie(5);
74+
movie.SetBoolState(2, "A", true);
75+
movie.SetBoolState(3, "B", true);
76+
77+
ValidateActionCanUndoAndRedo(movie, () =>
78+
{
79+
movie.InsertEmptyFrame(3);
80+
});
81+
}
82+
83+
[TestMethod]
84+
public void DeleteFrame()
85+
{
86+
TasMovie movie = MakeMovie(5);
87+
movie.SetBoolState(2, "A", true);
88+
movie.SetBoolState(4, "B", true);
89+
90+
ValidateActionCanUndoAndRedo(movie, () =>
91+
{
92+
movie.RemoveFrame(3);
93+
});
94+
}
95+
96+
[TestMethod]
97+
public void CloneFrame()
98+
{
99+
TasMovie movie = MakeMovie(5);
100+
movie.SetBoolState(2, "A", true);
101+
movie.SetBoolState(3, "B", true);
102+
103+
ValidateActionCanUndoAndRedo(movie, () =>
104+
{
105+
movie.InsertInput(2, movie.GetInputLogEntry(3));
106+
});
107+
}
108+
109+
[TestMethod]
110+
public void MultipleEdits()
111+
{
112+
TasMovie movie = MakeMovie(5);
113+
114+
ValidateActionCanUndoAndRedo(movie, () =>
115+
{
116+
movie.SetBoolState(2, "A", true);
117+
movie.SetBoolState(3, "B", true);
118+
}, 2);
119+
}
120+
121+
[TestMethod]
122+
public void BatchedEdit()
123+
{
124+
TasMovie movie = MakeMovie(5);
125+
126+
ValidateActionCanUndoAndRedo(movie, () =>
127+
{
128+
movie.ChangeLog.BeginNewBatch();
129+
movie.SetBoolState(2, "A", true);
130+
movie.SetBoolState(3, "B", true);
131+
movie.ChangeLog.EndBatch();
132+
});
133+
}
134+
135+
[TestMethod]
136+
public void RecordFrameAtEnd()
137+
{
138+
TasMovie movie = MakeMovie(5);
139+
140+
ValidateActionCanUndoAndRedo(movie, () =>
141+
{
142+
Bk2Controller controller = new Bk2Controller(movie.Emulator.ControllerDefinition);
143+
controller.SetBool("A", true);
144+
movie.RecordFrame(5, controller);
145+
});
146+
}
147+
148+
[TestMethod]
149+
public void RecordFrameInMiddle()
150+
{
151+
TasMovie movie = MakeMovie(5);
152+
153+
ValidateActionCanUndoAndRedo(movie, () =>
154+
{
155+
Bk2Controller controller = new Bk2Controller(movie.Emulator.ControllerDefinition);
156+
controller.SetBool("A", true);
157+
movie.RecordFrame(2, controller);
158+
});
159+
}
160+
161+
[TestMethod]
162+
public void RecordFrameZero()
163+
{
164+
TasMovie movie = MakeMovie(5);
165+
166+
ValidateActionCanUndoAndRedo(movie, () =>
167+
{
168+
Bk2Controller controller = new Bk2Controller(movie.Emulator.ControllerDefinition);
169+
controller.SetBool("A", true);
170+
movie.RecordFrame(0, controller);
171+
});
172+
}
173+
}
174+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
using System.IO;
2+
3+
using BizHawk.Client.Common;
4+
using BizHawk.Common;
5+
using BizHawk.Emulation.Common;
6+
7+
namespace BizHawk.Tests.Emulation.Common
8+
{
9+
internal class FakeEmulator : IEmulator, IStatable, IInputPollable
10+
{
11+
private BasicServiceProvider _serviceProvider;
12+
public IEmulatorServiceProvider ServiceProvider => _serviceProvider;
13+
14+
private readonly static ControllerDefinition _cd = new ControllerDefinition("fake controller")
15+
{
16+
BoolButtons = { "A", "B" },
17+
}
18+
.AddAxis("Stick", (-100).RangeTo(100), 0)
19+
.MakeImmutable();
20+
public static IMovieController Controller;
21+
static FakeEmulator()
22+
{
23+
_cd.BuildMnemonicsCache("fake");
24+
Controller = new Bk2Controller(_cd);
25+
}
26+
27+
public ControllerDefinition ControllerDefinition => Controller.Definition;
28+
29+
public int Frame { get; set; }
30+
31+
public string SystemId => "fake";
32+
33+
public bool DeterministicEmulation => true;
34+
35+
public bool AvoidRewind => false;
36+
37+
public int LagCount { get; set; }
38+
public bool IsLagFrame { get; set; }
39+
40+
public IInputCallbackSystem InputCallbacks => throw new NotImplementedException();
41+
42+
public FakeEmulator()
43+
{
44+
_serviceProvider = new(this);
45+
}
46+
47+
public void Dispose() { }
48+
public bool FrameAdvance(IController controller, bool render, bool renderSound = true)
49+
{
50+
Frame++;
51+
return true;
52+
}
53+
54+
public void LoadStateBinary(BinaryReader reader)
55+
{
56+
Frame = reader.ReadInt32();
57+
LagCount = reader.ReadInt32();
58+
IsLagFrame = reader.ReadBoolean();
59+
}
60+
61+
public void ResetCounters()
62+
{
63+
Frame = 0;
64+
LagCount = 0;
65+
IsLagFrame = false;
66+
}
67+
68+
public void SaveStateBinary(BinaryWriter writer)
69+
{
70+
writer.Write(Frame);
71+
writer.Write(LagCount);
72+
writer.Write(IsLagFrame);
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)