> Canonical HTML: [https://docs.schematic.tech/pup/supertests/](https://docs.schematic.tech/pup/supertests/)

# Writing supertests

A supertest states a requirement your code must satisfy. Its inputs describe the values it covers,
its assertions state what must hold, and optional assumptions limit which inputs the requirement
applies to. Pup uses mathematical reasoning to verify these requirements or find counterexamples
that reveal bugs.

Choose your language for the instructions and examples below: [language selector](https://docs.schematic.tech/pup/supertests/)

## Declare a supertest

**Python:** The [Python supertest library](https://github.com/schematic-tech/supertest-python) provides `@supertest` and `assume`. See [Add supertest support](/pup/get-started/#add-supertest-support) for installation.

**Rust:** The [Rust supertest library](https://github.com/schematic-tech/supertest-rust) provides `#[supertest]` and `assume`. See [Add supertest support](/pup/get-started/#add-supertest-support) for installation.

**C:** The [C supertest library](https://github.com/schematic-tech/supertest-c) provides `SUPERTEST` and `SUPERTEST_ASSUME`. See [Add supertest support](/pup/get-started/#add-supertest-support) for installation.

**C#:** The [C# supertest library](https://github.com/schematic-tech/supertest-csharp) provides `[Supertest]` and `Assumptions.Assume`. See [Add supertest support](/pup/get-started/#add-supertest-support) for installation.

**JavaScript:** The [JavaScript supertest library](https://github.com/schematic-tech/supertest-javascript) provides `supertest` and `assume`. See [Add supertest support](/pup/get-started/#add-supertest-support) for installation.

**Java:** The [Java supertest library](https://github.com/schematic-tech/supertest-java) provides `@Supertest` and `Assumptions.assume`. See [Add supertest support](/pup/get-started/#add-supertest-support) for installation.

**VHDL:** The [VHDL supertest library](https://github.com/schematic-tech/supertest-vhdl) provides `supertest_assume`. See [Add supertest support](/pup/get-started/#add-supertest-support) for installation.

**Python, C, JavaScript, VHDL:** We recommend keeping supertests in `supertests/` at the project root, or alongside your regular tests. They can live in other directories too: Pup searches the directory you check, including its subdirectories.

**Rust:** We recommend keeping supertests in `tests/` so Cargo compiles them as test targets. You can use a separate `supertests/` directory or another location by registering each test target with `[[test]]` in `Cargo.toml`.

**C#:** We recommend keeping supertests in your test project, alongside your regular tests. You can use any folder included in that project; this example uses `supertests/`.

**Java:** We recommend keeping supertests in `src/test/java/` so Maven compiles them with your test dependencies. You can use another directory if your build is configured to include it as a test source directory.

**Python, Rust, C, C#, JavaScript, Java:** For example, consider the function from [Welcome to Pup](/pup/) that normalizes text by replacing consecutive spaces with one space. This supertest checks that running it again leaves the result unchanged:

**VHDL:** For example, consider the 8-bit saturating incrementer from [Welcome to Pup](/pup/). It should add one to its input, stopping at 255. This supertest checks that incrementing a value never makes it smaller:

**Python · supertests/collapse_spaces.py**

```python
from schematic import *

from text_tools.text import collapse_spaces

@supertest
def collapsing_spaces_again_changes_nothing(text: str):
    once = collapse_spaces(text)
    twice = collapse_spaces(once)

    assert twice == once
```

**Rust · tests/collapse_spaces.rs**

```rust
use schematic::supertest;
use text_tools::collapse_spaces;

#[supertest]
pub fn collapsing_spaces_again_changes_nothing(text: String) {
    let once = collapse_spaces(&text);
    let twice = collapse_spaces(&once);

    assert_eq!(twice, once);
}
```

**C · supertests/collapse_spaces.c**

```c
#include <assert.h>
#include <schematic.h>
#include <stdlib.h>
#include <string.h>
#include "text.h"

SUPERTEST
void collapsing_spaces_again_changes_nothing(const char *text) {
    SUPERTEST_ASSUME(text != NULL);
    char *once = collapse_spaces(text);
    char *twice = collapse_spaces(once);
    int unchanged = strcmp(twice, once) == 0;

    free(once);
    free(twice);
    assert(unchanged);
}
```

**C# · supertests/CollapseSpaces.cs**

```csharp
using Schematic;
using Xunit;

namespace TextTools;

public static class CollapseSpaces
{
    [Supertest]
    public static void CollapsingSpacesAgainChangesNothing(string text)
    {
        string once = Text.CollapseSpaces(text);
        string twice = Text.CollapseSpaces(once);

        Assert.Equal(once, twice);
    }
}
```

**JavaScript · supertests/collapse_spaces.js**

```javascript
import assert from 'node:assert/strict';
import { assume, supertest } from 'schematic-supertest';
import { collapseSpaces } from '../src/text.js';

export const collapsingSpacesAgainChangesNothing = supertest((text) => {
  assume(typeof text === 'string');
  const once = collapseSpaces(text);
  const twice = collapseSpaces(once);

  assert.equal(twice, once);
});
```

**Java · src/test/java/texttools/CollapseSpaces.java**

```java
package texttools;

import static org.junit.Assert.assertEquals;
import static tech.schematic.Assumptions.assume;

import tech.schematic.Supertest;

public final class CollapseSpaces {
    private CollapseSpaces() {}

    @Supertest
    public static void collapsingSpacesAgainChangesNothing(String text) {
        assume(text != null);
        String once = Text.collapseSpaces(text);
        String twice = Text.collapseSpaces(once);

        assertEquals(once, twice);
    }
}
```

**VHDL · supertests/increment.vhd**

```vhdl
library ieee;
use ieee.numeric_std.all;
use work.incrementer.all;

entity increment_supertests is
  port (value : in natural range 0 to 255);
end entity;

architecture supertests of increment_supertests is
begin
  --% supertest
  increment_never_decreases : process
  begin
    assert to_integer(saturating_increment(to_unsigned(value, 8))) >= value
      report "Increment decreased the value" severity failure;
    wait;
  end process;
end architecture;
```

**Python:** Writing a supertest is much like writing an ordinary test function: call your code and use regular assertions to state what should happen. Give it parameters for the inputs the requirement covers, and mark it with `@supertest`. Define the function at the top level of the file, outside any class or other function.

**Rust:** Writing a supertest is much like writing an ordinary test function: call your code and use regular assertions to state what should happen. Give it parameters for the inputs the requirement covers, and mark it with `#[supertest]`. Define the function at the top level of the file, outside any inline module, `impl` block, or other function.

**C:** Write a supertest as a `void` function at file scope: call your code and use `assert` to state what should happen. Give it parameters for the inputs the requirement covers, and place `SUPERTEST` before the function.

**C#:** Write a supertest as a public static method: call your code and use regular assertions to state what should happen. Give it parameters for the inputs the requirement covers, and mark it with `[Supertest]`, imported with `using Schematic;`.

**JavaScript:** Writing a supertest is much like writing an ordinary test function: call your code and use regular assertions to state what should happen. Give it parameters for the inputs the requirement covers. Wrap the function with `supertest(...)` and assign the result to a named top-level constant.

**Java:** Write a supertest as a public static method: call your code and use regular assertions to state what should happen. Give it parameters for the inputs the requirement covers, and mark it with `@Supertest`, imported with `import tech.schematic.Supertest;`.

**VHDL:** Write a supertest as a labeled process with ordinary VHDL assertions. Use input ports on the enclosing entity for its inputs, and place `--% supertest` immediately before the process. The process label is the supertest name.

**Python, Rust, C, C#, JavaScript, Java:** Here, `text` represents any string, and the assertion requires that collapsing spaces a second time leaves the result unchanged. Pup checks that the assertions hold for every input allowed by the types and assumptions.

**C:** The function expects `text` to point to a NUL-terminated string. `SUPERTEST_ASSUME(text != NULL)` excludes null pointers from this requirement.

**JavaScript:** The assumption `typeof text === 'string'` limits the input to strings.

**Java:** The assumption `text != null` excludes null references from this requirement.

**VHDL:** Here, `value` covers every integer from 0 through 255, and the assertion requires that incrementing never produces a smaller value. Pup checks that the assertions hold for every input allowed by the types and assumptions.

**C#:** These examples use xUnit assertions. You can use your project’s existing assertion library instead.

**Java:** These examples use JUnit assertions. You can use your project’s existing assertion library instead.

Name each supertest after the behavior it checks. Keep related assertions together, and split
unrelated behaviors into separate supertests.

## Restrict inputs with assumptions

**Python, Rust, JavaScript, Java:** Use `assume` from the supertest library when a requirement applies only to some inputs. Pup checks the assertions for every input that satisfies the assumption.

**C:** Use `SUPERTEST_ASSUME` from the supertest library when a requirement applies only to some inputs. Pup checks the assertions for every input that satisfies the assumption.

**C#:** Use `Assumptions.Assume` from the supertest library when a requirement applies only to some inputs. Pup checks the assertions for every input that satisfies the assumption.

**VHDL:** Use `supertest_assume` from the supertest library when a requirement applies only to some inputs. Pup checks the assertions for every input that satisfies the assumption.

**Python, Rust, C, C#, JavaScript, Java:** If text already has no consecutive spaces, collapsing spaces should leave it unchanged. We can add a second supertest for this requirement, using an assumption to restrict it to already normalized text:

**VHDL:** Below the saturation limit, the result should equal the input plus one. We can add a second supertest with an assumption that the input is less than 255:

**Python · supertests/normalized_text.py**

```python
from schematic import *

from text_tools.text import collapse_spaces

@supertest
def collapsing_spaces_preserves_normalized_text(text: str):
    assume("  " not in text)

    result = collapse_spaces(text)
    assert result == text
```

**Rust · tests/normalized_text.rs**

```rust
use schematic::{assume, supertest};
use text_tools::collapse_spaces;

#[supertest]
pub fn collapsing_spaces_preserves_normalized_text(text: String) {
    assume(!text.contains("  "));

    let result = collapse_spaces(&text);
    assert_eq!(result, text);
}
```

**C · supertests/normalized_text.c**

```c
#include <assert.h>
#include <schematic.h>
#include <stdlib.h>
#include <string.h>
#include "text.h"

SUPERTEST
void collapsing_spaces_preserves_normalized_text(const char *text) {
    SUPERTEST_ASSUME(text != NULL);
    SUPERTEST_ASSUME(strstr(text, "  ") == NULL);

    char *result = collapse_spaces(text);
    int unchanged = strcmp(result, text) == 0;

    free(result);
    assert(unchanged);
}
```

**C# · supertests/NormalizedText.cs**

```csharp
using Schematic;
using Xunit;

namespace TextTools;

public static class NormalizedText
{
    [Supertest]
    public static void CollapsingSpacesPreservesNormalizedText(string text)
    {
        Assumptions.Assume(!text.Contains("  "));

        string result = Text.CollapseSpaces(text);
        Assert.Equal(text, result);
    }
}
```

**JavaScript · supertests/normalized_text.js**

```javascript
import assert from 'node:assert/strict';
import { assume, supertest } from 'schematic-supertest';
import { collapseSpaces } from '../src/text.js';

export const collapsingSpacesPreservesNormalizedText = supertest((text) => {
  assume(typeof text === 'string');
  assume(!text.includes('  '));

  const result = collapseSpaces(text);
  assert.equal(result, text);
});
```

**Java · src/test/java/texttools/NormalizedText.java**

```java
package texttools;

import static org.junit.Assert.assertEquals;
import static tech.schematic.Assumptions.assume;

import tech.schematic.Supertest;

public final class NormalizedText {
    private NormalizedText() {}

    @Supertest
    public static void collapsingSpacesPreservesNormalizedText(String text) {
        assume(text != null);
        assume(!text.contains("  "));

        String result = Text.collapseSpaces(text);
        assertEquals(text, result);
    }
}
```

**VHDL · supertests/increment_below_limit.vhd**

```vhdl
library ieee;
use ieee.numeric_std.all;
use work.incrementer.all;

entity increment_below_limit_supertests is
  port (value : in natural range 0 to 255);
end entity;

use work.schematic.all;

architecture supertests of increment_below_limit_supertests is
begin
  --% supertest
  increment_below_limit_adds_one : process
  begin
    supertest_assume(value < 255);
    assert to_integer(saturating_increment(to_unsigned(value, 8))) = value + 1
      report "Increment did not add one" severity failure;
    wait;
  end process;
end architecture;
```

Don't exclude a failing input just to make a supertest pass. If your code should reject that
input, check the rejection with an assertion.

See [`pup check`](/pup/commands/checks/#pup-check) to check your supertests and review the results.
