<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.Linq;
using System.Reflection;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Configuration;
using osu.Framework.Extensions;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Framework.IO.Stores;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Framework.Testing.Drawables;
using osu.Framework.Testing.Drawables.Steps;
using osu.Framework.Timing;
using osuTK;
using osuTK.Graphics;
using osuTK.Input;

namespace osu.Framework.Testing
{
    [Cached]
    public class TestBrowser : KeyBindingContainer<TestBrowserAction>, IKeyBindingHandler<TestBrowserAction>, IHandleGlobalKeyboardInput
    {
        public TestScene CurrentTest { get; private set; }

        private BasicTextBox searchTextBox;
        private SearchContainer<TestGroupButton> leftFlowContainer;
        private Container testContentContainer;
        private Container compilingNotice;

        public readonly List<Type> TestTypes = new List<Type>();

        private ConfigManager<TestBrowserSetting> config;

        private DynamicClassCompiler<TestScene> backgroundCompiler;

        private bool interactive;

        private readonly List<Assembly> assemblies;

        /// <summary>
        /// Creates a new TestBrowser that displays the TestCases of every assembly that start with either "osu" or the specified namespace (if it isn't null)
        /// </summary>
        /// <param name="assemblyNamespace">Assembly prefix which is used to match assemblies whose tests should be displayed</param>
        public TestBrowser(string assemblyNamespace = null)
        {
            assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(n => n.FullName.StartsWith("osu") || assemblyNamespace != null && n.FullName.StartsWith(assemblyNamespace)).ToList();

            //we want to build the lists here because we're interested in the assembly we were *created* on.
            foreach (Assembly asm in assemblies.ToList())
            {
                var tests = asm.GetLoadableTypes().Where(isValidVisualTest).ToList();

                if (!tests.Any())
                {
                    assemblies.Remove(asm);
                    continue;
                }

                foreach (Type type in tests)
                    TestTypes.Add(type);
            }

            TestTypes.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
        }

        private bool isValidVisualTest(Type t) => t.IsSubclassOf(typeof(TestScene)) && !t.IsAbstract && t.IsPublic && !t.GetCustomAttributes<HeadlessTestAttribute>().Any();

        private void updateList(ValueChangedEvent<Assembly> args)
        {
            leftFlowContainer.Clear();

            //Add buttons for each TestCase.
            string namespacePrefix = TestTypes.Select(t => t.Namespace).GetCommonPrefix();

            leftFlowContainer.AddRange(TestTypes.Where(t => t.Assembly == args.NewValue)
                                                .GroupBy(
                                                    t =>
                                                    {
                                                        string group = t.Namespace?.Substring(namespacePrefix.Length).TrimStart('.');
                                                        return string.IsNullOrWhiteSpace(group) ? TestScene.RemovePrefix(t.Name) : group;
                                                    },
                                                    t => t,
                                                    (group, types) => new TestGroup { Name = group, TestTypes = types.ToArray() }
                                                ).OrderBy(g => g.Name)
                                                .Select(t => new TestGroupButton(type => LoadTest(type), t)));
        }

        internal readonly BindableDouble PlaybackRate = new BindableDouble(1) { MinValue = 0, MaxValue = 2, Default = 1 };
        internal readonly Bindable<Assembly> Assembly = new Bindable<Assembly>();
        internal readonly Bindable<bool> RunAllSteps = new Bindable<bool>();
        internal readonly Bindable<RecordState> RecordState = new Bindable<RecordState>();
        internal readonly BindableInt CurrentFrame = new BindableInt { MinValue = 0, MaxValue = 0 };

        private TestBrowserToolbar toolbar;
        private Container leftContainer;
        private Container mainContainer;

        private const float test_list_width = 200;

        private Action exit;

        private Bindable<bool> showLogOverlay;

        private readonly BindableDouble audioRateAdjust = new BindableDouble(1);

