<commit_before>// Copyright (c) ppy Pty Ltd <<EMAIL>>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.IO.Stores;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.UI;
using osu.Game.Storyboards;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Rulesets;

namespace osu.Game.Tests.Visual
{
    [ExcludeFromDynamicCompile]
    public abstract class OsuTestScene : TestScene
    {
        [Cached]
        protected Bindable<WorkingBeatmap> Beatmap { get; } = new Bindable<WorkingBeatmap>();

        [Cached]
        protected Bindable<RulesetInfo> Ruleset { get; } = new Bindable<RulesetInfo>();

        [Cached]
        protected Bindable<IReadOnlyList<Mod>> SelectedMods { get; } = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());

        protected new DependencyContainer Dependencies { get; private set; }

        protected IResourceStore<byte[]> Resources;

        protected IAPIProvider API
        {
            get
            {
                if (UseOnlineAPI)
                    throw new InvalidOperationException($"Using the {nameof(OsuTestScene)} dummy API is not supported when {nameof(UseOnlineAPI)} is true");

                return dummyAPI;
            }
        }

        private DummyAPIAccess dummyAPI;

        /// <summary>
        /// Whether this test scene requires real-world API access.
        /// If true, this will bypass the local <see cref="DummyAPIAccess"/> and use the <see cref="OsuGameBase"/> provided one.
        /// </summary>
        protected virtual bool UseOnlineAPI => false;

        /// <summary>
        /// A database context factory to be used by test runs. Can be isolated and reset by setting <see cref="UseFreshStoragePerRun"/> to <c>true</c>.
        /// </summary>
        /// <remarks>
        /// In interactive runs (ie. VisualTests) this will use the user's database if <see cref="UseFreshStoragePerRun"/> is not set to <c>true</c>.
        /// </remarks>
        protected RealmAccess Realm => realm.Value;

        private Lazy<RealmAccess> realm;

        /// <summary>
        /// Whether a fresh storage should be initialised per test (method) run.
        /// </summary>
        /// <remarks>
        /// By default (ie. if not set to <c>true</c>):
        /// - in interactive runs, the user's storage will be used
        /// - in headless runs, a shared temporary storage will be used per test class.
        /// </remarks>
        protected virtual bool UseFreshStoragePerRun => false;

        /// <summary>
        /// A storage to be used by test runs. Can be isolated by setting <see cref="UseFreshStoragePerRun"/> to <c>true</c>.
        /// </summary>
        /// <remarks>
        /// In interactive runs (ie. VisualTests) this will use the user's storage if <see cref="UseFreshStoragePerRun"/> is not set to <c>true</c>.
        /// </remarks>
        protected Storage LocalStorage => localStorage.Value;

        /// <summary>
        /// A cache for ruleset configurations to be used in this test scene.
        /// </summary>
        /// <remarks>
        /// This <see cref="IRulesetConfigCache"/> instance is provided to the children of this test scene via DI.
        /// It is only exposed so that test scenes themselves can access the ruleset config cache in a safe manner
        /// (<see cref="OsuTestScene"/>s cannot use DI themselves, as they will end up accessing the real cached instance from <see cref="OsuGameBase"/>).
        /// </remarks>
        protected IRulesetConfigCache RulesetConfigs { get; private set; }

        private Lazy<Storage> localStorage;

        private Storage headlessHostStorage;

        private DrawableRulesetDependencies rulesetDependencies;

        protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
        {
            headlessHostStorage = (parent.Get<GameHost>() as HeadlessGameHost)?.Storage;

            Resources = parent.Get<OsuGameBase>().Resources;

            realm = new Lazy<RealmAccess>(() => new RealmAccess(LocalStorage, "client"));

            RecycleLocalStorage(false);

            var baseDependencies = base.CreateChildDependencies(parent);

            // to isolate ruleset configs in tests from the actual database and avoid state pollution problems,
            // as well as problems due to the implementation details of the "real" implementation (the configs only being available at `LoadComplete()`),
            // cache a test implementation of the ruleset config cache over the "real" one.
            var isolatedBaseDependencies = new DependencyContainer(baseDependencies);
            isolatedBaseDependencies.CacheAs(RulesetConfigs = new TestRulesetConfigCache());
            baseDependencies = isolatedBaseDependencies;

            var providedRuleset = CreateRuleset();
            if (providedRuleset != null)
                isolatedBaseDependencies = rulesetDependencies = new DrawableRulesetDependencies(providedRuleset, baseDependencies);

            Dependencies = isolatedBaseDependencies;<commit_msg>Fix visual test crashes due to local realm not having update thread<commit_after>// Copyright (c) ppy Pty Ltd <<EMAIL>>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.IO.Stores;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.UI;
using osu.Game.Storyboards;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Rulesets;

namespace osu.Game.Tests.Visual
{
    [ExcludeFromDynamicCompile]
    public abstract class OsuTestScene : TestScene
    {
        [Cached]
        protected Bindable<WorkingBeatmap> Beatmap { get; } = new Bindable<WorkingBeatmap>();

        [Cached]
        protected Bindable<RulesetInfo> Ruleset { get; } = new Bindable<RulesetInfo>();

        [Cached]
        protected Bindable<IReadOnlyList<Mod>> SelectedMods { get; } = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());

        protected new DependencyContainer Dependencies { get; private set; }

        protected IResourceStore<byte[]> Resources;

        protected IAPIProvider API
        {
            get
            {
                if (UseOnlineAPI)
                    throw new InvalidOperationException($"Using the {nameof(OsuTestScene)} dummy API is not supported when {nameof(UseOnlineAPI)} is true");

                return dummyAPI;
            }
        }

        private DummyAPIAccess dummyAPI;

        /// <summary>
        /// Whether this test scene requires real-world API access.
        /// If true, this will bypass the local <see cref="DummyAPIAccess"/> and use the <see cref="OsuGameBase"/> provided one.
        /// </summary>
        protected virtual bool UseOnlineAPI => false;

        /// <summary>
        /// A database context factory to be used by test runs. Can be isolated and reset by setting <see cref="UseFreshStoragePerRun"/> to <c>true</c>.
        /// </summary>
        /// <remarks>
        /// In interactive runs (ie. VisualTests) this will use the user's database if <see cref="UseFreshStoragePerRun"/> is not set to <c>true</c>.
        /// </remarks>
        protected RealmAccess Realm => realm.Value;

        private Lazy<RealmAccess> realm;

        /// <summary>
        /// Whether a fresh storage should be initialised per test (method) run.
        /// </summary>
        /// <remarks>
        /// By default (ie. if not set to <c>true</c>):
        /// - in interactive runs, the user's storage will be used
        /// - in headless runs, a shared temporary storage will be used per test class.
        /// </remarks>
        protected virtual bool UseFreshStoragePerRun => false;

        /// <summary>
        /// A storage to be used by test runs. Can be isolated by setting <see cref="UseFreshStoragePerRun"/> to <c>true</c>.
        /// </summary>
        /// <remarks>
        /// In interactive runs (ie. VisualTests) this will use the user's storage if <see cref="UseFreshStoragePerRun"/> is not set to <c>true</c>.
        /// </remarks>
        protected Storage LocalStorage => localStorage.Value;

        /// <summary>
        /// A cache for ruleset configurations to be used in this test scene.
        /// </summary>
        /// <remarks>
        /// This <see cref="IRulesetConfigCache"/> instance is provided to the children of this test scene via DI.
        /// It is only exposed so that test scenes themselves can access the ruleset config cache in a safe manner
        /// (<see cref="OsuTestScene"/>s cannot use DI themselves, as they will end up accessing the real cached instance from <see cref="OsuGameBase"/>).
        /// </remarks>
        protected IRulesetConfigCache RulesetConfigs { get; private set; }

        private Lazy<Storage> localStorage;

        private Storage headlessHostStorage;

        private DrawableRulesetDependencies rulesetDependencies;

        protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
        {
            var host = parent.Get<GameHost>();

            headlessHostStorage = (host as HeadlessGameHost)?.Storage;

            Resources = parent.Get<OsuGameBase>().Resources;

            realm = new Lazy<RealmAccess>(() => new RealmAccess(LocalStorage, "client", host.UpdateThread));

            RecycleLocalStorage(false);

            var baseDependencies = base.CreateChildDependencies(parent);

            // to isolate ruleset configs in tests from the actual database and avoid state pollution problems,
            // as well as problems due to the implementation details of the "real" implementation (the configs only being available at `LoadComplete()`),
            // cache a test implementation of the ruleset config cache over the "real" one.
            var isolatedBaseDependencies = new DependencyContainer(baseDependencies);
            isolatedBaseDependencies.CacheAs(RulesetConfigs = new TestRulesetConfigCache());
            baseDependencies = isolatedBaseDependencies;

            var providedRuleset = CreateRuleset();
            if (providedRuleset != null)
                isolatedBaseDependencies = rulesetDependencies = new DrawableRulesetDependencies(providedRuleset, baseDependencies);

            Dependencies = isolatedBaseDependencies;
{'fraction_non_alphanumeric': 0.098184607517259, 'fraction_numerical': 0.0, 'mean_word_length': 2.740516416958878, 'pattern_counts': {'":': 0, '<': 111, '<?xml version=': 0, '>': 121, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4610216', 'n_tokens_mistral': 3151, 'n_tokens_neox': 3033, 'n_words': 1014}