Structure

The package is made of the tests, and a test runner to launch them.

Tests (tests/)

To present how tests are organized, we take the chmod syscall as example.

There is a separate module for each syscall being tested. Within each of those modules, there may be either a single file, or a separate file for each aspect of the syscall.

The hierarchy is like this:

graph TD
TG[Syscall module<br /><i>chmod</i>] --> TC1[Aspect<br /><i>errno</i>]

TC1 --> TC1F1[Test case]
TC1 --> TC1F2[Test case]
TC1 --> TC1F3[Test case]
TC1 --> TC1F4[Test case]

TG --> TC2[Aspect<br /><i>permission</i>]

TC2 --> TC2F1[Test case]
TC2 --> TC2F2[Test case]

Layout

src/tests
├── chmod (syscall)
│   ├── errno.rs (aspect)
│   ├── mod.rs (syscall declaration)
│   └── permission.rs (aspect)
└── mod.rs (glues syscalls together)

tests/mod.rs

All the modules for the test groups should be declared in this file.

pub mod chmod;

Syscall module

A syscall module contains test cases related to a specific syscall. Its declaration should be in the mod.rs file of the relevant folder (chmod/ in our case). Common syscall-specific helpers can go here.

Aspect

An optional aspect module contains test cases that all relate to a common aspect of the syscall. Here "aspect" is a subjective area of related functionality. The aspect module may be either:

  • in a single file, which contains all the test functions,
  • in a folder, which contains multiple modules for the test functions and a mod.rs file, in which the case is declared.

Except in the case of a very large set of test functions, the first style should be preferred.

Test case

Each test case exercises a minimal piece of the syscall's functionality. Each must be registered with the test_case! macro.

crate::test_case! {ctime => [Regular, Fifo, Block, Char, Socket]}
fn ctime(ctx: &mut TestContext, f_type: FileType) {
    let path = ctx.create(f_type).unwrap();
    let ctime_before = stat(&path).unwrap().st_ctime;

    sleep(Duration::from_secs(1));

    chmod(&path, Mode::from_bits_truncate(0o111)).unwrap();

    let ctime_after = stat(&path).unwrap().st_ctime;
    assert!(ctime_after > ctime_before);
}

Test runner (main.rs)

The test runner has to run the tests, and provide a command-line interface to allow the user to modify how the tests should be run. It takes the tests from the specified test groups.