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