Esempio n. 1
0
        internal static async Task <string> ExecuteInternal(Action <ArgumentsBuilder> configure, CancellationToken cancellationToken)
        {
            var toolPath = ToolPath;

            ThrowIfNull(toolPath, nameof(Emulator));
            var builder = new ArgumentsBuilder();

            configure(builder);
            var args = builder.Build();

            Logger.WriteLine($"{toolPath} {args}", LogLevel.Normal);
            var stdErrBuffer = new StringBuilder();
            var stdOutBuffer = new StringBuilder();
            var stdOut       = PipeTarget.Merge(PipeTarget.ToStringBuilder(stdOutBuffer),
                                                PipeTarget.ToDelegate(l => Logger.WriteLine(l, LogLevel.Verbose)));

            await Cli.Wrap(toolPath)
            .WithArguments(args)
            .WithStandardErrorPipe(PipeTarget.ToStringBuilder(stdErrBuffer))
            .WithStandardOutputPipe(stdOut)
            .ExecuteAsync(cancellationToken);

            var stdErr = stdErrBuffer.ToString().Trim();

            if (!string.IsNullOrEmpty(stdErr))
            {
                throw new Exception(stdErr);
            }

            return(stdOutBuffer.ToString().Trim());
        }
Esempio n. 2
0
        public async Task I_can_execute_a_command_that_pipes_its_stdout_into_multiple_streams()
        {
            // Arrange
            const int expectedSize = 100_000;

            await using var stream1 = new MemoryStream();
            await using var stream2 = new MemoryStream();
            await using var stream3 = new MemoryStream();

            var pipeTarget = PipeTarget.Merge(
                PipeTarget.ToStream(stream1),
                PipeTarget.ToStream(stream2),
                PipeTarget.ToStream(stream3)
                );

            var cmd = Cli.Wrap("dotnet")
                      .WithArguments(a => a
                                     .Add(Dummy.Program.FilePath)
                                     .Add(Dummy.Program.PrintRandomBinary)
                                     .Add(expectedSize)) | pipeTarget;

            // Act
            await cmd.ExecuteAsync();

            // Assert
            stream1.Length.Should().Be(expectedSize);
            stream2.Length.Should().Be(expectedSize);
            stream3.Length.Should().Be(expectedSize);
            stream1.ToArray().Should().BeEquivalentTo(stream2.ToArray());
            stream2.ToArray().Should().BeEquivalentTo(stream3.ToArray());
        }
Esempio n. 3
0
        internal static async Task ExecuteInternal(Action <ArgumentsBuilder> configure, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return;
            }

            var builder = new ArgumentsBuilder();

            configure(builder);
            var args = builder.Build();

            Logger.WriteLine($"{ToolPath} {args}", LogLevel.Normal);
            var stdOutBuffer = new StringBuilder();
            var stdOut       = PipeTarget.Merge(PipeTarget.ToStringBuilder(stdOutBuffer),
                                                PipeTarget.ToDelegate(l => Logger.WriteLine(l, LogLevel.Verbose)));
            var stdError = PipeTarget.ToDelegate(l =>
            {
                if (string.IsNullOrEmpty(l))
                {
                    return;
                }

                // Suppress errors
                Logger.WriteWarning(l);
            });

            var result = await Cli.Wrap(ToolPath)
                         .WithArguments(args)
                         .WithValidation(CommandResultValidation.None)
                         .WithStandardErrorPipe(stdError)
                         .WithStandardOutputPipe(stdOut)
                         .ExecuteAsync(cancellationToken);
        }
        public async Task <int> ExecuteWithCliWrap()
        {
            await using var stream1 = new MemoryStream();
            await using var stream2 = new MemoryStream();

            var result = await(Cli.Wrap(FilePath).WithArguments(Args) | PipeTarget.Merge(PipeTarget.ToStream(stream1), PipeTarget.ToStream(stream2))).ExecuteAsync();

            return(result.ExitCode);
        }
