// Copyright (c) ppy Pty Ltd . 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.Reflection; using NUnit.Framework; using NUnit.Framework.Internal; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Platform; using osu.Framework.Timing; namespace osu.Framework.Development { public static class DebugUtils { /// /// This represents a clock that runs at a faster-than-realtime pace during unit testing, /// and is intended to substitute for a that would normally /// be used to track a realtime reference. /// /// /// /// /// Components such as use this to advance time at a faster /// pace in order to not induce artificial delays from having to way on wall clock time to elapse. /// /// /// Note that this property is only populated after the is run. /// /// /// internal static IClock? RealtimeClock { get; set; } public static bool IsNUnitRunning => is_nunit_running.Value; private static readonly Lazy is_nunit_running = new Lazy(() => { #pragma warning disable RS0030 var entry = Assembly.GetEntryAssembly(); #pragma warning restore RS0030 string? assemblyName = entry?.GetName().Name; // when running under nunit + netcore, entry assembly becomes nunit itself (testhost, Version=15.0.0.0), which isn't what we want. // when running under nunit + Rider > 2020.2 EAP6, entry assembly becomes ReSharperTestRunner[32|64], which isn't what we want. bool entryIsKnownTestAssembly = entry != null && (assemblyName!.Contains("testhost") || assemblyName.Contains("ReSharperTestRunner")); // null assembly can indicate nunit, but it can also indicate native code (e.g. android). // to distinguish nunit runs from android launches, check the class name of the current test. // if no actual test is running, nunit will make up an ad-hoc test context, which we can match on // to eliminate such false positives. bool nullEntryWithActualTestContext = entry == null && TestContext.CurrentContext.Test.ClassName != typeof(TestExecutionContext.AdhocContext).FullName; return entryIsKnownTestAssembly || nullEntryWithActualTestContext; } ); internal static Assembly NUnitTestAssembly => nunit_test_assembly.Value; private static readonly Lazy nunit_test_assembly = new Lazy(() => { Debug.Assert(IsNUnitRunning); string testName = TestContext.CurrentContext.Test.ClassName.AsNonNull(); return AppDomain.CurrentDomain.GetAssemblies().First(asm => asm.GetType(testName) != null); } ); public static bool IsDebugBuild => is_debug_build.Value; private static readonly Lazy is_debug_build = new Lazy(() => isDebugAssembly(typeof(DebugUtils).Assembly) || isDebugAssembly(RuntimeInfo.EntryAssembly) ); /// /// Whether the framework is currently logging performance issues. /// This should be used only when a configuration is not available via DI or otherwise (ie. in a static context). /// public static bool LogPerformanceIssues { get; internal set; } // https://stackoverflow.com/a/2186634 private static bool isDebugAssembly(Assembly? assembly) => assembly?.GetCustomAttributes(false).OfType().Any(da => da.IsJITTrackingEnabled) ?? false; } }