<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.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Development;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Platform;
using osu.Framework.Threading;
using osuTK;

namespace osu.Framework.Tests.Visual.Drawables
{
    public class TestSceneSynchronizationContext : FrameworkTestScene
    {
        [Resolved]
        private GameHost host { get; set; }

        private AsyncPerformingBox box;

        [Test]
        public void TestAsyncLoadComplete()
        {
            AddStep("add box", () => Child = box = new AsyncPerformingBox(true));
            AddAssert("not spun", () => box.Rotation == 0);
            AddStep("trigger", () => box.ReleaseAsyncLoadCompleteLock());
            AddUntilStep("has spun", () => box.Rotation == 180);
        }

        private GameThreadSynchronizationContext syncContext => SynchronizationContext.Current as GameThreadSynchronizationContext;

        [Test]
        public void TestNoAsyncDoesntUseScheduler()
        {
            int initialTasksRun = 0;
            AddStep("get initial run count", () => initialTasksRun = syncContext.TotalTasksRun);
            AddStep("add box", () => Child = box = new AsyncPerformingBox(false));
            AddAssert("no tasks run", () => syncContext.TotalTasksRun == initialTasksRun);
            AddStep("trigger", () => box.ReleaseAsyncLoadCompleteLock());
            AddAssert("no tasks run", () => syncContext.TotalTasksRun == initialTasksRun);
        }

        [Test]
        public void TestAsyncUsesScheduler()
        {
            int initialTasksRun = 0;
            AddStep("get initial run count", () => initialTasksRun = syncContext.TotalTasksRun);
            AddStep("add box", () => Child = box = new AsyncPerformingBox(true));
            AddAssert("no tasks run", () => syncContext.TotalTasksRun == initialTasksRun);
            AddStep("trigger", () => box.ReleaseAsyncLoadCompleteLock());
            AddUntilStep("one new task run", () => syncContext.TotalTasksRun == initialTasksRun + 1);
        }

        [Test]
        public void TestAsyncThrows()
        {
            Exception thrown = null;

            AddStep("watch for exceptions", () => host.ExceptionThrown += onException);
            AddStep("throw on update thread", () =>
            {
                // ReSharper disable once AsyncVoidLambda
                Scheduler.Add(async () =>
                {
                    Assert.That(ThreadSafety.IsUpdateThread);

                    await Task.Delay(100).ConfigureAwait(true);

                    Assert.That(ThreadSafety.IsUpdateThread);

                    throw new InvalidOperationException();
                });
            });

            AddUntilStep("wait for exception to arrive", () => thrown is InvalidOperationException);
            AddStep("stop watching for exceptions", () => host.ExceptionThrown -= onException);

            bool onException(Exception arg)
            {
                thrown = arg;
                return true;
            }<commit_msg>Add test coverage of order of execution and flushing<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.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Development;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Platform;
using osu.Framework.Threading;
using osuTK;

namespace osu.Framework.Tests.Visual.Drawables
{
    public class TestSceneSynchronizationContext : FrameworkTestScene
    {
        [Resolved]
        private GameHost host { get; set; }

        private AsyncPerformingBox box;

        [Test]
        public void TestAsyncLoadComplete()
        {
            AddStep("add box", () => Child = box = new AsyncPerformingBox(true));
            AddAssert("not spun", () => box.Rotation == 0);
            AddStep("trigger", () => box.ReleaseAsyncLoadCompleteLock());
            AddUntilStep("has spun", () => box.Rotation == 180);
        }

        private GameThreadSynchronizationContext syncContext => SynchronizationContext.Current as GameThreadSynchronizationContext;

        [Test]
        public void TestNoAsyncDoesntUseScheduler()
        {
            int initialTasksRun = 0;
            AddStep("get initial run count", () => initialTasksRun = syncContext.TotalTasksRun);
            AddStep("add box", () => Child = box = new AsyncPerformingBox(false));
            AddAssert("no tasks run", () => syncContext.TotalTasksRun == initialTasksRun);
            AddStep("trigger", () => box.ReleaseAsyncLoadCompleteLock());
            AddAssert("no tasks run", () => syncContext.TotalTasksRun == initialTasksRun);
        }

        [Test]
        public void TestAsyncUsesScheduler()
        {
            int initialTasksRun = 0;
            AddStep("get initial run count", () => initialTasksRun = syncContext.TotalTasksRun);
            AddStep("add box", () => Child = box = new AsyncPerformingBox(true));
            AddAssert("no tasks run", () => syncContext.TotalTasksRun == initialTasksRun);
            AddStep("trigger", () => box.ReleaseAsyncLoadCompleteLock());
            AddUntilStep("one new task run", () => syncContext.TotalTasksRun == initialTasksRun + 1);
        }

        [Test]
        public void TestOrderOfExecutionFlushing()
        {
            List<int> ran = new List<int>();

            AddStep("queue items", () =>
            {
                SynchronizationContext.Current?.Post(_ => ran.Add(1), null);
                SynchronizationContext.Current?.Post(_ => ran.Add(2), null);
                SynchronizationContext.Current?.Post(_ => ran.Add(3), null);

                Assert.That(ran, Is.Empty);

                SynchronizationContext.Current?.Send(_ => ran.Add(4), null);

                Assert.That(ran, Is.EqualTo(new[] { 1, 2, 3, 4 }));
            });
        }

        [Test]
        public void TestOrderOfExecutionFlushingAsyncThread()
        {
            ManualResetEventSlim finished = new ManualResetEventSlim();
            List<int> ran = new List<int>();

            AddStep("queue items", () =>
            {
                var updateContext = SynchronizationContext.Current;

                Debug.Assert(updateContext != null);

                updateContext.Post(_ => ran.Add(1), null);
                updateContext.Post(_ => ran.Add(2), null);
                updateContext.Post(_ => ran.Add(3), null);

                Assert.That(ran, Is.Empty);

                Task.Factory.StartNew(() =>
                {
                    updateContext.Send(_ => ran.Add(4), null);

                    Assert.That(ran, Is.EqualTo(new[] { 1, 2, 3, 4 }));

                    finished.Set();
                }, TaskCreationOptions.LongRunning);
            });

            AddUntilStep("wait for completion", () => finished.IsSet);
        }

        [Test]
        public void TestAsyncThrows()
        {
            Exception thrown = null;

            AddStep("watch for exceptions", () => host.ExceptionThrown += onException);
            AddStep("throw on update thread", () =>
            {
                // ReSharper disable once AsyncVoidLambda
                Scheduler.Add(async () =>
                {
                    Assert.That(ThreadSafety.IsUpdateThread);

                    await Task.Delay(100).ConfigureAwait(true);

                    Assert.That(ThreadSafety.IsUpdateThread);

                    throw new InvalidOperationException();
                });
            });

            AddUntilStep("wait for exception to arrive", () => thrown is InvalidOperationException);
            AddStep("stop watching for exceptions", () => host.ExceptionThrown -= onException);

            bool onException(Exception arg)
            {
                thrown = arg;
                return true;
            }
{'fraction_non_alphanumeric': 0.11429943570656742, 'fraction_numerical': 0.004322247568735743, 'mean_word_length': 2.149338374291115, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 63, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7489014', 'n_tokens_mistral': 2156, 'n_tokens_neox': 2052, 'n_words': 488}