Esempio n. 5
0
        public static Task <IDisposable> Run(string baseWorkingDirectory)
        {
            var completed          = false;
            var tcs                = new TaskCompletionSource <IDisposable>();
            var cancellationSource = new CancellationTokenSource();
            var logDirectory       = Path.Combine(baseWorkingDirectory, "logs");

            if (!Directory.Exists(logDirectory))
            {
                Directory.CreateDirectory(logDirectory);
            }

            void HandleConsoleLine(string line)
            {
                if (line.Contains("listener started on 0.0.0.0:4723"))
                {
                    Logger.WriteLine(line, LogLevel.Minimal, defaultLog);

                    if (!completed)
                    {
                        tcs.SetResult(new AppiumTask(cancellationSource));
                    }
                    completed = true;
                }
                else if (line.Contains("make sure there is no other instance of this server running already") ||
                         line.Contains("listen EADDRINUSE: address already in use 0.0.0.0:4723"))
                {
                    Logger.WriteWarning(line, defaultLog);

                    if (!completed)
                    {
                        tcs.SetResult(new AppiumTask(cancellationSource));
                    }
                    completed = true;
                }
                else
                {
                    Logger.WriteLine(line, LogLevel.Verbose, defaultLog);
                }
            }

            var stdOut = PipeTarget.ToDelegate(HandleConsoleLine);
            var stdErr = PipeTarget.Merge(
                PipeTarget.ToFile(Path.Combine(logDirectory, "appium-error.log")),
                PipeTarget.ToDelegate(HandleConsoleLine));

            Logger.WriteLine("Starting Appium...", LogLevel.Minimal);

            var toolPath = EnvironmentHelper.GetToolPath("appium");
            var cmd      = Cli.Wrap(toolPath)
                           .WithStandardOutputPipe(stdOut)
                           .WithStandardErrorPipe(stdErr)
                           .WithValidation(CommandResultValidation.None)
                           .ExecuteAsync(cancellationSource.Token);

            return(tcs.Task);
        }
Esempio n. 6
0
 public static Command AddTestOutputPipe(this Command command, ITestOutputHelper testOutputHelper)
 {
     return(command
            .WithStandardOutputPipe(
                PipeTarget.Merge(
                    command.StandardOutputPipe,
                    PipeTarget.ToDelegate(testOutputHelper.WriteLine)))
            .WithStandardErrorPipe(
                PipeTarget.Merge(
                    command.StandardErrorPipe,
                    PipeTarget.ToDelegate(testOutputHelper.WriteLine))));
 }
Esempio n. 7
0
        /// <summary>
        /// Executes the command as an observable event stream.
        /// </summary>
        public static IObservable <CommandEvent> Observe(
            this Command command,
            Encoding standardOutputEncoding,
            Encoding standardErrorEncoding,
            CancellationToken cancellationToken = default) =>
        Observable.Create <CommandEvent>(observer =>
        {
            var stdOutPipe = PipeTarget.Merge(
                command.StandardOutputPipe,
                PipeTarget.ToDelegate(
                    s => observer.OnNext(new StandardOutputCommandEvent(s)),
                    standardOutputEncoding)
                );

            var stdErrPipe = PipeTarget.Merge(
                command.StandardErrorPipe,
                PipeTarget.ToDelegate(
                    s => observer.OnNext(new StandardErrorCommandEvent(s)),
                    standardErrorEncoding)
                );

            var commandPiped = command
                               .WithStandardOutputPipe(stdOutPipe)
                               .WithStandardErrorPipe(stdErrPipe);

            var commandTask = commandPiped.ExecuteAsync(cancellationToken);
            observer.OnNext(new StartedCommandEvent(commandTask.ProcessId));

            // Don't pass cancellation token to continuation because we need it to always trigger
            // regardless of how the task completed.
            _ = commandTask
                .Task
                .ContinueWith(t =>
            {
                // Canceled tasks don't have exception
                if (t.IsCanceled)
                {
                    observer.OnError(new OperationCanceledException("Command execution has been canceled."));
                }
                else if (t.Exception == null)
                {
                    observer.OnNext(new ExitedCommandEvent(t.Result.ExitCode));
                    observer.OnCompleted();
                }
                else
                {
                    observer.OnError(t.Exception);
                }
            }, TaskContinuationOptions.None);

            return(Disposable.Null);
        });
