Пример #1
0
        /// <summary>
        /// Executes a program entry point synchronously, streaming some text as standard input,
        /// passing arguments and returning the result.
        /// </summary>
        /// <param name="main">The program entry point.</param>
        /// <param name="inputText">The text to be passed as standard input.</param>
        /// <param name="args">The arguments.</param>
        /// <returns>The <see cref="ExecuteResponse"/> returned by the simulated program run.</returns>
        public ExecuteResponse ExecuteWithInput(ProgramEntrypoint main, string inputText, params string[] args)
        {
            Covenant.Requires <ArgumentNullException>(main != null, nameof(main));
            Covenant.Requires <ArgumentNullException>(inputText != null);

            return(ExecuteWithInput(main, Encoding.UTF8.GetBytes(inputText), args));
        }
Пример #2
0
        /// <summary>
        /// Executes a program entry point synchronously, streaming some bytes as standard input,
        /// passing arguments and returning the result.
        /// </summary>
        /// <param name="main">The program entry point.</param>
        /// <param name="inputBytes">The bytes to be passed as standard input.</param>
        /// <param name="args">The arguments.</param>
        /// <returns>The <see cref="ExecuteResponse"/> returned by the simulated program run.</returns>
        public ExecuteResponse ExecuteWithInput(ProgramEntrypoint main, byte[] inputBytes, params string[] args)
        {
            Covenant.Requires <ArgumentNullException>(main != null, nameof(main));
            Covenant.Requires <ArgumentNullException>(inputBytes != null, nameof(inputBytes));

            this.inputBytes = inputBytes;

            if (programThread != null)
            {
                throw new InvalidOperationException("Only one simulated [program] can run at a time.");
            }

            var orgSTDOUT = Console.Out;
            var orgSTDERR = Console.Error;

            try
            {
                // Capture standard output and error and stream the input
                // text as STDIN.

                var sbOut = new StringBuilder();
                var sbErr = new StringBuilder();

                using (var stdOutCapture = new StringWriter(sbOut))
                {
                    using (var stdErrCapture = new StringWriter(sbErr))
                    {
                        var exitCode = 0;

                        Console.SetOut(stdOutCapture);
                        Console.SetError(stdErrCapture);

                        // Simulate executing the program.

                        programThread = new Thread(new ThreadStart(() => exitCode = main(args)));
                        programThread.Start();
                        programThread.Join();
                        programThread = null;

                        return(new ExecuteResponse()
                        {
                            ExitCode = exitCode,
                            OutputText = sbOut.ToString(),
                            ErrorText = sbErr.ToString()
                        });
                    }
                }
            }
            finally
            {
                // Restore the standard files.

                Console.SetOut(orgSTDOUT);
                Console.SetError(orgSTDERR);
            }
        }
Пример #3
0
        /// <summary>
        /// <para>
        /// Executes a program entry point asynchronously, without waiting for the command to complete.
        /// This is useful for commands that don't terminate by themselves.  Call <see cref="TerminateFork()"/>
        /// to kill the running command.
        /// </para>
        /// <note>
        /// <b>IMPORTANT:</b> The <paramref name="main"/> simulated entry point must call
        /// <see cref="WaitForExit()"/>.  This will block until the <see cref="TerminateFork"/>
        /// is called, returning when the program is expected to terminate itself.
        /// </note>
        /// </summary>
        /// <param name="main">The program entry point.</param>
        /// <param name="args">The arguments.</param>
        public void Fork(ProgramEntrypoint main, params string[] args)
        {
            Covenant.Requires <ArgumentNullException>(main != null, nameof(main));

            if (programThread != null)
            {
                throw new InvalidOperationException("Only one simulated [program] can run at a time.");
            }

            programIsReady         = false;
            programExitBeforeReady = false;

            programThread = new Thread(
                new ThreadStart(
                    () =>
            {
                programExitCode = main(args);

                if (!programIsReady)
                {
                    programExitBeforeReady = true;
                }
            }));

            programThread.Name         = "program-runner";
            programThread.IsBackground = true;
            programThread.Start();

            // We need to give the program enough time to do enough initialization
            // so the tests can succeed.  We're going to rely on the program to
            // signal this by calling [ProgramReady()] which will set the event
            // we'll listen on.

            if (!programReadyEvent.WaitOne(forkTimeout))
            {
                throw new TimeoutException($"The program runner timed out before the application called [{nameof(ProgramReady)}].");
            }

            if (programExitBeforeReady)
            {
                throw new InvalidOperationException($"The program returned with [exitcode={programExitCode}] before calling [{nameof(ProgramReady)}].");
            }
        }