.. default-domain:: chpl .. module:: UnitTest :synopsis: Support for automated testing in Chapel. UnitTest ======== **Usage** .. code-block:: chapel use UnitTest; or .. code-block:: chapel import UnitTest; Support for automated testing in Chapel. The UnitTest module is intended to be used with the `mason test `_ command, which automates execution of any test function. A unit test function is defined as any function with the following signature: .. code-block:: chapel proc funcName(test: borrowed Test) throws {} These functions must accept an argument of type ``borrowed Test``, and have a ``throws``. A program containing tests must execute the ``UnitTest`` :proc:`~UnitTest.main()` function to run the tests. Assert Functions ---------------- Here are the assert functions available in the UnitTest module: - :proc:`~Test.assertTrue` - :proc:`~Test.assertFalse` - :proc:`~Test.assertEqual` - :proc:`~Test.assertNotEqual` - :proc:`~Test.assertGreaterThan` - :proc:`~Test.assertLessThan` Test Metadata Functions ----------------------- UnitTest module also provides multiple methods for specifying test Metadata: - :proc:`~Test.skip` - :proc:`~Test.skipIf` - :proc:`~Test.addNumLocales` - :proc:`~Test.maxLocales` - :proc:`~Test.minLocales` - :proc:`~Test.dependsOn` Examples -------- Basic Usage ^^^^^^^^^^^ Here is a minimal example demonstrating how to use the UnitTest module: .. code-block:: chapel use UnitTest; proc celsius2fahrenheit(x) { // we should be returning "(x: real * 9/5)+32" return (x * 9/5)+32; } proc test_temperature(test: borrowed Test) throws { // we were expecting 98.6 but since we missed typecasting // the above function returned 98. test.assertFalse(celsius2fahrenheit(37) == 98); } UnitTest.main(); Output: .. code-block:: bash ====================================================================== FAIL xyz.chpl: test_temperature() ---------------------------------------------------------------------- AssertionError: assertFalse failed. Given expression is True ---------------------------------------------------------------------- Run 1 test FAILED failures = 1 Skipping Tests ^^^^^^^^^^^^^^^ You can skip tests unconditionally with :proc:`~Test.skip` and conditionally with :proc:`~Test.skipIf`: .. code-block:: chapel use UnitTest; /* calculates factorial */ proc factorial(x: int): int { return if x == 0 then 1 else x * factorial(x-1); } /*Conditional skip*/ proc test1(test: borrowed Test) throws { test.skipIf(factorial(0) != 1,"Base condition is wrong in factorial"); test.assertTrue(factorial(5) == 120); } /*Unconditional skip*/ proc test2(test: borrowed Test) throws { test.skip("Skipping the test directly"); } UnitTest.main(); Output: .. code-block:: bash ====================================================================== SKIPPED xyz.chpl: test2() ---------------------------------------------------------------------- TestSkipped: Skipping the test directly ---------------------------------------------------------------------- Run 1 test OK skipped = 1 Specifying locales ^^^^^^^^^^^^^^^^^^ You can specify the num of locales of a test using these method. :proc:`~Test.addNumLocales` :proc:`~Test.maxLocales` :proc:`~Test.minLocales` Here is an example demonstrating how to use the :proc:`~Test.addNumLocales` .. code-block:: chapel proc test_square(test: borrowed Test) throws { test.addNumLocales(5); var A: [Locales.domain] int; coforall i in 0..numLocales-1 with (ref A) { on Locales(i) { A[i+1] = (i+1)*(i+1); } } test.assertTrue(A[5]==25); } Output: .. code-block:: bash ---------------------------------------------------------------------- Run 1 test OK You can also specify multiple locales on which your code can run. .. code-block:: chapel proc test3(test: borrowed Test) throws { test.addNumLocales(16,8); } You can mention the range of locales using :proc:`~Test.maxLocales` and :proc:`~Test.minLocales` .. code-block:: chapel proc test4(test: borrowed Test) throws { test.maxLocales(4); test.minLocales(2); } Specifying Dependencies ^^^^^^^^^^^^^^^^^^^^^^^ You can specify the order in which tests should run using :proc:`~Test.dependsOn`: .. code-block:: chapel use UnitTest; var factorials: list(int); // calculates factorial proc factorial(x: int): int { return if x == 0 then 1 else x * factorial(x-1); } proc testFillFact(test: borrowed Test) throws { test.skipIf(factorial(0) != 1,"Base condition is wrong in factorial"); for i in 1..10 do factorials.pushBack(factorial(i)); } proc testSumFact(test: borrowed Test) throws { test.dependsOn(testFillFact); var s = 0; for i in factorials.indices do s += factorials[i]; test.assertGreaterThan(s,0); } UnitTest.main(); Output: .. code-block:: bash ---------------------------------------------------------------------- Run 2 tests OK .. class:: Test .. method:: proc skip(reason: string = "") throws Unconditionally skip a test. :arg reason: the reason for skipping :throws TestSkipped: Always .. method:: proc skipIf(condition: bool, reason: string = "") throws Skip a test if the condition is true. :arg condition: the boolean condition :arg reason: the reason for skipping :throws TestSkipped: If the `condition` is true. .. method:: proc assertTrue(test: bool) throws Assert that ``test`` is `true`. :arg test: the boolean condition :throws AssertionError: If the assertion is `false`. .. method:: proc assertFalse(test: bool) throws Assert that ``test`` is `false`. :arg test: the boolean condition :throws AssertionError: If the assertion is `true`. .. method:: proc assertEqual(first, second) throws Assert that ``first == second``. :arg first: The first object to compare. :arg second: The second object to compare. :throws AssertionError: If both the arguments are not equal. .. method:: proc assertNotEqual(first, second) throws Assert that ``first != second``. :arg first: The first object to compare. :arg second: The second object to compare. :throws AssertionError: If both the arguments are equal. .. method:: proc assertGreaterThan(first, second) throws Assert that ``first > second``. :arg first: The first object to compare. :arg second: The second object to compare. :throws AssertionError: If the first argument is not greater than second argument. .. method:: proc assertLessThan(first, second) throws Assert that ``first < second``. :arg first: The first object to compare. :arg second: The second object to compare. :throws AssertionError: If the first argument is not less than the second argument. .. method:: proc maxLocales(value: int) throws Specify maximum number of locales this test can run on. :arg value: Maximum number of locales with which the test can be run. :throws UnexpectedLocales: If `value` is less than 1 or `minNumLocales` .. method:: proc minLocales(value: int) throws Specify minimum number of locales required to run the test. :arg value: Minimum number of locales with which the test can be run. :throws UnexpectedLocales: If `value` is more than `maxNumLocales` .. method:: proc addNumLocales(locales: int ...?n) throws Indicate how many locales to run the test on. If a test can run on multiple different locale counts, they can be specified using multiple arguments. Only one of the locale counts specified will be run in testing. .. note:: To run a single test with multiple locale counts, create multiple tests where each test requires a specific locale count. :arg locales: locale counts :throws UnexpectedLocales: If `locales` are already added. .. method:: proc dependsOn(tests: argType ...?n) throws Adds the tests which must run before this test. :arg tests: First class functions :throws DependencyFound: If called for the first time in a function. .. function:: proc main() throws Runs the tests Call this as .. code-block:: chapel UnitTest.main();