Esempio n. 8
0
        private static async Task <string> ExecuteInternal(Action <ArgumentsBuilder> configure, CancellationToken cancellationToken, PipeSource stdInput = null)
        {
            var toolPath = ToolPath;

            ThrowIfNull(toolPath, nameof(AvdManager));
            var builder = new ArgumentsBuilder();

            configure(builder);
            var args = builder.Build();

            Logger.WriteLine($"{toolPath} {args}", LogLevel.Normal);
            var errorBuffer  = new List <string>();
            var stdOutBuffer = new StringBuilder();
            var stdOut       = PipeTarget.Merge(PipeTarget.ToStringBuilder(stdOutBuffer),
                                                PipeTarget.ToDelegate(l => Logger.WriteLine(l, LogLevel.Verbose)));

            var stdErr = PipeTarget.ToDelegate(l =>
            {
                if (string.IsNullOrEmpty(l))
                {
                    return;
                }
                else if (l.Contains("Warning: "))
                {
                    Logger.WriteWarning(l);
                }
                else
                {
                    errorBuffer.Add(l);
                }
            });

            var cmd = Cli.Wrap(toolPath)
                      .WithArguments(args)
                      .WithValidation(CommandResultValidation.None)
                      .WithStandardErrorPipe(stdErr)
                      .WithStandardOutputPipe(stdOut);

            if (stdInput != null)
            {
                cmd = cmd.WithStandardInputPipe(stdInput);
            }

            await cmd.ExecuteAsync(cancellationToken);

            if (errorBuffer.Any())
            {
                throw new Exception(string.Join(Environment.NewLine, errorBuffer));
            }

            return(stdOutBuffer.ToString().Trim());
        }
        /// <summary>
        /// Executes the command asynchronously.
        /// The result of this execution contains the standard output and standard error streams buffered in-memory as strings.
        /// This method can be awaited.
        /// </summary>
        public static CommandTask <BufferedCommandResult> ExecuteBufferedAsync(
            this Command command,
            Encoding standardOutputEncoding,
            Encoding standardErrorEncoding,
            CancellationToken cancellationToken = default)
        {
            var stdOutBuffer = new StringBuilder();
            var stdErrBuffer = new StringBuilder();

            var stdOutPipe = PipeTarget.Merge(
                command.StandardOutputPipe,
                PipeTarget.ToStringBuilder(stdOutBuffer, standardOutputEncoding)
                );

            var stdErrPipe = PipeTarget.Merge(
                command.StandardErrorPipe,
                PipeTarget.ToStringBuilder(stdErrBuffer, standardErrorEncoding)
                );

            var commandPiped = command
                               .WithStandardOutputPipe(stdOutPipe)
                               .WithStandardErrorPipe(stdErrPipe)
                               .WithValidation(CommandResultValidation.None); // disable validation because we have our own

            return(commandPiped
                   .ExecuteAsync(cancellationToken)
                   .Select(r =>
            {
                // Transform the result
                var result = new BufferedCommandResult(
                    r.ExitCode,
                    r.StartTime,
                    r.ExitTime,
                    stdOutBuffer.ToString(),
                    stdErrBuffer.ToString()
                    );

                // We perform validation separately here because we want to include stderr in the exception as well
                if (result.ExitCode != 0 && command.Validation.IsZeroExitCodeValidationEnabled())
                {
                    throw CommandExecutionException.ExitCodeValidation(
                        command.TargetFilePath,
                        command.Arguments,
                        result.ExitCode,
                        result.StandardError.Trim()
                        );
                }

                return result;
            }));
        }
Esempio n. 10
0
        private async Task <CommandResult> RunAsyncInner(
            IEnumerable <string> args,
            string workingDir,
            IDictionary <string, string?> additionalEnv,
            Action <string>?onStandardOutput    = null,
            Action <string>?onStandardError     = null,
            EventLogFile?eventLogFile           = null,
            CancellationToken cancellationToken = default)
        {
            var stdOutBuffer = new StringBuilder();
            var stdOutPipe   = PipeTarget.ToStringBuilder(stdOutBuffer);

            if (onStandardOutput != null)
            {
                stdOutPipe = PipeTarget.Merge(stdOutPipe, PipeTarget.ToDelegate(onStandardOutput));
            }

            var stdErrBuffer = new StringBuilder();
            var stdErrPipe   = PipeTarget.ToStringBuilder(stdErrBuffer);

            if (onStandardError != null)
            {
                stdErrPipe = PipeTarget.Merge(stdErrPipe, PipeTarget.ToDelegate(onStandardError));
            }

            var pulumiCmd = Cli.Wrap("pulumi")
                            .WithArguments(PulumiArgs(args, eventLogFile), escape: true)
                            .WithWorkingDirectory(workingDir)
                            .WithEnvironmentVariables(PulumiEnvironment(additionalEnv, debugCommands: eventLogFile != null))
                            .WithStandardOutputPipe(stdOutPipe)
                            .WithStandardErrorPipe(stdErrPipe)
                            .WithValidation(CommandResultValidation.None); // we check non-0 exit code ourselves

            var pulumiCmdResult = await pulumiCmd.ExecuteAsync(cancellationToken);

            var result = new CommandResult(
                pulumiCmdResult.ExitCode,
                standardOutput: stdOutBuffer.ToString(),
                standardError: stdErrBuffer.ToString());

            if (pulumiCmdResult.ExitCode != 0)
            {
                throw CommandException.CreateFromResult(result);
            }
            else
            {
                return(result);
            }
        }
