Skip to content

Commit

Permalink
Merge pull request #12 from mackysoft/feature/lifecycle-sequence-test
Browse files Browse the repository at this point in the history
Implement `ISceneEntryPoint` lifecycle sequence test
  • Loading branch information
mackysoft committed Nov 4, 2023
2 parents 5a573f0 + cbc474b commit 4ca58c1
Show file tree
Hide file tree
Showing 9 changed files with 479 additions and 65 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using Cysharp.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Threading;
using Cysharp.Threading.Tasks;

namespace MackySoft.Navigathena.SceneManagement.Tests
{
Expand All @@ -16,15 +17,15 @@ public enum SceneEntryPointCallbackFlags
OnFinalize = 1 << 3,
}

public sealed class SceneEntryPointCallbackFlagsStore
public interface ISceneEntryPointLifecycleListener
{
public SceneEntryPointCallbackFlags Value { get; set; }
void OnReceive (SceneEntryPointCallbackFlags flags);
}

public sealed class AnonymousSceneEntryPoint : SceneEntryPointBase
{

SceneEntryPointCallbackFlagsStore m_Flags = new();
readonly List<ISceneEntryPointLifecycleListener> m_Listeners = new();

Func<ISceneDataReader, IProgress<IProgressDataStore>, CancellationToken, UniTask> m_OnInitialize;
Func<ISceneDataReader, CancellationToken, UniTask> m_OnEnter;
Expand All @@ -44,33 +45,42 @@ public sealed class AnonymousSceneEntryPoint : SceneEntryPointBase
m_OnFinalize = onFinalize;
}

public void RegisterFlags (SceneEntryPointCallbackFlagsStore store)
public void Register (ISceneEntryPointLifecycleListener listener)
{
m_Flags = store;
m_Listeners.Add(listener);
}

protected override UniTask OnInitialize (ISceneDataReader reader, IProgress<IProgressDataStore> progress, CancellationToken cancellationToken)
{
m_Flags.Value |= SceneEntryPointCallbackFlags.OnInitialize;
Send(SceneEntryPointCallbackFlags.OnInitialize);
return m_OnInitialize?.Invoke(reader, progress, cancellationToken) ?? UniTask.CompletedTask;
}

protected override UniTask OnEnter (ISceneDataReader reader, CancellationToken cancellationToken)
{
m_Flags.Value |= SceneEntryPointCallbackFlags.OnEnter;
Send(SceneEntryPointCallbackFlags.OnEnter);
return m_OnEnter?.Invoke(reader, cancellationToken) ?? UniTask.CompletedTask;
}

protected override UniTask OnExit (ISceneDataWriter writer, CancellationToken cancellationToken)
{
m_Flags.Value |= SceneEntryPointCallbackFlags.OnExit;
Send(SceneEntryPointCallbackFlags.OnExit);
return m_OnExit?.Invoke(writer, cancellationToken) ?? UniTask.CompletedTask;
}

protected override UniTask OnFinalize (ISceneDataWriter writer, IProgress<IProgressDataStore> progress, CancellationToken cancellationToken)
{
m_Flags.Value |= SceneEntryPointCallbackFlags.OnFinalize;
Send(SceneEntryPointCallbackFlags.OnFinalize);
return m_OnFinalize?.Invoke(writer, progress, cancellationToken) ?? UniTask.CompletedTask;
}

