1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-31 01:42:55 +08:00
osu-lazer/osu.Game.Tests/Database/RealmLiveTests.cs

370 lines
13 KiB
C#
Raw Normal View History

2021-09-30 22:46:16 +08:00
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;
using osu.Framework.Extensions;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
2021-09-30 22:46:16 +08:00
using osu.Game.Database;
using Realms;
namespace osu.Game.Tests.Database
{
public class RealmLiveTests : RealmTest
{
2021-10-01 13:30:27 +08:00
[Test]
2021-11-26 13:38:39 +08:00
public void TestLiveEquality()
2021-10-01 13:30:27 +08:00
{
RunTestWithRealm((realm, _) =>
2021-10-01 13:30:27 +08:00
{
Live<BeatmapInfo> beatmap = realm.Run(r => r.Write(_ => r.Add(new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata()))).ToLive(realm));
2021-10-01 13:30:27 +08:00
Live<BeatmapInfo> beatmap2 = realm.Run(r => r.All<BeatmapInfo>().First().ToLive(realm));
2021-10-01 13:30:27 +08:00
2021-11-26 13:38:39 +08:00
Assert.AreEqual(beatmap, beatmap2);
2021-10-01 13:30:27 +08:00
});
}
[Test]
public void TestAccessAfterStorageMigrate()
{
RunTestWithRealm((realm, storage) =>
{
var beatmap = new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata());
Live<BeatmapInfo>? liveBeatmap = null;
realm.Run(r =>
{
r.Write(_ => r.Add(beatmap));
liveBeatmap = beatmap.ToLive(realm);
});
using (var migratedStorage = new TemporaryNativeStorage("realm-test-migration-target"))
{
migratedStorage.DeleteDirectory(string.Empty);
using (realm.BlockAllOperations("testing"))
{
storage.Migrate(migratedStorage);
}
Assert.IsFalse(liveBeatmap?.PerformRead(l => l.Hidden));
}
});
}
2022-07-07 17:15:15 +08:00
[Test]
public void TestFailedWritePerformsRollback()
{
RunTestWithRealm((realm, _) =>
{
Assert.Throws<InvalidOperationException>(() =>
{
realm.Write(r =>
{
r.Add(new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata()));
throw new InvalidOperationException();
});
});
Assert.That(realm.Run(r => r.All<BeatmapInfo>()), Is.Empty);
});
}
[Test]
public void TestFailedNestedWritePerformsRollback()
{
RunTestWithRealm((realm, _) =>
{
Assert.Throws<InvalidOperationException>(() =>
{
realm.Write(r =>
{
realm.Write(_ =>
{
r.Add(new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata()));
throw new InvalidOperationException();
});
});
});
Assert.That(realm.Run(r => r.All<BeatmapInfo>()), Is.Empty);
});
}
[Test]
public void TestNestedWriteCalls()
{
RunTestWithRealm((realm, _) =>
{
var beatmap = new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata());
var liveBeatmap = beatmap.ToLive(realm);
realm.Run(r =>
r.Write(_ =>
r.Write(_ =>
r.Add(beatmap)))
);
Assert.IsFalse(liveBeatmap.PerformRead(l => l.Hidden));
});
}
[Test]
public void TestAccessAfterAttach()
{
RunTestWithRealm((realm, _) =>
{
var beatmap = new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata());
var liveBeatmap = beatmap.ToLive(realm);
realm.Run(r => r.Write(_ => r.Add(beatmap)));
Assert.IsFalse(liveBeatmap.PerformRead(l => l.Hidden));
});
}
[Test]
public void TestAccessNonManaged()
{
var beatmap = new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata());
var liveBeatmap = beatmap.ToLiveUnmanaged();
Assert.IsFalse(beatmap.Hidden);
Assert.IsFalse(liveBeatmap.Value.Hidden);
Assert.IsFalse(liveBeatmap.PerformRead(l => l.Hidden));
Assert.Throws<InvalidOperationException>(() => liveBeatmap.PerformWrite(l => l.Hidden = true));
Assert.IsFalse(beatmap.Hidden);
Assert.IsFalse(liveBeatmap.Value.Hidden);
Assert.IsFalse(liveBeatmap.PerformRead(l => l.Hidden));
}
[Test]
public void TestTransactionRolledBackOnException()
{
RunTestWithRealm((realm, _) =>
{
var beatmap = new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata());
realm.Run(r => r.Write(_ => r.Add(beatmap)));
var liveBeatmap = beatmap.ToLive(realm);
Assert.Throws<InvalidOperationException>(() => liveBeatmap.PerformWrite(l => throw new InvalidOperationException()));
Assert.IsFalse(liveBeatmap.PerformRead(l => l.Hidden));
liveBeatmap.PerformWrite(l => l.Hidden = true);
Assert.IsTrue(liveBeatmap.PerformRead(l => l.Hidden));
});
}
2021-09-30 22:46:16 +08:00
[Test]
2021-11-30 10:55:13 +08:00
public void TestScopedReadWithoutContext()
2021-09-30 22:46:16 +08:00
{
RunTestWithRealm((realm, _) =>
2021-09-30 22:46:16 +08:00
{
Live<BeatmapInfo>? liveBeatmap = null;
2021-09-30 22:46:16 +08:00
Task.Factory.StartNew(() =>
{
realm.Run(threadContext =>
2021-09-30 22:46:16 +08:00
{
var beatmap = threadContext.Write(r => r.Add(new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata())));
2021-09-30 22:46:16 +08:00
liveBeatmap = beatmap.ToLive(realm);
});
}, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely();
2021-09-30 22:46:16 +08:00
Debug.Assert(liveBeatmap != null);
Task.Factory.StartNew(() =>
{
2021-11-30 10:55:13 +08:00
liveBeatmap.PerformRead(beatmap =>
2021-09-30 22:46:16 +08:00
{
2021-11-30 10:55:13 +08:00
Assert.IsTrue(beatmap.IsValid);
Assert.IsFalse(beatmap.Hidden);
});
}, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely();
2021-09-30 22:46:16 +08:00
});
}
[Test]
2021-11-30 10:55:13 +08:00
public void TestScopedWriteWithoutContext()
2021-09-30 22:46:16 +08:00
{
RunTestWithRealm((realm, _) =>
2021-09-30 22:46:16 +08:00
{
Live<BeatmapInfo>? liveBeatmap = null;
2021-09-30 22:46:16 +08:00
Task.Factory.StartNew(() =>
{
realm.Run(threadContext =>
2021-09-30 22:46:16 +08:00
{
var beatmap = threadContext.Write(r => r.Add(new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata())));
2021-09-30 22:46:16 +08:00
liveBeatmap = beatmap.ToLive(realm);
});
}, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely();
2021-09-30 22:46:16 +08:00
Debug.Assert(liveBeatmap != null);
Task.Factory.StartNew(() =>
{
2021-11-30 10:55:13 +08:00
liveBeatmap.PerformWrite(beatmap => { beatmap.Hidden = true; });
liveBeatmap.PerformRead(beatmap => { Assert.IsTrue(beatmap.Hidden); });
}, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely();
2021-09-30 22:46:16 +08:00
});
}
[Test]
2021-11-30 10:55:13 +08:00
public void TestValueAccessNonManaged()
{
RunTestWithRealm((realm, _) =>
2021-11-30 10:55:13 +08:00
{
var beatmap = new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata());
var liveBeatmap = beatmap.ToLive(realm);
2021-11-30 10:55:13 +08:00
Assert.DoesNotThrow(() =>
{
var __ = liveBeatmap.Value;
});
});
}
[Test]
public void TestValueAccessWithOpenContextFails()
2021-09-30 22:46:16 +08:00
{
RunTestWithRealm((realm, _) =>
2021-09-30 22:46:16 +08:00
{
Live<BeatmapInfo>? liveBeatmap = null;
2021-09-30 22:46:16 +08:00
Task.Factory.StartNew(() =>
{
realm.Run(threadContext =>
2021-09-30 22:46:16 +08:00
{
var beatmap = threadContext.Write(r => r.Add(new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata())));
2021-09-30 22:46:16 +08:00
liveBeatmap = beatmap.ToLive(realm);
});
}, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely();
2021-09-30 22:46:16 +08:00
Debug.Assert(liveBeatmap != null);
Task.Factory.StartNew(() =>
{
2021-11-30 10:55:13 +08:00
// Can't be used, without a valid context.
Assert.Throws<InvalidOperationException>(() =>
{
var __ = liveBeatmap.Value;
});
// Can't be used, even from within a valid context.
2022-06-24 20:25:23 +08:00
realm.Run(_ =>
2021-11-30 10:55:13 +08:00
{
Assert.Throws<InvalidOperationException>(() =>
{
var __ = liveBeatmap.Value;
});
});
}, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely();
2021-09-30 22:46:16 +08:00
});
}
[Test]
public void TestValueAccessWithoutOpenContextFails()
{
RunTestWithRealm((realm, _) =>
2021-09-30 22:46:16 +08:00
{
Live<BeatmapInfo>? liveBeatmap = null;
2021-09-30 22:46:16 +08:00
Task.Factory.StartNew(() =>
{
realm.Run(threadContext =>
2021-09-30 22:46:16 +08:00
{
var beatmap = threadContext.Write(r => r.Add(new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata())));
2021-09-30 22:46:16 +08:00
liveBeatmap = beatmap.ToLive(realm);
});
}, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely();
2021-09-30 22:46:16 +08:00
Debug.Assert(liveBeatmap != null);
Task.Factory.StartNew(() =>
{
Assert.Throws<InvalidOperationException>(() =>
{
var unused = liveBeatmap.Value;
});
}, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely();
2021-09-30 22:46:16 +08:00
});
}
[Test]
public void TestLiveAssumptions()
{
RunTestWithRealm((realm, _) =>
2021-09-30 22:46:16 +08:00
{
int changesTriggered = 0;
realm.RegisterCustomSubscription(outerRealm =>
2021-09-30 22:46:16 +08:00
{
outerRealm.All<BeatmapInfo>().QueryAsyncWithNotifications(gotChange);
Live<BeatmapInfo>? liveBeatmap = null;
2021-09-30 22:46:16 +08:00
Task.Factory.StartNew(() =>
{
realm.Run(innerRealm =>
2021-09-30 22:46:16 +08:00
{
var ruleset = CreateRuleset();
var beatmap = innerRealm.Write(r => r.Add(new BeatmapInfo(ruleset, new BeatmapDifficulty(), new BeatmapMetadata())));
2021-09-30 22:46:16 +08:00
// add a second beatmap to ensure that a full refresh occurs below.
// not just a refresh from the resolved Live.
innerRealm.Write(r => r.Add(new BeatmapInfo(ruleset, new BeatmapDifficulty(), new BeatmapMetadata())));
2021-09-30 22:46:16 +08:00
liveBeatmap = beatmap.ToLive(realm);
});
}, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).WaitSafely();
2021-09-30 22:46:16 +08:00
Debug.Assert(liveBeatmap != null);
// not yet seen by main context
Assert.AreEqual(0, outerRealm.All<BeatmapInfo>().Count());
2021-09-30 22:46:16 +08:00
Assert.AreEqual(0, changesTriggered);
liveBeatmap.PerformRead(resolved =>
{
// retrieval causes an implicit refresh. even changes that aren't related to the retrieval are fired at this point.
// ReSharper disable once AccessToDisposedClosure
Assert.AreEqual(2, outerRealm.All<BeatmapInfo>().Count());
Assert.AreEqual(1, changesTriggered);
2021-09-30 22:46:16 +08:00
// can access properties without a crash.
Assert.IsFalse(resolved.Hidden);
2021-09-30 22:46:16 +08:00
// ReSharper disable once AccessToDisposedClosure
outerRealm.Write(r =>
{
// can use with the main context.
r.Remove(resolved);
});
2021-09-30 22:46:16 +08:00
});
return null;
});
2021-09-30 22:46:16 +08:00
void gotChange(IRealmCollection<BeatmapInfo> sender, ChangeSet changes, Exception error)
2021-09-30 22:46:16 +08:00
{
changesTriggered++;
}
});
}
}
}