@@ -259,9 +259,6 @@ public void TestFormatScoreMultiplier()
259259 new MultiplayerTestScenario ( true , true , [ new OsuModPerfect ( ) ] , [ ] ) ,
260260 new MultiplayerTestScenario ( true , true , [ new OsuModDoubleTime ( ) ] , [ ] ) ,
261261 new MultiplayerTestScenario ( true , true , [ new OsuModNightcore ( ) ] , [ ] ) ,
262- new MultiplayerTestScenario ( true , true , [ new OsuModHidden ( ) ] , [ ] ) ,
263- new MultiplayerTestScenario ( true , true , [ new OsuModFlashlight ( ) ] , [ ] ) ,
264- new MultiplayerTestScenario ( true , true , [ new OsuModAccuracyChallenge ( ) ] , [ ] ) ,
265262 new MultiplayerTestScenario ( true , true , [ new OsuModDifficultyAdjust ( ) ] , [ ] ) ,
266263 new MultiplayerTestScenario ( true , true , [ new ModWindUp ( ) ] , [ ] ) ,
267264 new MultiplayerTestScenario ( true , true , [ new ModWindDown ( ) ] , [ ] ) ,
@@ -347,8 +344,44 @@ public void TestFreestyleRulesetCompatibility()
347344 {
348345 if ( mod . ValidForFreestyleAsRequiredMod && mod . UserPlayable && ! commonAcronyms . Contains ( mod . Acronym ) )
349346 Assert . Fail ( $ "{ mod . GetType ( ) . ReadableName ( ) } declares { nameof ( Mod . ValidForFreestyleAsRequiredMod ) } but does not exist in all four basic rulesets!") ;
347+
348+ // downgraded to warning, because there are valid reasons why they may still not be specified to be valid for freestyle as required
349+ // (see `TestModsValidForRequiredFreestyleAreConsistentlyCompatibleAcrossRulesets()` test case below).
350350 if ( ! mod . ValidForFreestyleAsRequiredMod && mod . UserPlayable && commonAcronyms . Contains ( mod . Acronym ) )
351- Assert . Fail ( $ "{ mod . GetType ( ) . ReadableName ( ) } does not declare { nameof ( Mod . ValidForFreestyleAsRequiredMod ) } but exists in all four basic rulesets!") ;
351+ Assert . Warn ( $ "{ mod . GetType ( ) . ReadableName ( ) } does not declare { nameof ( Mod . ValidForFreestyleAsRequiredMod ) } but exists in all four basic rulesets.") ;
352+ }
353+ }
354+ } ) ;
355+ }
356+
357+ [ Test ]
358+ public void TestModsValidForRequiredFreestyleAreConsistentlyCompatibleAcrossRulesets ( )
359+ {
360+ Dictionary < ( string firstMod , string secondMod ) , bool > compatibilityMap = new Dictionary < ( string , string ) , bool > ( ) ;
361+
362+ Assert . Multiple ( ( ) =>
363+ {
364+ for ( int rulesetId = 0 ; rulesetId < 4 ; ++ rulesetId )
365+ {
366+ var rulesetStore = new AssemblyRulesetStore ( ) ;
367+ var ruleset = rulesetStore . GetRuleset ( rulesetId ) ! . CreateInstance ( ) ;
368+
369+ var modsValidForFreestyleAsRequired = ruleset . CreateAllMods ( ) . Where ( m => m . ValidForFreestyleAsRequiredMod ) . OrderBy ( m => m . Acronym ) . ToList ( ) ;
370+
371+ for ( int i = 0 ; i < modsValidForFreestyleAsRequired . Count ; i ++ )
372+ {
373+ for ( int j = i ; j < modsValidForFreestyleAsRequired . Count ; ++ j )
374+ {
375+ var first = modsValidForFreestyleAsRequired [ i ] ;
376+ var second = modsValidForFreestyleAsRequired [ j ] ;
377+
378+ bool compatible = ModUtils . CheckCompatibleSet ( [ first , second ] ) ;
379+
380+ if ( ! compatibilityMap . TryGetValue ( ( first . Acronym , second . Acronym ) , out bool previousCompatible ) )
381+ compatibilityMap [ ( first . Acronym , second . Acronym ) ] = compatible ;
382+ else if ( previousCompatible != compatible )
383+ Assert . Fail ( $ "{ first . Acronym } and { second . Acronym } declare { nameof ( Mod . ValidForFreestyleAsRequiredMod ) } while not being consistently compatible in all four rulesets!") ;
384+ }
352385 }
353386 }
354387 } ) ;
0 commit comments