Skip to content

Commit

Permalink
Merge pull request #1885 from andy840119/re-design-hit-object-validator
Browse files Browse the repository at this point in the history
Re-design working hit-object validator.
  • Loading branch information
andy840119 authored Mar 26, 2023
2 parents 9d16309 + 4cef74e commit 7740375
Show file tree
Hide file tree
Showing 16 changed files with 399 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
using System;
using System.Linq;
using NUnit.Framework;
using osu.Game.Rulesets.Karaoke.Objects;
using osu.Game.Rulesets.Karaoke.Flags;

namespace osu.Game.Rulesets.Karaoke.Tests.Objects;
namespace osu.Game.Rulesets.Karaoke.Tests.Flags;

public class HitObjectValidatorTest
public class FlagStateTest
{
[TestCase(default, new[] { TestEnum.Enum0, TestEnum.Enum1, TestEnum.Enum2 })]
[TestCase(TestEnum.Enum0, new[] { TestEnum.Enum1, TestEnum.Enum2 })]
Expand All @@ -18,7 +18,7 @@ public class HitObjectValidatorTest
[TestCase(TestEnum.Enum0 | TestEnum.Enum0, new[] { TestEnum.Enum1, TestEnum.Enum2 })] // Test edge case.
public void TestInvalidate(TestEnum invalidFlags, TestEnum[] expectedValue)
{
var validator = new HitObjectValidator<TestEnum>();
var validator = new FlagState<TestEnum>();
validator.ValidateAll();

// check the value.
Expand All @@ -43,7 +43,7 @@ public void TestInvalidate(TestEnum invalidFlags, TestEnum[] expectedValue)
[TestCase(TestEnum.Enum0 | TestEnum.Enum0)] // Test edge case.
public void TestInvalidateAll(TestEnum defaultFlags)
{
var validator = new HitObjectValidator<TestEnum>();
var validator = new FlagState<TestEnum>();
validator.Validate(defaultFlags);

// check the value.
Expand All @@ -61,7 +61,7 @@ public void TestInvalidateAll(TestEnum defaultFlags)
[TestCase(TestEnum.Enum2 | TestEnum.Enum2, new[] { TestEnum.Enum2 })] // Test edge case.
public void TestValidate(TestEnum validateFlags, TestEnum[] expectedValue)
{
var validator = new HitObjectValidator<TestEnum>();
var validator = new FlagState<TestEnum>();

// check the value.
validator.Validate(validateFlags);
Expand All @@ -85,7 +85,7 @@ public void TestValidate(TestEnum validateFlags, TestEnum[] expectedValue)
[TestCase(TestEnum.Enum0 | TestEnum.Enum0)] // Test edge case.
public void TestValidateAll(TestEnum validateFlags)
{
var validator = new HitObjectValidator<TestEnum>();
var validator = new FlagState<TestEnum>();

// check the value.
validator.ValidateAll();
Expand All @@ -104,7 +104,7 @@ public void TestValidateAll(TestEnum validateFlags)
[TestCase(TestEnum.Enum2 | TestEnum.Enum2, TestEnum.Enum2, true)] // Test edge case.
public void TestIsValid(TestEnum validateFlags, TestEnum validFlag, bool expectedValue)
{
var validator = new HitObjectValidator<TestEnum>();
var validator = new FlagState<TestEnum>();

// check the value.
validator.Validate(validateFlags);
Expand All @@ -114,7 +114,7 @@ public void TestIsValid(TestEnum validateFlags, TestEnum validFlag, bool expecte
[Test]
public void TestGetAllValidFlags()
{
var validator = new HitObjectValidator<TestEnum>();
var validator = new FlagState<TestEnum>();

// Should be possible to get all tags.
validator.ValidateAll();
Expand All @@ -128,7 +128,7 @@ public void TestGetAllValidFlags()
[Test]
public void TestGetAllInvalidFlags()
{
var validator = new HitObjectValidator<TestEnum>();
var validator = new FlagState<TestEnum>();

// Should be possible to get all tags.
validator.ValidateAll();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +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 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);
}
}
33 changes: 13 additions & 20 deletions osu.Game.Rulesets.Karaoke/Beatmaps/KaraokeBeatmapProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Karaoke.Beatmaps.Patterns;
using osu.Game.Rulesets.Karaoke.Objects;
using osu.Game.Rulesets.Karaoke.Objects.Workings;

namespace osu.Game.Rulesets.Karaoke.Beatmaps
{
Expand All @@ -32,70 +33,62 @@ private void applyInvalidProperty(IBeatmap beatmap)
switch (hitObject)
{
case Lyric lyric:
foreach (var flag in lyric.Validator.GetAllInvalidFlags())
foreach (var flag in lyric.WorkingPropertyValidator.GetAllInvalidFlags())
{
applyInvalidProperty(lyric, flag);
lyric.Validator.Validate(flag);
}

break;

case Note note:
foreach (var flag in note.Validator.GetAllInvalidFlags())
foreach (var flag in note.WorkingPropertyValidator.GetAllInvalidFlags())
{
applyInvalidProperty(note, flag);
note.Validator.Validate(flag);
}

break;
}
}
}

private void applyInvalidProperty(Lyric lyric, LyricInvalidation flag)
private void applyInvalidProperty(Lyric lyric, LyricWorkingProperty flag)
{
switch (flag)
{
case LyricInvalidation.Page:
case LyricWorkingProperty.Page:
var pageInfo = Beatmap.PageInfo;
lyric.PageIndex = pageInfo.GetPageIndexAt(lyric.LyricStartTime);
break;

case LyricInvalidation.ReferenceLyric:
if (lyric.ReferenceLyric != null || lyric.ReferenceLyricId == null)
return;

lyric.ReferenceLyric = findLyricById(lyric.ReferenceLyricId.Value);
case LyricWorkingProperty.ReferenceLyric:
lyric.ReferenceLyric = findLyricById(lyric.ReferenceLyricId);
break;

default:
throw new ArgumentOutOfRangeException();
}
}

private void applyInvalidProperty(Note note, NoteInvalidation flag)
private void applyInvalidProperty(Note note, NoteWorkingProperty flag)
{
switch (flag)
{
case NoteInvalidation.Page:
case NoteWorkingProperty.Page:
var pageInfo = Beatmap.PageInfo;
note.PageIndex = pageInfo.GetPageIndexAt(note.StartTime);
break;

case NoteInvalidation.ReferenceLyric:
if (note.ReferenceLyric != null || note.ReferenceLyricId == null)
return;

note.ReferenceLyric = findLyricById(note.ReferenceLyricId.Value);
case NoteWorkingProperty.ReferenceLyric:
note.ReferenceLyric = findLyricById(note.ReferenceLyricId);
break;

default:
throw new ArgumentOutOfRangeException();
}
}

private Lyric findLyricById(int id) =>
Beatmap.HitObjects.OfType<Lyric>().Single(x => x.ID == id);
private Lyric? findLyricById(int? id) =>
id == null ? null : Beatmap.HitObjects.OfType<Lyric>().Single(x => x.ID == id);

public override void PostProcess()
{
Expand Down
Loading

0 comments on commit 7740375

Please sign in to comment.