        [BackgroundDependencyLoader]
        private void load(Storage storage, GameHost host, FrameworkConfigManager frameworkConfig, FontStore fonts, Game game, AudioManager audio)
        {
            interactive = host.Window != null;
            config = new TestBrowserConfig(storage);

            exit = host.Exit;

            audio.AddAdjustment(AdjustableProperty.Frequency, audioRateAdjust);

            var resources = game.Resources;

            //Roboto
            fonts.AddStore(new GlyphStore(resources, @"Fonts/Roboto/Roboto-Regular"));
            fonts.AddStore(new GlyphStore(resources, @"Fonts/Roboto/Roboto-Bold"));

            //RobotoCondensed
            fonts.AddStore(new GlyphStore(resources, @"Fonts/RobotoCondensed/RobotoCondensed-Regular"));
            fonts.AddStore(new GlyphStore(resources, @"Fonts/RobotoCondensed/RobotoCondensed-Bold"));

            showLogOverlay = frameworkConfig.GetBindable<bool>(FrameworkSetting.ShowLogOverlay);

            var rateAdjustClock = new StopwatchClock(true);
            var framedClock = new FramedClock(rateAdjustClock);

            Children = new Drawable[]
            {
                mainContainer = new Container
                {
                    RelativeSizeAxes = Axes.Both,
                    Padding = new MarginPadding { Left = test_list_width },
                    Children = new Drawable[]
                    {
                        new SafeAreaContainer
                        {
                            SafeAreaOverrideEdges = Edges.Right | Edges.Bottom,
                            RelativeSizeAxes = Axes.Both,
                            Child = testContentContainer = new Container
                            {
                                Clock = framedClock,
                                RelativeSizeAxes = Axes.Both,
                                Padding = new MarginPadding { Top = 50 },
                                Child = compilingNotice = new Container
                                {
                                    Alpha = 0,
                                    Anchor = Anchor.Centre,
                                    Origin = Anchor.Centre,
                                    Masking = true,
                                    Depth = float.MinValue,
                                    CornerRadius = 5,
                                    AutoSizeAxes = Axes.Both,
                                    Children = new Drawable[]
                                    {
                                        new Box
                                        {
                                            RelativeSizeAxes = Axes.Both,
                                            Colour = Color4.Black,
                                        },
                                        new SpriteText
                                        {
                                            Font = new FontUsage(size: 30),
                                            Text = @"Compiling new version..."
                                        }
                                    },
                                }
                            }
                        },
                        toolbar = new TestBrowserToolbar
                        {
                            RelativeSizeAxes = Axes.X,
                            Height = 50,
                        },
                    }
                },
                leftContainer = new Container
                {
                    RelativeSizeAxes = Axes.Y,
                    Size = new Vector2(test_list_width, 1),
                    Children = new Drawable[]
                    {
                        new SafeAreaContainer
                        {
                            SafeAreaOverrideEdges = Edges.Left | Edges.Top | Edges.Bottom,
                            RelativeSizeAxes = Axes.Both,
                            Child = new Box
                            {
                                Colour = FrameworkColour.GreenDark,
                                RelativeSizeAxes = Axes.Both
                            }
                        },
                        new FillFlowContainer
                        {
                            Direction = FillDirection.Vertical,
                            RelativeSizeAxes = Axes.Both,
                            Children = new Drawable[]<commit_msg>Fix incorrect masking of TestBrowser list when hidden via Ctrl+H<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.Linq;
using System.Reflection;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Configuration;
using osu.Framework.Extensions;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Framework.IO.Stores;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Framework.Testing.Drawables;
using osu.Framework.Testing.Drawables.Steps;
using osu.Framework.Timing;
using osuTK;
using osuTK.Graphics;
using osuTK.Input;

namespace osu.Framework.Testing
{
    [Cached]
    public class TestBrowser : KeyBindingContainer<TestBrowserAction>, IKeyBindingHandler<TestBrowserAction>, IHandleGlobalKeyboardInput
    {
        public TestScene CurrentTest { get; private set; }

        private BasicTextBox searchTextBox;
        private SearchContainer<TestGroupButton> leftFlowContainer;
        private Container testContentContainer;
        private Container compilingNotice;

        public readonly List<Type> TestTypes = new List<Type>();

        private ConfigManager<TestBrowserSetting> config;

        private DynamicClassCompiler<TestScene> backgroundCompiler;

        private bool interactive;

        private readonly List<Assembly> assemblies;

        /// <summary>
        /// Creates a new TestBrowser that displays the TestCases of every assembly that start with either "osu" or the specified namespace (if it isn't null)
        /// </summary>
        /// <param name="assemblyNamespace">Assembly prefix which is used to match assemblies whose tests should be displayed</param>
        public TestBrowser(string assemblyNamespace = null)
        {
            assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(n => n.FullName.StartsWith("osu") || assemblyNamespace != null && n.FullName.StartsWith(assemblyNamespace)).ToList();

            //we want to build the lists here because we're interested in the assembly we were *created* on.
            foreach (Assembly asm in assemblies.ToList())
            {
                var tests = asm.GetLoadableTypes().Where(isValidVisualTest).ToList();

                if (!tests.Any())
                {
                    assemblies.Remove(asm);
                    continue;
                }

                foreach (Type type in tests)
                    TestTypes.Add(type);
            }

            TestTypes.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
        }

        private bool isValidVisualTest(Type t) => t.IsSubclassOf(typeof(TestScene)) && !t.IsAbstract && t.IsPublic && !t.GetCustomAttributes<HeadlessTestAttribute>().Any();

        private void updateList(ValueChangedEvent<Assembly> args)
        {
            leftFlowContainer.Clear();

            //Add buttons for each TestCase.
            string namespacePrefix = TestTypes.Select(t => t.Namespace).GetCommonPrefix();

            leftFlowContainer.AddRange(TestTypes.Where(t => t.Assembly == args.NewValue)
                                                .GroupBy(
                                                    t =>
                                                    {
                                                        string group = t.Namespace?.Substring(namespacePrefix.Length).TrimStart('.');
                                                        return string.IsNullOrWhiteSpace(group) ? TestScene.RemovePrefix(t.Name) : group;
                                                    },
                                                    t => t,
                                                    (group, types) => new TestGroup { Name = group, TestTypes = types.ToArray() }
                                                ).OrderBy(g => g.Name)
                                                .Select(t => new TestGroupButton(type => LoadTest(type), t)));
        }

        internal readonly BindableDouble PlaybackRate = new BindableDouble(1) { MinValue = 0, MaxValue = 2, Default = 1 };
        internal readonly Bindable<Assembly> Assembly = new Bindable<Assembly>();
        internal readonly Bindable<bool> RunAllSteps = new Bindable<bool>();
        internal readonly Bindable<RecordState> RecordState = new Bindable<RecordState>();
        internal readonly BindableInt CurrentFrame = new BindableInt { MinValue = 0, MaxValue = 0 };

        private TestBrowserToolbar toolbar;
        private Container leftContainer;
        private Container mainContainer;

        private const float test_list_width = 200;

        private Action exit;

        private Bindable<bool> showLogOverlay;

        private readonly BindableDouble audioRateAdjust = new BindableDouble(1);

        [BackgroundDependencyLoader]
        private void load(Storage storage, GameHost host, FrameworkConfigManager frameworkConfig, FontStore fonts, Game game, AudioManager audio)
        {
            interactive = host.Window != null;
            config = new TestBrowserConfig(storage);

            exit = host.Exit;

            audio.AddAdjustment(AdjustableProperty.Frequency, audioRateAdjust);

            var resources = game.Resources;

            //Roboto
            fonts.AddStore(new GlyphStore(resources, @"Fonts/Roboto/Roboto-Regular"));
            fonts.AddStore(new GlyphStore(resources, @"Fonts/Roboto/Roboto-Bold"));

            //RobotoCondensed
            fonts.AddStore(new GlyphStore(resources, @"Fonts/RobotoCondensed/RobotoCondensed-Regular"));
            fonts.AddStore(new GlyphStore(resources, @"Fonts/RobotoCondensed/RobotoCondensed-Bold"));

            showLogOverlay = frameworkConfig.GetBindable<bool>(FrameworkSetting.ShowLogOverlay);

            var rateAdjustClock = new StopwatchClock(true);
            var framedClock = new FramedClock(rateAdjustClock);

            Children = new Drawable[]
            {
                mainContainer = new Container
                {
                    RelativeSizeAxes = Axes.Both,
                    Padding = new MarginPadding { Left = test_list_width },
                    Children = new Drawable[]
                    {
                        new SafeAreaContainer
                        {
                            SafeAreaOverrideEdges = Edges.Right | Edges.Bottom,
                            RelativeSizeAxes = Axes.Both,
                            Child = testContentContainer = new Container
                            {
                                Clock = framedClock,
                                RelativeSizeAxes = Axes.Both,
                                Padding = new MarginPadding { Top = 50 },
                                Child = compilingNotice = new Container
                                {
                                    Alpha = 0,
                                    Anchor = Anchor.Centre,
                                    Origin = Anchor.Centre,
                                    Masking = true,
                                    Depth = float.MinValue,
                                    CornerRadius = 5,
                                    AutoSizeAxes = Axes.Both,
                                    Children = new Drawable[]
                                    {
                                        new Box
                                        {
                                            RelativeSizeAxes = Axes.Both,
                                            Colour = Color4.Black,
                                        },
                                        new SpriteText
                                        {
                                            Font = new FontUsage(size: 30),
                                            Text = @"Compiling new version..."
                                        }
                                    },
                                }
                            }
                        },
                        toolbar = new TestBrowserToolbar
                        {
                            RelativeSizeAxes = Axes.X,
                            Height = 50,
                        },
                    }
                },
                leftContainer = new Container
                {
                    RelativeSizeAxes = Axes.Y,
                    Size = new Vector2(test_list_width, 1),
                    Masking = true,
                    Children = new Drawable[]
                    {
                        new SafeAreaContainer
                        {
                            SafeAreaOverrideEdges = Edges.Left | Edges.Top | Edges.Bottom,
                            RelativeSizeAxes = Axes.Both,
                            Child = new Box
                            {
                                Colour = FrameworkColour.GreenDark,
                                RelativeSizeAxes = Axes.Both
                            }
                        },
                        new FillFlowContainer
                        {
                            Direction = FillDirection.Vertical,
                            RelativeSizeAxes = Axes.Both,
                            Children = new Drawable[]
{'fraction_non_alphanumeric': 0.07331438904675794, 'fraction_numerical': 0.00216998191681736, 'mean_word_length': 1.3447607510599637, 'pattern_counts': {'":': 0, '<': 51, '<?xml version=': 0, '>': 73, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 90, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1195786', 'n_tokens_mistral': 4413, 'n_tokens_neox': 4093, 'n_words': 1012}