Discovers @tag spec:, @moduletag spec:, and @describetag spec:
values in ExUnit files.
Literal ids are recorded. A non-literal value is never guessed: it
becomes a dynamic entry instead. fold_to_subjects/1 lifts requirement
ids to their parent subject ids for the detector.
Summary
Functions
Folds requirement-id tags up to parent subject ids.
Scans each directory in paths for *_test.exs files and aggregates results.
Scans a single test file and returns its @tag spec: occurrences.
Types
@type dynamic_entry() :: %{ file: String.t(), line: non_neg_integer(), test_name: String.t() | nil }
@type tag_entry() :: %{ id: String.t(), file: String.t(), line: non_neg_integer(), test_line: non_neg_integer(), test_name: String.t() | nil }
Functions
@spec fold_to_subjects([tag_entry()]) :: [tag_entry()]
@spec fold_to_subjects(%{required(String.t()) => [tag_entry()]}) :: %{ required(String.t()) => [tag_entry()] }
Folds requirement-id tags up to parent subject ids.
A tag ancora.parsing.tag_discovery also attributes its file to
ancora.parsing. The detector uses the folded entries to attribute
test files to subjects. Non-literal tags never enter this list.
@spec scan( [String.t()], keyword() ) :: {:ok, %{required(String.t()) => [tag_entry()]}, [parse_error()], [dynamic_entry()]}
Scans each directory in paths for *_test.exs files and aggregates results.
Returns {:ok, tag_map, parse_errors, dynamic_entries} where:
tag_mapis%{requirement_id => [tag_entry]}parse_errorsis a list of%{file, reason}for unparseable filesdynamic_entriesis a list of annotations whose value could not be resolved to a literal
@spec scan_file( String.t(), keyword() ) :: {:ok, [tag_entry()]} | {:ok, [tag_entry()], [dynamic_entry()]} | {:error, term()}
Scans a single test file and returns its @tag spec: occurrences.
Returns {:ok, tags} on a parseable file, or {:ok, tags, dynamic} when
:include_dynamic is true. Returns {:error, reason} when the file cannot be
parsed as Elixir.