defmodule Mix.Tasks.Test.Seq do @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 """ use Mix.Task @shortdoc "Run tests sequentially, one file at a time" 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("\nFailed files: #{Enum.join(failed_files, ", ")}") Mix.raise("#{length(failed_files)} test file(s) failed") else IO.puts("\nāœ“ All tests passed!") 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, 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 defp find_test_files do Path.wildcard("test/**/*_test.exs") |> Enum.sort() end end