Search documentation of Metastatic

# Metastatic.Test.AdapterHelper (Metastatic v0.13.2)

View Source

Testing utilities for language adapters (M1 ↔ M2 transformations).

Provides macros and helper functions to test adapter round-trip fidelity, validate conformance, and measure transformation quality.

## M1 ↔ M2 Round-Trip Testing

A round-trip test validates the following pipeline:

    Source → M1 → M2 → M1 → Source

Where:

- Source → M1: `adapter.parse/1`
- M1 → M2: `adapter.to_meta/1`
- M2 → M1: `adapter.from_meta/2`
- M1 → Source: `adapter.unparse/1`

## Fidelity Metrics

- Exact match: Source output matches input exactly
- Semantic match: ASTs match even if formatting differs
- Partial match: Core structure preserved but details lost

## Usage

    defmodule MyAdapterTest do
      use ExUnit.Case
      import Metastatic.Test.AdapterHelper

      test "round-trip simple arithmetic" do
        source = "x + 5"
        assert_round_trip(MyAdapter, source)
      end

      test "validates M2 conformance" do
        source = "x + 5"
        assert_valid_meta_ast(MyAdapter, source)
      end
    end

## Summary

### Functions

assert_round_trip(adapter, source, opts \\ [])

Assert that source code round-trips successfully through the adapter.

assert_valid_meta_ast(adapter, source, opts \\ [])

Assert that source code produces a valid MetaAST.

assert_valid_reification(adapter, meta_ast, metadata)

Assert that MetaAST can be reified back to source.

calculate_fidelity(original, result)

Calculate round-trip fidelity score.

load_fixtures(dir)

Load test fixtures from a directory.

measure_performance(adapter, source)

Measure adapter performance.

## Functions

# assert_round_trip(adapter, source, opts \\ [])

    @spec assert_round_trip(module(), String.t(), keyword()) :: :ok

Assert that source code round-trips successfully through the adapter.

Validates: Source → M1 → M2 → M1 → Source

## Options

- `:exact` - Require exact source match (default: `false`)
- `:semantic` - Allow formatting differences (default: `true`)

## Examples

    assert_round_trip(MyAdapter, "x + 5")
    assert_round_trip(MyAdapter, "x + 5", exact: true)

# assert_valid_meta_ast(adapter, source, opts \\ [])

    @spec assert_valid_meta_ast(module(), String.t(), keyword()) ::
      Metastatic.Document.t()

Assert that source code produces a valid MetaAST.

Validates: Source → M1 → M2 and checks M2 conformance.

## Options

- `:mode` - Validation mode (`:strict`, `:standard`, `:permissive`)
- `:max_depth` - Maximum allowed AST depth
- `:max_nodes` - Maximum allowed node count

## Examples

    assert_valid_meta_ast(MyAdapter, "x + 5")
    assert_valid_meta_ast(MyAdapter, "x + 5", mode: :strict)

# assert_valid_reification(adapter, meta_ast, metadata)

    @spec assert_valid_reification(module(), Metastatic.AST.meta_ast(), map()) ::
      String.t()

Assert that MetaAST can be reified back to source.

Validates: M2 → M1 → Source

## Examples

    ast = {:binary_op, :arithmetic, :+, {:variable, "x"}, {:literal, :integer, 5}}
    assert_valid_reification(MyAdapter, ast, %{})

# calculate_fidelity(original, result)

    @spec calculate_fidelity(String.t(), String.t()) :: float()

Calculate round-trip fidelity score.

Returns a percentage (0.0 to 100.0) representing how closely the round-trip output matches the input.

## Examples

    fidelity = calculate_fidelity(original, round_trip_result)
    # => 95.5

# load_fixtures(dir)

    @spec load_fixtures(String.t()) :: [{String.t(), String.t(), term() | nil}]

Load test fixtures from a directory.

Expects a directory structure like:

    fixtures/
    ├── python/
    │   ├── simple_arithmetic.py
    │   ├── list_comprehension.py
    │   └── expected/
    │       ├── simple_arithmetic.exs
    │       └── list_comprehension.exs

Returns a list of `{source_file, source_code, expected_meta_ast}` tuples.

## Examples

    fixtures = load_fixtures("test/fixtures/python")

# measure_performance(adapter, source)

    @spec measure_performance(module(), String.t()) :: map()

Measure adapter performance.

Returns timing information for each stage of the transformation pipeline.

## Examples

    metrics = measure_performance(MyAdapter, source)
    # => %{
    #   parse_time_us: 150,
    #   to_meta_time_us: 200,
    #   from_meta_time_us: 180,
    #   unparse_time_us: 120,
    #   total_time_us: 650
    # }