Esempio n. 11
0
        public async Task <(Stream, Stream)> ExecuteWithCliWrap_PipeToMultipleStreams()
        {
            await using var stream1 = new MemoryStream();
            await using var stream2 = new MemoryStream();

            var target = PipeTarget.Merge(
                PipeTarget.ToStream(stream1),
                PipeTarget.ToStream(stream2)
                );

            var command = Cli.Wrap(FilePath).WithArguments(Args) | target;
            await command.ExecuteAsync();

            return(stream1, stream2);
        }
        /// <summary>
        /// Executes the command as an asynchronous (pull-based) event stream.
        /// Use <code>await foreach</code> to listen to the stream and handle command events.
        /// </summary>
        public static async IAsyncEnumerable <CommandEvent> ListenAsync(
            this Command command,
            Encoding standardOutputEncoding,
            Encoding standardErrorEncoding,
            [EnumeratorCancellation] CancellationToken cancellationToken = default)
        {
            using var channel = new Channel <CommandEvent>();

            var stdOutPipe = PipeTarget.Merge(
                command.StandardOutputPipe,
                PipeTarget.ToDelegate(
                    s => channel.PublishAsync(new StandardOutputCommandEvent(s), cancellationToken),
                    standardOutputEncoding)
                );

            var stdErrPipe = PipeTarget.Merge(
                command.StandardErrorPipe,
                PipeTarget.ToDelegate(
                    s => channel.PublishAsync(new StandardErrorCommandEvent(s), cancellationToken),
                    standardErrorEncoding)
                );

            var commandPiped = command
                               .WithStandardOutputPipe(stdOutPipe)
                               .WithStandardErrorPipe(stdErrPipe);

            var commandTask = commandPiped.ExecuteAsync(cancellationToken);

            yield return(new StartedCommandEvent(commandTask.ProcessId));

            // Don't pass cancellation token to continuation because we need it to always trigger
            // regardless of how the task completed.
            _ = commandTask
                .Task
                .ContinueWith(_ => channel.Close(), TaskContinuationOptions.None);

            await foreach (var cmdEvent in channel.ReceiveAsync(cancellationToken).ConfigureAwait(false))
            {
                yield return(cmdEvent);
            }

            var exitCode = await commandTask.Select(r => r.ExitCode).ConfigureAwait(false);

            yield return(new ExitedCommandEvent(exitCode));
        }
Esempio n. 13
0
        internal static async Task <string> ExecuteInternal(Action <ArgumentsBuilder> configure, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return(null);
            }

            var toolPath = ToolPath;
            var builder  = new ArgumentsBuilder();

            configure(builder);
            var args = builder.Build();

            Logger.WriteLine($"{toolPath} {args}", LogLevel.Normal);
            var stdErrBuffer = new StringBuilder();
            var stdOutBuffer = new StringBuilder();
            var stdOut       = PipeTarget.Merge(PipeTarget.ToStringBuilder(stdOutBuffer),
                                                PipeTarget.ToDelegate(l => Logger.WriteLine(l, LogLevel.Verbose)));

            var result = await Cli.Wrap(toolPath)
                         .WithArguments(args)
                         .WithValidation(CommandResultValidation.None)
                         .WithStandardErrorPipe(PipeTarget.ToStringBuilder(stdErrBuffer))
                         .WithStandardOutputPipe(stdOut)
                         .ExecuteAsync(cancellationToken);

            var stdErr = stdErrBuffer.ToString().Trim();

            if (!string.IsNullOrEmpty(stdErr))
            {
                if (stdErr.Split('\n').Select(x => x.Trim()).All(x => x.StartsWith("Warning:", StringComparison.InvariantCultureIgnoreCase)))
                {
                    Logger.WriteWarning(stdErr);
                }
                else
                {
                    throw new Exception(stdErr);
                }
            }

            return(stdOutBuffer.ToString().Trim());
        }