Skip to content

Test Tag Taxonomy and Execution Guide

Radixor uses JUnit tags as an explicit execution policy for its test suite.

The project uses three orthogonal axes:

  1. Scope (how the test is executed in the pipeline)
  2. Domain (where in the system it belongs)
  3. Intent (what behavior it verifies)

Canonical scope tags

Tag Description Typical usage
unit Fast, deterministic tests that exercise a specific class or behavior without external processes. Default developer feedback; should stay near-zero flakiness and low run time.
integration Tests that span multiple components or end-to-end flows of the public pipeline. Parser/loader/CLI/IO integration checks and multi-step compile-then-load validations.
property Property-based tests with generator-driven coverage for invariants. Semantics-preserving laws and edge-case exploration beyond curated fixtures.
fuzz Randomized stress checks with bounded runtime. Heavier probabilistic verification of robustness and reduction invariants.
compat Backward/forward compatibility and reproducibility checks for persisted artifacts. Artifact fingerprints, deterministic rebuild, and regression fixtures.
slow Long-running or expensive tests that should not execute in every fast gate. Heavy fuzz/property budgets or high-duration integration checks.

Canonical domain tags

Tag Description Typical usage
core Core algorithm and foundational platform behavior. Traversal direction, base data structures, low-level helpers.
trie All mutable/compiled trie behaviors and traversal internals. Lookup path selection, node shape, child representation, subtree behavior.
frequency-trie Algorithms and corner cases specific to frequency-aware trie logic. Ranking, weighted reductions, persistence of weighted nodes.
stemmer End-user stemming pipeline semantics. Parse-encode-apply flows and output invariants.
patch Patch encoding, decoding, and application semantics. PatchCommandEncoder behavior and related compatibility contracts.
io Input/output and resource loading boundaries. Filesystem readers, streams, and stream lifecycle handling.
serialization Binary persistence contract of compiled artifacts. Versioned format reads/writes and checksum/consistency checks.
parser Dictionary and metadata parsing concerns. Dictionary input parsing and malformed-source rejection.
cli Command-line entrypoint and command orchestration behavior. Compile CLI integration and CLI argument validation.
metadata Trie metadata semantics, compatibility fields, and schema expectations. Version flags, structural properties, and metadata round-trips.
compile Compile-time pipeline and build-oriented behavior. Building, reduction-mode behavior, and compiled artifact generation.
diacritic Unicode diacritic normalization and stripping behavior. Accent-removal correctness and locale-safe normalization checks.

Canonical intent tags

Tag Description Typical usage
construction Tests around construction and assembly of runtime structures. Builders, loaders, and compile-time object construction contracts.
lookup Read behavior and retrieval semantics. get(), getAll(), traversal and missing-key behavior.
persistence Storage lifecycle semantics. Serialization/deserialization and round-trip correctness.
reduction Reduction algorithm correctness and corner cases. Dominance threshold, subtree deduplication, rank-preservation invariants.
encoding Encoding transformation direction. PatchCommandEncoder.encode and serialized command form generation.
decoding Decoding/interpretation of persisted or runtime commands. Optional consumers that parse and apply encoded command payloads.
apply Patch application and transformation behavior. Verifies that applied patches produce expected derived forms.
normalization Canonicalization and cleanup behavior. String normalization around case/shape and mirrored input paths.
validation Input rejection and defensive checks. Null/empty/invalid contracts and explicit failure conditions.
regression Guard tests for behavior changes over time. Known historical bugs and behavioral drift prevention.
determinism Repeatable results under fixed input and settings. Compile determinism, stable ordering, and artifact reproducibility.
error-handling Exception surface and robustness expectations. Recovery/failure modes and diagnostics quality.

Class-level rules

  1. Every test class has exactly one scope tag.
  2. Every test class has at least one domain tag.
  3. Additional tags describe intent and may be used on classes or nested tests.
  4. For each test class, intent tags should reflect the primary behavior under test, not historical naming conventions.

Governance and execution policy

The following rules are used to keep the suite auditable and stable:

