@@ -16,7 +16,7 @@ public class Ogkr测试
1616 public static IEnumerable < object [ ] > GetTestInputsOnlyLv3 ( string dataDir ) => GetTestInputs ( dataDir )
1717 . Where ( x=> int . TryParse ( ( ( OgkrTestInput ) x [ 0 ] ) . DifficultyId , out var id ) && id == 3 ) ;
1818
19- [ Theory ( Skip = "ogkr的解析和生成还没实现完,所以暂时跳过" ) ]
19+ [ Theory ]
2020 [ MemberData ( nameof ( GetTestInputs ) , "官谱" ) ]
2121 public void 解析Ogkr再生成回去 ( OgkrTestInput c )
2222 {
@@ -83,8 +83,11 @@ private void AssertOgkChartOk(OgkChart chart, IEnumerable<Alert> alerts)
8383/// 1. [HEADER]:忽略 T_ 开头的统计量与 TUTORIAL,其余逐行严格比较。
8484/// 2. [B_PALETTE]:不直接逐行比较;但要求 actual 内不存在两行“实质相同”
8585/// (除去 ID 之外其余字段全部相同)。
86- /// 3. [COMPOSITION] / [LANE] / [LANE_BLOCK] / [BEAM] / [FLICK] / [NOTES]:逐行严格比较。
87- /// 4. [BULLET] / [BELL]:逐行比较,但其中引用 B_PALETTE ID 的字段,比较的是
86+ /// 3. [COMPOSITION] / [LANE] / [BEAM] / [FLICK] / [NOTES]:逐行严格比较。
87+ /// 4. [LANE_BLOCK]:将每一行视为一个多重集元素进行比较(顺序不敏感)。
88+ /// 这是因为官谱中观察到不同谱面对同一时刻的多条LBK使用了不同的排序约定,
89+ /// 且其在游戏内并无实际顺序意义,故宽松比较以适应这一点。
90+ /// 5. [BULLET] / [BELL]:逐行比较,但其中引用 B_PALETTE ID 的字段,比较的是
8891/// 所引用的 BPL 行的实质内容(去 ID 后的其余字段)是否相同,而非 ID 字面相等。
8992///
9093/// 比较失败时,会打印差异所在 expected 中的行号(1-based)。
@@ -113,7 +116,7 @@ public static void AssertOgkrEqual(string expectedText, string actualText)
113116
114117 CompareSimpleSection ( "COMPOSITION" , expectedSections , actualSections ) ;
115118 CompareSimpleSection ( "LANE" , expectedSections , actualSections ) ;
116- CompareSimpleSection ( "LANE_BLOCK" , expectedSections , actualSections ) ;
119+ CompareUnorderedSection ( "LANE_BLOCK" , expectedSections , actualSections ) ;
117120 CompareSimpleSection ( "BEAM" , expectedSections , actualSections ) ;
118121 CompareSimpleSection ( "FLICK" , expectedSections , actualSections ) ;
119122 CompareSimpleSection ( "NOTES" , expectedSections , actualSections ) ;
@@ -176,6 +179,35 @@ private static void CompareSimpleSection(string section,
176179 CompareLinesStrict ( section , expected , actual ) ;
177180 }
178181
182+ /// <summary>
183+ /// 把两个section内的所有行作为multiset做比较:只关心是否同时存在相同的行集合(含重数),不关心顺序。
184+ /// </summary>
185+ private static void CompareUnorderedSection ( string section ,
186+ Dictionary < string , List < LineEntry > > expectedSections ,
187+ Dictionary < string , List < LineEntry > > actualSections )
188+ {
189+ var expected = expectedSections . TryGetValue ( section , out var le ) ? le : [ ] ;
190+ var actual = actualSections . TryGetValue ( section , out var la ) ? la : [ ] ;
191+
192+ var expectedCounts = expected . GroupBy ( e => e . Content ) . ToDictionary ( g => g . Key , g => g . Count ( ) ) ;
193+ var actualCounts = actual . GroupBy ( e => e . Content ) . ToDictionary ( g => g . Key , g => g . Count ( ) ) ;
194+
195+ var diffs = new List < string > ( ) ;
196+ foreach ( var ( k , ec ) in expectedCounts )
197+ {
198+ actualCounts . TryGetValue ( k , out var ac ) ;
199+ if ( ac != ec ) diffs . Add ( $ " missing/uneven in actual (expected x{ ec } , actual x{ ac } ): { k } ") ;
200+ }
201+ foreach ( var ( k , ac ) in actualCounts )
202+ {
203+ if ( ! expectedCounts . ContainsKey ( k ) ) diffs . Add ( $ " unexpected in actual (x{ ac } ): { k } ") ;
204+ }
205+ if ( diffs . Count > 0 )
206+ {
207+ Assert . Fail ( $ "[{ section } ] section (unordered) mismatch:{ Environment . NewLine } { string . Join ( Environment . NewLine , diffs ) } ") ;
208+ }
209+ }
210+
179211 private static void CompareLinesStrict ( string section , List < LineEntry > expected , List < LineEntry > actual )
180212 {
181213 var max = Math . Max ( expected . Count , actual . Count ) ;
0 commit comments