void Send (SceneEntryPointCallbackFlags flags)
{
for (int i = 0; i < m_Listeners.Count; i++)
{
ISceneEntryPointLifecycleListener listener = m_Listeners[i];
listener.OnReceive(flags);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
using System;
using System.Collections.Generic;
using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace MackySoft.Navigathena.SceneManagement
namespace MackySoft.Navigathena.SceneManagement.Tests
{
public sealed class BlankSceneIdentifier<T> : ISceneIdentifier where T : Component, ISceneEntryPoint
public sealed class AnonymousSceneIdentifier : ISceneIdentifier
{

readonly string m_SceneName;
readonly Action<T> m_OnCreate;
readonly Action<AnonymousSceneEntryPoint> m_OnCreate;
readonly List<ISceneEntryPointLifecycleListener> m_Listeners = new();

public BlankSceneIdentifier (string sceneName, Action<T> onCreate = null)
public AnonymousSceneIdentifier (string sceneName, Action<AnonymousSceneEntryPoint> onCreate = null)
{
if (string.IsNullOrEmpty(sceneName))
{
Expand All @@ -22,26 +24,40 @@ public BlankSceneIdentifier (string sceneName, Action<T> onCreate = null)
m_OnCreate = onCreate;
}

public AnonymousSceneIdentifier Register (ISceneEntryPointLifecycleListener listener)
{
m_Listeners.Add(listener);
return this;
}

public AnonymousSceneIdentifier Register (Func<AnonymousSceneIdentifier, ISceneEntryPointLifecycleListener> factory)
{
m_Listeners.Add(factory(this));
return this;
}

public ISceneHandle CreateHandle ()
{
return new BlankSceneHandle<T>(m_SceneName, m_OnCreate);
return new AnonymousSceneHandle(m_SceneName, m_OnCreate, m_Listeners);
}

public override string ToString ()
{
return $"{m_SceneName} ({typeof(BlankSceneIdentifier<T>).Name})";
return $"{m_SceneName} {nameof(AnonymousSceneIdentifier)}";
}

sealed class BlankSceneHandle<T> : ISceneHandle where T : Component, ISceneEntryPoint
sealed class AnonymousSceneHandle : ISceneHandle
{

readonly string m_SceneName;
readonly Action<T> m_OnCreate;
readonly Action<AnonymousSceneEntryPoint> m_OnCreate;
readonly List<ISceneEntryPointLifecycleListener> m_Listeners;

public BlankSceneHandle (string sceneName, Action<T> onCreate)
public AnonymousSceneHandle (string sceneName, Action<AnonymousSceneEntryPoint> onCreate, List<ISceneEntryPointLifecycleListener> listeners)
{
m_SceneName = sceneName;
m_OnCreate = onCreate;
m_Listeners = listeners;
}

public UniTask<Scene> Load (IProgress<float> progress = null, CancellationToken cancellationToken = default)
Expand All @@ -56,7 +72,12 @@ public UniTask<Scene> Load (IProgress<float> progress = null, CancellationToken

// Create entry point
GameObject entryPointGameObject = new GameObject("SceneEntryPoint");
var entryPoint = entryPointGameObject.AddComponent<T>();
var entryPoint = entryPointGameObject.AddComponent<AnonymousSceneEntryPoint>();
for (int i = 0; i < m_Listeners.Count; i++)
{
entryPoint.Register(m_Listeners[i]);
}

m_OnCreate?.Invoke(entryPoint);

SceneManager.MoveGameObjectToScene(entryPointGameObject, newScene);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace MackySoft.Navigathena.SceneManagement.Tests
{
public sealed class SceneEntryPointCallbackFlagsStore : ISceneEntryPointLifecycleListener
{
public SceneEntryPointCallbackFlags Value { get; private set; }

void ISceneEntryPointLifecycleListener.OnReceive (SceneEntryPointCallbackFlags flags)
{
Value |= flags;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace MackySoft.Navigathena.SceneManagement.Tests
{

public interface ISceneEntryPointLifecycleAsserter
{
ISceneEntryPointLifecycleAsserter On (ISceneIdentifier identifier);
ISceneEntryPointLifecycleAsserter Called (SceneEntryPointCallbackFlags flags);
void SequenceEqual ();
}

public sealed class SceneEntryPointLifecycleSequenceRecorder
{

readonly List<(ISceneIdentifier identifier, SceneEntryPointCallbackFlags flags)> m_Sequence = new();

public ISceneEntryPointLifecycleAsserter CreateSequenceAsserter ()
{
return new SceneEntryPointLifecycleAsserter(this);
}

public ISceneEntryPointLifecycleListener With (ISceneIdentifier identifier)
{
return new Listener(this, identifier);
}

sealed class Listener : ISceneEntryPointLifecycleListener
{

readonly SceneEntryPointLifecycleSequenceRecorder m_Assert;
readonly ISceneIdentifier m_Identifier;

public Listener (SceneEntryPointLifecycleSequenceRecorder assert, ISceneIdentifier identifier)
{
m_Assert = assert;
m_Identifier = identifier;
}

void ISceneEntryPointLifecycleListener.OnReceive (SceneEntryPointCallbackFlags flags)
{
m_Assert.m_Sequence.Add((m_Identifier, flags));
}
}

sealed class SceneEntryPointLifecycleAsserter : ISceneEntryPointLifecycleAsserter
{

readonly SceneEntryPointLifecycleSequenceRecorder m_Recorder;
readonly List<(ISceneIdentifier identifier, SceneEntryPointCallbackFlags flags)> m_Sequence = new();

ISceneIdentifier m_Current;

public SceneEntryPointLifecycleAsserter (SceneEntryPointLifecycleSequenceRecorder recorder)
{
m_Recorder = recorder;
}

public ISceneEntryPointLifecycleAsserter On (ISceneIdentifier identifier)
{
m_Current = identifier;
return this;
}

public ISceneEntryPointLifecycleAsserter Called (SceneEntryPointCallbackFlags flags)
{
m_Sequence.Add((m_Current, flags));
return this;
}

public void SequenceEqual ()
{
if (m_Recorder.m_Sequence.Count != m_Sequence.Count)
{
throw new Exception($"Expected sequence length is {m_Sequence.Count} but actual is {m_Recorder.m_Sequence.Count}.\n{EnumerateActualSequence()}");
}

for (int i = 0; i < m_Sequence.Count; i++)
{
var actual = m_Recorder.m_Sequence[i];
var expected = m_Sequence[i];

if (actual.identifier != expected.identifier)
{
throw new Exception($"Expected identifier is {expected.identifier} but actual is {actual.identifier}.\n{EnumerateActualSequence()}");
}

if (actual.flags != expected.flags)
{
throw new Exception($"Expected flags is {expected.flags} but actual is {actual.flags}.\n{EnumerateActualSequence()}");
}
}
}

string EnumerateActualSequence ()
{
return string.Join("\n", m_Recorder.m_Sequence.Select(x => $"{x.identifier}: {x.flags}"));
}
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 4ca58c1

Please sign in to comment.