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

Reply via email to