From c79e612a87bd81d28510eab9eaed7f93efdf3131 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Sun, 26 Mar 2023 07:54:33 +0800 Subject: [PATCH] Implement the working property validator for helping to mark the property in the note/lyric as invalid if change the property. --- .../HitObjectWorkingPropertyValidatorTest.cs | 32 +++++++++- .../LyricWorkingPropertyValidatorTest.cs | 64 +++++++++++++++++++ .../NoteWorkingPropertyValidatorTest.cs | 64 +++++++++++++++++++ .../HitObjectWorkingPropertyValidator.cs | 41 +++++++++++- .../Workings/LyricWorkingPropertyValidator.cs | 30 +++++++++ .../Workings/NoteWorkingPropertyValidator.cs | 30 +++++++++ 6 files changed, 258 insertions(+), 3 deletions(-) create mode 100644 osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/LyricWorkingPropertyValidatorTest.cs create mode 100644 osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/NoteWorkingPropertyValidatorTest.cs create mode 100644 osu.Game.Rulesets.Karaoke/Objects/Workings/LyricWorkingPropertyValidator.cs create mode 100644 osu.Game.Rulesets.Karaoke/Objects/Workings/NoteWorkingPropertyValidator.cs diff --git a/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/HitObjectWorkingPropertyValidatorTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/HitObjectWorkingPropertyValidatorTest.cs index 503372e32..a7243643b 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/HitObjectWorkingPropertyValidatorTest.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/HitObjectWorkingPropertyValidatorTest.cs @@ -1,8 +1,38 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using NUnit.Framework; +using osu.Game.Rulesets.Karaoke.Objects; +using osu.Game.Rulesets.Karaoke.Objects.Workings; + namespace osu.Game.Rulesets.Karaoke.Tests.Objects.Workings; -public class HitObjectWorkingPropertyValidatorTest +public abstract class HitObjectWorkingPropertyValidatorTest + where TFlag : struct, Enum + where THitObject : KaraokeHitObject, new() { + protected abstract HitObjectWorkingPropertyValidator GetValidatorFromHitObject(THitObject hitObject); + + [Test] + public void RunAllInvalidateTest([Values] TFlag flag) + { + // run this test case just make sure that all working property are checked. + var validator = GetValidatorFromHitObject(new THitObject()); + Assert.DoesNotThrow(() => validator.Invalidate(flag)); + } + + [Test] + public void RunAllValidateTest([Values] TFlag flag) + { + // run this test case just make sure that all working property are checked. + var validator = GetValidatorFromHitObject(new THitObject()); + Assert.DoesNotThrow(() => validator.Validate(flag)); + } + + protected void AssetIsValid(THitObject hitObject, TFlag flag, bool isValid) + { + var validator = GetValidatorFromHitObject(hitObject); + Assert.AreEqual(isValid, validator.IsValid(flag)); + } } diff --git a/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/LyricWorkingPropertyValidatorTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/LyricWorkingPropertyValidatorTest.cs new file mode 100644 index 000000000..605b7cf01 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/LyricWorkingPropertyValidatorTest.cs @@ -0,0 +1,64 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Rulesets.Karaoke.Objects; +using osu.Game.Rulesets.Karaoke.Objects.Workings; + +namespace osu.Game.Rulesets.Karaoke.Tests.Objects.Workings; + +public class LyricWorkingPropertyValidatorTest : HitObjectWorkingPropertyValidatorTest +{ + protected override HitObjectWorkingPropertyValidator GetValidatorFromHitObject(Lyric hitObject) + => hitObject.WorkingPropertyValidator; + + [Test] + public void TestPage() + { + var lyric = new Lyric(); + + // should be invalid on the first load. + AssetIsValid(lyric, LyricWorkingProperty.Page, false); + + // page state is valid because assign the property. + Assert.DoesNotThrow(() => lyric.PageIndex = 1); + AssetIsValid(lyric, LyricWorkingProperty.Page, true); + } + + [Test] + public void TestReferenceLyric() + { + var lyric = new Lyric(); + + // should be invalid on the first load. + AssetIsValid(lyric, LyricWorkingProperty.ReferenceLyric, false); + + // should be valid if change the reference lyric id. + Assert.DoesNotThrow(() => + { + lyric.ReferenceLyricId = null; + lyric.ReferenceLyric = null; + }); + AssetIsValid(lyric, LyricWorkingProperty.ReferenceLyric, true); + + // should be valid if change the reference lyric id. + Assert.DoesNotThrow(() => + { + lyric.ReferenceLyricId = 1; + lyric.ReferenceLyric = new Lyric { ID = 1 }; + }); + AssetIsValid(lyric, LyricWorkingProperty.ReferenceLyric, true); + + // should be invalid if change the reference lyric id. + Assert.DoesNotThrow(() => lyric.ReferenceLyricId = 2); + AssetIsValid(lyric, LyricWorkingProperty.ReferenceLyric, false); + + // should be valid again if assign the reference lyric to the matched lyric. + Assert.DoesNotThrow(() => lyric.ReferenceLyric = new Lyric { ID = 2 }); + AssetIsValid(lyric, LyricWorkingProperty.ReferenceLyric, true); + + // should throw the exception if assign the working reference lyric to the unmatched reference lyric id. + Assert.Throws(() => lyric.ReferenceLyric = new Lyric { ID = 3 }); + Assert.Throws(() => lyric.ReferenceLyric = null); + } +} diff --git a/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/NoteWorkingPropertyValidatorTest.cs b/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/NoteWorkingPropertyValidatorTest.cs new file mode 100644 index 000000000..f03836644 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke.Tests/Objects/Workings/NoteWorkingPropertyValidatorTest.cs @@ -0,0 +1,64 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Rulesets.Karaoke.Objects; +using osu.Game.Rulesets.Karaoke.Objects.Workings; + +namespace osu.Game.Rulesets.Karaoke.Tests.Objects.Workings; + +public class NoteWorkingPropertyValidatorTest : HitObjectWorkingPropertyValidatorTest +{ + protected override HitObjectWorkingPropertyValidator GetValidatorFromHitObject(Note hitObject) + => hitObject.WorkingPropertyValidator; + + [Test] + public void TestPage() + { + var note = new Note(); + + // should be invalid on the first load. + AssetIsValid(note, NoteWorkingProperty.Page, false); + + // page state is valid because assign the property. + Assert.DoesNotThrow(() => note.PageIndex = 1); + AssetIsValid(note, NoteWorkingProperty.Page, true); + } + + [Test] + public void TestReferenceLyric() + { + var note = new Note(); + + // should be invalid on the first load. + AssetIsValid(note, NoteWorkingProperty.ReferenceLyric, false); + + // should be valid if change the reference lyric id. + Assert.DoesNotThrow(() => + { + note.ReferenceLyricId = null; + note.ReferenceLyric = null; + }); + AssetIsValid(note, NoteWorkingProperty.ReferenceLyric, true); + + // should be valid if change the reference lyric id. + Assert.DoesNotThrow(() => + { + note.ReferenceLyricId = 1; + note.ReferenceLyric = new Lyric { ID = 1 }; + }); + AssetIsValid(note, NoteWorkingProperty.ReferenceLyric, true); + + // should be invalid if change the reference lyric id. + Assert.DoesNotThrow(() => note.ReferenceLyricId = 2); + AssetIsValid(note, NoteWorkingProperty.ReferenceLyric, false); + + // should be valid again if assign the reference lyric to the matched lyric. + Assert.DoesNotThrow(() => note.ReferenceLyric = new Lyric { ID = 2 }); + AssetIsValid(note, NoteWorkingProperty.ReferenceLyric, true); + + // should throw the exception if assign the working reference lyric to the unmatched reference lyric id. + Assert.Throws(() => note.ReferenceLyric = new Lyric { ID = 3 }); + Assert.Throws(() => note.ReferenceLyric = null); + } +} diff --git a/osu.Game.Rulesets.Karaoke/Objects/Workings/HitObjectWorkingPropertyValidator.cs b/osu.Game.Rulesets.Karaoke/Objects/Workings/HitObjectWorkingPropertyValidator.cs index 3082265b6..50d23adce 100644 --- a/osu.Game.Rulesets.Karaoke/Objects/Workings/HitObjectWorkingPropertyValidator.cs +++ b/osu.Game.Rulesets.Karaoke/Objects/Workings/HitObjectWorkingPropertyValidator.cs @@ -2,11 +2,48 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Game.Rulesets.Karaoke.Beatmaps; using osu.Game.Rulesets.Karaoke.Flags; namespace osu.Game.Rulesets.Karaoke.Objects.Workings; -public class HitObjectWorkingPropertyValidator : FlagState - where T : struct, Enum +/// +/// This class is used to check the working property is same as data property in the . +/// Should mark as invalid when data property is changed. +/// Should mark as valid when working property is synced with data property +/// +/// +/// +public abstract class HitObjectWorkingPropertyValidator : FlagState + where TFlag : struct, Enum { + private readonly THitObject hitObject; + + protected HitObjectWorkingPropertyValidator(THitObject hitObject) + { + this.hitObject = hitObject; + } + + /// + /// This method is called after assign the working property changed in the by . + /// We should make sure that the working property is same as data property. + /// + /// + public new bool Validate(TFlag flag) + { + if (!CanValidate(flag)) + throw new InvalidWorkingPropertyAssignException(); + + return base.Validate(flag); + } + + protected sealed override bool CanInvalidate(TFlag flags) + => CanCheckWorkingPropertySync(hitObject, flags) || NeedToSyncWorkingProperty(hitObject, flags); + + protected sealed override bool CanValidate(TFlag flags) + => CanCheckWorkingPropertySync(hitObject, flags) || !NeedToSyncWorkingProperty(hitObject, flags); + + protected abstract bool CanCheckWorkingPropertySync(THitObject hitObject, TFlag flags); + + protected abstract bool NeedToSyncWorkingProperty(THitObject hitObject, TFlag flags); } diff --git a/osu.Game.Rulesets.Karaoke/Objects/Workings/LyricWorkingPropertyValidator.cs b/osu.Game.Rulesets.Karaoke/Objects/Workings/LyricWorkingPropertyValidator.cs new file mode 100644 index 000000000..7f1133e21 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Objects/Workings/LyricWorkingPropertyValidator.cs @@ -0,0 +1,30 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Game.Rulesets.Karaoke.Objects.Workings; + +public class LyricWorkingPropertyValidator : HitObjectWorkingPropertyValidator +{ + public LyricWorkingPropertyValidator(Lyric hitObject) + : base(hitObject) + { + } + + protected override bool CanCheckWorkingPropertySync(Lyric hitObject, LyricWorkingProperty flags) => + flags switch + { + LyricWorkingProperty.Page => true, // there's no way to check working page is sync to the page info. + LyricWorkingProperty.ReferenceLyric => false, + _ => throw new ArgumentOutOfRangeException(nameof(flags), flags, null) + }; + + protected override bool NeedToSyncWorkingProperty(Lyric hitObject, LyricWorkingProperty flags) => + flags switch + { + LyricWorkingProperty.Page => false, + LyricWorkingProperty.ReferenceLyric => hitObject.ReferenceLyric?.ID != hitObject.ReferenceLyricId, + _ => throw new ArgumentOutOfRangeException(nameof(flags), flags, null) + }; +} diff --git a/osu.Game.Rulesets.Karaoke/Objects/Workings/NoteWorkingPropertyValidator.cs b/osu.Game.Rulesets.Karaoke/Objects/Workings/NoteWorkingPropertyValidator.cs new file mode 100644 index 000000000..ba61ca0f8 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Objects/Workings/NoteWorkingPropertyValidator.cs @@ -0,0 +1,30 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Game.Rulesets.Karaoke.Objects.Workings; + +public class NoteWorkingPropertyValidator : HitObjectWorkingPropertyValidator +{ + public NoteWorkingPropertyValidator(Note hitObject) + : base(hitObject) + { + } + + protected override bool CanCheckWorkingPropertySync(Note hitObject, NoteWorkingProperty flags) => + flags switch + { + NoteWorkingProperty.Page => true, // there's no way to check working page is sync to the page info. + NoteWorkingProperty.ReferenceLyric => false, + _ => throw new ArgumentOutOfRangeException(nameof(flags), flags, null) + }; + + protected override bool NeedToSyncWorkingProperty(Note hitObject, NoteWorkingProperty flags) => + flags switch + { + NoteWorkingProperty.Page => false, + NoteWorkingProperty.ReferenceLyric => hitObject.ReferenceLyric?.ID != hitObject.ReferenceLyricId, + _ => throw new ArgumentOutOfRangeException(nameof(flags), flags, null) + }; +}