Skip to content

Commit f92777d

Browse files
committed
[+] 补一些测试
1 parent e465881 commit f92777d

10 files changed

Lines changed: 4540 additions & 24 deletions

File tree

tests/MA2_103测试.cs

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
using System.Text;
2+
using MuConvert.generator;
3+
using MuConvert.maidata;
4+
using MuConvert.parser;
5+
using MuConvert.utils;
6+
using static MuConvert.Tests.TestUtils;
7+
8+
namespace MuConvert.Tests;
9+
10+
/// <summary>
11+
/// 官谱中 golden MA2 头为 <c>1.03.00</c> 的谱面:Simai(lv5)→ <see cref="MA2_103Generator"/> 与对应 <c>*03.ma2</c> 音符段一致。
12+
/// </summary>
13+
public class MA2_103测试
14+
{
15+
public static IEnumerable<object[]> Official103Lv5()
16+
{
17+
const int levelId = 5;
18+
var repoRoot = FindRepoRoot();
19+
var testsetRoot = Path.Combine(repoRoot.FullName, "tests", "testset", "官谱");
20+
if (!Directory.Exists(testsetRoot))
21+
throw new DirectoryNotFoundException($"Testset root not found: {testsetRoot}");
22+
23+
foreach (var maidataPath in Directory.EnumerateFiles(testsetRoot, "maidata.txt", SearchOption.AllDirectories)
24+
.OrderBy(p => p, StringComparer.Ordinal))
25+
{
26+
var maidata = new Maidata(File.ReadAllText(maidataPath, Encoding.UTF8));
27+
if (!maidata.Levels.ContainsKey(levelId))
28+
continue;
29+
30+
var input = new TestInput(maidataPath, levelId);
31+
var golden = File.ReadAllText(input.MA2, Encoding.UTF8);
32+
if (TryParseMa2HeaderVersion(golden) != 103)
33+
continue;
34+
35+
yield return [input];
36+
}
37+
}
38+
39+
[Theory]
40+
[MemberData(nameof(Official103Lv5))]
41+
public void Simai转MA2_103(TestInput input)
42+
{
43+
var maidata = new Maidata(File.ReadAllText(input.Maidata, Encoding.UTF8));
44+
var chartInfo = maidata.Levels[input.LevelId];
45+
var expectedMa2 = File.ReadAllText(input.MA2, Encoding.UTF8);
46+
47+
var (chart, parseAlerts) = new SimaiParser(bigTouch: false, clockCount: maidata.ClockCount).Parse(chartInfo.Inote);
48+
Assert.DoesNotContain(parseAlerts, a => a.Level >= Alert.LEVEL.Error);
49+
50+
var (ma2, genAlerts) = new MA2_103Generator(isUtage: false).Generate(chart);
51+
Assert.DoesNotContain(genAlerts, a => a.Level >= Alert.LEVEL.Error);
52+
53+
ma2 = KeepNotesOnly(ma2);
54+
expectedMa2 = KeepNotesOnly(expectedMa2);
55+
AssertMa2NotesEqual(expectedMa2, ma2, input.ToString());
56+
}
57+
58+
/// <summary>
59+
/// 提取音符段至 <c>T_REC</c> 之前:跳过头部与 <c>BPM</c> 行;若存在 <c>MET\t</c> 小节行则跳过该行;
60+
/// 部分旧官谱 golden 无 <c>MET</c>,则在 <c>BPM</c> 块后的首条非头行开始收集。
61+
/// </summary>
62+
private static string KeepNotesOnly(string text)
63+
{
64+
var result = new StringBuilder();
65+
var inNotes = false;
66+
foreach (var l in text.EnumerateLines())
67+
{
68+
var line = l.ToString().TrimEnd('\r');
69+
if (string.IsNullOrWhiteSpace(line)) continue;
70+
if (line.StartsWith("T_REC", StringComparison.Ordinal)) break;
71+
72+
if (!inNotes)
73+
{
74+
if (IsMa2HeaderOrBpmLine(line)) continue;
75+
if (line.StartsWith("MET\t", StringComparison.Ordinal)) continue;
76+
inNotes = true;
77+
}
78+
79+
result.Append(line).Append('\n');
80+
}
81+
82+
return result.ToString();
83+
}
84+
85+
private static bool IsMa2HeaderOrBpmLine(string line) =>
86+
line.StartsWith("VERSION\t", StringComparison.Ordinal) ||
87+
line.StartsWith("FES_MODE\t", StringComparison.Ordinal) ||
88+
line.StartsWith("BPM_DEF\t", StringComparison.Ordinal) ||
89+
line.StartsWith("MET_DEF\t", StringComparison.Ordinal) ||
90+
line.StartsWith("RESOLUTION\t", StringComparison.Ordinal) ||
91+
line.StartsWith("CLK_DEF\t", StringComparison.Ordinal) ||
92+
line.StartsWith("COMPATIBLE_CODE\t", StringComparison.Ordinal) ||
93+
line.StartsWith("GENERATED_BY\t", StringComparison.Ordinal) ||
94+
line.StartsWith("BPM\t", StringComparison.Ordinal);
95+
96+
private static (int TimeTick, int Len, string Extra) GetSlideTime(string slide)
97+
{
98+
var values = slide.Split('\t');
99+
return (int.Parse(values[1]) * 384 + int.Parse(values[2]),
100+
int.Parse(values[5]),
101+
string.Join("\t", values[0], values[3], values[4], values[6]));
102+
}
103+
104+
private static bool CompareLine(string exp, string act)
105+
{
106+
var result = string.Equals(exp, act, StringComparison.Ordinal);
107+
if (!result && exp.Length >= 5 && act.Length >= 5 && exp[..5] == act[..5] && SlideTypeTool.IsSlide(exp[2..5]))
108+
{
109+
var (expTime, expLen, expExtra) = GetSlideTime(exp);
110+
var (actTime, actLen, actExtra) = GetSlideTime(act);
111+
if (expExtra != actExtra) return result;
112+
if (exp[..2] == "CN")
113+
{
114+
if (expTime + expLen == actTime + actLen || Math.Abs(expLen - actLen) <= 1) result = true;
115+
}
116+
else
117+
{
118+
if (expTime == actTime && Math.Abs(expLen - actLen) <= 1) result = true;
119+
}
120+
}
121+
122+
return result;
123+
}
124+
125+
private static void AssertMa2NotesEqual(string expected, string actual, string context)
126+
{
127+
var expectedLines = expected.Split('\n');
128+
var actualLines = actual.Split('\n');
129+
var max = Math.Max(expectedLines.Length, actualLines.Length);
130+
131+
for (var i = 0; i < max; i++)
132+
{
133+
var exp = i < expectedLines.Length ? expectedLines[i] : "<EOF>";
134+
var act = i < actualLines.Length ? actualLines[i] : "<EOF>";
135+
var result = CompareLine(exp, act);
136+
if (!result)
137+
{
138+
for (var j = 1; j < Math.Min(expectedLines.Length, i + 5); j++)
139+
{
140+
if (CompareLine(expectedLines[j], act))
141+
{
142+
(expectedLines[j], expectedLines[i]) = (expectedLines[i], expectedLines[j]);
143+
result = true;
144+
break;
145+
}
146+
}
147+
}
148+
149+
if (!result)
150+
{
151+
Assert.Fail(
152+
$"{context}: first difference at line {i + 1}:{Environment.NewLine}" +
153+
$"EXPECTED: {exp}{Environment.NewLine}" +
154+
$"ACTUAL : {act}");
155+
}
156+
}
157+
}
158+
}

