digitalmars.D - Named unittests and __traits(getModules)
- Jacob Carlborg (40/40) May 19 2019 I'm staring a new thread here on the topic of Name unittests because the...
- Andre Pany (8/11) May 19 2019 As far as i remember there was another suggestion of Andrei (in
- Andrei Alexandrescu (3/14) May 19 2019 Yah, it's quite a universal pattern. It can be used as "import
- Andrei Alexandrescu (9/24) May 19 2019 Related: https://github.com/dlang/dmd/pull/9814. There, the idea is to
- Mike Franklin (18/22) May 19 2019 I'm not sure I fully understand this, so forgive me if I'm just
- Andrei Alexandrescu (9/34) May 20 2019 Having the compiler detect constructs in code would work, as would
- Paul Backus (8/23) May 22 2019 Allowing import statements to have side effects seems like a
- Andrei Alexandrescu (2/25) May 23 2019 Good point, thanks.
- Jacob Carlborg (11/14) May 20 2019 Ok, that sounds useful as well. But in this case I think my solution is
- Johannes Pfau (13/26) May 21 2019 Do you happen to have a link for that original post?
- Andre Pany (5/18) May 21 2019 Here the link
- Nick Sabalausky (Abscissa) (11/15) May 20 2019 Keep in mind, most of that thread is from 4 years ago.
- Johannes Loher (7/24) May 20 2019 unit-threaded is great but it requires a prebuild command to
- Nick Sabalausky (Abscissa) (8/15) May 20 2019 Ahh, right, right, I gotcha. And TBH, that's been LOOOOONG standing
- Atila Neves (2/5) May 21 2019 How would this work with separate compilation?
- Andre Pany (8/14) May 21 2019 I assume with the proposal of Andrei, even the separate
- Jacob Carlborg (4/5) May 21 2019 Hmm, I'm not sure. That would be tested when it's implemented :)
- Atila Neves (3/6) May 22 2019 Testing in production is the best way to test things. It is
- Jacob Carlborg (10/11) May 23 2019 I have given this some more thought. For my idea to work, regardless of
- Andre Pany (11/21) May 23 2019 One thing I do not understand. Once module A, which contains the
- Atila Neves (2/12) May 24 2019 Not being able to use separate compilation is a non-starter.
- Jacob Carlborg (26/33) May 24 2019 It works with separate compilation if the unit tests are collected into
- Andre Pany (13/46) May 24 2019 Maybe my understanding is wrong. As far as I understand, your
- Jacob Carlborg (8/20) May 24 2019 The compiler would need to invoke a druntime function every time the
I'm staring a new thread here on the topic of Name unittests because the existing one is getting too long [1]. The best way to add support for named unit tests, without adding any new syntax to the language, is to leverage UDAs. This has already been discussed in the previous post [1]. The problem with UDAs and the current unit test runner is by the time the unit test runner runs the unit tests, the UDAs are long gone. They need to be fetched at compile-time. It's also not possible to continue running unit tests in the same module after a failed unit test with the current runner. This is because the compiler generates one function that calls all the unit tests in the whole module. To solve these problems `__traits(getUnitTests)` can be used to access the unit tests at compile-time. This leads to the next problem. To access the unit tests one needs to import the modules where the unit tests are defined. There are currently no way to get a list of all modules. Existing third party unit test runners usually use a pre-build script that collects all files with unit tests and generates a new file with all the imports. This is also noted in the previous thread [2]. The solution of running a pre-build script is not workable to include in druntime. I purpose we add two new traits: `__traits(getRootModules)` and `__traits(getImportedModules)`. `getRootModules` will return all root modules, i.e. the files which were passed to the compiler on the command line. `getImportedModules` will return all imported modules, i.e. the non-root modules. Adding these two together will give access to all modules the compiler has processed. Note that these traits are useful for other things than building a unit test runner. With these two traits (only getRootModules might be necessary) and using `static foreach` to generate the imports, `getUnitTests` can later be used to retrieve the unit tests at compile-time which also gives access to the UDAs. Using this technique to run the unit tests allows to add additional features, like continue running after a failed test, before and after callbacks and other features. There's already a unit test runner in the DMD test suite that uses this technique (but with the pre-build script). With these new traits this unit test runner could be added pretty easily to druntime. Thoughts? [1] https://forum.dlang.org/thread/mfcgj3$12a0$1 digitalmars.com [2] https://forum.dlang.org/post/qbr7dd$16fd$1 digitalmars.com -- /Jacob Carlborg
May 19 2019
On Sunday, 19 May 2019 at 18:56:33 UTC, Jacob Carlborg wrote:I'm staring a new thread here on the topic of Name unittests because the existing one is getting too long [1]. [...]As far as i remember there was another suggestion of Andrei (in another context). By importing a module B in module A, the module B can specify coding which is executed and gets the module A as info. This might could also solve this issue. Kind regards Andre
May 19 2019
On 5/19/19 8:13 PM, Andre Pany wrote:On Sunday, 19 May 2019 at 18:56:33 UTC, Jacob Carlborg wrote:Yah, it's quite a universal pattern. It can be used as "import core.rtti; to add RTTI support for this module" etc.I'm staring a new thread here on the topic of Name unittests because the existing one is getting too long [1]. [...]As far as i remember there was another suggestion of Andrei (in another context). By importing a module B in module A, the module B can specify coding which is executed and gets the module A as info. This might could also solve this issue.
May 19 2019
On 5/19/19 11:41 PM, Andrei Alexandrescu wrote:On 5/19/19 8:13 PM, Andre Pany wrote:Related: https://github.com/dlang/dmd/pull/9814. There, the idea is to predicate an import for each language feature or groups of features. Many, however, can be done as templates and realized naturally by means of template instantiation (e.g. a[] = b[] triggers the generation of the array assign support function). RTTI is different because it instructs the compiler to generate code a specific way without a detectable action in the source code. That kind of feature could be done with import + hook "execute this on the importing module".On Sunday, 19 May 2019 at 18:56:33 UTC, Jacob Carlborg wrote:Yah, it's quite a universal pattern. It can be used as "import core.rtti; to add RTTI support for this module" etc.I'm staring a new thread here on the topic of Name unittests because the existing one is getting too long [1]. [...]As far as i remember there was another suggestion of Andrei (in another context). By importing a module B in module A, the module B can specify coding which is executed and gets the module A as info. This might could also solve this issue.
May 19 2019
On Monday, 20 May 2019 at 00:14:01 UTC, Andrei Alexandrescu wrote:RTTI is different because it instructs the compiler to generate code a specific way without a detectable action in the source code. That kind of feature could be done with import + hook "execute this on the importing module".I'm not sure I fully understand this, so forgive me if I'm just making noise, but does the pattern in this PR give you what you need? https://github.com/dlang/dmd/pull/7799 The idea is to put a detectable action in the source code. In the case of the PR referenced above, the detectable action is the existence of the `class TypeInfo` declaration. The compiler has been programmed to look for that declaration, and generate code based on whether or not it exists. Because the declaration exists in druntime's source code, users can also check for its existence in their source code. Basically, the compiler is doing design-by-introspection just like the user. The runtime's source code informs the compiler what to do rather than the other way around. So the compiler does have a detectable action in the source code (i.e. the existence of `class TypeInfo`) and that same detectable action is available to the user as well. Mike
May 19 2019
On 5/19/19 8:55 PM, Mike Franklin wrote:On Monday, 20 May 2019 at 00:14:01 UTC, Andrei Alexandrescu wrote:Having the compiler detect constructs in code would work, as would built-in attributes such as: noRTTI module mymodule; and/or noRTTI class BareBones { ... } The disadvantage is that the compiler would need to be modified for each of these, whereas if we define and avail ourselves of powerful introspection, we can achieve this kind of stuff in library code.RTTI is different because it instructs the compiler to generate code a specific way without a detectable action in the source code. That kind of feature could be done with import + hook "execute this on the importing module".I'm not sure I fully understand this, so forgive me if I'm just making noise, but does the pattern in this PR give you what you need? https://github.com/dlang/dmd/pull/7799 The idea is to put a detectable action in the source code. In the case of the PR referenced above, the detectable action is the existence of the `class TypeInfo` declaration. The compiler has been programmed to look for that declaration, and generate code based on whether or not it exists. Because the declaration exists in druntime's source code, users can also check for its existence in their source code. Basically, the compiler is doing design-by-introspection just like the user. The runtime's source code informs the compiler what to do rather than the other way around. So the compiler does have a detectable action in the source code (i.e. the existence of `class TypeInfo`) and that same detectable action is available to the user as well.
May 20 2019
On Sunday, 19 May 2019 at 22:41:52 UTC, Andrei Alexandrescu wrote:On 5/19/19 8:13 PM, Andre Pany wrote:Allowing import statements to have side effects seems like a pretty big can of worms to open just so that we can avoid typing "mixin RTTISupport!()". One of the great things about D's module system is that a D module can't meddle around in another module's global scope the way a header file can in C or C++. I hope that guarantee won't be broken simply for the sake of syntactic convenience.On Sunday, 19 May 2019 at 18:56:33 UTC, Jacob Carlborg wrote:Yah, it's quite a universal pattern. It can be used as "import core.rtti; to add RTTI support for this module" etc.I'm staring a new thread here on the topic of Name unittests because the existing one is getting too long [1]. [...]As far as i remember there was another suggestion of Andrei (in another context). By importing a module B in module A, the module B can specify coding which is executed and gets the module A as info. This might could also solve this issue.
May 22 2019
On 5/22/19 5:08 PM, Paul Backus wrote:On Sunday, 19 May 2019 at 22:41:52 UTC, Andrei Alexandrescu wrote:Good point, thanks.On 5/19/19 8:13 PM, Andre Pany wrote:Allowing import statements to have side effects seems like a pretty big can of worms to open just so that we can avoid typing "mixin RTTISupport!()". One of the great things about D's module system is that a D module can't meddle around in another module's global scope the way a header file can in C or C++. I hope that guarantee won't be broken simply for the sake of syntactic convenience.On Sunday, 19 May 2019 at 18:56:33 UTC, Jacob Carlborg wrote:Yah, it's quite a universal pattern. It can be used as "import core.rtti; to add RTTI support for this module" etc.I'm staring a new thread here on the topic of Name unittests because the existing one is getting too long [1]. [...]As far as i remember there was another suggestion of Andrei (in another context). By importing a module B in module A, the module B can specify coding which is executed and gets the module A as info. This might could also solve this issue.
May 23 2019
On 2019-05-19 21:13, Andre Pany wrote:As far as i remember there was another suggestion of Andrei (in another context). By importing a module B in module A, the module B can specify coding which is executed and gets the module A as info.Ok, that sounds useful as well. But in this case I think my solution is easier to implement, works less like magic and is better suited to implement a unit test runner. With Andrei's proposal, as far as I understand, would require to add an import. The implementation would also iterate the unit tests locally for each module. I'm not sure how you would be able to combine all of them into a single list. Also how to know when all unit tests have been collected and the runner can actually start running the tests. -- /Jacob Carlborg
May 20 2019
Am Sun, 19 May 2019 19:13:31 +0000 schrieb Andre Pany:On Sunday, 19 May 2019 at 18:56:33 UTC, Jacob Carlborg wrote:Do you happen to have a link for that original post? Andrei how exactly should something like this be implemented, do you agree that mixin templates are the right tool for this (e.g. as described here: https://forum.dlang.org/post/qbr7dd$16fd$1 digitalmars.com)? If so, I could write a DIP and the compiler implementation for that. Of course the DIP would have to adress the details (selective imports, public imports, ...), but I'd like to have some feedback on the general idea* first. * annotate template mixin with import, then automatically add mixin(template(thisModule))) in every importing module -- JohannesI'm staring a new thread here on the topic of Name unittests because the existing one is getting too long [1]. [...]As far as i remember there was another suggestion of Andrei (in another context). By importing a module B in module A, the module B can specify coding which is executed and gets the module A as info. This might could also solve this issue. Kind regards Andre
May 21 2019
On Tuesday, 21 May 2019 at 17:54:36 UTC, Johannes Pfau wrote:Am Sun, 19 May 2019 19:13:31 +0000 schrieb Andre Pany:Here the link https://forum.dlang.org/post/q7l56r$bo6$1 digitalmars.com Kind regards Andre[...]Do you happen to have a link for that original post? Andrei how exactly should something like this be implemented, do you agree that mixin templates are the right tool for this (e.g. as described here: https://forum.dlang.org/post/qbr7dd$16fd$1 digitalmars.com)? If so, I could write a DIP and the compiler implementation for that. Of course the DIP would have to adress the details (selective imports, public imports, ...), but I'd like to have some feedback on the general idea* first. * annotate template mixin with import, then automatically add mixin(template(thisModule))) in every importing module
May 21 2019
On 5/19/19 2:56 PM, Jacob Carlborg wrote:I'm staring a new thread here on the topic of Name unittests because the existing one is getting too long [1]. [1] https://forum.dlang.org/thread/mfcgj3$12a0$1 digitalmars.comKeep in mind, most of that thread is from 4 years ago. But, umm, seriously dudes...we have unit-threaded now. Takes care of all that and then some. Just make it official, bake to into Phobos or whatever, and be done with it. Don't we have more important things to do than re-implement stuff we already have that's *already* working quite well? I mean seriously, we've got people highly averse to *small, but actual, improvements*, but completely re-implementing something that's already working very well is worthwhile??? WAT??? (Or am I misunderstanding the gist of the small, non-outdated branch of that thread?)
May 20 2019
On Monday, 20 May 2019 at 16:38:39 UTC, Nick Sabalausky (Abscissa) wrote:On 5/19/19 2:56 PM, Jacob Carlborg wrote:unit-threaded is great but it requires a prebuild command to gather all the modules. Can’t really make that official... This is also not currently fixable on a library level because there is no way to reflect on what modules are being compiled. Adding a way to do this is basically what Jacob was suggesting.I'm staring a new thread here on the topic of Name unittests because the existing one is getting too long [1]. [1] https://forum.dlang.org/thread/mfcgj3$12a0$1 digitalmars.comKeep in mind, most of that thread is from 4 years ago. But, umm, seriously dudes...we have unit-threaded now. Takes care of all that and then some. Just make it official, bake to into Phobos or whatever, and be done with it. Don't we have more important things to do than re-implement stuff we already have that's *already* working quite well? I mean seriously, we've got people highly averse to *small, but actual, improvements*, but completely re-implementing something that's already working very well is worthwhile??? WAT??? (Or am I misunderstanding the gist of the small, non-outdated branch of that thread?)
May 20 2019
On 5/20/19 4:07 PM, Johannes Loher wrote:unit-threaded is great but it requires a prebuild command to gather all the modules. Can’t really make that official... This is also not currently fixable on a library level because there is no way to reflect on what modules are being compiled. Adding a way to do this is basically what Jacob was suggesting.Ahh, right, right, I gotcha. And TBH, that's been LOOOOONG standing giant gaping hole in D's reflection. Would be fantastic to finally get that fixed even regardless of the unittest stuff. IIRC, that was also the one main technical issue in the way of being able to build a Java-style runtime reflection system on top of D's compiletime reflection (well, that and the odd choice of reflection not being able to see through private, which has luckily been fixed now AIUI).
May 20 2019
On Sunday, 19 May 2019 at 18:56:33 UTC, Jacob Carlborg wrote:I'm staring a new thread here on the topic of Name unittests because the existing one is getting too long [1]. [...]How would this work with separate compilation?
May 21 2019
On Tuesday, 21 May 2019 at 09:43:32 UTC, Atila Neves wrote:On Sunday, 19 May 2019 at 18:56:33 UTC, Jacob Carlborg wrote:I assume with the proposal of Andrei, even the separate compilation can be solved. Of course the points of Jacob (https://forum.dlang.org/post/qbtrre$ilr$1 digitalmars.com) have to be investigated and addressed in detail. Kind regards AndréI'm staring a new thread here on the topic of Name unittests because the existing one is getting too long [1]. [...]How would this work with separate compilation?
May 21 2019
On 2019-05-21 11:43, Atila Neves wrote:How would this work with separate compilation?Hmm, I'm not sure. That would be tested when it's implemented :) -- /Jacob Carlborg
May 21 2019
On Tuesday, 21 May 2019 at 20:04:38 UTC, Jacob Carlborg wrote:On 2019-05-21 11:43, Atila Neves wrote:Testing in production is the best way to test things. It is known. :PHow would this work with separate compilation?Hmm, I'm not sure. That would be tested when it's implemented :)
May 22 2019
On 2019-05-21 11:43, Atila Neves wrote:How would this work with separate compilation?I have given this some more thought. For my idea to work, regardless of separate compilation, I think the compiler needs to invoke a template that uses `__traits(getRootModules)` which collects all tests. If it's not a template I'm guessing this would contain the files passed to the compiler when druntime was built and not when the user application is built. Then it would collect the unit tests to a global variable, that should work with separate compilation as well, I think. -- /Jacob Carlborg
May 23 2019
On Thursday, 23 May 2019 at 18:01:32 UTC, Jacob Carlborg wrote:On 2019-05-21 11:43, Atila Neves wrote:One thing I do not understand. Once module A, which contains the new traits, is compiled using separate compilation, the list of found modules is fixed. If now new module B is added, nothing will cause a rebuild of A and therefore the list of found modules is incomplete. This can of course be solved by telling the user to not use separate compilation, which might be a fair tradeoff, I am not sure. Kind regards AndreHow would this work with separate compilation?I have given this some more thought. For my idea to work, regardless of separate compilation, I think the compiler needs to invoke a template that uses `__traits(getRootModules)` which collects all tests. If it's not a template I'm guessing this would contain the files passed to the compiler when druntime was built and not when the user application is built. Then it would collect the unit tests to a global variable, that should work with separate compilation as well, I think.
May 23 2019
On Thursday, 23 May 2019 at 20:42:29 UTC, Andre Pany wrote:On Thursday, 23 May 2019 at 18:01:32 UTC, Jacob Carlborg wrote:Not being able to use separate compilation is a non-starter.[...]One thing I do not understand. Once module A, which contains the new traits, is compiled using separate compilation, the list of found modules is fixed. If now new module B is added, nothing will cause a rebuild of A and therefore the list of found modules is incomplete. This can of course be solved by telling the user to not use separate compilation, which might be a fair tradeoff, I am not sure.
May 24 2019
On 2019-05-23 22:42, Andre Pany wrote:One thing I do not understand. Once module A, which contains the new traits, is compiled using separate compilation, the list of found modules is fixed. If now new module B is added, nothing will cause a rebuild of A and therefore the list of found modules is incomplete. This can of course be solved by telling the user to not use separate compilation, which might be a fair tradeoff, I am not sure.It works with separate compilation if the unit tests are collected into a global variable. Here's an example using the existing __traits(allMembers): $ cat main.d module bar; extern (C) __gshared string[] members; void main() { import foo; members ~= [__traits(allMembers, bar)]; foobar(); assert(members == ["object", "members", "main", "object", "members", "foobar"]); } $ cat foo.d module foo; extern (C) extern __gshared string[] members; void foobar() { members ~= [__traits(allMembers, foo)]; } $ dmd -c foo.d && dmd foo.o -run main.d The assertion passes as expected. -- /Jacob Carlborg
May 24 2019
On Friday, 24 May 2019 at 15:32:15 UTC, Jacob Carlborg wrote:On 2019-05-23 22:42, Andre Pany wrote:Maybe my understanding is wrong. As far as I understand, your example only works as long as foo.d is compiled first and and main.d last. The order is guaranteed by the import foo statement. But if now use __traits(getModules) in main.d and compile with this command: dmd -c main.d && dmd main.o -run foo.d I assume it won't work anymore. The order of compilation will then matter and I do not how you can ensure the file containing the __traits(getModules) is always compiled as last. Kind regards AndreOne thing I do not understand. Once module A, which contains the new traits, is compiled using separate compilation, the list of found modules is fixed. If now new module B is added, nothing will cause a rebuild of A and therefore the list of found modules is incomplete. This can of course be solved by telling the user to not use separate compilation, which might be a fair tradeoff, I am not sure.It works with separate compilation if the unit tests are collected into a global variable. Here's an example using the existing __traits(allMembers): $ cat main.d module bar; extern (C) __gshared string[] members; void main() { import foo; members ~= [__traits(allMembers, bar)]; foobar(); assert(members == ["object", "members", "main", "object", "members", "foobar"]); } $ cat foo.d module foo; extern (C) extern __gshared string[] members; void foobar() { members ~= [__traits(allMembers, foo)]; } $ dmd -c foo.d && dmd foo.o -run main.d The assertion passes as expected.
May 24 2019
On 2019-05-24 20:53, Andre Pany wrote:Maybe my understanding is wrong. As far as I understand, your example only works as long as foo.d is compiled first and and main.d last.No, please give it a try. It works.The order is guaranteed by the import foo statement. But if now use __traits(getModules) in main.d and compile with this command: dmd -c main.d && dmd main.o -run foo.d I assume it won't work anymore. The order of compilation will then matter and I do not how you can ensure the file containing the __traits(getModules) is always compiled as last.The compiler would need to invoke a druntime function every time the compiler is invoked. This druntime function will call run `__traits(getModules)`. It's basically the same thing as I've done in my example. -- /Jacob Carlborg
May 24 2019