Rule Required state Why
Scope discipline Exactly one scope tag per class. Prevents accidental promotion of integration-only behavior into fast unit runs.
Coverage breadth At least one domain tag per class. Ensures tests can be grouped by subsystem for targeted review.
Intent specificity Use at least one intent tag when behavior is non-trivial. Makes failure triage faster and profile composition explicit.
Runtime policy Never run slow tests in the default unit profile unless explicitly required. Preserves turnaround for PR feedback while preserving deep checks.
Change risk Any persistence or compatibility-affecting change must include compat in validation. Protects long-lived binary artifact contracts.
Mutation resistance fuzz/property sets should be gated to dedicated profiles. Limits flakiness exposure and controls CI resource cost.

Suggested CI profiles

These are recommended launch profiles for local and CI usage and are also exposed as Gradle tasks:

  • Profile: ci-smoke (fast feedback):
./gradlew test -DincludeTags=unit -DexcludeTags=slow
./gradlew ciSmoke

ciSmoke also excludes org.egothor.stemmer.CompileIntegrationTest* at test-name filter level as a defensive fallback in case of future tag drift. ciRelease also excludes org.egothor.stemmer.StemmerPatchTrieLoaderTest$BundledDictionaryTests* at filter level.

  • Profile: ci-core (core behavioral coverage):
./gradlew test -DincludeTags=unit,trie,frequency-trie,property
./gradlew ciCore
  • Profile: ci-integration (pipeline correctness):
./gradlew test -DincludeTags=integration
./gradlew ciIntegration
  • Profile: ci-slow (explicit heavy validation):
./gradlew ciSlow
  • Profile: ci-compat (artifact stability):
./gradlew test -DincludeTags=compat,regression
./gradlew ciCompat
  • Profile: ci-release (strong confidence before release):

./gradlew test -DexcludeTags=slow
./gradlew ciRelease
ciRelease is non-slow by policy and uses the same defensive name-based exclusion for org.egothor.stemmer.CompileIntegrationTest* and org.egothor.stemmer.StemmerPatchTrieLoaderTest$BundledDictionaryTests* in addition to tag filtering.

  • Profile: ci-nightly (extended hardening):
./gradlew test -DincludeTags=fuzz
./gradlew ciNightly
  • Profile: ci (enterprise umbrella):
./gradlew ci

ci and ciRelease intentionally do not include slow paths. Run ciSlow explicitly for production-dictionary stress and long-running corpus checks.

Practical examples

All examples use Gradle with JUnit Platform integration:

  • Default fast test run:
./gradlew test

The default test task excludes slow tests. Supplying -DincludeTags or -PincludeTags still excludes slow unless the include expression contains slow; supplying an explicit exclude expression replaces the default. Long-running bundled-dictionary compilation and full-language loading checks therefore run only through an explicit tag expression such as -DincludeTags=slow or a dedicated profile such as ciSlow.

  • Only unit tests:
./gradlew test -DincludeTags=unit
  • Integration tests only:
./gradlew test -DincludeTags=integration -DexcludeTags=slow
  • Only trie subsystem tests:
./gradlew test -DincludeTags=trie
  • Deterministic fuzz checks:
./gradlew test -DincludeTags=fuzz
  • Property tests:
./gradlew test -DincludeTags=property
  • Stemmer + patch command behavior:
./gradlew test -DincludeTags=stemmer,patch
  • Compatibility artifacts and regression checks:
./gradlew test -DincludeTags=compat
  • Keep regression suite and remove long-running cases:
./gradlew test -DincludeTags=regression -DexcludeTags=slow
  • Core + patch behavior:
./gradlew test -DincludeTags=trie,patch
  • Deterministic compatibility and persistence checks:
./gradlew test -DincludeTags=compat,determinism,serialization

Notes

  • -DincludeTags and -DexcludeTags are interpreted by Gradle task filtering and forwarded into JUnit tag filtering.
  • Class-name filtering is also available via Gradle test selectors where needed (for example, --tests *CompileTest), but tag filtering remains the default execution strategy.
  • -DincludeTags supports comma-separated literal tags. When you need a single exact tag with special characters, quote the argument for the shell.