Packages
Multi-surface application runtime for Elixir. One TEA module renders to terminal, browser (LiveView), SSH, and MCP (agents). 30+ widgets, flexbox + CSS grid, AI agent runtime, distributed swarm with CRDTs, time-travel debugging, session recording, sandboxed REPL, and agentic commerce.
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/raxol.test.ex
defmodule Mix.Tasks.Raxol.Test do
@moduledoc """
Enhanced test runner for the Raxol project.
Provides additional test running capabilities beyond the standard mix test,
including parallel execution, flaky test detection, and coverage reporting.
## Usage
mix raxol.test [OPTIONS] [FILES]
## Options
* `--parallel` - Run tests in parallel
* `--coverage` - Generate coverage report
* `--failed` - Run only previously failed tests
* `--watch` - Watch files and re-run tests on changes
* `--profile` - Profile test execution time
* `--max-failures N` - Stop after N failures
* `--seed N` - Set random seed
* `--only TAG` - Run only tests with specific tag
* `--exclude TAG` - Exclude tests with specific tag
## Examples
# Run all tests
mix raxol.test
# Run tests with coverage
mix raxol.test --coverage
# Run only failed tests
mix raxol.test --failed
# Run specific test file
mix raxol.test test/raxol/terminal/emulator_test.exs
# Run tests in parallel
mix raxol.test --parallel
"""
use Mix.Task
require Logger
@shortdoc "Enhanced test runner with additional features"
@impl Mix.Task
def run(args) do
{opts, files, _} =
OptionParser.parse(args,
strict: [
parallel: :boolean,
coverage: :boolean,
failed: :boolean,
watch: :boolean,
profile: :boolean,
max_failures: :integer,
seed: :integer,
only: :string,
exclude: :string
]
)
# Set environment variables
System.put_env("TMPDIR", "/tmp")
System.put_env("SKIP_TERMBOX2_TESTS", "true")
System.put_env("MIX_ENV", "test")
cond do
opts[:watch] ->
run_watch_mode(opts, files)
opts[:parallel] ->
run_parallel_tests(opts, files)
opts[:coverage] ->
run_with_coverage(opts, files)
opts[:profile] ->
run_with_profiling(opts, files)
true ->
run_standard_tests(opts, files)
end
end
defp run_standard_tests(opts, files) do
Mix.shell().info("Running Raxol test suite...")
test_args = build_test_args(opts, files)
case Mix.shell().cmd("mix test #{Enum.join(test_args, " ")}") do
0 ->
Mix.shell().info("[OK] All tests passed")
_ ->
Mix.shell().error("[FAIL] Some tests failed")
Mix.raise("Test suite failed")
end
end
defp run_parallel_tests(opts, files) do
Mix.shell().info("Running tests in parallel...")
# Get number of cores
cores = System.schedulers_online()
Mix.shell().info("Using #{cores} parallel processes")
test_args = build_test_args(opts, files)
# Add partitions for parallel execution
test_args = test_args ++ ["--partitions", "#{cores}"]
case Mix.shell().cmd("mix test #{Enum.join(test_args, " ")}") do
0 ->
Mix.shell().info("[OK] All tests passed")
_ ->
Mix.shell().error("[FAIL] Some tests failed")
Mix.raise("Test suite failed")
end
end
defp run_with_coverage(opts, files) do
Mix.shell().info("Running tests with coverage...")
test_args = build_test_args(opts, files)
test_args = ["--cover" | test_args]
case Mix.shell().cmd("mix test #{Enum.join(test_args, " ")}") do
0 ->
Mix.shell().info("[OK] All tests passed")
show_coverage_summary()
_ ->
Mix.shell().error("[FAIL] Some tests failed")
show_coverage_summary()
Mix.raise("Test suite failed")
end
end
defp run_with_profiling(opts, files) do
Mix.shell().info("Running tests with profiling...")
start_time = System.monotonic_time(:millisecond)
test_args = build_test_args(opts, files)
test_args = ["--profile-tests" | test_args]
result = Mix.shell().cmd("mix test #{Enum.join(test_args, " ")}")
end_time = System.monotonic_time(:millisecond)
duration = end_time - start_time
Mix.shell().info("")
Mix.shell().info("Test execution time: #{duration}ms")
case result do
0 ->
Mix.shell().info("[OK] All tests passed")
_ ->
Mix.shell().error("[FAIL] Some tests failed")
Mix.raise("Test suite failed")
end
end
defp run_watch_mode(opts, files) do
Mix.shell().info("Starting test watcher...")
Mix.shell().info("Press Ctrl+C to stop")
test_args = build_test_args(opts, files)
# Initial run
Mix.shell().cmd("mix test #{Enum.join(test_args, " ")}")
# Start file watcher
{:ok, pid} = FileSystem.start_link(dirs: ["lib", "test"])
FileSystem.subscribe(pid)
watch_loop(test_args)
end
defp watch_loop(test_args) do
receive do
{:file_event, _pid, {path, _events}} ->
if String.ends_with?(path, ".ex") or String.ends_with?(path, ".exs") do
Mix.shell().info("\nFile changed: #{path}")
Mix.shell().info("Re-running tests...")
Mix.shell().cmd("mix test #{Enum.join(test_args, " ")}")
end
watch_loop(test_args)
_ ->
watch_loop(test_args)
end
end
defp build_test_args(opts, files) do
args = []
# Add exclusions for slow tests by default
args = [
"--exclude",
"slow",
"--exclude",
"integration",
"--exclude",
"docker" | args
]
# Add options
args = if opts[:failed], do: ["--failed" | args], else: args
args =
if opts[:max_failures],
do: ["--max-failures", "#{opts[:max_failures]}" | args],
else: args
args = if opts[:seed], do: ["--seed", "#{opts[:seed]}" | args], else: args
args = if opts[:only], do: ["--only", opts[:only] | args], else: args
args =
if opts[:exclude] && opts[:exclude] != "",
do: ["--exclude", opts[:exclude] | args],
else: args
# Add files
args ++ files
end
defp show_coverage_summary do
Mix.shell().info("")
Mix.shell().info("Coverage Summary:")
Mix.shell().info("-----------------")
# Read coverage data if available
cover_dir = "cover"
if File.exists?(cover_dir) do
# This would parse and display coverage data
Mix.shell().info("Coverage report generated in #{cover_dir}/")
else
Mix.shell().info("No coverage data available")
end
end
end