1
0

test: add test for tas oper and fix issue

This commit is contained in:
2026-03-31 14:26:17 +08:00
parent d0174bbf86
commit b8184c6ab4
6 changed files with 427 additions and 51 deletions

View File

@@ -1,4 +1,4 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -6,8 +6,8 @@ using System.Text;
using System.Threading.Tasks;
namespace BallanceTasEditorTests {
public static class AssertExtension {
public static T ThrowsDerivedException<T>(Action action) where T : Exception {
internal static class AssertExtension {
internal static T ThrowsDerivedException<T>(Action action) where T : Exception {
try {
action();
} catch (T ex) {

View File

@@ -10,12 +10,123 @@ namespace BallanceTasEditorTests.Backend {
[TestClass]
public class TasOperationTests {
private static IEnumerable<object[]> TasSequenceInstanceProvider {
#region Utilities
private static IEnumerable<object[]> TestDataCombiner<T>(IEnumerable<ITasSequence> seqs, IEnumerable<T> payloads) where T : notnull {
// YYC MARK:
// We must iterate payload as outside "for" and iterate sequence as inner "for",
// because if we flip this order, each payload will share the same reference to sequence,
// which cause test error.
foreach (T payload in payloads) {
foreach (ITasSequence seq in seqs) {
yield return new object[] { seq, payload };
}
}
}
private static void AssertRevocableOperation(ITasSequence sequence, ITasRevocableOperation op, string source, string expected) {
// Prepare environment
sequence.Clear();
GenerateSequence(sequence, source);
// Test execute
op.Execute(sequence);
Assert.AreEqual(expected, SummarizeSequence(sequence));
// Test revoke
op.Revoke(sequence);
Assert.AreEqual(source, SummarizeSequence(sequence));
// Test re-execute and re-revoke again
op.Execute(sequence);
Assert.AreEqual(expected, SummarizeSequence(sequence));
op.Revoke(sequence);
Assert.AreEqual(source, SummarizeSequence(sequence));
}
private static void AssertOperation(ITasSequence sequence, ITasOperation op, string source, string expected) {
// Prepare environment
sequence.Clear();
GenerateSequence(sequence, source);
// Test execute
op.Execute(sequence);
Assert.AreEqual(expected, SummarizeSequence(sequence));
}
private static void GenerateSequence(ITasSequence sequence, string pattern) {
var strFrame = pattern.Split(';');
var frameIter = strFrame.Select((s) => {
// Extract FPS and Keys
var framePair = s.Split(',');
var fps = uint.Parse(framePair[0]);
var keyFlags = uint.Parse(framePair[1]);
// Build raw frame and convert it to frame.
var rawFrame = new RawTasFrame() { TimeDelta = FpsConverter.ToDelta(fps), KeyFlags = keyFlags };
return TasFrame.FromRaw(rawFrame);
});
var frameExactSizeIter = new ExactSizeEnumerableAdapter<TasFrame>(frameIter, strFrame.Length);
sequence.Insert(0, frameExactSizeIter);
}
private static string SummarizeSequence(ITasSequence sequence) {
return string.Join(
";",
sequence.Select((f) => {
var rawFrame = f.ToRaw();
return $"{FpsConverter.ToRoundFps(rawFrame.TimeDelta)},{rawFrame.KeyFlags}";
})
);
}
#endregion
#region Cell Keys Operation
public record CellKeysOperationTestPayload {
public required string Source { get; init; }
public required string Expected { get; init; }
public required CellKeysOperationKind Kind { get; init; }
public required int StartIndex { get; init; }
public required int EndIndex { get; init; }
public required TasKey StartKey { get; init; }
public required TasKey EndKey { get; init; }
}
private static IEnumerable<CellKeysOperationTestPayload> GetCellKeysOperationTestPayload() {
yield return new CellKeysOperationTestPayload {
Source = "1,1;1,2;1,3;1,4;1,5",
Expected = "1,15;1,15;1,15;1,4;1,5",
Kind = CellKeysOperationKind.Set,
StartIndex = 0,
EndIndex = 2,
StartKey = TasKey.KEY_UP,
EndKey = TasKey.KEY_RIGHT
};
yield return new CellKeysOperationTestPayload {
Source = "1,1;1,2;1,3;1,4;1,5",
Expected = "1,0;1,0;1,0;1,4;1,5",
Kind = CellKeysOperationKind.Unset,
StartIndex = 0,
EndIndex = 2,
StartKey = TasKey.KEY_UP,
EndKey = TasKey.KEY_RIGHT
};
yield return new CellKeysOperationTestPayload {
Source = "1,1;1,2;1,3;1,4;1,5",
Expected = "1,14;1,13;1,12;1,4;1,5",
Kind = CellKeysOperationKind.Flip,
StartIndex = 0,
EndIndex = 2,
StartKey = TasKey.KEY_UP,
EndKey = TasKey.KEY_RIGHT
};
}
private static IEnumerable<object[]> CellKeysOperationTestDataProvider {
get {
yield return new object[] { new ListTasSequence() };
yield return new object[] { new LegacyTasSequence() };
// TODO: Add GapBufferTasSequence once we finish it.
//yield return new object[] { new GapBufferTasSequence() };
return TestDataCombiner(TasSequenceUtils.EnumerateTasSequenceImplementation(), GetCellKeysOperationTestPayload());
}
}
@@ -23,64 +134,280 @@ namespace BallanceTasEditorTests.Backend {
/// CellKeysOperation测试。
/// </summary>
[DataTestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
public void CellKeysOperationTest(ITasSequence sequence, CellKeysOperationKind kind, int startIndex, int endIndex, TasKey startKey, TasKey endKey) {
throw new NotImplementedException();
[DynamicData(nameof(CellKeysOperationTestDataProvider))]
public void CellKeysOperationTest(ITasSequence sequence, CellKeysOperationTestPayload payload) {
var op = CellKeysOperation.FromCellRange(payload.Kind, payload.StartIndex, payload.EndIndex, payload.StartKey, payload.EndKey);
AssertRevocableOperation(sequence, op, payload.Source, payload.Expected);
}
#endregion
#region Frame Fps Operation
public record FrameFpsOperationTestPayload {
public required string Source { get; init; }
public required string Expected { get; init; }
public required int StartIndex { get; init; }
public required int EndIndex { get; init; }
public required uint Fps { get; init; }
}
private static IEnumerable<FrameFpsOperationTestPayload> GetFrameFpsOperationTestPayload() {
yield return new FrameFpsOperationTestPayload {
Source = "1,1;1,2;1,3;1,4;1,5",
Expected = "2,1;2,2;2,3;2,4;1,5",
StartIndex = 0,
EndIndex = 3,
Fps = 2
};
yield return new FrameFpsOperationTestPayload {
Source = "1,1;1,2;1,3;1,4;1,5",
Expected = "1,1;4,2;4,3;4,4;4,5",
StartIndex = 1,
EndIndex = 4,
Fps = 4
};
}
private static IEnumerable<object[]> FrameFpsOperationTestDataProvider {
get {
return TestDataCombiner(TasSequenceUtils.EnumerateTasSequenceImplementation(), GetFrameFpsOperationTestPayload());
}
}
/// <summary>
/// FrameFpsOperation测试。
/// </summary>
[DataTestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
public void FrameFpsOperationTest(ITasSequence sequence) {
throw new NotImplementedException();
[DynamicData(nameof(FrameFpsOperationTestDataProvider))]
public void FrameFpsOperationTest(ITasSequence sequence, FrameFpsOperationTestPayload payload) {
var op = FrameFpsOperation.FromFrameRange(payload.StartIndex, payload.EndIndex, payload.Fps);
AssertRevocableOperation(sequence, op, payload.Source, payload.Expected);
}
#endregion
#region Remove Frame Operation
public record RemoveFrameOperationTestPayload {
public required string Source { get; init; }
public required string Expected { get; init; }
public required int StartIndex { get; init; }
public required int EndIndex { get; init; }
}
private static IEnumerable<RemoveFrameOperationTestPayload> GetRemoveFrameOperationTestPayload() {
yield return new RemoveFrameOperationTestPayload {
Source = "1,1;1,2;1,3;1,4;1,5",
Expected = "1,3;1,4;1,5",
StartIndex = 0,
EndIndex = 1
};
yield return new RemoveFrameOperationTestPayload {
Source = "1,1;1,2;1,3;1,4;1,5",
Expected = "",
StartIndex = 0,
EndIndex = 4
};
yield return new RemoveFrameOperationTestPayload {
Source = "1,1;1,2;1,3;1,4;1,5",
Expected = "1,1;1,2;1,3",
StartIndex = 3,
EndIndex = 4
};
yield return new RemoveFrameOperationTestPayload {
Source = "1,1;1,2;1,3;1,4;1,5",
Expected = "1,2;1,3;1,4;1,5",
StartIndex = 0,
EndIndex = 0
};
yield return new RemoveFrameOperationTestPayload {
Source = "1,1;1,2;1,3;1,4;1,5",
Expected = "1,1;1,2;1,3;1,4",
StartIndex = 4,
EndIndex = 4
};
}
private static IEnumerable<object[]> RemoveFrameOperationTestDataProvider {
get {
return TestDataCombiner(TasSequenceUtils.EnumerateTasSequenceImplementation(), GetRemoveFrameOperationTestPayload());
}
}
/// <summary>
/// RemoveFrameOperation测试。
/// </summary>
[DataTestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
public void RemoveFrameOperationTest(ITasSequence sequence) {
throw new NotImplementedException();
[DynamicData(nameof(RemoveFrameOperationTestDataProvider))]
public void RemoveFrameOperationTest(ITasSequence sequence, RemoveFrameOperationTestPayload payload) {
var op = new RemoveFrameOperation(payload.StartIndex, payload.EndIndex);
AssertRevocableOperation(sequence, op, payload.Source, payload.Expected);
}
#endregion
#region Add Frame Operation
public record AddFrameOperationTestPayload {
public required string Source { get; init; }
public required string Expected { get; init; }
public required AddFrameOperationKind Kind { get; init; }
public required int Index { get; init; }
public required uint Fps { get; init; }
public required int Count { get; init; }
}
private static IEnumerable<AddFrameOperationTestPayload> GetAddFrameOperationTestPayload() {
yield return new AddFrameOperationTestPayload {
Source = "1,1;1,2;1,3;1,4;1,5",
Expected = "1,1;1,2;240,0;240,0;240,0;1,3;1,4;1,5",
Kind = AddFrameOperationKind.Before,
Index = 2,
Fps = 240,
Count = 3
};
yield return new AddFrameOperationTestPayload {
Source = "1,1;1,2;1,3;1,4;1,5",
Expected = "1,1;1,2;1,3;240,0;240,0;240,0;1,4;1,5",
Kind = AddFrameOperationKind.After,
Index = 2,
Fps = 240,
Count = 3
};
yield return new AddFrameOperationTestPayload {
Source = "1,1;1,2;1,3;1,4;1,5",
Expected = "240,0;240,0;240,0;1,1;1,2;1,3;1,4;1,5",
Kind = AddFrameOperationKind.Before,
Index = 0,
Fps = 240,
Count = 3
};
yield return new AddFrameOperationTestPayload {
Source = "1,1;1,2;1,3;1,4;1,5",
Expected = "1,1;1,2;1,3;1,4;1,5;240,0;240,0;240,0",
Kind = AddFrameOperationKind.After,
Index = 4,
Fps = 240,
Count = 3
};
}
private static IEnumerable<object[]> AddFrameOperationTestDataProvider {
get {
return TestDataCombiner(TasSequenceUtils.EnumerateTasSequenceImplementation(), GetAddFrameOperationTestPayload());
}
}
/// <summary>
/// AddFrameOperation测试。
/// </summary>
[DataTestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
public void AddFrameOperationTest(ITasSequence sequence) {
throw new NotImplementedException();
[DynamicData(nameof(AddFrameOperationTestDataProvider))]
public void AddFrameOperationTest(ITasSequence sequence, AddFrameOperationTestPayload payload) {
var op = new AddFrameOperation(payload.Kind, payload.Index, payload.Fps, payload.Count);
AssertRevocableOperation(sequence, op, payload.Source, payload.Expected);
}
#endregion
#region Insert Frame Operation
public record InsertFrameOperationTestPayload {
public required string Source { get; init; }
public required string Inserted { get; init; }
public required string Expected { get; init; }
public required InsertFrameOperationKind Kind { get; init; }
public required int Index { get; init; }
public required uint Fps { get; init; }
public required int Count { get; init; }
}
private static IEnumerable<InsertFrameOperationTestPayload> GetInsertFrameOperationTestPayload() {
yield break;
}
private static IEnumerable<object[]> InsertFrameOperationTestDataProvider {
get {
return TestDataCombiner(TasSequenceUtils.EnumerateTasSequenceImplementation(), GetInsertFrameOperationTestPayload());
}
}
/// <summary>
/// InsertFrameOperation测试。
/// </summary>
[DataTestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
public void InsertFrameOperationTest(ITasSequence sequence) {
throw new NotImplementedException();
[DynamicData(nameof(InsertFrameOperationTestDataProvider))]
public void InsertFrameOperationTest(ITasSequence sequence, InsertFrameOperationTestPayload payload) {
//var op = new InsertFrameOperation(payload.Kind, payload.Index, payload.Fps, payload.Count);
//AssertRevocableOperation(sequence, op, payload.Source, payload.Expected);
}
#endregion
#region Clear Keys Operation
public record ClearKeysOperationTestPayload {
public required string Source { get; init; }
public required string Expected { get; init; }
}
private static IEnumerable<ClearKeysOperationTestPayload> GetClearKeysOperationTestPayload() {
yield break;
}
private static IEnumerable<object[]> ClearKeysOperationTestDataProvider {
get {
return TestDataCombiner(TasSequenceUtils.EnumerateTasSequenceImplementation(), GetClearKeysOperationTestPayload());
}
}
/// <summary>
/// ClearKeysOperation测试。
/// </summary>
[DataTestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
public void ClearKeysOperationTest(ITasSequence sequence) {
throw new NotImplementedException();
[DynamicData(nameof(ClearKeysOperationTestDataProvider))]
public void ClearKeysOperationTest(ITasSequence sequence, ClearKeysOperationTestPayload payload) {
var op = new ClearKeysOperation();
AssertOperation(sequence, op, payload.Source, payload.Expected);
}
#endregion
#region Uniform Fps Operation
public record UniformFpsOperationTestPayload {
public required string Source { get; init; }
public required string Expected { get; init; }
public required uint Fps { get; init; }
}
private static IEnumerable<UniformFpsOperationTestPayload> GetUniformFpsOperationTestPayload() {
yield break;
}
private static IEnumerable<object[]> UniformFpsOperationTestDataProvider {
get {
return TestDataCombiner(TasSequenceUtils.EnumerateTasSequenceImplementation(), GetUniformFpsOperationTestPayload());
}
}
/// <summary>
/// UniformFpsOperation测试。
/// </summary>
[DataTestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
public void UniformFpsOperationTest(ITasSequence sequence) {
throw new NotImplementedException();
[DynamicData(nameof(UniformFpsOperationTestDataProvider))]
public void UniformFpsOperationTest(ITasSequence sequence, UniformFpsOperationTestPayload payload) {
var op = new UniformFpsOperation(payload.Fps);
AssertOperation(sequence, op, payload.Source, payload.Expected);
}
#endregion
}
}

View File

@@ -28,12 +28,9 @@ namespace BallanceTasEditorTests.Backend {
return new ExactSizeEnumerableAdapter<TasFrame>(BLANK, BLANK.Length);
}
private static IEnumerable<object[]> TasSequenceInstanceProvider {
private static IEnumerable<object[]> CommonTestDataProvider {
get {
yield return new object[] { new ListTasSequence() };
yield return new object[] { new LegacyTasSequence() };
// TODO: Add GapBufferTasSequence once we finish it.
//yield return new object[] { new GapBufferTasSequence() };
return TasSequenceUtils.EnumerateTasSequenceImplementation().Select((seq) => new object[] { seq });
}
}
@@ -41,7 +38,7 @@ namespace BallanceTasEditorTests.Backend {
/// Visit函数独立测试。
/// </summary>
[DataTestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
[DynamicData(nameof(CommonTestDataProvider))]
public void VisitTest(ITasSequence sequence) {
// 空时访问
AssertExtension.ThrowsDerivedException<IndexOutOfRangeException>(() => sequence.Visit(-1));
@@ -62,7 +59,7 @@ namespace BallanceTasEditorTests.Backend {
/// Insert函数独立测试。
/// </summary>
[DataTestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
[DynamicData(nameof(CommonTestDataProvider))]
public void InsertTest(ITasSequence sequence) {
// 需要在不同的存储器上,分别检测在空的时候插入,
// 和在非空时的头,中,尾分别插入的结果。
@@ -101,7 +98,7 @@ namespace BallanceTasEditorTests.Backend {
/// Remove函数独立测试。
/// </summary>
[DataTestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
[DynamicData(nameof(CommonTestDataProvider))]
public void RemoveTest(ITasSequence sequence) {
// 插入项目后尝试在头中尾分别删除
var indices = new int[] { 0, PROBE.Length / 2, PROBE.Length - 1 };
@@ -128,7 +125,7 @@ namespace BallanceTasEditorTests.Backend {
/// Clear函数独立测试。
/// </summary>
[DataTestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
[DynamicData(nameof(CommonTestDataProvider))]
public void ClearTest(ITasSequence sequence) {
// 设置数据后清空
sequence.Insert(0, GetExactSizeProbe());
@@ -142,7 +139,7 @@ namespace BallanceTasEditorTests.Backend {
/// IsEmpty函数独立测试。
/// </summary>
[DataTestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
[DynamicData(nameof(CommonTestDataProvider))]
public void IsEmptyTest(ITasSequence sequence) {
// 检查是否为空
Assert.IsTrue(sequence.IsEmpty());
@@ -156,7 +153,7 @@ namespace BallanceTasEditorTests.Backend {
/// GetCount函数独立测试。
/// </summary>
[DataTestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
[DynamicData(nameof(CommonTestDataProvider))]
public void GetCountTest(ITasSequence sequence) {
// 检查长度为0
Assert.AreEqual(sequence.GetCount(), 0);
@@ -171,7 +168,7 @@ namespace BallanceTasEditorTests.Backend {
/// </summary>
/// <param name="sequence"></param>
[DataTestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
[DynamicData(nameof(CommonTestDataProvider))]
public void HybridTest(ITasSequence sequence) {
// 检查空和大小
Assert.IsTrue(sequence.IsEmpty());

View File

@@ -0,0 +1,22 @@
using BallanceTasEditor.Backend;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BallanceTasEditorTests {
internal static class TasSequenceUtils {
internal static IEnumerable<ITasSequence> EnumerateTasSequenceImplementation() {
yield return new ListTasSequence();
yield return new LegacyTasSequence();
// TODO: Add GapBufferTasSequence once we finish it.
//yield return new GapBufferTasSequence();
}
}
}