Thread overview
[Issue 11255] Support for inner unittests
May 28, 2021
Witold Baryluk
May 28, 2021
Witold Baryluk
Dec 17, 2022
Iain Buclaw
May 28, 2021
https://issues.dlang.org/show_bug.cgi?id=11255

Witold Baryluk <witold.baryluk+d@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |witold.baryluk+d@gmail.com

--- Comment #1 from Witold Baryluk <witold.baryluk+d@gmail.com> ---
I agree, this would be useful. I often created inner functions or inner structs in functions, as mini helpers. They are self contained, and only used in one place, so they can be tested on their own, but I don't want to pollute the module level symbols with it, which makes unittesting them harder.

I think it should only be allowed to be attached to static nested functions, or when running otherwise, should only have access to static functions.

This probably also should apply to the nested structs and nested static classes, and nested static structs.

Using it for non-static nested functions or aggregates, doesn't make sense, because they can have access to some dynamic state, that is not available before the program starts or executes these functions and initializes the dynamic state.

The only issue I can think of it how to deal with instance of unittests in templated functions and classes. This could create many "duplicates" of the unittest, but it actually could be something good and intended.

So for example maybe these two things only for the start:


void main() {
    static int sqr(int x) { return x * x; }
    unittest {
        assert(sqr(3) == 9);
    }
}

class A {
    static class HelperClass {
       int y = 2;
       int m(int x) { return x * y; }
    }
    unittest {
        auto c = new HelperClass();
        assert(c.m(3) == 6);
    }
}


In the second case, maybe the unittest should be inside the nested class itself maybe, or maybe it should have `static` prefix or something.

Named unittests should also work, and probably full name should be a scoped path (i.e. `mod1.main.sqr.unittest.myunittest1`, `mod1.A.HelperClass.myunittest1`).

--
May 28, 2021
https://issues.dlang.org/show_bug.cgi?id=11255

--- Comment #2 from Witold Baryluk <witold.baryluk+d@gmail.com> ---
One would think that it might be possible to access nested static functions using some naming tricks, from outside, like this:

```d
int bar(int a) {
    static int d;

    static int foo(int b) {
        b = d;
        return b + 1;
    }

    return foo(a);
}

unittest {
  alias foo = bar.foo;
  assert(foo(5) == 6);
}
```

but this have two issues that I can think of:

1) nested static functions can access nested static variables, other nested functions (including themselves), and globals. That can be problematic for unittesting.

2) It is possible to create multiple nested functions with the same name (not
just function overloads), for example:

```d
int bar(int a) {
    static int d;

    if (a > 10) {
        static int foo(int b) {
            b = d;
            return b + 1;
        }
        return foo(a);
    } else {
        static int foo(int b) {
            b += d * 100;
            return b + 100;
        }
        return foo(a);
    }
}
unittest {
  bar.foo  // refers to which foo?
}
```

or make multiple different symbols with the same name:

```d
int bar(int a) {
    static int d;

    if (a > 10) {
        static int foo(int b) {
            b = d;
            return b + 1;
        }
        return foo(a);
    } else {
        int foo = 5;
        return foo + a;
    }
}

unittest {
  bar.foo // refers to what?
}
```

So, that is going to be too complex and require too much ambiguity.

Directly nested unittest makes this obvious and explicit, by using local scoping rules and lookup:

```
int bar(int a) {
    static int d;

    if (a > 10) {
        static int foo(int b) {
            b = d;
            return b + 1;
        }
        unittest {
            d = 42;
            assert(foo(137) == 43);
        }
        return foo(a);
    } else {
        static int foo(int b) {
            b += d * 100;
            return b + 100;
        }
        unittest {
            d = 1;
            assert(foo(137) == 337);
        }
        return foo(a);
    }
}

```

--
December 17, 2022
https://issues.dlang.org/show_bug.cgi?id=11255

Iain Buclaw <ibuclaw@gdcproject.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Priority|P2                          |P4

--