tests/MA2转Simai测试.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ private void TestChart(TestInput c)
3535
var (chart, _) = new MA2Parser().Parse(ma2Text);
3636
var (simai, _) = new SimaiGenerator().Generate(chart);
3737

38+
Assert.Equal(TestUtils.TryParseMa2ClkDef(ma2Text), chart.ClockCount * 96);
3839
var expectedTimeline = SimaiCommaTimeline.Flatten(inote);
3940
var actualTimeline = SimaiCommaTimeline.Flatten(simai);
4041
SimaiCommaTimeline.AssertTimelineEqual(expectedTimeline, actualTimeline, chart, _output);

tests/Simai转MA2测试.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ private void TestChart(TestInput input)
3030
var (chart, _) = new SimaiParser(bigTouch: false, clockCount: maidata.ClockCount).Parse(chartInfo.Inote);
3131
var (ma2, _) = new MA2Generator(isUtage: false).Generate(chart);
3232

33+
Assert.Equal(maidata.ClockCount * 96, TestUtils.TryParseMa2ClkDef(ma2));
3334
ma2 = keepNotesOnly(ma2);
3435
expectedMa2 = keepNotesOnly(expectedMa2);
3536
AssertTextEqual(expectedMa2, ma2);

tests/Statistics测试.cs

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -49,27 +49,6 @@ public void 统计段与原版一致(TestInput input)
4949
var actual = Ma2StatisticsSection.Parse(ma2RoundTrip);
5050
Ma2StatisticsSection.AssertEqual(expected, actual, input.ToString(), _output);
5151
}
52-
53-
/// <summary>解析 <c>VERSION</c> 行第三列(如 <c>1.03.00</c>)为整数版本号(如 103);未找到则返回 <c>null</c>。</summary>
54-
private static int? TryParseMa2HeaderVersion(string ma2Text)
55-
{
56-
foreach (var raw in ma2Text.EnumerateLines())
57-
{
58-
if (raw.IsWhiteSpace()) continue;
59-
var line = raw.ToString().TrimEnd('\r');
60-
var parts = line.Split('\t');
61-
if (parts.Length < 3 || parts[0] != "VERSION")
62-
continue;
63-
var ver = parts[2].Split('.');
64-
if (ver.Length >= 2 &&
65-
int.TryParse(ver[0], out var major) &&
66-
int.TryParse(ver[1], out var minor))
67-
return major * 100 + minor;
68-
return null;
69-
}
70-
71-
return null;
72-
}
7352
}
7453

