On Tuesday, 4 January 2022 at 17:01:41 UTC, Amit wrote:
Hi!
I wrote a text parser that takes a File argument and parses
that file's contents. Now I would like to write a unit-test for
that parser. I need a File (or a general IO interface) that
reads from an in-memory buffer, similar to python's `StringIO`
or go's `strings.Reader`.
How can I achieve that?
Probably the easiest way to do it is to have your parser take a
generic [range][1] as its argument instead of a `File`. Then you
can pass in whatever source of data you like--a file, an
in-memory buffer, a class that generates data on-the-fly--and it
will all Just Work.
For example, here's a function that parses an integer from an
input range:
```d
import std.range;
int parseInteger(Input)(Input input)
if (isInputRange!Input && is(typeof(input.front - '0') : int))
{
import std.ascii: isDigit;
import std.exception: enforce;
int result = 0;
bool success = false;
while (!input.empty && input.front.isDigit)
{
success = true;
result *= 10;
result += input.front - '0';
input.popFront;
}
enforce(success, "input did not contain an integer");
return result;
}
unittest
{
import std.ascii: isDigit;
import std.algorithm: filter;
import std.exception: assertThrown;
// can use strings
assert(parseInteger("123") == 123);
assert(parseInteger("123 hello") == 123);
// can use arbitrary range types
assert(parseInteger("321".retro) == 123);
assert(parseInteger("a1b2c3".filter!isDigit) == 123);
// failure cases
assertThrown!Exception(parseInteger("hello"));
assertThrown!Exception(parseInteger(""));
}
```
[1]: https://dlang.org/phobos/std_range.html