Class TestPrograms

java.lang.Object
org.ek9lang.compiler.common.TestPrograms

public final class TestPrograms extends Object
THE answer to "is this a @Test, and what execution group is it in?" — use this, do not read the squirrelled key directly.

The encoding contract, in one place because it keeps needing restating

Phase 1 (DefinitionListener) records a @Test directive on the program aggregate as two deliberately separate squirrelled keys:

  • CommonValues.TEST_DIRECTIVE — its presence is the authoritative "this is a test". Its value is the sequential execution group ("" when ungrouped), which TestRunner schedules on.
  • CommonValues.REQUIREMENT_REF — the requirement the test anchors, on its own key. It is NOT packed into the group value, because a group name that happened to look like a requirement id would otherwise silently reschedule the test.
  • CommonValues.TEST_EXIT_CODE — the exit code the test declared with "EXIT:<n>", on its own key for the same reason; absent means 0.

Consolidated 2026-08-04: the presence check was inlined at seven sites across three modules (compiler-main's call-graph collector, the MCP anchor source / anchor tool / neighbourhood extractor, the CLI test runner, and two IDE providers), two of them private methods that had independently arrived at the same name. Nothing tied them together, so the contract above lived only in prose comments that each site restated slightly differently.

  • Method Details

    • isTestProgram

      public static boolean isTestProgram(ISymbol symbol)
      Whether this symbol is itself a @Test program.

      This is the direct question, correct for callers already holding a program aggregate — which is most of them, since programs are what AllProgramsSupplier yields.

      Parameters:
      symbol - any symbol, or null
      Returns:
      true when the symbol carries the test marker
    • isWithinTestProgram

      public static boolean isWithinTestProgram(ISymbol symbol)
      Whether this symbol is a test program or a method declared inside one.

      The wider question, for callers walking arbitrary callables rather than programs. Phase 1 puts the marker on the program aggregate and not on its synthetic _main, so a caller looking at methods must climb one scope or it will conclude that no test exists anywhere.

      Parameters:
      symbol - any symbol, or null
      Returns:
      true when the symbol, or its enclosing aggregate, carries the test marker
    • executionGroupOf

      public static String executionGroupOf(ISymbol symbol)
      The sequential execution group a test belongs to.

      Null for a non-test; empty for a test with no group, which is the ordinary case and means "runs in the parallel pool". Callers rewriting a directive must preserve this — dropping it moves a serialised test back into the parallel pool with no diagnostic anywhere.

      Parameters:
      symbol - any symbol, or null
      Returns:
      the group name, "" when ungrouped, or null when this is not a test
    • declaredExitCodeOf

      public static String declaredExitCodeOf(ISymbol symbol)
      The exit code a test DECLARED with "EXIT:<n>", or null when it declared none.

      For callers rewriting a directive line: they must carry a declared expectation over, and must not invent one (EXIT:0 written where nothing was declared is a change of text, if not of meaning). Callers deciding a test's outcome use expectedExitCodeOf(ISymbol).

      Parameters:
      symbol - any symbol, or null
      Returns:
      the declared code as its decimal text, or null
    • expectedExitCodeOf

      public static int expectedExitCodeOf(ISymbol symbol)
      The exit code a test program must end with for the test to pass: the one it declared with "EXIT:<n>", else 0.
      Parameters:
      symbol - any symbol, or null
      Returns:
      the expected exit code, 0 when none was declared