Testing moulinettes =================== Introduction ------------ In order to test your moulinette on your own computer, we provide a dedicated tool called **Dulcinea**. Installation instructions can be found in the :doc:`Installing ` tutorial. Once installed, **Dulcinea** provides a ``dulcinea`` script that can be executed from the shell: :: > dulcinea usage: dulcinea [-h] [-c CONFIG_FILE] [-i INFO_FILE | --auto-info] [-r ROOT_DIR] [--docker-bridge-ip DOCKER_BRIDGE_IP] [--override-deliveries OVERRIDE_DELIVERIES] moulinette_directory dulcinea: error: the following arguments are required: moulinette_directory As you notice, ``dulcinea`` allows quite a bit of configuration to be specified, and that is what this tutorial will explain, along with guiding you through a step-by-step example. An example blueprint --------------------- In order to illustrate how **Dulcinea** can be used, we'll get our hands dirty and dive into a bit of practice. Let's pretend we wrote a simple blueprint just like the one below, which ensures the delivery can be compiled into a C program: .. literalinclude:: resources/testing_moulinettes_locally/original_moulinette/blueprint.py :linenos: :language: python .. note:: You can download the source for this blueprint :download:`here ` This moulinette looks ready for testing! However, there is one issue: what if we have no student deliveries to test it on (yet) ? For this example, we will thus first see how we can create our own deliveries to test the moulinette with. Using custom deliveries ----------------------- In order to use custom, handcrafted deliveries, we can replace the fetch part of our moulinette with another function. That function will use the :py:func:`~quixote.fetch.copy.copy` built-in, which allows copying deliveries from a local directory instead of downloading them. That gives us the following change: .. code-block:: python def fetch_delivery(): fetch.gitlab() from quixote.fetch.copy import copy @quixote.fetcher def copy_delivery(): copy("deliveries/") .. note:: Since Quixote uses the ``@quixote.fetcher`` marker to select the fetch steps, we just removed it from the original ``fetch_delivery`` function, and added it to the new function. Next, we can create the corresponding directory for the deliveries: :: > mkdir deliveries By looking at the documentation for the :py:func:`quixote.fetch.copy.copy` built-in, we can learn how to organize our deliveries: :: The structure of ``directory`` must be like so: directory/ └── 123/ └── file1.txt └── file2.png └── 124/ └── file1.txt └── file2.png where 123 and 124 are group IDs. With that in mind, we will create two deliveries (one passing the tests, another one failing). :: > mkdir deliveries/1 deliveries/2 In the delivery with group ID ``1``, we will create a ``main.c`` file with the following contents: .. code-block:: c :linenos: int main(void) { return 0; } In the delivery with group ID ``2``, we will create a ``main.c`` file containing the following incorrect C code: .. code-block:: c :linenos: return 0; We now have the following directory structure, which is valid for the :func:`~quixote.fetch.copy.copy` built-in: :: deliveries/ └── 1/ └── main.c └── 2/ └── main.c The moulinette directory ------------------------ Now for the easiest part, the ``moulinette_directory`` parameter is simply the path to the directory containing your moulinette. Thus, if you have a directory ``my_moulinette`` containing your ``blueprint.py`` and your resources, you must give the path to this directory as value for this argument. The configuration file ---------------------- Dulcinea's configuration file can be specified using the ``-c`` flag, and must contain a JSON dictionary. That dictionary will be used as base for the context that will be provided to the moulinette. Example: .. code-block:: json { "intra_user": "prof_x", "intra_password": "super_secret_pass", "gitlab_token": "super_cute_token" } .. note:: See the `Context cheat sheet `__ for a list of the values you can or must provide. It is entirely optional and is not often used when testing moulinettes locally, so we can simply omit it in this tutorial. The information file -------------------- Last, but not least, the information file is specified using the ``-i`` flag, and must contain a JSON list. Each element of that list is a JSON dictionary describing one of the deliveries to inspect. The elements must contain at least the ``group_id`` field. Each element's content will also be added as part of the context of the job it describes. We will thus fill our information file using group IDs matching the deliveries we created earlier. .. code-block:: json [ { "module_id": 1, "activity_id": 1, "group_id": 1, "leader": "login_x" }, { "module_id": 1, "activity_id": 1, "group_id": 2, "leader": "login_y" } ] .. note:: Since we don't have a module or activity yet, we use ``1`` as a dummy value for those fields. Testing ------- We can now start ``dulcinea`` with our deliveries and our moulinette. :: > dulcinea -i information.json my_moulinette/ Making it easier ---------------- As you probably noticed, modifying the blueprint for testing purposes and creating the information file tends to be a bit cumbersome. Happily, ``dulcinea`` provides two helper flags for that. The first one is ``--override-deliveries DELIVERIES``, which can be used to tell ``dulcinea`` to override the existing fetchers by replacing them with a fetcher calling the :func:`~quixote.fetch.copy.copy` built-in. :: > dulcinea -i information.json --override-deliveries deliveries/ my_unmodified_moulinette/ The second flag must be used in conjunction with ``--override-deliveries``, and will allow ``dulcinea`` to automatically deduce group IDs from the names of the directories in the ``deliveries/`` directory. Using the flag removes the need for an information file, so we can do: :: > dulcinea --override-deliveries deliveries/ --auto-info my_unmodified_moulinette/ .. note:: The full source for this tutorial can be downloaded :download:`here `