D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 11255 - Support for inner unittests
Summary: Support for inner unittests
Status: NEW
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: All All
: P4 enhancement
Assignee: No Owner
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-10-14 02:55 UTC by bearophile_hugs
Modified: 2024-12-13 18:12 UTC (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description bearophile_hugs 2013-10-14 02:55:43 UTC
void main() {
    int sqr(int x) { return x ^^ 2; }
    unittest {
        assert(sqr(3) == 9);
    }
}


dmd 2.064beta gives:

test.d(3): Error: found 'unittest' instead of statement
test.d(6): Error: unrecognized declaration


This is kind of necessary to make nested functions usable in real code, because in some cases you can't avoid writing some unit tests for a function.
Comment 1 Witold Baryluk 2021-05-28 00:09:17 UTC
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`).
Comment 2 Witold Baryluk 2021-05-28 00:39:25 UTC
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);
    }
}

```
Comment 3 dlangBugzillaToGithub 2024-12-13 18:12:45 UTC
THIS ISSUE HAS BEEN MOVED TO GITHUB

https://github.com/dlang/dmd/issues/18696

DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB