Skip to content

Commit

Permalink
Implement the working property validator for helping to mark the prop…
Browse files Browse the repository at this point in the history
…erty in the note/lyric as invalid if change the property.
  • Loading branch information
andy840119 committed Mar 26, 2023
1 parent c93870d commit c79e612
Show file tree
Hide file tree
Showing 6 changed files with 258 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,38 @@
// Copyright (c) andy840119 <[email protected]>. 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<THitObject, TFlag>
where TFlag : struct, Enum
where THitObject : KaraokeHitObject, new()
{
protected abstract HitObjectWorkingPropertyValidator<THitObject, TFlag> 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));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright (c) andy840119 <[email protected]>. 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<Lyric, LyricWorkingProperty>
{
protected override HitObjectWorkingPropertyValidator<Lyric, LyricWorkingProperty> 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<InvalidWorkingPropertyAssignException>(() => lyric.ReferenceLyric = new Lyric { ID = 3 });
Assert.Throws<InvalidWorkingPropertyAssignException>(() => lyric.ReferenceLyric = null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright (c) andy840119 <[email protected]>. 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<Note, NoteWorkingProperty>
{
protected override HitObjectWorkingPropertyValidator<Note, NoteWorkingProperty> 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<InvalidWorkingPropertyAssignException>(() => note.ReferenceLyric = new Lyric { ID = 3 });
Assert.Throws<InvalidWorkingPropertyAssignException>(() => note.ReferenceLyric = null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> : FlagState<T>
where T : struct, Enum
/// <summary>
/// This class is used to check the working property is same as data property in the <typeparamref name="THitObject"/>.
/// Should mark as invalid when data property is changed.
/// Should mark as valid when working property is synced with data property
/// </summary>
/// <typeparam name="THitObject"></typeparam>
/// <typeparam name="TFlag"></typeparam>
public abstract class HitObjectWorkingPropertyValidator<THitObject, TFlag> : FlagState<TFlag>
where TFlag : struct, Enum
{
private readonly THitObject hitObject;

protected HitObjectWorkingPropertyValidator(THitObject hitObject)
{
this.hitObject = hitObject;
}

/// <summary>
/// This method is called after assign the working property changed in the <typeparamref name="THitObject"/> by <see cref="KaraokeBeatmapProcessor"/>.
/// We should make sure that the working property is same as data property.
/// </summary>
/// <param name="flag"></param>
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);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) andy840119 <[email protected]>. 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<Lyric, LyricWorkingProperty>
{
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)
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) andy840119 <[email protected]>. 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<Note, NoteWorkingProperty>
{
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)
};
}

0 comments on commit c79e612

Please sign in to comment.