/// <summary>
        /// Invoke the Unity3D editor
        /// </summary>
        /// <param name="method">The Unity scipt method to call</param>
        /// <param name="args">arguments for the command line</param>
        protected void RunUnityCommand(string method, Dictionary <string, string> args)
        {
            // The command line arguments to use.
            // All options which start with duel hyphens are used internally by
            // the automated build script.
            var buildArguments =
                "-batchmode " +
                "-quit " +
                $"-projectPath \"{System.IO.Path.GetFullPath(m_projectOptions.ProjectFolder.FullPath)}\" " +
                $"-executeMethod {method} ";

            if (args != null)
            {
                foreach (KeyValuePair <string, string> arg in args)
                {
                    buildArguments += $"--{arg.Key}={EncodeCmdLineValue(arg.Value)} ";
                }
            }

            string fileName = m_projectOptions.UnityEditorLocation;

            if (fileName.EndsWith(".app"))
            {
                // Use open from command line to invoke unity
                buildArguments = $"{fileName} -W --args {buildArguments}";
                fileName       = "open";
            }

            // Create the process using the Unity editor and arguments above.
            var process = new Process()
            {
                StartInfo = new ProcessStartInfo()
                {
                    FileName        = fileName,
                    Arguments       = buildArguments,
                    CreateNoWindow  = true,
                    UseShellExecute = true,
                }
            };

            Console.WriteLine($"Running: \"{m_projectOptions.UnityEditorLocation}\" {buildArguments}");

            // Unity will output to a log, and not to the console.
            // So we have to periodically parse the log and redirect the output to the console.
            // We do this by storing how far through the log we have already seen and outputting
            // the remaining lines. This works because Unity flushes in full lines, so we should
            // always have a full line to output.
            var outputLineIndex = 0;
            var logLocation     = Unity3DEditor.GetEditorLogLocation();

            // Start the process.
            process.Start();

            // Will be set to true if an error is detected within the Unity editor log.
            var logReportedError = false;

            // Whilst the process is still running, periodically redirect the editor log
            // to the console if required.
            while (!process.HasExited)
            {
                System.Threading.Thread.Sleep(100);
                logReportedError |= Unity3DEditor.ProcessEditorLog(m_cakeContext, m_projectOptions.OutputEditorLog, logLocation, ref outputLineIndex);
            }

            if (logReportedError)
            {
                throw new Exception("An error was reported in the Unity3D editor log.");
            }
        }
        /// <summary>
        /// Perform a build using the contexts project directory and build options.
        /// </summary>
        public void Test()
        {
            var testArguments =
                "-batchmode " +
                "-runTests " +
                $"-projectPath \"{m_projectFolder.FullPath}\" " +
                $"-testPlatform {m_testOptions.TestMode.ToString().ToLower()} " +
                $"-testResults \"{m_testOptions.TestResultOutputPath}\"";

            if (System.IO.File.Exists(m_testOptions.TestResultOutputPath))
            {
                try
                {
                    System.IO.File.Delete(m_testOptions.TestResultOutputPath);
                }
                catch (Exception e)
                {
                    throw new Exception($"Failed to delete the existing test results file from '{m_testOptions.TestResultOutputPath}'", e);
                }
            }

            // Create the process using the Unity editor and arguments above.
            using (var process = new Process()
            {
                StartInfo = new ProcessStartInfo()
                {
                    FileName = m_testOptions.UnityEditorLocation,
                    Arguments = testArguments,
                    CreateNoWindow = true,
                    UseShellExecute = false
                }
            })
            {
                Console.WriteLine($"Running: \"{m_testOptions.UnityEditorLocation}\" {testArguments}");

                // Unity will output to a log, and not to the console.
                // So we have to periodically parse the log and redirect the output to the console.
                // We do this by storing how far through the log we have already seen and outputting
                // the remaining lines. This works because Unity flushes in full lines, so we should
                // always have a full line to output.
                var outputLineIndex = 0;
                var logLocation     = Unity3DEditor.GetEditorLogLocation();

                // Start the process.
                process.Start();

                // Will be set to true if an error is detected within the Unity editor log.
                var logReportedError = false;

                // Whilst the process is still running, periodically redirect the editor log
                // to the console if required.
                while (!process.HasExited)
                {
                    System.Threading.Thread.Sleep(100);
                    logReportedError |= Unity3DEditor.ProcessEditorLog(
                        m_cakeContext,
                        m_testOptions.OutputEditorLog,
                        logLocation,
                        ref outputLineIndex);
                }

                if (logReportedError)
                {
                    throw new Exception("An error was reported in the Unity3D editor log.");
                }
            }

            GenerateTestResultsOverview();
        }
        /// <summary>
        /// Perform a build using the contexts project directory and build options.
        /// </summary>
        public void Build()
        {
            // Make sure the automated build script has been copied to the Unity project.
            // The build script is a Unity script that actually invokes the build.
            if (!ProjectHasAutomatedBuildScript() || m_buildOptions.ForceScriptInstall)
            {
                InstallAutomatedBuildScript();
            }

            // The command line arguments to use.
            // All options which start with duel hyphens are used internally by
            // the automated build script.
            var buildArguments =
                "-batchmode " +
                "-quit " +
                $"-projectPath \"{m_projectFolder.FullPath}\" " +
                "-executeMethod Cake.Unity3D.AutomatedBuild.Build " +
                $"--output-path=\"{m_buildOptions.OutputPath}\" " +
                $"--platform={m_buildOptions.Platform} ";

            if (!string.IsNullOrEmpty(m_buildOptions.BuildVersion))
            {
                buildArguments += $"--version={m_buildOptions.BuildVersion} ";
            }

            // Create the process using the Unity editor and arguments above.
            using (var process = new Process()
            {
                StartInfo = new ProcessStartInfo()
                {
                    FileName = m_buildOptions.UnityEditorLocation,
                    Arguments = buildArguments,
                    CreateNoWindow = true,
                    UseShellExecute = false
                }
            })
            {
                Console.WriteLine($"Running: \"{m_buildOptions.UnityEditorLocation}\" {buildArguments}");

                // Unity will output to a log, and not to the console.
                // So we have to periodically parse the log and redirect the output to the console.
                // We do this by storing how far through the log we have already seen and outputting
                // the remaining lines. This works because Unity flushes in full lines, so we should
                // always have a full line to output.
                var outputLineIndex = 0;
                var logLocation     = Unity3DEditor.GetEditorLogLocation();

                // Start the process.
                process.Start();

                // Will be set to true if an error is detected within the Unity editor log.
                var logReportedError = false;

                // Whilst the process is still running, periodically redirect the editor log
                // to the console if required.
                while (!process.HasExited)
                {
                    System.Threading.Thread.Sleep(100);
                    logReportedError |= Unity3DEditor.ProcessEditorLog(
                        m_cakeContext,
                        m_buildOptions.OutputEditorLog,
                        logLocation,
                        ref outputLineIndex);
                }

                if (logReportedError)
                {
                    throw new Exception("An error was reported in the Unity3D editor log.");
                }
            }
        }