< Summary

Class:GDX.Platform
Assembly:GDX
File(s):D:/BuildAgent/work/GDX-Documentation/Projects/GDX_Development/Packages/com.dotbunny.gdx/GDX/Platform.cs
Covered lines:50
Uncovered lines:48
Coverable lines:98
Total lines:265
Line coverage:51% (50 of 98)
Covered branches:0
Total branches:0
Covered methods:6
Total methods:11
Method coverage:54.5% (6 of 11)

Coverage History

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
EnsureFolderHierarchyExists(...)0%330100%
EnsureFileFolderHierarchyExists(...)0%2100%
EnsureFileWritable(...)0%20400%
ForceDeleteFile(...)0%220100%
GetHardwareGeneration()0%2100%
GetOutputFolder(...)0%6.246081.25%
GetRandomSafeCharacter(...)0%110100%
GetUniqueOutputFilePath(...)0%3.493062.07%
IsFocused()0%2100%
IsFileWritable(...)0%20400%
IsHeadless()0%110100%

File(s)

D:/BuildAgent/work/GDX-Documentation/Projects/GDX_Development/Packages/com.dotbunny.gdx/GDX/Platform.cs

#LineLine coverage
 1// Copyright (c) 2020-2022 dotBunny Inc.
 2// dotBunny licenses this file to you under the BSL-1.0 license.
 3// See the LICENSE file in the project root for more information.
 4
 5using System.IO;
 6using System.Runtime.CompilerServices;
 7using System.Text;
 8using GDX.Mathematics.Random;
 9using UnityEngine;
 10
 11namespace GDX
 12{
 13    /// <summary>
 14    ///     A collection of platform related helper utilities.
 15    /// </summary>
 16    [VisualScriptingCompatible(8)]
 17    public static class Platform
 18    {
 19        public const float ImageCompareTolerance = 0.99f;
 20        public const float FloatTolerance = 0.000001f;
 21        public const double DoubleTolerance = 0.000001d;
 22        public const string SafeCharacterPool = "abcdefghijklmnopqrstuvwxyz";
 23        public const int CharacterPoolLength = 25;
 24        public const int CharacterPoolLengthExclusive = 24;
 25        /// <summary>
 26        ///     A filename safe version of the timestamp format.
 27        /// </summary>
 28        public const string FilenameTimestampFormat = "yyyyMMdd_HHmmss";
 29
 30        static string s_OutputFolder;
 31
 32        /// <summary>
 33        ///     Validate that all directories are created for a given <paramref name="folderPath" />.
 34        /// </summary>
 35        /// <param name="folderPath">The path to process and validate.</param>
 36        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 37        public static void EnsureFolderHierarchyExists(string folderPath)
 738        {
 739            if (!string.IsNullOrEmpty(folderPath) && !Directory.Exists(folderPath))
 140            {
 141                Directory.CreateDirectory(folderPath);
 142            }
 743        }
 44
 45        /// <summary>
 46        ///     Validate that all parent directories are created for a given <paramref name="filePath" />.
 47        /// </summary>
 48        /// <param name="filePath">The path to process and validate.</param>
 49        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 50        public static void EnsureFileFolderHierarchyExists(string filePath)
 051        {
 052            string targetDirectory = Path.GetDirectoryName(filePath);
 053            EnsureFolderHierarchyExists(targetDirectory);
 054        }
 55
 56        /// <summary>
 57        ///     Validate that the file path is writable, making the necessary folder structure and setting permissions.
 58        /// </summary>
 59        /// <param name="filePath">The absolute path to validate.</param>
 60        public static void EnsureFileWritable(string filePath)
 061        {
 062            string fileName = Path.GetFileName(filePath);
 063            if (fileName != null)
 064            {
 065                string directoryPath = filePath.TrimEnd(fileName.ToCharArray());
 066                if (!Directory.Exists(directoryPath))
 067                {
 068                    Directory.CreateDirectory(directoryPath);
 069                }
 070            }
 071            if (File.Exists(filePath))
 072            {
 073                File.SetAttributes(filePath, File.GetAttributes(filePath) & ~FileAttributes.ReadOnly);
 074            }
 075        }
 76
 77        /// <summary>
 78        ///     Use our best attempt to remove a file at the designated <paramref name="filePath"/>.
 79        /// </summary>
 80        /// <param name="filePath">The file path to remove forcefully.</param>
 81        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 82        public static void ForceDeleteFile(string filePath)
 883        {
 884            if (File.Exists(filePath))
 485            {
 486                File.SetAttributes(filePath, File.GetAttributes(filePath) & ~FileAttributes.ReadOnly);
 487                File.Delete(filePath);
 488            }
 889        }
 90
 91        /// <summary>
 92        ///     Gets the current platforms hardware generation number?
 93        /// </summary>
 94        /// <returns>Returns 0 for base hardware, 1 for updates.</returns>
 95        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 96        public static int GetHardwareGeneration()
 097        {
 98#if UNITY_XBOXONE && !UNITY_EDITOR
 99            if (Hardware.version == HardwareVersion.XboxOneX_Devkit ||
 100                Hardware.version == HardwareVersion.XboxOneX_Retail)
 101            {
 102                return 1;
 103            }
 104            return 0;
 105#elif UNITY_PS4 && !UNITY_EDITOR
 106            return 1;
 107#else
 0108            return 0;
 109#endif
 0110        }
 111
 112        /// <summary>
 113        ///     Returns a runtime writable folder.
 114        /// </summary>
 115        /// <param name="folderName">An optional additional folder under the provided path that will be created if neces
 116        /// <returns>The full path to a writable folder at runtime.</returns>
 117        /// <remarks>
 118        ///     Depending on the platform, different routes are taken to finding a writable folder.
 119        ///     <list type="table">
 120        ///         <item>
 121        ///             <term>Editor</term>
 122        ///             <description>The project's root folder is used in this case.</description>
 123        ///         </item>
 124        ///         <item>
 125        ///             <term>Standard Player</term>
 126        ///             <description>Utilizes <see cref="Application.persistentDataPath" /> to find a suitable place.</d
 127        ///         </item>
 128        ///         <item>
 129        ///             <term>DOTS Runtime</term>
 130        ///             <description>Uses <see cref="Directory.GetCurrentDirectory()"/>.</description>
 131        ///         </item>
 132        ///     </list>
 133        ///     The path can be overridden by assigning GDX_OUTPUT_FOLDER in the launching arguments.
 134        /// </remarks>
 135        public static string GetOutputFolder(string folderName = null)
 6136        {
 6137            if (s_OutputFolder != null && string.IsNullOrEmpty(folderName)) return s_OutputFolder;
 6138            if (s_OutputFolder == null)
 1139            {
 1140                s_OutputFolder = Developer.CommandLineParser.Arguments.ContainsKey("GDX_OUTPUT_FOLDER") ?
 141                    Developer.CommandLineParser.Arguments["GDX_OUTPUT_FOLDER"] :
 142#if UNITY_EDITOR
 143                    Path.Combine(Application.dataPath, "..");
 144#elif UNITY_DOTSRUNTIME
 145                    Directory.GetCurrentDirectory();
 146#else
 147                    Application.persistentDataPath;
 148#endif
 149
 150                // Cleanup the folder pathing
 1151                s_OutputFolder = Path.GetFullPath(s_OutputFolder);
 152
 153                // Ensure that it is created
 1154                EnsureFolderHierarchyExists(s_OutputFolder);
 1155            }
 156
 6157            if (string.IsNullOrEmpty(folderName))
 0158            {
 0159                return s_OutputFolder;
 160            }
 161
 6162            string fullPath = Path.Combine(s_OutputFolder, folderName);
 6163            EnsureFolderHierarchyExists(fullPath);
 6164            return fullPath;
 6165        }
 166
 167        public static char GetRandomSafeCharacter(IRandomProvider random)
 24168        {
 24169            return SafeCharacterPool[random.NextInteger(0, CharacterPoolLengthExclusive)];
 24170        }
 171
 172        public static string GetUniqueOutputFilePath(string prefix = "GDX_", string extension = ".log", string folderNam
 4173        {
 4174            string tempFolder = GetOutputFolder(folderName);
 4175            StringBuilder tmpFileName = new StringBuilder(260);
 4176            tmpFileName.Append(prefix);
 4177            RandomWrapper random = new RandomWrapper(
 178                System.DateTime.Now.Ticks.ToString().GetStableHashCode());
 179
 4180            tmpFileName.Append(GetRandomSafeCharacter(random));
 4181            tmpFileName.Append(GetRandomSafeCharacter(random));
 4182            tmpFileName.Append(GetRandomSafeCharacter(random));
 4183            tmpFileName.Append(GetRandomSafeCharacter(random));
 4184            tmpFileName.Append(GetRandomSafeCharacter(random));
 185
 4186            while (true)
 4187            {
 4188                tmpFileName.Append(GetRandomSafeCharacter(random));
 4189                string filePath = Path.Combine(tempFolder, $"{tmpFileName}{extension}");
 4190                if (!File.Exists(filePath))
 4191                {
 4192                    return filePath;
 193                }
 194
 0195                if (tmpFileName.Length <= 260)
 0196                {
 0197                    continue;
 198                }
 199
 0200                tmpFileName.Clear();
 0201                tmpFileName.Append(prefix);
 0202                tmpFileName.Append(GetRandomSafeCharacter(random));
 0203                tmpFileName.Append(GetRandomSafeCharacter(random));
 0204                tmpFileName.Append(GetRandomSafeCharacter(random));
 0205                tmpFileName.Append(GetRandomSafeCharacter(random));
 0206                tmpFileName.Append(GetRandomSafeCharacter(random));
 0207            }
 4208        }
 209
 210#if !UNITY_DOTSRUNTIME
 211        /// <summary>
 212        ///     Is the application focused?
 213        /// </summary>
 214        /// <remarks>
 215        ///     There are issues on some platforms with getting an accurate reading.
 216        /// </remarks>
 217        /// <returns>true/false if the application has focus.</returns>
 218        /// <exception cref="UnsupportedRuntimeException">Not supported on DOTS Runtime.</exception>
 219        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 220        public static bool IsFocused()
 0221        {
 222#if UNITY_XBOXONE && !UNITY_EDITOR
 223            return !XboxOnePLM.AmConstrained();
 224#elif UNITY_PS4 && !UNITY_EDITOR
 225            return true;
 226#else
 0227            return Application.isFocused;
 228#endif
 0229        }
 230#endif
 231
 232        /// <summary>
 233        /// Is it safe to write to the indicated <paramref name="filePath"/>?
 234        /// </summary>
 235        /// <param name="filePath">The file path to check if it can be written.</param>
 236        /// <returns>true/false if the path can be written too.</returns>
 237        public static bool IsFileWritable(string filePath)
 0238        {
 0239            if (File.Exists(filePath))
 0240            {
 0241                FileAttributes attributes = File.GetAttributes(filePath);
 0242                if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly ||
 243                    (attributes & FileAttributes.Offline) == FileAttributes.Offline)
 0244                {
 0245                    return false;
 246                }
 0247            }
 0248            return true;
 0249        }
 250
 251#if !UNITY_DOTSRUNTIME
 252        /// <summary>
 253        /// Is the application running in headless mode?.
 254        /// </summary>
 255        /// <remarks>Useful for detecting running a server.</remarks>
 256        /// <returns>true/false if the application is without an initialized graphics device.</returns>
 257        /// <exception cref="UnsupportedRuntimeException">Not supported on DOTS Runtime.</exception>
 258        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 259        public static bool IsHeadless()
 3260        {
 3261            return SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Null;
 3262        }
 263#endif
 264    }
 265}