KogenCredo.TestModuleInfo (KogenCredo v0.1.0)
View SourceReads ExUnit test modules out of an AST, for the async-safety checks.
For every defmodule that directly uses ExUnit.Case (or a configured
case template) it reports:
:mode::asyncor:serial, from the literalasync:option;:unknownwhen the option is not a literal boolean.:group: the literalgroup:option,:dynamicwhen it is not a literal atom,nilwhen absent.:serial_tags: atoms from@moduletag serial: atom;non_literal_reason/0stands forserial: true,serial: nilor any value that is not a literal atom.:accesses: global-state touches found anywhere in the module body (nesteddefmodules are scanned separately).:teardowns: calls that destroy or stop named, app-wide state.
Case templates
Modules that use anything other than ExUnit.Case are invisible unless
the template is declared. Declare templates as a map or keyword list from
module name to behaviour:
%{"MyApp.DataCase" => :inherit, "MyApp.AsyncCase" => :async, "MyApp.SerialCase" => :serial}:inherit: the template forwards its options touse ExUnit.Case, so theasync:andgroup:given at theusesite apply (default serial).:async/:serial: the template forces that mode regardless of options.
An undeclared template yields no module at all (the checks stay quiet) rather than a guess. The declaration is not verified against the template source.
Limits
Everything is syntactic. Only literal resource names are tracked (atoms,
strings and module attributes holding them). Calls in helper modules, macros
or quote blocks are not followed. Scope is lexical: a nested defmodule is a module of its
own (a nested test module has its own mode and group; a nested helper module's calls are not
charged to the enclosing test module). Pipes are expanded before classification, so
"p" |> File.write!(data) is a write to "p".
Summary
Functions
Module attributes with a literal binary or atom value.
Like literal/2, but module aliases also count (expanded name as a string).
Resolves a node to a literal binary/atom, through attributes and Path.join.
Marker found in serial_tags when the serial: value is not a literal reason atom.
Normalises a template declaration (map or keyword/tuple list) to a string-keyed map.
Parses test modules from ast. Options: :case_templates (see moduledoc).
Parses source text; invalid source yields [].
Types
@type access() :: %{ kind: atom(), key: String.t() | atom(), access: :read | :write, line: pos_integer(), call: String.t() }
Functions
Module attributes with a literal binary or atom value.
@spec global_name(Macro.t(), map(), KogenCredo.SafetySupport.aliases()) :: {:ok, String.t()} | :error
Like literal/2, but module aliases also count (expanded name as a string).
Resolves a node to a literal binary/atom, through attributes and Path.join.
@spec non_literal_reason() :: atom()
Marker found in serial_tags when the serial: value is not a literal reason atom.
@spec normalize_templates(map() | list()) :: %{ required(String.t()) => :async | :serial | :inherit }
Normalises a template declaration (map or keyword/tuple list) to a string-keyed map.
Parses test modules from ast. Options: :case_templates (see moduledoc).
Parses source text; invalid source yields [].