diff --git a/src/test/java/org/egothor/stemmer/CompileIntegrationTest.java b/src/test/java/org/egothor/stemmer/CompileIntegrationTest.java index 48bd5ba..f138261 100644 --- a/src/test/java/org/egothor/stemmer/CompileIntegrationTest.java +++ b/src/test/java/org/egothor/stemmer/CompileIntegrationTest.java @@ -96,6 +96,7 @@ import org.junit.jupiter.params.provider.MethodSource; @Tag("cli") @Tag("stemmer") @Tag("compile") +@Tag("construction") @Tag("slow") @TestInstance(TestInstance.Lifecycle.PER_CLASS) @DisplayName("Compile integration") @@ -184,6 +185,11 @@ final class CompileIntegrationTest { @Nested @DisplayName("Remark-aware fixture workflow") + @Tag("integration") + @Tag("cli") + @Tag("stemmer") + @Tag("compile") + @Tag("construction") final class RemarkAwareFixtureWorkflow { /** @@ -306,6 +312,11 @@ final class CompileIntegrationTest { @Nested @DisplayName("Bundled project dictionary workflows") @Tag("slow") + @Tag("integration") + @Tag("cli") + @Tag("stemmer") + @Tag("compile") + @Tag("construction") final class BundledProjectDictionaryWorkflows { /** diff --git a/src/test/java/org/egothor/stemmer/CompileTest.java b/src/test/java/org/egothor/stemmer/CompileTest.java index 0069457..2eb6571 100644 --- a/src/test/java/org/egothor/stemmer/CompileTest.java +++ b/src/test/java/org/egothor/stemmer/CompileTest.java @@ -70,6 +70,7 @@ import org.junit.jupiter.api.io.TempDir; @Tag("cli") @Tag("compile") @Tag("stemmer") +@Tag("construction") @DisplayName("Compile") class CompileTest { @@ -178,6 +179,11 @@ class CompileTest { @Nested @DisplayName("argument validation") + @Tag("integration") + @Tag("cli") + @Tag("compile") + @Tag("stemmer") + @Tag("validation") class ArgumentValidationTest { @Test diff --git a/src/test/java/org/egothor/stemmer/DiacriticStripperTest.java b/src/test/java/org/egothor/stemmer/DiacriticStripperTest.java index 6f87781..3655244 100644 --- a/src/test/java/org/egothor/stemmer/DiacriticStripperTest.java +++ b/src/test/java/org/egothor/stemmer/DiacriticStripperTest.java @@ -43,6 +43,7 @@ import org.junit.jupiter.api.Test; @Tag("unit") @Tag("diacritic") @Tag("stemmer") +@Tag("normalization") @DisplayName("DiacriticStripper") class DiacriticStripperTest { diff --git a/src/test/java/org/egothor/stemmer/FuzzStemmerAndTrieCompilationTest.java b/src/test/java/org/egothor/stemmer/FuzzStemmerAndTrieCompilationTest.java index a0e11b0..c7f6179 100644 --- a/src/test/java/org/egothor/stemmer/FuzzStemmerAndTrieCompilationTest.java +++ b/src/test/java/org/egothor/stemmer/FuzzStemmerAndTrieCompilationTest.java @@ -68,6 +68,7 @@ import org.junit.jupiter.api.io.TempDir; @Tag("fuzz") @Tag("trie") @Tag("stemmer") +@Tag("determinism") class FuzzStemmerAndTrieCompilationTest { /** diff --git a/src/test/java/org/egothor/stemmer/StemmerDictionaryParserTest.java b/src/test/java/org/egothor/stemmer/StemmerDictionaryParserTest.java index bbf9267..d49748b 100644 --- a/src/test/java/org/egothor/stemmer/StemmerDictionaryParserTest.java +++ b/src/test/java/org/egothor/stemmer/StemmerDictionaryParserTest.java @@ -76,6 +76,7 @@ import org.junit.jupiter.api.io.TempDir; @Tag("unit") @Tag("parser") @Tag("stemmer") +@Tag("validation") class StemmerDictionaryParserTest { /** @@ -98,6 +99,10 @@ class StemmerDictionaryParserTest { /** * Log handler capturing parser diagnostics for assertions. */ + @Tag("unit") + @Tag("parser") + @Tag("stemmer") + @Tag("validation") private static final class CapturedLogHandler extends Handler { /** @@ -158,6 +163,10 @@ class StemmerDictionaryParserTest { @Nested @DisplayName("parse(Reader, String, EntryHandler)") + @Tag("unit") + @Tag("parser") + @Tag("stemmer") + @Tag("validation") class ReaderParsingTests { @Test @@ -334,6 +343,10 @@ class StemmerDictionaryParserTest { @Nested @DisplayName("parse(Path, EntryHandler) and parse(String, EntryHandler)") + @Tag("unit") + @Tag("parser") + @Tag("stemmer") + @Tag("validation") class FileParsingTests { @Test @@ -424,6 +437,10 @@ class StemmerDictionaryParserTest { @Nested @DisplayName("ParseStatistics") + @Tag("unit") + @Tag("parser") + @Tag("stemmer") + @Tag("validation") class ParseStatisticsTests { @Test diff --git a/src/test/java/org/egothor/stemmer/StemmerKnowledgeExperimentTest.java b/src/test/java/org/egothor/stemmer/StemmerKnowledgeExperimentTest.java index ad2031f..b16b353 100644 --- a/src/test/java/org/egothor/stemmer/StemmerKnowledgeExperimentTest.java +++ b/src/test/java/org/egothor/stemmer/StemmerKnowledgeExperimentTest.java @@ -57,6 +57,7 @@ import org.junit.jupiter.api.io.TempDir; @Tag("integration") @Tag("stemmer") @Tag("trie") +@Tag("reduction") final class StemmerKnowledgeExperimentTest { /** diff --git a/src/test/java/org/egothor/stemmer/StemmerPatchTrieBinaryIOTest.java b/src/test/java/org/egothor/stemmer/StemmerPatchTrieBinaryIOTest.java index bbcca83..1abcc4c 100644 --- a/src/test/java/org/egothor/stemmer/StemmerPatchTrieBinaryIOTest.java +++ b/src/test/java/org/egothor/stemmer/StemmerPatchTrieBinaryIOTest.java @@ -134,6 +134,10 @@ class StemmerPatchTrieBinaryIOTest { */ @Nested @DisplayName("write(...)") + @Tag("unit") + @Tag("io") + @Tag("trie") + @Tag("persistence") class WriteTests { /** @@ -290,6 +294,10 @@ class StemmerPatchTrieBinaryIOTest { */ @Nested @DisplayName("read(...)") + @Tag("unit") + @Tag("io") + @Tag("trie") + @Tag("persistence") class ReadTests { /** @@ -633,6 +641,10 @@ class StemmerPatchTrieBinaryIOTest { /** * Output stream that records whether it has been closed. */ + @Tag("unit") + @Tag("io") + @Tag("trie") + @Tag("persistence") private static final class TrackingOutputStream extends ByteArrayOutputStream { /** @@ -659,6 +671,10 @@ class StemmerPatchTrieBinaryIOTest { /** * Input stream that records whether it has been closed. */ + @Tag("unit") + @Tag("io") + @Tag("trie") + @Tag("persistence") private static final class TrackingInputStream extends ByteArrayInputStream { /** diff --git a/src/test/java/org/egothor/stemmer/StemmerPatchTrieLoaderTest.java b/src/test/java/org/egothor/stemmer/StemmerPatchTrieLoaderTest.java index 6778214..22e0fa8 100644 --- a/src/test/java/org/egothor/stemmer/StemmerPatchTrieLoaderTest.java +++ b/src/test/java/org/egothor/stemmer/StemmerPatchTrieLoaderTest.java @@ -89,6 +89,8 @@ import org.junit.jupiter.params.provider.MethodSource; @Tag("stemmer") @Tag("io") @Tag("parser") +@Tag("trie") +@Tag("persistence") @TestInstance(TestInstance.Lifecycle.PER_CLASS) final class StemmerPatchTrieLoaderTest { @@ -266,6 +268,9 @@ final class StemmerPatchTrieLoaderTest { */ @Nested @DisplayName("API contracts") + @Tag("validation") + @Tag("integration") + @Tag("trie") final class ApiContractTests { /** @@ -314,11 +319,83 @@ final class StemmerPatchTrieLoaderTest { } } + /** + * Focused internal loader behavior tests. + */ + @Nested + @DisplayName("Internal helper behavior") + @Tag("construction") + @Tag("integration") + @Tag("trie") + final class InternalLoaderBehaviorTests { + + /** + * Verifies that bundled language loading follows explicit + * right-to-left metadata mapping. + */ + @Test + @DisplayName("bundled language loading must infer traversal direction from language metadata") + void shouldLoadBundledLanguagesUsingLanguageRightToLeftMetadata() throws IOException { + final ReductionSettings settings = ReductionSettings.withDefaults(DEFAULT_REDUCTION_MODE); + final FrequencyTrie leftToRightDictionary = StemmerPatchTrieLoader.load( + StemmerPatchTrieLoader.Language.US_UK, true, settings); + final FrequencyTrie rightToLeftDictionary = StemmerPatchTrieLoader.load( + StemmerPatchTrieLoader.Language.FA_IR, true, settings); + + assertEquals(WordTraversalDirection.BACKWARD, leftToRightDictionary.traversalDirection(), + "Left-to-right languages should use backward traversal."); + assertEquals(WordTraversalDirection.FORWARD, rightToLeftDictionary.traversalDirection(), + "Right-to-left languages should use forward traversal."); + } + + /** + * Verifies the mode-based and settings-based bundled load overloads remain + * semantically consistent for traversal direction. + */ + @Test + @DisplayName("load(Language,.., ReductionMode) and load(Language,.., ReductionSettings) should agree on traversal direction") + void shouldKeepBundledTraversalDirectionConsistentAcrossOverloads() throws IOException { + final FrequencyTrie byMode = StemmerPatchTrieLoader.load( + StemmerPatchTrieLoader.Language.US_UK, true, DEFAULT_REDUCTION_MODE); + final FrequencyTrie bySettings = StemmerPatchTrieLoader.load( + StemmerPatchTrieLoader.Language.US_UK, true, + ReductionSettings.withDefaults(DEFAULT_REDUCTION_MODE)); + + assertEquals(byMode.traversalDirection(), bySettings.traversalDirection()); + assertEquals(byMode.metadata().reductionSettings().reductionMode(), + bySettings.metadata().reductionSettings().reductionMode()); + } + + /** + * Verifies bundled resource access succeeds for known resources and fails + * for unknown resources. + */ + @Test + @DisplayName("openBundledResource should return readable streams for known resources and fail for unknown ones") + void shouldOpenBundledResourcesSuccessfullyAndRejectUnknowns() throws IOException { + final String resourcePath = StemmerPatchTrieLoader.Language.US_UK.resourcePath(); + try (InputStream inputStream = StemmerPatchTrieLoader.openBundledResource(resourcePath); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { + assertNotNull(reader.readLine(), "Known bundled resource must expose readable content."); + } + + final String missingResource = "org/egothor/stemmer/missing-dictionary.dict.gz"; + final IOException exception = assertThrows(IOException.class, + () -> StemmerPatchTrieLoader.openBundledResource(missingResource)); + + assertEquals("Stemmer resource not found: " + missingResource, exception.getMessage()); + } + } + /** * Focused filesystem and parser behavior tests. */ @Nested @DisplayName("Filesystem and parser behavior") + @Tag("io") + @Tag("construction") + @Tag("integration") + @Tag("trie") final class FilesystemAndParserTests { /** @@ -578,6 +655,9 @@ final class StemmerPatchTrieLoaderTest { @Nested @Tag("slow") @DisplayName("Bundled dictionaries") + @Tag("compat") + @Tag("trie") + @Tag("regression") final class BundledDictionaryTests { /** diff --git a/src/test/java/org/egothor/stemmer/TrieMetadataTest.java b/src/test/java/org/egothor/stemmer/TrieMetadataTest.java index 6d0dfb8..a33dcaf 100644 --- a/src/test/java/org/egothor/stemmer/TrieMetadataTest.java +++ b/src/test/java/org/egothor/stemmer/TrieMetadataTest.java @@ -42,6 +42,7 @@ import org.junit.jupiter.api.Test; @Tag("unit") @Tag("metadata") @Tag("trie") +@Tag("validation") @DisplayName("TrieMetadata") class TrieMetadataTest { diff --git a/src/test/java/org/egothor/stemmer/WordTraversalDirectionTest.java b/src/test/java/org/egothor/stemmer/WordTraversalDirectionTest.java index 1c8d376..40ee79e 100644 --- a/src/test/java/org/egothor/stemmer/WordTraversalDirectionTest.java +++ b/src/test/java/org/egothor/stemmer/WordTraversalDirectionTest.java @@ -42,6 +42,7 @@ import org.junit.jupiter.api.Test; @Tag("unit") @Tag("core") @Tag("stemmer") +@Tag("validation") @DisplayName("WordTraversalDirection") class WordTraversalDirectionTest { diff --git a/src/test/java/org/egothor/stemmer/trie/ChildDescriptorTest.java b/src/test/java/org/egothor/stemmer/trie/ChildDescriptorTest.java index ee4c394..7d58d44 100644 --- a/src/test/java/org/egothor/stemmer/trie/ChildDescriptorTest.java +++ b/src/test/java/org/egothor/stemmer/trie/ChildDescriptorTest.java @@ -46,6 +46,7 @@ import org.junit.jupiter.api.Test; */ @Tag("unit") @Tag("trie") +@Tag("reduction") @DisplayName("ChildDescriptor") class ChildDescriptorTest { diff --git a/src/test/java/org/egothor/stemmer/trie/DominantLocalDescriptorTest.java b/src/test/java/org/egothor/stemmer/trie/DominantLocalDescriptorTest.java index df9855f..f1b2e98 100644 --- a/src/test/java/org/egothor/stemmer/trie/DominantLocalDescriptorTest.java +++ b/src/test/java/org/egothor/stemmer/trie/DominantLocalDescriptorTest.java @@ -42,6 +42,7 @@ import org.junit.jupiter.api.Test; */ @Tag("unit") @Tag("trie") +@Tag("reduction") @DisplayName("DominantLocalDescriptor") class DominantLocalDescriptorTest { diff --git a/src/test/java/org/egothor/stemmer/trie/LocalValueSummaryTest.java b/src/test/java/org/egothor/stemmer/trie/LocalValueSummaryTest.java index 3c9e3bb..a665513 100644 --- a/src/test/java/org/egothor/stemmer/trie/LocalValueSummaryTest.java +++ b/src/test/java/org/egothor/stemmer/trie/LocalValueSummaryTest.java @@ -51,6 +51,7 @@ import org.junit.jupiter.api.Test; */ @Tag("unit") @Tag("trie") +@Tag("reduction") @DisplayName("LocalValueSummary") class LocalValueSummaryTest { @@ -190,6 +191,9 @@ class LocalValueSummaryTest { /** * Test helper with identical textual form but distinct identity. */ + @Tag("unit") + @Tag("trie") + @Tag("reduction") private static final class TextTwin { /** diff --git a/src/test/java/org/egothor/stemmer/trie/MutableNodeTest.java b/src/test/java/org/egothor/stemmer/trie/MutableNodeTest.java index c936e4d..828e204 100644 --- a/src/test/java/org/egothor/stemmer/trie/MutableNodeTest.java +++ b/src/test/java/org/egothor/stemmer/trie/MutableNodeTest.java @@ -45,6 +45,7 @@ import org.junit.jupiter.api.Test; */ @Tag("unit") @Tag("trie") +@Tag("construction") @DisplayName("MutableNode") class MutableNodeTest { diff --git a/src/test/java/org/egothor/stemmer/trie/RankedLocalDescriptorTest.java b/src/test/java/org/egothor/stemmer/trie/RankedLocalDescriptorTest.java index 37abf38..fcba293 100644 --- a/src/test/java/org/egothor/stemmer/trie/RankedLocalDescriptorTest.java +++ b/src/test/java/org/egothor/stemmer/trie/RankedLocalDescriptorTest.java @@ -42,6 +42,7 @@ import org.junit.jupiter.api.Test; */ @Tag("unit") @Tag("trie") +@Tag("reduction") @DisplayName("RankedLocalDescriptor") class RankedLocalDescriptorTest { diff --git a/src/test/java/org/egothor/stemmer/trie/ReducedNodeTest.java b/src/test/java/org/egothor/stemmer/trie/ReducedNodeTest.java index 976cb54..34d5221 100644 --- a/src/test/java/org/egothor/stemmer/trie/ReducedNodeTest.java +++ b/src/test/java/org/egothor/stemmer/trie/ReducedNodeTest.java @@ -49,6 +49,7 @@ import org.junit.jupiter.api.Test; */ @Tag("unit") @Tag("trie") +@Tag("reduction") @DisplayName("ReducedNode") class ReducedNodeTest { diff --git a/src/test/java/org/egothor/stemmer/trie/ReductionContextTest.java b/src/test/java/org/egothor/stemmer/trie/ReductionContextTest.java index bff1c75..09808b2 100644 --- a/src/test/java/org/egothor/stemmer/trie/ReductionContextTest.java +++ b/src/test/java/org/egothor/stemmer/trie/ReductionContextTest.java @@ -48,6 +48,7 @@ import org.junit.jupiter.api.Test; */ @Tag("unit") @Tag("trie") +@Tag("reduction") @DisplayName("ReductionContext") class ReductionContextTest { diff --git a/src/test/java/org/egothor/stemmer/trie/ReductionSignatureTest.java b/src/test/java/org/egothor/stemmer/trie/ReductionSignatureTest.java index 676f354..dfdf3c5 100644 --- a/src/test/java/org/egothor/stemmer/trie/ReductionSignatureTest.java +++ b/src/test/java/org/egothor/stemmer/trie/ReductionSignatureTest.java @@ -47,6 +47,7 @@ import org.junit.jupiter.api.Test; */ @Tag("unit") @Tag("trie") +@Tag("reduction") @DisplayName("ReductionSignature") class ReductionSignatureTest { diff --git a/src/test/java/org/egothor/stemmer/trie/UnorderedLocalDescriptorTest.java b/src/test/java/org/egothor/stemmer/trie/UnorderedLocalDescriptorTest.java index f2bdf0a..c940b90 100644 --- a/src/test/java/org/egothor/stemmer/trie/UnorderedLocalDescriptorTest.java +++ b/src/test/java/org/egothor/stemmer/trie/UnorderedLocalDescriptorTest.java @@ -42,6 +42,7 @@ import org.junit.jupiter.api.Test; */ @Tag("unit") @Tag("trie") +@Tag("reduction") @DisplayName("UnorderedLocalDescriptor") class UnorderedLocalDescriptorTest {