Fixed EOF order issues. Many fixes for bugs, hints, warnings. Added ReadAll(Action) method. Updated Readme.
This commit is contained in:
parent
cde6dc865c
commit
117df18c56
217
readme.md
217
readme.md
@ -6,10 +6,10 @@ This library lets you read text input from STDIN that is entered from the consol
|
||||
|
||||
## Consuming the Library
|
||||
|
||||
You can use the library in several ways:
|
||||
- By copying the code to your source repo and adding a project reference to the DotnetStreams project.
|
||||
- By adding a NuGet folder resource to your NuGet sources configuration which points to this project.
|
||||
- By adding a NuGet reference to the DotnetStreams project in the repo at nuget.pillidar.com.
|
||||
You can use the library in several ways:
|
||||
- By copying the code to your source repo and adding a project reference to the DotnetStreams project.
|
||||
- By adding a NuGet folder resource to your NuGet sources configuration which points to this project.
|
||||
- By adding a NuGet reference to the DotnetStreams project in the repo at nuget.pillidar.com.
|
||||
|
||||
> **How To Configure NuGet Sources**
|
||||
>
|
||||
@ -37,9 +37,9 @@ Add a new Package Source.
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
### Consuming Application
|
||||
### The Consuming Application
|
||||
|
||||
Your console app can use DotnetStreams to enable usage such as these:
|
||||
Your console app can use DotnetStreams to enable usage such as the following examples, using a single approach in code regardless of the source of your input:
|
||||
|
||||
```powershell
|
||||
echo "This is a test." | myconsoleapp.exe
|
||||
@ -49,6 +49,14 @@ echo "This is a test." | myconsoleapp.exe
|
||||
type myfile.txt | myconsoleapp.exe
|
||||
```
|
||||
|
||||
```powershell
|
||||
myconsoleapp.exe --filename myfile.txt
|
||||
```
|
||||
|
||||
```powershell
|
||||
myconsoleapp.exe "line1" "line2" "line3"
|
||||
```
|
||||
|
||||
```powershell
|
||||
c:> myconsoleapp.exe
|
||||
line 1
|
||||
@ -58,9 +66,101 @@ line 3
|
||||
^Z
|
||||
```
|
||||
|
||||
The above scenarios can all be handled in a single application in same way using an interface. It may look something like this:
|
||||
|
||||
```csharp
|
||||
ITextSource source;
|
||||
if (args[0].Equals("--filename"))
|
||||
source = new FileTextSource(args[1]);
|
||||
else if (args.Length > 0)
|
||||
source = new ListTextSource(args);
|
||||
else
|
||||
source = new StdInTextSource();
|
||||
```
|
||||
|
||||
### ITextSource
|
||||
|
||||
ITextSource is the abstraction of the available kinds of inputs.
|
||||
|
||||
Implementations of ITextSource have `Open()` and `Close()` methods. They must be called for the implementations to operate properly. The `Close()` method should always be in a `finally` block.
|
||||
|
||||
*Example:*
|
||||
```csharp
|
||||
ITextSource source = ... ;
|
||||
source.Open();
|
||||
try
|
||||
{
|
||||
IEnumerable<string> lines = source.ReadAll();
|
||||
}
|
||||
finally
|
||||
{
|
||||
source.Close();
|
||||
}
|
||||
```
|
||||
|
||||
### IOutputTarget
|
||||
|
||||
Like ITextSource, implementations of IOutputTarget have `Open()` and `Close()` methods. They must be called for the implementations to operate properly. The `Close()` method should always be in a `finally` block.
|
||||
|
||||
```csharp
|
||||
IOutputTarget outputTarget = ... ;
|
||||
outputTarget.Open();
|
||||
try
|
||||
{
|
||||
outputTarget.Output("Hello World");
|
||||
}
|
||||
finally
|
||||
{
|
||||
outputTarget.Close();
|
||||
}
|
||||
```
|
||||
|
||||
### Several Sources to Read
|
||||
|
||||
#### ITextSource
|
||||
As described above, this is the interface that all text readers implement. It abstracts the source and the EOF behavior for uniform consumption of text sources. It has `Open()` and `Close()` fixture methods for implementations that need them.
|
||||
|
||||
#### StdInTextSource
|
||||
This reads lines of text from the StdIn stream. This means that data can come from the Keyboard or can be piped in through the command line using the `|` pipe character using a `type` command to provide the contents of a file or using `write-output` (PowerShell) or `echo` (Cmd) to provide a literal text string from the command line. StdIn can also be redevined outside of the application in interesting ways.
|
||||
|
||||
#### FileTextSource
|
||||
This reads lines of text from a text file on disk. You will need to provide your own mechanism for determining when to use it and what the filename will be. For example, in this document most examples demonstrate doing this by passing the filename as a command-line parameter which I think seems quite obvious.
|
||||
|
||||
#### ListTextSource
|
||||
This wraps an IEnumerable<string>. This means it can also wrap a list. That, in turn, means you can wrap another ITextSource. This was written for experimentation and unit testing but may have other creative uses, such as transforming input sources by sorting the data, for example.
|
||||
|
||||
### Several Targets to Write
|
||||
|
||||
#### IOutputTarget
|
||||
As described above, this is a simple interface that wraps an output destination. It has `Open()` and `Close()` methods to match the ITextSource and an `Output()` method to do the writing. The `Output()` method does NOT insert or append newlines or other line endings. Since you probably already have them in your strings from their original ITextSource or other processing, you won't usually need them. If your strings do not have line endings but need them, you must concatenate them yourself.
|
||||
|
||||
#### ListOutputTarget
|
||||
This wraps an IList<string> and will store data to it when Output() is called. This is useful for unit testing or other creative workarounds.
|
||||
|
||||
#### ConsoleOutputTarget
|
||||
This writes data to the StdOut device when Output() is called.
|
||||
|
||||
#### AnonOutputTarget
|
||||
This will take an Action<string> parameter in its constructor to instruct it how to behave when Output() is called. This implementation can easily be used to fulfill any output need. Other obvious OutputTarget types (e.g. TextFileOutputTarget) have not been implemented because this method is simpler to use than those classes would be and it gives you more control than you would have with those classes. For example, you can control the access parameters when opening a text file.
|
||||
|
||||
*Example:*
|
||||
|
||||
```csharp
|
||||
// Writes to the console.
|
||||
IOutputTarget target = new AnonOutputTarget(static s =>
|
||||
{
|
||||
int maxLen = 15;
|
||||
string truncatedString = s.Substring(0, Math.Min(s.Length, maxLen))
|
||||
string logLine = $"[{DateTime.Now}] {truncatedString}";
|
||||
Console.WriteLine(logLine);
|
||||
};
|
||||
|
||||
target.Output("Super Califragilistic Expiyallydocious!");
|
||||
```
|
||||
|
||||
### Code Usage
|
||||
|
||||
```powershell
|
||||
```csharp
|
||||
static void Main(string[] args)
|
||||
{
|
||||
ITextSource textSource;
|
||||
@ -88,3 +188,106 @@ public static void Execute(ITextSource source, IOutputTarget target)
|
||||
source.Close();
|
||||
}
|
||||
```
|
||||
|
||||
### Several Ways To Read
|
||||
|
||||
#### Iterator
|
||||
|
||||
You can read all lines using an iterator:
|
||||
```csharp
|
||||
foreach(string line in source.ReadAll())
|
||||
// Do stuff with line here.
|
||||
```
|
||||
|
||||
#### Callback
|
||||
|
||||
You can read all lines using a callback:
|
||||
```csharp
|
||||
source.ReadAll(static line => /*do stuff with line here*/);
|
||||
```
|
||||
|
||||
#### Input / Output Method Group
|
||||
|
||||
The action callback version of the ReadAll() method is compatible with the delegate for IOutputTarget.Output(). Therefore, you can pass an IOutputTarget.Output method directly as a parameter to the ReadAll() method without wrapping it in a lambda function.
|
||||
|
||||
```csharp
|
||||
ITextSource source = ... ;
|
||||
IOutputTarget target = ... ;
|
||||
source.Open();
|
||||
target.Open();
|
||||
|
||||
// Notice .Output does not have parentheses and this is not using a lambda. We are passing the function directly.
|
||||
source.ReadAll(target.Output);
|
||||
|
||||
target.Close();
|
||||
source.Close();
|
||||
```
|
||||
|
||||
#### One Line at a Time in a While Loop
|
||||
|
||||
You can read one line at a time in a loop, watching for EOF.
|
||||
Be careful to check for EOF in this specific way. The text source implementations are specifically engineered to behave with this logic:
|
||||
|
||||
EOF is FALSE after each read that returns data.
|
||||
EOF is TRUE after the first and subsequent reads where data is exhausted.
|
||||
|
||||
```csharp
|
||||
ITextSource source = ... ;
|
||||
string line = source.Read();
|
||||
while (!source.Eof())
|
||||
{
|
||||
// Do stuff with line here.
|
||||
|
||||
string line = source.Read();
|
||||
}
|
||||
```
|
||||
|
||||
#### One Line at a Time in a FOR Loop
|
||||
|
||||
```csharp
|
||||
source.Open();
|
||||
target.Open();
|
||||
|
||||
for (string? line = source.Read(); !source.Eof(); line = source.Read())
|
||||
{
|
||||
target.Output(line);
|
||||
}
|
||||
|
||||
target.Close();
|
||||
source.Close();
|
||||
```
|
||||
|
||||
|
||||
#### ListTextSource
|
||||
This is a text source that wraps an in-memory List<string> object. It is useful for unit testing or for creative workarounds. This is also useful as if you want to use it to wrap input from other text sources, such as a FileTextSource when you want to do document-level operations, such as sorting all the lines, which require all lines to be available at once instead of one at a time in an iterator pattern as is provided by the text source itself. It takes an IEnumerable<string> as a constructor parameter, so it basically just wraps another collection as-is. Example:
|
||||
|
||||
```csharp
|
||||
ITextSource stdin = new StdInTextSource();
|
||||
stdin.Open();
|
||||
List<string> sortedStdIn = new List<string>(stdin.ReadAll());
|
||||
sortedStdIn.Sort();
|
||||
stdin.Close();
|
||||
|
||||
ITextSource listSource = new ListTextSource(sortedStdIn);
|
||||
listSource.Open();
|
||||
listSource.ReadAll(static line => { /*Do stuff with line here.*/ });
|
||||
listSource.Close();
|
||||
```
|
||||
|
||||
## Why not IDisposable?
|
||||
|
||||
At its inception, there were no ITextSource or IOutputTarget implementations that had underlying IDisposable aspects. It wasn't until I added the FileTextSource that I encountered one.
|
||||
|
||||
Now that we have one, the intent is to unwind the Dispose() handling in that class, Modify ITextSource and IOutputTarget to inherit IDisposable, then modify all implementations to implement IDisposable. This will probably involve a base class to generalize the disposal code and also the ReadAll(Action<string> readAction) method implementation since it's repeated verbatim in all the ITextSource implementations.
|
||||
|
||||
This will be done in the next version.
|
||||
|
||||
## Why not IEnumerable?
|
||||
|
||||
This is being considered. The primary concern is that the operations must be Open()d and Close()d. Along those lines, the Open() and Close() may be reconsidered as well and consolidated into the constructor and Dispose() methods, getting rid of them altogether. The IEnumerable would then be freely enumerated. I will experiment with this in the next few versions.
|
||||
|
||||
## Why not Async?
|
||||
|
||||
Generally speaking, async operations should be used for I/O-bound or CPU-bound operations. These operations are not really CPU-bound as items can be iterated independently. They may be I/O-bound but that depends on the implementation. The nature of these operations generally puts them in a category of startup code, wherein you can't do much else until they have completed anyway.
|
||||
|
||||
I am still considering adding async versions but for now I am satisfied with synchronous operation.
|
||||
@ -1,30 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DotnetStandardStreams
|
||||
{
|
||||
public class AnonOutputTarget : IOutputTarget
|
||||
{
|
||||
private readonly Action<string> outputProc;
|
||||
|
||||
public AnonOutputTarget(Action<string> outputProc)
|
||||
{
|
||||
this.outputProc = outputProc;
|
||||
}
|
||||
|
||||
public virtual void Close()
|
||||
{
|
||||
}
|
||||
public virtual void Close() { }
|
||||
|
||||
public virtual void Open()
|
||||
{
|
||||
}
|
||||
public virtual void Open() { }
|
||||
|
||||
public virtual void Output(string line)
|
||||
{
|
||||
this.outputProc?.Invoke(line);
|
||||
outputProc.Invoke(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,39 +6,41 @@ namespace DotnetStandardStreams
|
||||
{
|
||||
public byte[] Data { get; }
|
||||
public int Size { get; }
|
||||
|
||||
public BytesReadEventArgs(byte[] data, int size)
|
||||
{
|
||||
this.Data = data;
|
||||
this.Size = size;
|
||||
Data = data;
|
||||
Size = size;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void BytesReadEventHandler(object sender, BytesReadEventArgs e);
|
||||
|
||||
// TODO: This class is unfinished, untested, unused. Ignore it or fix it but don't use it as-is. Notice there is no interface to it yet.
|
||||
public class BinaryStdinReader
|
||||
{
|
||||
protected readonly Action<byte[], int>? dataReceiverProc;
|
||||
protected readonly Action<int>? doneProc;
|
||||
public event BytesReadEventHandler? OnBytesRead;
|
||||
public event EventHandler? OnDone;
|
||||
|
||||
public BinaryStdinReader(Action<byte[], int> dataReceiverProc, Action<int> doneProc)
|
||||
{
|
||||
this.dataReceiverProc = dataReceiverProc;
|
||||
this.doneProc = doneProc;
|
||||
}
|
||||
|
||||
public BinaryStdinReader()
|
||||
{
|
||||
}
|
||||
public BinaryStdinReader() { }
|
||||
|
||||
public void ReadBytes(Action<byte[], int> receiverProc, Action<int> doneProc)
|
||||
{
|
||||
using System.IO.Stream stdinStream = System.Console.OpenStandardInput();
|
||||
|
||||
int totalBytesRead = 0;
|
||||
int bufferSize = 2048;
|
||||
const int bufferSize = 2048;
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
int bytesRead = stdinStream.Read(buffer, 0, bufferSize);
|
||||
|
||||
while (bytesRead > 0)
|
||||
{
|
||||
OnBytesRead?.Invoke(this, new BytesReadEventArgs(buffer, bytesRead));
|
||||
|
||||
@ -1,22 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DotnetStandardStreams
|
||||
{
|
||||
public class ConsoleOutputTarget : IOutputTarget
|
||||
{
|
||||
public virtual void Open()
|
||||
{
|
||||
}
|
||||
public virtual void Open() { }
|
||||
public virtual void Output(string line)
|
||||
{
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
public virtual void Close()
|
||||
{
|
||||
}
|
||||
public virtual void Close() { }
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<TargetFrameworks>netstandard2.1;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
|
||||
<Nullable>enable</Nullable>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Authors>Phil Gilmore</Authors>
|
||||
|
||||
@ -7,11 +7,17 @@ using System.IO;
|
||||
|
||||
namespace DotnetStandardStreams
|
||||
{
|
||||
|
||||
// THE GOAL:
|
||||
// READ will return NULL when you read after the last line. This is fine.
|
||||
// EOF will return TRUE when you read the last line.
|
||||
// We want EOF to return FALSE after you read the last line and TRUE when you read AFTER the last line.
|
||||
public class FileTextSource : ITextSource
|
||||
{
|
||||
protected FileStream? file;
|
||||
protected StreamReader? reader;
|
||||
protected string filename;
|
||||
protected string? lastLineRead = string.Empty; // Do NOT make this null at first.
|
||||
|
||||
public FileTextSource(string filename)
|
||||
{
|
||||
@ -28,31 +34,40 @@ namespace DotnetStandardStreams
|
||||
|
||||
public virtual IEnumerable<string> ReadAll()
|
||||
{
|
||||
string? line = reader?.ReadLine();
|
||||
while (line != null)
|
||||
string line = Read();
|
||||
|
||||
while (!Eof())
|
||||
{
|
||||
yield return line;
|
||||
line = reader?.ReadLine();
|
||||
|
||||
line = Read();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string Read()
|
||||
{
|
||||
if (!Eof())
|
||||
return reader?.ReadLine() ?? string.Empty;
|
||||
{
|
||||
string? result = reader?.ReadLine();
|
||||
lastLineRead = result;
|
||||
|
||||
return result ?? string.Empty;
|
||||
}
|
||||
else
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public virtual bool Eof()
|
||||
{
|
||||
return reader?.EndOfStream ?? true;
|
||||
//return reader?.EndOfStream ?? true;
|
||||
return lastLineRead == null;
|
||||
}
|
||||
|
||||
public virtual void Close()
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: Uh... why are we calling Flush() on a file-read operation?
|
||||
file?.Flush();
|
||||
}
|
||||
finally
|
||||
@ -67,5 +82,11 @@ namespace DotnetStandardStreams
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ReadAll(Action<string> readAction)
|
||||
{
|
||||
foreach (string line in ReadAll())
|
||||
readAction(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DotnetStandardStreams
|
||||
{
|
||||
@ -10,6 +7,7 @@ namespace DotnetStandardStreams
|
||||
{
|
||||
void Open();
|
||||
IEnumerable<string> ReadAll();
|
||||
void ReadAll(Action<string> readAction);
|
||||
string Read();
|
||||
bool Eof();
|
||||
void Close();
|
||||
|
||||
@ -11,53 +11,33 @@ namespace DotnetStandardStreams
|
||||
private bool isEof;
|
||||
private readonly IEnumerable<string> source;
|
||||
private IEnumerator<string>? enumerator;
|
||||
//private string lastValue;
|
||||
private bool firstIsRead;
|
||||
private string nextLine;
|
||||
|
||||
public ListTextSource(IEnumerable<string> source)
|
||||
{
|
||||
this.source = source;
|
||||
enumerator = null;
|
||||
firstIsRead = false;
|
||||
nextLine = string.Empty;
|
||||
//firstIsRead = false;
|
||||
//nextLine = string.Empty;
|
||||
}
|
||||
|
||||
public virtual void Open()
|
||||
{
|
||||
}
|
||||
public virtual void Open() { }
|
||||
|
||||
public virtual IEnumerable<string> ReadAll()
|
||||
{
|
||||
return source.AsEnumerable();
|
||||
}
|
||||
public virtual IEnumerable<string> ReadAll() => source.AsEnumerable();
|
||||
|
||||
public virtual string Read()
|
||||
{
|
||||
if (enumerator == null)
|
||||
enumerator = source.GetEnumerator();
|
||||
|
||||
string thisLine;
|
||||
if (!firstIsRead)
|
||||
{
|
||||
// Read the first, put it in the "last" buffer.
|
||||
isEof = !enumerator.MoveNext();
|
||||
nextLine = enumerator.Current;
|
||||
firstIsRead = true;
|
||||
}
|
||||
|
||||
thisLine = nextLine;
|
||||
//string thisLine;
|
||||
|
||||
if (!isEof)
|
||||
{
|
||||
isEof = !enumerator.MoveNext();
|
||||
if (!isEof)
|
||||
nextLine = enumerator.Current;
|
||||
else
|
||||
nextLine = string.Empty;
|
||||
|
||||
}
|
||||
|
||||
return thisLine;
|
||||
if (!isEof)
|
||||
return enumerator.Current;
|
||||
else
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public virtual bool Eof() => isEof;
|
||||
@ -67,5 +47,11 @@ namespace DotnetStandardStreams
|
||||
enumerator = null;
|
||||
isEof = false;
|
||||
}
|
||||
|
||||
public void ReadAll(Action<string> readAction)
|
||||
{
|
||||
foreach (string line in ReadAll())
|
||||
readAction(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,21 +17,24 @@ namespace DotnetStandardStreams
|
||||
|
||||
public virtual IEnumerable<string> ReadAll()
|
||||
{
|
||||
string? s = Console.ReadLine();
|
||||
while (s != null)
|
||||
string s = Read();
|
||||
|
||||
while (!Eof())
|
||||
{
|
||||
yield return s;
|
||||
s = Console.ReadLine();
|
||||
|
||||
s = Read();
|
||||
}
|
||||
isEof = true;
|
||||
}
|
||||
|
||||
public virtual string Read()
|
||||
{
|
||||
string? s = Console.ReadLine();
|
||||
|
||||
if (s == null)
|
||||
{
|
||||
isEof = true;
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
else
|
||||
@ -40,8 +43,12 @@ namespace DotnetStandardStreams
|
||||
|
||||
public virtual bool Eof() => isEof;
|
||||
|
||||
public virtual void Close()
|
||||
public virtual void Close() { }
|
||||
|
||||
public void ReadAll(Action<string> readAction)
|
||||
{
|
||||
foreach (string line in ReadAll())
|
||||
readAction(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,87 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using DotnetStandardStreams;
|
||||
using Console = System.Console;
|
||||
|
||||
namespace StreamsTest
|
||||
namespace DotnetStandardStreamsApp;
|
||||
|
||||
/// <summary>
|
||||
/// Typical command line for testing in PowerShell:
|
||||
/// @(1,2,3,4,5,6,7,8,9,10) | .\DotnetStandardStreamsApp.exe .\input.txt
|
||||
/// </summary>
|
||||
public static class Program
|
||||
{
|
||||
class Program
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
ITextSource textSource;
|
||||
IOutputTarget textTarget = new ConsoleOutputTarget();
|
||||
|
||||
if (args.Length > 0)
|
||||
textSource = new FileTextSource(args[0]);
|
||||
else
|
||||
textSource = new StdInTextSource();
|
||||
|
||||
Console.WriteLine("---- ExecuteAll");
|
||||
ExecuteAll(
|
||||
textSource,
|
||||
textTarget);
|
||||
|
||||
//Console.WriteLine("---- ExecuteAction");
|
||||
//ExecuteAction(
|
||||
// textSource,
|
||||
// textTarget);
|
||||
|
||||
//Console.WriteLine("---- ExecuteWhileNotEof");
|
||||
//ExecuteWhileNotEof(
|
||||
// textSource,
|
||||
// textTarget);
|
||||
|
||||
//Console.WriteLine("---- ExecuteForNotEof");
|
||||
//ExecuteForNotEof(
|
||||
// textSource,
|
||||
// textTarget);
|
||||
}
|
||||
|
||||
private static void ExecuteWhileNotEof(ITextSource source, IOutputTarget target)
|
||||
{
|
||||
source.Open();
|
||||
target.Open();
|
||||
|
||||
while (!source.Eof())
|
||||
{
|
||||
//Program p = new();
|
||||
|
||||
ITextSource textSource;
|
||||
IOutputTarget textTarget = new ConsoleOutputTarget();
|
||||
|
||||
if (args.Length > 0)
|
||||
textSource = new FileTextSource(args[0]);
|
||||
else
|
||||
textSource = new StdInTextSource();
|
||||
|
||||
|
||||
Execute(
|
||||
textSource,
|
||||
textTarget);
|
||||
string thisLine = source.Read();
|
||||
target.Output(thisLine);
|
||||
}
|
||||
|
||||
// Works, but needs to be more compact.
|
||||
//static void Main(string[] args)
|
||||
//{
|
||||
// Program p = new Program();
|
||||
target.Close();
|
||||
source.Close();
|
||||
}
|
||||
|
||||
// ITextSource textSource = null;
|
||||
private static void ExecuteForNotEof(ITextSource source, IOutputTarget target)
|
||||
{
|
||||
source.Open();
|
||||
target.Open();
|
||||
|
||||
// if (args.Length > 0)
|
||||
// {
|
||||
// var filename = args[0];
|
||||
// if (!File.Exists(filename))
|
||||
// {
|
||||
// Console.WriteLine($"File not found ({filename}).");
|
||||
// Environment.Exit(1);
|
||||
// }
|
||||
// else
|
||||
// textSource = new FileTextSource(filename);
|
||||
// }
|
||||
// else
|
||||
// textSource = new StdInTextSource();
|
||||
|
||||
// p.Execute(
|
||||
// textSource,
|
||||
// new ConsoleOutputTarget());
|
||||
//}
|
||||
|
||||
|
||||
//public void ExecuteReadLine(string[] args)
|
||||
//{
|
||||
// string s = Console.ReadLine();
|
||||
// // This terminates on blank lines, no surprise
|
||||
// //while (!string.IsNullOrEmpty(s))
|
||||
// while (s != null)
|
||||
// {
|
||||
// s = s.Replace("\r", "{CR}")
|
||||
// .Replace("\n", "{LF}")
|
||||
// .Replace("\t", "{TAB}");
|
||||
// if (s == string.Empty)
|
||||
// s = "{EMPTYSTRING}";
|
||||
// Console.WriteLine($"{s}");
|
||||
// s = Console.ReadLine();
|
||||
// }
|
||||
// if (s == null)
|
||||
// Console.WriteLine("/s/ is null");
|
||||
//}
|
||||
|
||||
public static void Execute(ITextSource source, IOutputTarget target)
|
||||
for (string? line = source.Read(); !source.Eof(); line = source.Read())
|
||||
{
|
||||
source.Open();
|
||||
target.Open();
|
||||
|
||||
foreach (string line in source.ReadAll())
|
||||
target.Output(line);
|
||||
|
||||
target.Close();
|
||||
source.Close();
|
||||
target.Output(line);
|
||||
}
|
||||
|
||||
target.Close();
|
||||
source.Close();
|
||||
}
|
||||
|
||||
private static void ExecuteAction(ITextSource source, IOutputTarget target)
|
||||
{
|
||||
source.Open();
|
||||
target.Open();
|
||||
|
||||
source.ReadAll(target.Output);
|
||||
|
||||
target.Close();
|
||||
source.Close();
|
||||
}
|
||||
|
||||
private static void ExecuteAll(ITextSource source, IOutputTarget target)
|
||||
{
|
||||
source.Open();
|
||||
target.Open();
|
||||
|
||||
foreach (string line in source.ReadAll())
|
||||
target.Output(line);
|
||||
|
||||
target.Close();
|
||||
source.Close();
|
||||
}
|
||||
}
|
||||
|
||||
47
source/DotnetStandardStreamsTests/AnonOutputTargetTests.cs
Normal file
47
source/DotnetStandardStreamsTests/AnonOutputTargetTests.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DotnetStandardStreams;
|
||||
|
||||
namespace DotnetStandardStreamsTests;
|
||||
|
||||
public class AnonOutputTargetTests
|
||||
{
|
||||
[Fact]
|
||||
public void CanConstruct()
|
||||
{
|
||||
_ = new AnonOutputTarget(static s => { });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WritesEachLine()
|
||||
{
|
||||
List<string> expected = ["1", "2", "", "4", "5", "6", "7", "8", "9", "10"];
|
||||
List<string> actual = new();
|
||||
IOutputTarget target = new AnonOutputTarget(s => actual.Add(s));
|
||||
|
||||
foreach (string s in expected)
|
||||
target.Output(s);
|
||||
|
||||
actual.ShouldBe(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OutputsConditionally()
|
||||
{
|
||||
List<string> input = ["1", "2", "", "4", "5", "6", "7", "8", "9", "10"];
|
||||
List<string> expected = ["2", "4", "6", "8", "10"];
|
||||
List<string> actual = new();
|
||||
|
||||
IOutputTarget target = new AnonOutputTarget(s =>
|
||||
{
|
||||
if (int.TryParse(s, out int i))
|
||||
if (i % 2 == 0)
|
||||
actual.Add(s);
|
||||
});
|
||||
|
||||
foreach (string s in input)
|
||||
target.Output(s);
|
||||
|
||||
actual.ShouldBe(expected);
|
||||
}
|
||||
}
|
||||
@ -1,25 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
using Shouldly;
|
||||
using DotnetStandardStreams;
|
||||
using System.IO;
|
||||
using DotnetStandardStreamsTests.Testables;
|
||||
|
||||
namespace DotnetStandardStreamsTests
|
||||
{
|
||||
public class ConsoleOutputTargetTests
|
||||
{
|
||||
private IOutputTarget outputTarget;
|
||||
|
||||
[Fact]
|
||||
public void WritesToOutputStream()
|
||||
{
|
||||
ListWriter writer = new();
|
||||
outputTarget = new TestableConsoleOutputTarget(writer);
|
||||
IOutputTarget outputTarget = new TestableConsoleOutputTarget(writer);
|
||||
|
||||
outputTarget.Open();
|
||||
outputTarget.Output("1");
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
|
||||
<nullable>enable</nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
181
source/DotnetStandardStreamsTests/FileTextSourceTests.cs
Normal file
181
source/DotnetStandardStreamsTests/FileTextSourceTests.cs
Normal file
@ -0,0 +1,181 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using DotnetStandardStreams;
|
||||
using DotnetStandardStreamsTests.Testables;
|
||||
|
||||
namespace DotnetStandardStreamsTests;
|
||||
|
||||
public class FileTextSourceTests
|
||||
{
|
||||
private string tempFilename { get; set; }
|
||||
|
||||
private void CleanupTempFile()
|
||||
{
|
||||
if (File.Exists(tempFilename))
|
||||
File.Delete(tempFilename);
|
||||
}
|
||||
|
||||
private void CreateInputFile()
|
||||
{
|
||||
tempFilename = System.IO.Path.GetTempFileName();
|
||||
const string content = "1\r\n2\r\n\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n";
|
||||
CleanupTempFile();
|
||||
File.WriteAllText(tempFilename, content);
|
||||
}
|
||||
|
||||
private void CreateEmptyInputFile()
|
||||
{
|
||||
tempFilename = System.IO.Path.GetTempFileName();
|
||||
CleanupTempFile();
|
||||
File.WriteAllText(tempFilename, string.Empty);
|
||||
}
|
||||
|
||||
public void WrapFileTest(Action<ITextSource> testCode, Action? explicitSourceCreation = null)
|
||||
{
|
||||
explicitSourceCreation = explicitSourceCreation ?? CreateInputFile;
|
||||
explicitSourceCreation();
|
||||
try
|
||||
{
|
||||
ITextSource source = new FileTextSource(tempFilename);
|
||||
source.Open();
|
||||
try
|
||||
{
|
||||
testCode(source);
|
||||
}
|
||||
finally
|
||||
{
|
||||
source.Close();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanupTempFile();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanReadAllFromFile()
|
||||
{
|
||||
WrapFileTest(static textSource =>
|
||||
{
|
||||
List<string> actual = textSource.ReadAll().ToList();
|
||||
actual.Count.ShouldBe(10);
|
||||
actual.ShouldBe(["1", "2", "", "4", "5", "6", "7", "8", "9", "10"]);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanReadIndividualLinesFromFile()
|
||||
{
|
||||
WrapFileTest(static textSource =>
|
||||
{
|
||||
string s;
|
||||
s = textSource.Read();
|
||||
textSource.Eof().ShouldBe(false);
|
||||
s.ShouldBe("1");
|
||||
|
||||
s = textSource.Read();
|
||||
textSource.Eof().ShouldBe(false);
|
||||
s.ShouldBe("2");
|
||||
|
||||
s = textSource.Read();
|
||||
textSource.Eof().ShouldBe(false);
|
||||
s.ShouldBe("");
|
||||
|
||||
s = textSource.Read();
|
||||
textSource.Eof().ShouldBe(false);
|
||||
s.ShouldBe("4");
|
||||
|
||||
s = textSource.Read();
|
||||
textSource.Eof().ShouldBe(false);
|
||||
s.ShouldBe("5");
|
||||
|
||||
s = textSource.Read();
|
||||
textSource.Eof().ShouldBe(false);
|
||||
s.ShouldBe("6");
|
||||
|
||||
s = textSource.Read();
|
||||
textSource.Eof().ShouldBe(false);
|
||||
s.ShouldBe("7");
|
||||
|
||||
s = textSource.Read();
|
||||
textSource.Eof().ShouldBe(false);
|
||||
s.ShouldBe("8");
|
||||
|
||||
s = textSource.Read();
|
||||
textSource.Eof().ShouldBe(false);
|
||||
s.ShouldBe("9");
|
||||
|
||||
s = textSource.Read();
|
||||
textSource.Eof().ShouldBe(false);
|
||||
s.ShouldBe("10");
|
||||
|
||||
s = textSource.Read();
|
||||
textSource.Eof().ShouldBe(true);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanWhileLoopThroughFileNormally()
|
||||
{
|
||||
WrapFileTest(static textSource =>
|
||||
{
|
||||
int lineCount = 0;
|
||||
_ = textSource.Read();
|
||||
|
||||
while (!textSource.Eof())
|
||||
{
|
||||
lineCount++;
|
||||
_ = textSource.Read();
|
||||
}
|
||||
|
||||
lineCount.ShouldBe(10);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanReadAllThroughFileNormally()
|
||||
{
|
||||
WrapFileTest(static textSource =>
|
||||
{
|
||||
List<string> lines = textSource.ReadAll().ToList();
|
||||
int lineCount = lines.Count;
|
||||
lineCount.ShouldBe(10);
|
||||
lines.ShouldBe(["1", "2", "", "4", "5", "6", "7", "8", "9", "10"]);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanWhileLoopThroughEmptyFile()
|
||||
{
|
||||
WrapFileTest(
|
||||
static textSource =>
|
||||
{
|
||||
int lineCount = 0;
|
||||
_ = textSource.Read();
|
||||
|
||||
while (!textSource.Eof())
|
||||
{
|
||||
lineCount++;
|
||||
_ = textSource.Read();
|
||||
}
|
||||
|
||||
lineCount.ShouldBe(0);
|
||||
},
|
||||
CreateEmptyInputFile);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanReadAllThroughEmptyFile()
|
||||
{
|
||||
WrapFileTest(static textSource =>
|
||||
{
|
||||
List<string> lines = textSource.ReadAll().ToList();
|
||||
int lineCount = lines.Count;
|
||||
lineCount.ShouldBe(0);
|
||||
},
|
||||
CreateEmptyInputFile);
|
||||
}
|
||||
}
|
||||
2
source/DotnetStandardStreamsTests/GlobalUsings.cs
Normal file
2
source/DotnetStandardStreamsTests/GlobalUsings.cs
Normal file
@ -0,0 +1,2 @@
|
||||
global using Xunit;
|
||||
global using Shouldly;
|
||||
@ -1,27 +1,23 @@
|
||||
using DotnetStandardStreams;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
using Shouldly;
|
||||
using DotnetStandardStreams;
|
||||
|
||||
namespace DotnetStandardStreamsTests
|
||||
namespace DotnetStandardStreamsTests;
|
||||
public class ListOutputTargetTests
|
||||
{
|
||||
public class ListOutputTargetTests
|
||||
[Fact]
|
||||
public void WritesToList()
|
||||
{
|
||||
[Fact]
|
||||
public void WritesToList()
|
||||
{
|
||||
ListOutputTarget target = new(new List<string>());
|
||||
target.Output("1");
|
||||
target.Output("2");
|
||||
target.Output("");
|
||||
target.Output("3");
|
||||
ListOutputTarget target = new(new List<string>());
|
||||
target.Output("1");
|
||||
target.Output("2");
|
||||
target.Output("");
|
||||
target.Output("3");
|
||||
|
||||
target.OutputList.Count.ShouldBe(4);
|
||||
target.OutputList[0].ShouldBe("1");
|
||||
target.OutputList[1].ShouldBe("2");
|
||||
target.OutputList[2].ShouldBe(string.Empty);
|
||||
target.OutputList[3].ShouldBe("3");
|
||||
}
|
||||
target.OutputList.Count.ShouldBe(4);
|
||||
target.OutputList[0].ShouldBe("1");
|
||||
target.OutputList[1].ShouldBe("2");
|
||||
target.OutputList[2].ShouldBe(string.Empty);
|
||||
target.OutputList[3].ShouldBe("3");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,39 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
using Shouldly;
|
||||
using DotnetStandardStreams;
|
||||
|
||||
namespace DotnetStandardStreamsTests
|
||||
namespace DotnetStandardStreamsTests;
|
||||
|
||||
public class ListTextSourceTests
|
||||
{
|
||||
public class ListTextSourceTests
|
||||
[Fact]
|
||||
public void ReadsFromList()
|
||||
{
|
||||
[Fact]
|
||||
public void ReadsFromList()
|
||||
{
|
||||
ITextSource reader = new ListTextSource(new[] { "1", "2", "", "3" });
|
||||
string s0 = reader.Read();
|
||||
s0.ShouldNotBeNull();
|
||||
s0.ShouldBe("1");
|
||||
reader.Eof().ShouldBe(false);
|
||||
ITextSource reader = new ListTextSource(["1", "2", "", "3"]);
|
||||
string s0 = reader.Read();
|
||||
s0.ShouldNotBeNull();
|
||||
s0.ShouldBe("1");
|
||||
reader.Eof().ShouldBe(false);
|
||||
|
||||
string s1 = reader.Read();
|
||||
s1.ShouldNotBeNull();
|
||||
s1.ShouldBe("2");
|
||||
reader.Eof().ShouldBe(false);
|
||||
string s1 = reader.Read();
|
||||
s1.ShouldNotBeNull();
|
||||
s1.ShouldBe("2");
|
||||
reader.Eof().ShouldBe(false);
|
||||
|
||||
string s2 = reader.Read();
|
||||
s2.ShouldNotBeNull();
|
||||
s2.ShouldBe(string.Empty);
|
||||
reader.Eof().ShouldBe(false);
|
||||
string s2 = reader.Read();
|
||||
s2.ShouldNotBeNull();
|
||||
s2.ShouldBe(string.Empty);
|
||||
reader.Eof().ShouldBe(false);
|
||||
|
||||
string s3 = reader.Read();
|
||||
s3.ShouldNotBeNull();
|
||||
s3.ShouldBe("3");
|
||||
reader.Eof().ShouldBe(true);
|
||||
}
|
||||
string s3 = reader.Read();
|
||||
s3.ShouldNotBeNull();
|
||||
s3.ShouldBe("3");
|
||||
reader.Eof().ShouldBe(false);
|
||||
|
||||
string s4 = reader.Read();
|
||||
s4.ShouldBe("");
|
||||
reader.Eof().ShouldBe(true);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadsFromEmptyList()
|
||||
{
|
||||
ITextSource reader = new ListTextSource([]);
|
||||
string s0 = reader.Read();
|
||||
//s0.ShouldBeNull();
|
||||
reader.Eof().ShouldBe(true);
|
||||
}
|
||||
}
|
||||
@ -1,210 +1,148 @@
|
||||
using DotnetStandardStreamsTests.Testables;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
using Shouldly;
|
||||
using DotnetStandardStreams;
|
||||
using DotnetStandardStreamsTests.Testables;
|
||||
|
||||
namespace DotnetStandardStreamsTests
|
||||
namespace DotnetStandardStreamsTests;
|
||||
|
||||
public class StdInTextSourceTests
|
||||
{
|
||||
public class StdInTextSourceTests
|
||||
private static TextReader CreateStdIn()
|
||||
{
|
||||
private static TextReader CreateStdIn()
|
||||
{
|
||||
return new ListTextReader(new[] { "1", "2", "", "3" });
|
||||
}
|
||||
return new ListTextReader(new[] { "1", "2", "", "3" });
|
||||
}
|
||||
|
||||
private static void WrapStdInTest(Action<ITextSource> testCode)
|
||||
private static TextReader CreateEmptyStdIn() => new ListTextReader([]);
|
||||
|
||||
private static void WrapStdInTest(Action<ITextSource> testCode, TextReader? explicitStdIn = null)
|
||||
{
|
||||
if (explicitStdIn == null)
|
||||
explicitStdIn = CreateStdIn();
|
||||
|
||||
var oldInputReader = System.Console.In;
|
||||
try
|
||||
{
|
||||
var oldInputReader = System.Console.In;
|
||||
System.Console.SetIn(explicitStdIn);
|
||||
|
||||
ITextSource stdin = new StdInTextSource();
|
||||
stdin.Open();
|
||||
try
|
||||
{
|
||||
TextReader reader = CreateStdIn();
|
||||
System.Console.SetIn(reader);
|
||||
|
||||
ITextSource stdin = new StdInTextSource();
|
||||
stdin.Open();
|
||||
try
|
||||
{
|
||||
testCode?.Invoke(stdin);
|
||||
}
|
||||
finally
|
||||
{
|
||||
stdin.Close();
|
||||
}
|
||||
testCode?.Invoke(stdin);
|
||||
}
|
||||
finally
|
||||
{
|
||||
System.Console.SetIn(oldInputReader);
|
||||
stdin.Close();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanReadAllFromStandardIn()
|
||||
finally
|
||||
{
|
||||
WrapStdInTest(stdin =>
|
||||
{
|
||||
var actual = stdin.ReadAll().ToList();
|
||||
actual.Count.ShouldBe(4);
|
||||
actual[0].ShouldBe("1");
|
||||
actual[1].ShouldBe("2");
|
||||
actual[2].ShouldBe(string.Empty);
|
||||
actual[3].ShouldBe("3");
|
||||
});
|
||||
System.Console.SetIn(oldInputReader);
|
||||
}
|
||||
}
|
||||
|
||||
//{
|
||||
|
||||
// // Fake the StdIn
|
||||
// var oldInputReader = System.Console.In;
|
||||
// try
|
||||
// {
|
||||
// TextReader reader = CreateStdIn();
|
||||
// System.Console.SetIn(reader);
|
||||
|
||||
// ITextSource stdin = new StdInTextSource();
|
||||
// stdin.Open();
|
||||
// try
|
||||
// {
|
||||
// var actual = stdin.ReadAll().ToList();
|
||||
// actual.Count.ShouldBe(4);
|
||||
// actual[0].ShouldBe("1");
|
||||
// actual[1].ShouldBe("2");
|
||||
// actual[2].ShouldBe(string.Empty);
|
||||
// actual[3].ShouldBe("3");
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// stdin.Close();
|
||||
// }
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// System.Console.SetIn(oldInputReader);
|
||||
// }
|
||||
//}
|
||||
|
||||
[Fact]
|
||||
public void CanReadIndividualLinesFromStandardIn()
|
||||
[Fact]
|
||||
public void CanReadAllFromStandardIn()
|
||||
{
|
||||
WrapStdInTest(static stdin =>
|
||||
{
|
||||
WrapStdInTest(stdin =>
|
||||
var actual = stdin.ReadAll().ToList();
|
||||
actual.Count.ShouldBe(4);
|
||||
actual[0].ShouldBe("1");
|
||||
actual[1].ShouldBe("2");
|
||||
actual[2].ShouldBe(string.Empty);
|
||||
actual[3].ShouldBe("3");
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanReadIndividualLinesFromStandardIn()
|
||||
{
|
||||
WrapStdInTest(static stdin =>
|
||||
{
|
||||
string s;
|
||||
s = stdin.Read();
|
||||
stdin.Eof().ShouldBe(false);
|
||||
s.ShouldBe("1");
|
||||
|
||||
s = stdin.Read();
|
||||
stdin.Eof().ShouldBe(false);
|
||||
s.ShouldBe("2");
|
||||
|
||||
s = stdin.Read();
|
||||
stdin.Eof().ShouldBe(false);
|
||||
s.ShouldBe(string.Empty);
|
||||
|
||||
s = stdin.Read();
|
||||
stdin.Eof().ShouldBe(false);
|
||||
s.ShouldBe("3");
|
||||
|
||||
s = stdin.Read();
|
||||
stdin.Eof().ShouldBe(true);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanWhileLoopThroughStdInNormally()
|
||||
{
|
||||
WrapStdInTest(static stdin =>
|
||||
{
|
||||
int lineCount = 0;
|
||||
string s = stdin.Read();
|
||||
|
||||
while (!stdin.Eof())
|
||||
{
|
||||
string s;
|
||||
lineCount++;
|
||||
s = stdin.Read();
|
||||
stdin.Eof().ShouldBe(false);
|
||||
s.ShouldBe("1");
|
||||
}
|
||||
|
||||
s = stdin.Read();
|
||||
stdin.Eof().ShouldBe(false);
|
||||
s.ShouldBe("2");
|
||||
lineCount.ShouldBe(4);
|
||||
});
|
||||
}
|
||||
|
||||
s = stdin.Read();
|
||||
stdin.Eof().ShouldBe(false);
|
||||
s.ShouldBe(string.Empty);
|
||||
|
||||
s = stdin.Read();
|
||||
stdin.Eof().ShouldBe(false);
|
||||
s.ShouldBe("3");
|
||||
|
||||
s = stdin.Read();
|
||||
stdin.Eof().ShouldBe(true);
|
||||
});
|
||||
|
||||
//// Fake the StdIn
|
||||
//var oldInputReader = System.Console.In;
|
||||
//try
|
||||
//{
|
||||
// TextReader reader = CreateStdIn();
|
||||
// System.Console.SetIn(reader);
|
||||
|
||||
// ITextSource stdin = new StdInTextSource();
|
||||
// stdin.Open();
|
||||
// try
|
||||
// {
|
||||
// string s;
|
||||
// s = stdin.Read();
|
||||
// stdin.Eof().ShouldBe(false);
|
||||
// s.ShouldBe("1");
|
||||
|
||||
// s = stdin.Read();
|
||||
// stdin.Eof().ShouldBe(false);
|
||||
// s.ShouldBe("2");
|
||||
|
||||
// s = stdin.Read();
|
||||
// stdin.Eof().ShouldBe(false);
|
||||
// s.ShouldBe(string.Empty);
|
||||
|
||||
// s = stdin.Read();
|
||||
// stdin.Eof().ShouldBe(false);
|
||||
// s.ShouldBe("3");
|
||||
|
||||
// s = stdin.Read();
|
||||
// stdin.Eof().ShouldBe(true);
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// stdin.Close();
|
||||
// }
|
||||
//}
|
||||
//finally
|
||||
//{
|
||||
// System.Console.SetIn(oldInputReader);
|
||||
//}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanWhileLoopThroughStdInProperly()
|
||||
[Fact]
|
||||
public void CanReadAllThroughStdInNormally()
|
||||
{
|
||||
WrapStdInTest(static stdin =>
|
||||
{
|
||||
WrapStdInTest(stdin =>
|
||||
List<string> lines = stdin.ReadAll().ToList();
|
||||
int lineCount = lines.Count;
|
||||
lineCount.ShouldBe(4);
|
||||
lines.ShouldBe(["1", "2", "", "3"]);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanWhileLoopThroughStdInEmpty()
|
||||
{
|
||||
WrapStdInTest(
|
||||
static stdin =>
|
||||
{
|
||||
int lineCount = 0;
|
||||
var s = stdin.Read();
|
||||
string s = stdin.Read();
|
||||
|
||||
while (!stdin.Eof())
|
||||
{
|
||||
lineCount++;
|
||||
s = stdin.Read();
|
||||
}
|
||||
|
||||
lineCount.ShouldBe(4);
|
||||
});
|
||||
}
|
||||
lineCount.ShouldBe(0);
|
||||
},
|
||||
CreateEmptyStdIn());
|
||||
}
|
||||
|
||||
//{
|
||||
// // Fake the StdIn
|
||||
// var oldInputReader = System.Console.In;
|
||||
// try
|
||||
// {
|
||||
// TextReader reader = CreateStdIn();
|
||||
// System.Console.SetIn(reader);
|
||||
|
||||
// ITextSource stdin = new StdInTextSource();
|
||||
// stdin.Open();
|
||||
// try
|
||||
// {
|
||||
// int lineCount = 0;
|
||||
// var s = stdin.Read();
|
||||
// while (!stdin.Eof())
|
||||
// {
|
||||
// // Do stuff with s here normally.
|
||||
// lineCount++;
|
||||
// s = stdin.Read();
|
||||
// }
|
||||
|
||||
// lineCount.ShouldBe(4);
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// stdin.Close();
|
||||
// }
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// System.Console.SetIn(oldInputReader);
|
||||
// }
|
||||
//}
|
||||
[Fact]
|
||||
public void CanReadAllThroughStdInEmpty()
|
||||
{
|
||||
WrapStdInTest(static stdin =>
|
||||
{
|
||||
List<string> lines = stdin.ReadAll().ToList();
|
||||
int lineCount = lines.Count;
|
||||
lineCount.ShouldBe(0);
|
||||
},
|
||||
CreateEmptyStdIn());
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ namespace DotnetStandardStreamsTests.Testables
|
||||
enumerator = data.GetEnumerator();
|
||||
}
|
||||
|
||||
public override string ReadLine()
|
||||
public override string? ReadLine()
|
||||
{
|
||||
if (enumerator.MoveNext())
|
||||
return enumerator.Current;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user