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)
│   └── permission.rs (aspect)
├── mod.rs (glues syscalls together)
└── chmod.rs (syscall declaration and simple test cases)

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 <syscall_name>.rs file at the root of the tests/ directory. 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, 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! {
    /// open do not update parent directory ctime and mtime fields if
    /// the file previously existed.
    exists_no_update
}
fn exists_no_update(ctx: &mut TestContext) {
    let file = ctx.create(FileType::Regular).unwrap();

    assert_times_unchanged()
        .path(ctx.base_path(), CTIME | MTIME)
        .execute(ctx, false, || {
            assert!(open_wrapper(&file, Mode::from_bits_truncate(0o755)).is_ok());
        });
}

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.