// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 using System; using System.Diagnostics; using System.IO; using AWS.Deploy.Orchestration; namespace AWS.Deploy.CLI.IntegrationTests.Services { public class InMemoryInteractiveService : IToolInteractiveService, IOrchestratorInteractiveService { private static readonly object s_readToEndLocker = new object(); private readonly object _writeLocker = new object(); private readonly object _readLocker = new object(); private long _stdOutWriterPosition; private long _stdInReaderPosition; private readonly StreamWriter _stdOutWriter; private readonly StreamReader _stdInReader; /// <summary> /// Allows consumers to write string to the BaseStream /// which will be returned on <see cref="ReadLine"/> method call. /// </summary> public StreamWriter StdInWriter { get; } /// <summary> /// Allows consumers to read string which is written via <see cref="WriteLine"/> /// </summary> public StreamReader StdOutReader { get; } public InMemoryInteractiveService() { var stdOut = new MemoryStream(); _stdOutWriter = new StreamWriter(stdOut); StdOutReader = new StreamReader(stdOut); var stdIn = new MemoryStream(); _stdInReader = new StreamReader(stdIn); StdInWriter = new StreamWriter(stdIn); } public void Write(string message) { lock (_writeLocker) { Debug.Write(message); // Save BaseStream position, it must be only modified by the consumer of StdOutReader // After writing to the BaseStream, we will reset it to the original position. var stdOutReaderPosition = StdOutReader.BaseStream.Position; // Reset the BaseStream to the last save position to continue writing from where we left. _stdOutWriter.BaseStream.Position = _stdOutWriterPosition; _stdOutWriter.Write(message); _stdOutWriter.Flush(); // Save the BaseStream position for future writes. _stdOutWriterPosition = _stdOutWriter.BaseStream.Position; // Reset the BaseStream position to the original position StdOutReader.BaseStream.Position = stdOutReaderPosition; } } public void WriteLine(string message) { lock (_writeLocker) { Debug.WriteLine(message); // Save BaseStream position, it must be only modified by the consumer of StdOutReader // After writing to the BaseStream, we will reset it to the original position. var stdOutReaderPosition = StdOutReader.BaseStream.Position; // Reset the BaseStream to the last save position to continue writing from where we left. _stdOutWriter.BaseStream.Position = _stdOutWriterPosition; _stdOutWriter.WriteLine(message); _stdOutWriter.Flush(); // Save the BaseStream position for future writes. _stdOutWriterPosition = _stdOutWriter.BaseStream.Position; // Reset the BaseStream position to the original position StdOutReader.BaseStream.Position = stdOutReaderPosition; } } public void WriteDebugLine(string message) { WriteLine(message); } public void WriteErrorLine(string message) { WriteLine(message); } public string ReadLine() { lock (_readLocker) { var stdInWriterPosition = StdInWriter.BaseStream.Position; // Reset the BaseStream to the last save position to continue writing from where we left. _stdInReader.BaseStream.Position = _stdInReaderPosition; var readLine = _stdInReader.ReadLine(); if (readLine == null) { throw new InvalidOperationException(); } // Save the BaseStream position for future reads. _stdInReaderPosition = _stdInReader.BaseStream.Position; // Reset the BaseStream position to the original position StdInWriter.BaseStream.Position = stdInWriterPosition; WriteLine(readLine); return readLine; } } public bool Diagnostics { get; set; } public bool DisableInteractive { get; set; } public ConsoleKeyInfo ReadKey(bool intercept) { var stdInWriterPosition = StdInWriter.BaseStream.Position; // Reset the BaseStream to the last save position to continue writing from where we left. _stdInReader.BaseStream.Position = _stdInReaderPosition; var keyChar = _stdInReader.Read(); var key = new ConsoleKeyInfo((char)keyChar, (ConsoleKey)keyChar, false, false, false); // Save the BaseStream position for future reads. _stdInReaderPosition = _stdInReader.BaseStream.Position; // Reset the BaseStream position to the original position StdInWriter.BaseStream.Position = stdInWriterPosition; WriteLine(key.ToString()); return key; } public void ReadStdOutStartToEnd() { lock (s_readToEndLocker) { // Save BaseStream position, it must be only modified by the consumer of StdOutReader // After writing to the BaseStream, we will reset it to the original position. var stdOutReaderPosition = StdOutReader.BaseStream.Position; StdOutReader.BaseStream.Position = 0; var output = StdOutReader.ReadToEnd(); Console.WriteLine(output); Debug.WriteLine(output); // Reset the BaseStream position to the original position StdOutReader.BaseStream.Position = stdOutReaderPosition; } } public void LogSectionStart(string message, string description) { WriteLine(message); } public void LogErrorMessage(string message) { WriteLine(message); } public void LogInfoMessage(string message) { WriteLine(message); } public void LogDebugMessage(string message) { WriteLine(message); } } }