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 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:

 1import quixote
 2from quixote import get_context
 3import quixote.build.apt as apt
 4import quixote.fetch.gitlab as fetch
 5from quixote.inspection.build import gcc
 6
 7blueprint = quixote.Blueprint(
 8    name="c_program_demo_moulinette",
 9    author="Don Quixote",
10)
11
12
13@quixote.builder
14def install_gcc():
15    """
16    Install the GCC compiler using the APT package manager.
17    """
18
19    apt.update()
20    apt.install("gcc")
21
22
23@quixote.fetcher
24def fetch_delivery():
25    fetch.gitlab()
26
27
28@quixote.inspector
29def compile_delivery():
30    """
31    Compile all the C files in the student delivery to make a program.
32    """
33
34    delivery_path = get_context()["delivery_path"]
35    gcc(f"{delivery_path}/*.c").check("we cannot compile your delivery")

Note

You can download the source for this blueprint 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 copy() built-in, which allows copying deliveries from a local directory instead of downloading them.

That gives us the following change:

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 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:

1int main(void)
2{
3    return 0;
4}

In the delivery with group ID 2, we will create a main.c file containing the following incorrect C code:

1return 0;

We now have the following directory structure, which is valid for the 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:

{
    "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.

[
    {
        "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 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 here