MixAffected.TestMap.Ast (MixAffected v0.1.0)

Copy Markdown View Source

Reads a test file's source without compiling it: the modules it names (the fallback when requiring the file fails) and its affected_by globs.

Alias resolution is file-wide, not lexical: alias A.B anywhere makes every B... reference resolve to A.B..., and the unaliased reading is kept too. Over-matching only selects more tests; it never drops one.

Summary

Functions

The globs of every literal @moduletag affected_by: ["glob", ...] (also among other tags in the same keyword list) and @affected_by ["glob", ...] in the source; [] if none or unparseable.

Whether the source defines a protocol impl (defimpl); true if it does not parse.

The root-relative globs of the files a test reads from the repo itself (a source-scanning guard reading lib/**, a doc test reading guides/*.md, a Dockerfile test), found without compiling it: [] when the source calls no file-reading or -listing function (File.read!/1, Path.wildcard/1, ... or @external_resource), else a glob per path literal anywhere in the file whose first segment is one of top_level (the entries of the project root). A literal names a file or a directory, so "lib/app" reads lib/app and lib/app/**; literal globs are kept.

Every module the source names through an alias (Foo.Bar), resolving alias, alias ..., as: and alias Foo.{A, B}. :error if it does not parse.

Functions

affected_by(source)

@spec affected_by(String.t()) :: [String.t()]

The globs of every literal @moduletag affected_by: ["glob", ...] (also among other tags in the same keyword list) and @affected_by ["glob", ...] in the source; [] if none or unparseable.

defimpl?(source)

@spec defimpl?(String.t()) :: boolean()

Whether the source defines a protocol impl (defimpl); true if it does not parse.

reads(source, test_file, top_level)

@spec reads(String.t(), String.t(), [String.t()]) :: [String.t()]

The root-relative globs of the files a test reads from the repo itself (a source-scanning guard reading lib/**, a doc test reading guides/*.md, a Dockerfile test), found without compiling it: [] when the source calls no file-reading or -listing function (File.read!/1, Path.wildcard/1, ... or @external_resource), else a glob per path literal anywhere in the file whose first segment is one of top_level (the entries of the project root). A literal names a file or a directory, so "lib/app" reads lib/app and lib/app/**; literal globs are kept.

Path.join/1,2 of leading literals reads the joined path, Path.expand/2 and Path.join/2 from __DIR__ resolve against test_file's directory, :code.priv_dir/1 is priv, ~w words and the literal parts of an interpolation ("#{File.cwd!()}/guides/x.md") count as literals, other sigils (regexes) do not.

["**"] (reads everything) when the reads cannot be pinned to the repo: the source does not parse, it reads but no literal names a repo path, a reading call's literal argument is not anchored at the root (".", "**/*.ex", a relative "fixtures/x.json"), it lists the working directory (File.ls!/0, File.cwd!()), a __DIR__ path leaves the root, or a literal glob does not compile (MixAffected.Glob.compile/1) or uses a [...] character class MixAffected.Glob does not implement, so selection never matches against it.

references(source)

@spec references(String.t()) :: {:ok, [module()]} | :error

Every module the source names through an alias (Foo.Bar), resolving alias, alias ..., as: and alias Foo.{A, B}. :error if it does not parse.