t27.aiРусский

Coverage lies

You will learn

Why a coverage number can be a check that checked nothing, and how to spot it.

A coverage percentage is a claim about the tests, and claims can be empty. The widget found the empty kind: 11 specs refused at parse, 0 blocked only by prose, and 23 broken on purpose as fixtures -- but elsewhere in the corpus a 'test' that is only prose passes every check while executing nothing. The tells are always the same: a number with no denominator, a pass with no assert that ran, a sweep that cannot fail. Ask of every percentage: what would make this number move?

Try it

In the prose sweep, find the difference between 'refused at parse' and 'blocked only by prose'; then write a test whose pass executes no assert.

Open the interactive lesson →

tri prose report: prose, or code?
tri prose report: prose, or code? ↗

11 specs refused at parse, 0 blocked only by prose, 23 broken on purpose as fixtures.

specs/fpga/testbench/fifo_tb.t27

// FIFO testbench spec
// This spec exercises a small synchronous FIFO with a power-of-two depth.
// It provides write/read helpers, a combined transaction function,
// and a set of tests covering basic operation, edge cases, and invariants.

const DEPTH : u32 = 8;

struct Fifo {
    mem : [DEPTH]u32,
    head : u32,
    tail : u32,
    fill_count : u32,
}

var fifo : Fifo = Fifo {
    mem = [0, 0, 0, 0, 0, 0, 0, 0],
    head = 0,
    tail = 0,
    fill_count = 0,
};

var wr_en : bool = false;
var rd_en : bool = false;
var wr_data : u32 = 0;
var rd_data : u32 = 0;

fn reset() {
    fifo.head = 0;
    fifo.tail = 0;
    fifo.fill_count = 0;
    wr_en = false;
    rd_en = false;
    wr_data = 0;
    rd_data = 0;
}

fn tick() {
    if wr_en && !rd_en && !full() {
        fifo.mem[fifo.tail] = wr_data;
        fifo.tail = (fifo.tail + 1) % DEPTH;
        fifo.fill_count = fifo.fill_count + 1;
    } else if rd_en && !wr_en && !empty() {
        rd_data = fifo.mem[fifo.head];
        fifo.head = (fifo.head + 1) % DEPTH;
        fifo.fill_count = fifo.fill_count - 1;
    } else if wr_en && rd_en {
        if !full() && !empty() {
            rd_data = fifo.mem[fifo.head];
            fifo.mem[fifo.tail] = wr_data;
            fifo.head = (fifo.head + 1) % DEPTH;
            fifo.tail = (fifo.tail + 1) % DEPTH;
        } else if !full() && empty() {
            fifo.mem[fifo.tail] = wr_data;
            fifo.tail = (fifo.tail + 1) % DEPTH;
            fifo.fill_count = fifo.fill_count + 1;
        } else if full() && !empty() {
            rd_data = fifo.mem[fifo.head];
            fifo.head = (fifo.head + 1) % DEPTH;
            fifo.fill_count = fifo.fill_count - 1;
        }
    }
    wr_en = false;
    rd_en = false;
}

fn full() -> bool {
    return fifo.fill_count == DEPTH;
}

fn empty() -> bool {
    return fifo.fill_count == 0;
}

fn write_word(data : u32) -> bool {
    if full() {
        return false;
    }
    wr_en = true;
    wr_data = data;
    tick();
    wr_en = false;
    return true;
}

fn read_word() -> u32 {
    if empty() {
        return 0xFFFF;
    }
    rd_en = true;
    tick();
    rd_en = false;
    return rd_data;
}

fn on_comb(data: u32) -> bool {
    return write_word(data);
}

test test_basic_write {
    reset();
    var ok = write_word(0xDEAD);
    assert ok == true;
    assert fifo.fill_count == 1;
}

test test_basic_read {
    reset();
    write_word(0xBEEF);
    var data = read_word();
    assert data == 0xBEEF;
    assert fifo.fill_count == 0;
}

test test_full_after_writes {
    reset();
    var i = 0;
    while i < DEPTH {
        var ok = write_word(i);
        assert ok == true;
        i = i + 1;
    }
    assert full() == true;
    var ok = write_word(0xFFFF);
    assert ok == false;
}

test test_empty_after_read {
    reset();
    write_word(0x1234);
    write_word(0x5678);
    var d1 = read_word();
    var d2 = read_word();
    assert d1 == 0x1234;
    assert d2 == 0x5678;
    assert empty() == true;
}

test test_overflow_rejected {
    reset();
    var i = 0;
    while i < DEPTH {
        write_word(i);
        i = i + 1;
    }
    var ok = write_word(0xABCD);
    assert ok == false;
    assert fifo.fill_count == DEPTH;
}

test test_underflow_returns_sentinel {
    reset();
    var data = read_word();
    assert data == 0xFFFF;
    assert fifo.fill_count == 0;
}

test test_overflow_then_underflow {
    reset();
    var i = 0;
    while i < DEPTH {
        write_word(i);
        i = i + 1;
    }
    var extra = write_word(0xFFFF);
    assert extra == false;
    var j = 0;
    while j < DEPTH {
        var data = read_word();
        assert data == j;
        j = j + 1;
    }
    var sentinel = read_word();
    assert sentinel == 0xFFFF;
}

test test_rapid_alternating {
    reset();
    var i = 0;
    while i < 20 {
        var ok = write_word(i);
        assert ok == true;
        var data = read_word();
        assert data == i;
        i = i + 1;
    }
    assert empty() == true;
}

test test_wrap_behavior {
    reset();
    var i = 0;
    while i < 16 {
        write_word(i);
        i = i + 1;
    }
    var j = 0;
    while j < 8 {
        var data = read_word();
        assert data == (j + 8);
        j = j + 1;
    }
    assert fifo.fill_count == 8;
}

test test_simultaneous_rw {
    reset();
    write_word(0x42);
    wr_en = true;
    rd_en = true;
    wr_data = 0x43;
    tick();
    wr_en = false;
    rd_en = false;
    invariant fill_count == 1;
}

test test_on_comb_writes_when_not_full {
    reset();
    var result = on_comb(0xAAAA);
    assert result == true;
    assert fifo.fill_count == 1;
    assert fifo.mem[0] == 0xAAAA;
}

test test_on_comb_rejects_when_full {
    reset();
    var i = 0;
    while i < DEPTH {
        write_word(i);
        i = i + 1;
    }
    var result = on_comb(0xBBBB);
    assert result == false;
    assert fifo.fill_count == DEPTH;
}

Open the lesson's spec in the player ↗

All lessons