7554
internal static class Ma2StatisticsSection
@@ -143,9 +122,6 @@ public static void AssertEqual(
143122
}
144123
}
145124

146-
private static bool IsZeroStatValue(string v) =>
147-
v == "0" || v == "0.0" || v == "0.00" || v == "0.000";
148-
149125
private static bool ValuesEqual(string key, string expected, string actual)
150126
{
151127
return string.Equals(expected, actual, StringComparison.Ordinal);

tests/TestUtils.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Text;
2+
using System.Text.RegularExpressions;
23
using MuConvert.chart;
34
using MuConvert.maidata;
45
using MuConvert.parser;
@@ -8,6 +9,8 @@ namespace MuConvert.Tests;
89

910
internal static class TestUtils
1011
{
12+
private static readonly Regex Ma2ClkDefLineRegex = new(@"^CLK_DEF\t(\d+)\s*$", RegexOptions.Multiline | RegexOptions.CultureInvariant);
13+
1114
/// <summary>
1215
/// 从测试运行目录向上查找包含 MuConvert.csproj 的仓库根目录。
1316
/// </summary>
@@ -19,6 +22,37 @@ public static DirectoryInfo FindRepoRoot()
1922
return dir ?? throw new DirectoryNotFoundException("Could not locate repo root (MuConvert.csproj).");
2023
}
2124

25+
/// <summary>
26+
/// 自 MA2 文本中用正则匹配首行 <c>CLK_DEF\t…</c>(官机头字段名;部分资料误写为 CLOCK_DEF),返回其整数值;
27+
/// 与 <see cref="Chart.ClockCount"/> 的关系为 <c>CLK_DEF = 96 * ClockCount</c>(<c>RESOLUTION</c> 为 384 时)。
28+
/// </summary>
29+
public static int? TryParseMa2ClkDef(string ma2Text)
30+
{
31+
var m = Ma2ClkDefLineRegex.Match(ma2Text);
32+
if (!m.Success || !int.TryParse(m.Groups[1].Value, out var v)) return null;
33+
return v;
34+
}
35+
36+
/// <summary>解析 <c>VERSION</c> 行第三列(如 <c>1.03.00</c>)为整数版本号(如 103);未找到则返回 <c>null</c>。</summary>
37+
public static int? TryParseMa2HeaderVersion(string ma2Text)
38+
{
39+
foreach (var raw in ma2Text.EnumerateLines())
40+
{
41+
if (raw.IsWhiteSpace()) continue;
42+
var line = raw.ToString().TrimEnd('\r');
43+
var parts = line.Split('\t');
44+
if (parts.Length < 3 || parts[0] != "VERSION")
45+
continue;
46+
var ver = parts[2].Split('.');
47+
if (ver.Length >= 2 &&
48+
int.TryParse(ver[0], out var major) &&
49+
int.TryParse(ver[1], out var minor))
50+
return major * 100 + minor;
51+
return null;
52+
}
53+
return null;
54+
}
55+
2256
public static Chart LoadOneChart(out List<Alert> alerts)
2357
{
2458
var repo = FindRepoRoot();

0 commit comments

Comments
 (0)