Current section

Files

Jump to
xqlite lib mix tasks test_seq.ex
Raw

lib/mix/tasks/test_seq.ex

defmodule Mix.Tasks.Test.Seq do
@shortdoc "Run tests sequentially, one file at a time"
@moduledoc """
Runs all test files sequentially, each in its own OS process.
This avoids SQLite's global VFS contention that causes spurious
"out of memory" errors when test files run in parallel.
## Usage
mix test.seq
mix test.seq --trace
mix test.seq --cover # per-file .coverdata exports under cover/
With `--cover`, each file's OS process exports a distinctly named
`.coverdata` (derived from the file path), so runs don't overwrite
each other. Merge and publish afterwards, e.g.
`mix coveralls.github --import-cover cover test/xqlite_test.exs`.
"""
use Mix.Task
def run(args) do
test_files = find_test_files()
IO.puts("Found #{length(test_files)} test files")
IO.puts("Running tests sequentially...")
failed_files = run_test_files(test_files, args, [])
if failed_files == [] do
IO.puts("\n✓ All tests passed!")
else
IO.puts("\nFailed files: #{Enum.join(failed_files, ", ")}")
Mix.raise("#{length(failed_files)} test file(s) failed")
end
end
defp run_test_files([], _args, failed_files), do: failed_files
defp run_test_files([file | rest], args, failed_files) do
IO.puts("\n=== Running #{file} ===")
case System.cmd("mix", ["test", file] ++ args ++ coverage_args(args, file),
into: IO.stream()
) do
{_, 0} ->
IO.puts("✓ #{file} passed")
run_test_files(rest, args, failed_files)
{_, exit_code} ->
IO.puts("✗ #{file} failed (exit code: #{exit_code})")
run_test_files(rest, args, [file | failed_files])
end
end
# Distinct export names per file (full-path-derived — bare basenames
# collide, e.g. test/pragma_test.exs vs test/nif/pragma_test.exs).
defp coverage_args(args, file) do
if "--cover" in args do
["--export-coverage", String.replace(Path.rootname(file), ["/", "\\"], "_")]
else
[]
end
end
defp find_test_files do
Path.wildcard("test/**/*_test.exs")
|> Enum.sort()
end
end