<div dir="ltr"><div class="gmail_extra">Hi Nikos,</div><div class="gmail_extra"></div><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Oct 5, 2016 at 7:39 PM, Nikos Alexandris <span dir="ltr"><<a href="mailto:nik@nikosalexandris.net" target="_blank">nik@nikosalexandris.net</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div id="gmail-:1ox" class="gmail-a3s gmail-aXjCH gmail-m157979cf1c53f77a">could you name some good examples (not to the<br>
manual/s), some real modules with integrated tests? I am much in need<br>
for good code.</div></blockquote></div><br>I'm not sure if they are good, but here are some examples based on Anna's test categories.<br><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Oct 4, 2016 at 4:55 PM, Anna Petrášová <span dir="ltr"><<a href="mailto:kratochanna@gmail.com" target="_blank">kratochanna@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div id="gmail-:1t3" class="gmail-a3s gmail-aXjCH gmail-m157917b4fe0b7199">[...]<br>
gunittests can serve to a) test inputs/outputs b) catch changes in<br>
results (whether correct or incorrect) c) test correctness of results.<br>
It just depends how you write them, and yes, for some modules c) is<br>
more difficult to implement than for others.</div></blockquote><div><br></div><div>a) inputs/outputs<br><br>This one tests if the outputs are there (module runs, accepts flags, gives outputs) and if the output values are in expected ranges ("basic correctness") and the output are expected data types. It also tests some stats like sum and variance of the result, but that's more for the category b).<br></div><div><br><a href="https://trac.osgeo.org/grass/browser/grass-addons/grass7/raster/r.forestfrag/testsuite/r_forestfrag_ncspm.py">https://trac.osgeo.org/grass/browser/grass-addons/grass7/raster/r.forestfrag/testsuite/r_forestfrag_ncspm.py</a> <br><br></div><div>b) result changes<br><br>This uses a miniature dataset and results obtained by the module itself. It tests all values of the result and expects them to match the reference. This will catch a regression in a module code (or in dependencies).<br></div><div><br><a href="https://trac.osgeo.org/grass/browser/grass-addons/grass7/raster/r.forestfrag/testsuite/r_forestfrag_xy.py">https://trac.osgeo.org/grass/browser/grass-addons/grass7/raster/r.forestfrag/testsuite/r_forestfrag_xy.py</a><br><br> It assumes that the model worked at the point these were obtained. If that was not the case, the data must be changed like in this commit:<br><br><a href="https://trac.osgeo.org/grass/changeset/68722">https://trac.osgeo.org/grass/changeset/68722</a><br><br></div><div>c) correctness<br><br>Here is a test on a miniature example which was possible to compute by hand and was part of the original publication.<br></div><div><br><a href="https://trac.osgeo.org/grass/browser/grass-addons/grass7/raster/r.forestfrag/testsuite/r_forestfrag_trivial.py">https://trac.osgeo.org/grass/browser/grass-addons/grass7/raster/r.forestfrag/testsuite/r_forestfrag_trivial.py</a><br><br></div><div>Here is a test which uses reference data computed using another software:<br></div><div><br><a href="https://trac.osgeo.org/grass/browser/grass/trunk/raster/r.slope.aspect/testsuite/test_r_slope_aspect.py?rev=68557#L44">https://trac.osgeo.org/grass/browser/grass/trunk/raster/r.slope.aspect/testsuite/test_r_slope_aspect.py?rev=68557#L44</a><br><a href="https://trac.osgeo.org/grass/browser/grass/trunk/raster/r.slope.aspect/testsuite/data">https://trac.osgeo.org/grass/browser/grass/trunk/raster/r.slope.aspect/testsuite/data</a><br><br></div><div>Using gdaldem to test r.slope.aspect it is actually not ideal, because gdaldem uses old r.slope.aspect code. So in this case, it is more a test against very old version of r.slope.aspect.<br><br></div><div>This is definitively a creative part because you need to get the results without using the code you are developing to get the result. For example, if you have prototype implementation in Python or r.mapcalc and then your write C code to do the same more effectively with more options, you can use the prototype to cover part of your outputs. This of course does not give a guarantee that you did not make the same mistake twice, but it is almost guaranteed that you will make two separate sets of mistakes and this will expose them.<br></div><div><br> It of course never hurts to test also the "basic correctness", e.g. that the values are all positive or that they are from 0 to 255 and are integers.<br><br></div><div>d) small parts and dependencies<br></div><div><br></div><div>There is one more category I would add to this list. These are the tests of building blocks (units) you are using in your module. It can be functions your wrote but also functions or modules you are using. When writing a C module you can write a separate module which uses the functions from your module one by one to generate some testable outputs. These output can be then tested using the gunittest procedure.<br><br><a href="https://trac.osgeo.org/grass/browser/grass/trunk/raster3d/r3.flow/test_main.c">https://trac.osgeo.org/grass/browser/grass/trunk/raster3d/r3.flow/test_main.c</a><br><a href="https://trac.osgeo.org/grass/browser/grass/trunk/raster3d/r3.flow/testsuite/r3flow_test.py">https://trac.osgeo.org/grass/browser/grass/trunk/raster3d/r3.flow/testsuite/r3flow_test.py</a><br><br></div><div>Further, you can write a ctypes-based (fully) Python test for the library functions your are using:<br></div><div><br><a href="https://trac.osgeo.org/grass/browser/grass/trunk/lib/gis/testsuite/gis_lib_tokenize.py">https://trac.osgeo.org/grass/browser/grass/trunk/lib/gis/testsuite/gis_lib_tokenize.py</a><br><br></div><div>For Python modules, you can do the same it is just much easier and Soeren already provided examples for it.<br><br></div><div>Hope this helps,<br></div><div>Vaclav<br></div></div></div></div></div>