Skip to content

Commit 8193682

Browse files
committed
[F&O] 修复一些小问题,补充测试等
1 parent 828dbe6 commit 8193682

1 file changed

Lines changed: 75 additions & 2 deletions

File tree

tests/chu/ChuTests.cs

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1+
using System.Reflection;
12
using MuConvert.chu;
2-
using MuConvert.parser;
3-
using MuConvert.utils;
43

54
namespace MuConvert.Tests.chu;
65

@@ -28,7 +27,67 @@ public void C2sRoundTrip()
2827
var (chart, _) = new C2sParser().Parse(File.ReadAllText(C2sPath));
2928
var (rt, _) = new C2sGenerator().Generate(chart);
3029
var (reparsed, _) = new C2sParser().Parse(rt);
30+
3131
Assert.Equal(chart.Notes.Count, reparsed.Notes.Count);
32+
33+
var originalSnapshots = chart.Notes
34+
.Select(SnapshotNote)
35+
.OrderBy(s => s)
36+
.ToArray();
37+
38+
var reparsedSnapshots = reparsed.Notes
39+
.Select(SnapshotNote)
40+
.OrderBy(s => s)
41+
.ToArray();
42+
43+
Assert.Equal(originalSnapshots, reparsedSnapshots);
44+
}
45+
46+
/// <summary>
47+
/// Builds a stable, comparable string from a note's public instance properties (name-sorted)
48+
/// so round-trip tests verify no field loss without hard-coding each property in the test.
49+
/// </summary>
50+
private static string SnapshotNote(ChuNote note)
51+
{
52+
var props = typeof(ChuNote).GetProperties(BindingFlags.Instance | BindingFlags.Public);
53+
var parts = props
54+
.OrderBy(p => p.Name)
55+
.Select(p => $"{p.Name}={p.GetValue(note)}");
56+
return string.Join("|", parts);
57+
}
58+
59+
/// <summary>
60+
/// Same tick scaling as <see cref="C2sGenerator"/> when converting UGC → C2S (384 ticks per measure).
61+
/// </summary>
62+
private static ChuNote UgcNoteScaledToC2sTicks(ChuNote n, int ticksPerBeat)
63+
{
64+
const int c2sResolution = 384;
65+
int scaleDown(int v) => (int)((long)v * (c2sResolution / 4) / ticksPerBeat);
66+
return new ChuNote
67+
{
68+
Type = n.Type, Measure = n.Measure, Offset = scaleDown(n.Offset),
69+
Cell = n.Cell, Width = n.Width,
70+
HoldDuration = scaleDown(n.HoldDuration), SlideDuration = scaleDown(n.SlideDuration),
71+
EndCell = n.EndCell, EndWidth = n.EndWidth,
72+
Extra = n.Extra, TargetNote = n.TargetNote, AirHoldDuration = scaleDown(n.AirHoldDuration),
73+
StartHeight = n.StartHeight, TargetHeight = n.TargetHeight, NoteColor = n.NoteColor,
74+
};
75+
}
76+
77+
/// <summary>
78+
/// Compares UGC-side notes to C2S-side notes in C2S tick space (384): snapshots of
79+
/// <see cref="UgcNoteScaledToC2sTicks"/> for each <paramref name="ugc"/> note vs snapshots of <paramref name="c2s"/> notes.
80+
/// Use with <c>UgcToC2sViaGenerator</c> (source UGC, C2S from generate+parse) or <c>C2sToUgcViaGenerator</c> (UGC from generate+parse, source C2S).
81+
/// </summary>
82+
private static void AssertUgcNotesEquivalentToReparsedC2s(UgcChart ugc, C2sChart c2s, bool isUgcReference)
83+
{
84+
var ugcSnaps = ugc.Notes
85+
.Select(n => SnapshotNote(UgcNoteScaledToC2sTicks(n, ugc.TicksPerBeat)))
86+
.OrderBy(s => s)
87+
.ToArray();
88+
var c2sSnaps = c2s.Notes.Select(SnapshotNote).OrderBy(s => s).ToArray();
89+
if (isUgcReference) Assert.Equal(ugcSnaps, c2sSnaps);
90+
else Assert.Equal(c2sSnaps, ugcSnaps);
3291
}
3392

3493
[Fact]
@@ -45,18 +104,32 @@ public void UgcToC2sViaGenerator()
45104
{
46105
if (!File.Exists(UgcPath)) throw new SkipException($"Missing: {UgcPath}");
47106
var (ugc, _) = new UgcParser().Parse(File.ReadAllText(UgcPath));
107+
Assert.NotEmpty(ugc.Notes);
108+
48109
var (c2sText, _) = new C2sGenerator().Generate(ugc);
49110
Assert.Contains("VERSION", c2sText);
50111
Assert.Contains("TAP\t", c2sText);
112+
113+
// 再把转出来的c2s,parse回去,比较是否和一开始的ugc等价(注意不是文本 round-trip,而是 IR 等价,允许字段重排但不允许信息丢失)
114+
var (c2sChart, _) = new C2sParser().Parse(c2sText);
115+
Assert.NotEmpty(c2sChart.Notes);
116+
AssertUgcNotesEquivalentToReparsedC2s(ugc, c2sChart, true);
51117
}
52118

53119
[Fact]
54120
public void C2sToUgcViaGenerator()
55121
{
56122
if (!File.Exists(C2sPath)) throw new SkipException($"Missing: {C2sPath}");
57123
var (c2s, _) = new C2sParser().Parse(File.ReadAllText(C2sPath));
124+
Assert.NotEmpty(c2s.Notes);
125+
58126
var (ugcText, _) = new UgcGenerator().Generate(c2s);
59127
Assert.Contains("@VER", ugcText);
60128
Assert.Contains("#5'0", ugcText);
129+
130+
// 再把转出来的ugc,parse回去,比较是否和一开始的c2s等价
131+
var (ugcReparsed, _) = new UgcParser().Parse(ugcText);
132+
Assert.NotEmpty(ugcReparsed.Notes);
133+
AssertUgcNotesEquivalentToReparsedC2s(ugcReparsed, c2s, false);
61134
}
62135
}

0 commit comments

Comments
 (0)