Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for inner unittests #18696

Open
dlangBugzillaToGithub opened this issue Oct 14, 2013 · 2 comments
Open

Support for inner unittests #18696

dlangBugzillaToGithub opened this issue Oct 14, 2013 · 2 comments

Comments

@dlangBugzillaToGithub
Copy link

bearophile_hugs reported this on 2013-10-14T02:55:43Z

Transferred from https://issues.dlang.org/show_bug.cgi?id=11255

CC List

Description

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.
@dlangBugzillaToGithub
Copy link
Author

witold.baryluk+d (@baryluk) commented on 2021-05-28T00:09:17Z

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`).

@dlangBugzillaToGithub
Copy link
Author

witold.baryluk+d (@baryluk) commented on 2021-05-28T00:39:25Z

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);
    }
}

```

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant