How do I run the EK9 fuzzer?

← Fuzzing and Mutation Testing · Ref: Q750

The -fuzz flag runs grammar-based fuzz testing that stress-tests the compiler with randomly generated EK9 source files.

BASIC USAGE

  ek9 -fuzz 60

This runs the fuzzer for 60 minutes, generating random EK9 programs and feeding them through the full compilation pipeline. Crash-inducing inputs are saved to ./fuzz-crashes/ automatically.

DURATION

The parameter is in minutes. Use longer durations for deeper coverage:

  ek9 -fuzz 5      Quick smoke test (5 minutes).
  ek9 -fuzz 60     Normal development session (1 hour).
  ek9 -fuzz 1440   Overnight run (24 hours).

WORKER THREADS

The fuzzer uses multiple worker threads to maximise throughput. It automatically detects available CPU cores and distributes work across them.

WHAT IT GENERATES

Each iteration creates a syntactically-aware EK9 source file containing randomly selected constructs: functions, classes, records, traits, enumerations, streams, control flow, operators, and generics. The generator understands EK9 grammar rules so that most files parse successfully and exercise deeper compiler phases.

CRASH FILES

When the compiler throws an unexpected exception on a generated file, that file is saved to ./fuzz-crashes/ with a descriptive filename. These files are minimal reproduction cases for bug reports.

See Q749 for fuzzing overview. See Q751 for output formats. See Q752 for the HTML dashboard. See Q757 for crash file details. See Q2 for compile and run basics.

Example

defines module qa.fuzzingandmutation.runfuzzer

  <?-
    Example code that a fuzzer might generate and test.
    This demonstrates the kind of constructs the grammar-based
    fuzzer combines: classes with operators, pure functions,
    stream pipelines, and control flow.
  -?>

  defines class

    Temperature
      reading as Float: Float()

      Temperature()
        ->
          initial as Float
        this.reading :=? initial

      operator $ as pure
        <- rtn as String: $this.reading

      operator < as pure
        -> other as Temperature
        <- rtn as Boolean: this.reading < other.reading

      default operator ?

  defines function

    averageReading()
      ->
        readings as List of Float
      <- average as Float: 0.0

      count <- length readings
      zero <- 0
      if count > zero
        total <- 0.0
        for reading in readings
          total: total + reading
        average: total / #^count

  defines program

    RunFuzzerDemo()
      stdout <- Stdout()

      readings <- [20.5, 22.1, 19.8, 25.3, 18.7]
      avg <- averageReading(readings)
      stdout.println(`Average temperature: ${avg}`)

      cold <- Temperature(15.0)
      warm <- Temperature(30.0)
      stdout.println(`Cold: ${cold}, Warm: ${warm}`)
      coldLessThanWarm <- cold < warm
      stdout.println(`Cold < Warm: ${coldLessThanWarm}`)
Other ways to ask this
  • What is the ek9 -fuzz flag for?
  • How do I fuzz the EK9 compiler to find crashes?
  • How long should I run the EK9 fuzzer?

Coming from another language?

Java: no built-in fuzzer, use Jazzer or JQF (separate tools, separate setup). Python: no built-in, Hypothesis for property-based testing, Atheris for coverage-guided fuzzing. Rust: cargo-fuzz wraps libFuzzer (must write harness functions). Go: go test -fuzz (built-in since 1.18 but requires writing Fuzz* functions). EK9: ek9 -fuzz <minutes> runs immediately with zero setup, no harness code needed.

Keywords: run, thread, crash, fuzz, grammar, generate, random, compiler, smoke, minutes, worker, duration