From 820dfd4965828083e73e729d5b78bb5e4c2fc44a Mon Sep 17 00:00:00 2001 From: bibonix Date: Fri, 8 May 2026 00:38:34 +0000 Subject: [PATCH 1/3] #554 reproduce: assert every motive file is referenced by a lint Add MotivesAreUsedTest, which walks every .md under src/main/resources/org/eolang/motives/ and fails if its filename (without .md) is not the name() of a lint produced by PkMono. Fails on master with the orphan motive files that #554 calls out. --- .../org/eolang/lints/MotivesAreUsedTest.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/test/java/org/eolang/lints/MotivesAreUsedTest.java diff --git a/src/test/java/org/eolang/lints/MotivesAreUsedTest.java b/src/test/java/org/eolang/lints/MotivesAreUsedTest.java new file mode 100644 index 000000000..6b88efca7 --- /dev/null +++ b/src/test/java/org/eolang/lints/MotivesAreUsedTest.java @@ -0,0 +1,72 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2016-2026 Objectionary.com + * SPDX-License-Identifier: MIT + */ +package org.eolang.lints; + +import com.jcabi.log.Logger; +import io.github.secretx33.resourceresolver.PathMatchingResourcePatternResolver; +import io.github.secretx33.resourceresolver.Resource; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +/** + * Test that ensures every motive markdown under + * {@code org/eolang/motives/} is referenced by a registered lint. + * + *

Each lint exposes a motive document via its {@link Lint#motive()} method, + * loaded from {@code src/main/resources/org/eolang/motives/{dimension}/{name}.md}. + * If a markdown file lives under that tree but no lint claims it, the file is + * orphaned: it ships in the JAR, contributes to the build, and confuses + * future contributors who assume the lint exists. This test fails when such + * orphans appear.

+ * + * @since 0.1.0 + */ +final class MotivesAreUsedTest { + + @Test + void everyMotiveFileIsLinkedToALint() throws Exception { + final Set names = StreamSupport.stream( + new PkMono().spliterator(), false + ).map(Lint::name).collect(Collectors.toSet()); + final List orphans = Arrays.stream( + new PathMatchingResourcePatternResolver().getResources( + "classpath*:org/eolang/motives/**/*.md" + ) + ).map(MotivesAreUsedTest::shortName) + .filter(name -> !names.contains(name)) + .distinct() + .sorted() + .collect(Collectors.toList()); + MatcherAssert.assertThat( + Logger.format( + "Motive files without a corresponding lint: %[list]s. Each .md under src/main/resources/org/eolang/motives/ must match the name() of a lint produced by PkMono.", + orphans + ), + orphans, + Matchers.empty() + ); + } + + /** + * Pull the lint name out of a motive resource filename. + * @param res Classpath resource for a motive markdown file + * @return Lint name (filename without {@code .md}) + */ + private static String shortName(final Resource res) { + final String filename = res.getFilename(); + if (filename == null) { + throw new IllegalStateException( + String.format("Resource %s has no filename", res) + ); + } + return filename.replaceAll("\\.md$", ""); + } +} From b19be31821e865b0128c470ff3f2de7a9940aa47 Mon Sep 17 00:00:00 2001 From: bibonix Date: Fri, 8 May 2026 00:47:09 +0000 Subject: [PATCH 2/3] #554 fix: drop orphan motive files and align incorrect-unlint motive - Rename motives/errors/lt-incorrect-unlint.md to incorrect-unlint.md to match the lint name returned by LtIncorrectUnlint.name(), and update LtIncorrectUnlint.motive() to load the renamed resource. - Delete motive files that no lint in PkMono references: the empty units/unit-test-missing.md and units/unit-test-without-live-file.md leftovers, and the documented-but-unimplemented motives for same-line-names, noname-attribute, signed-binding-indexes, architect-duplicate, correct-package-meta, zero-version, one-high-level-object, test-object-is-not-verb-in-singular, and object-does-not-match-filename. - Drop the .vale.ini override for the deleted same-line-names motive. After this change, MotivesAreUsedTest passes against master. --- .vale.ini | 3 -- .../org/eolang/lints/LtIncorrectUnlint.java | 2 +- .../motives/critical/same-line-names.md | 5 --- ...ncorrect-unlint.md => incorrect-unlint.md} | 0 .../eolang/motives/errors/noname-attribute.md | 29 -------------- .../motives/errors/signed-binding-indexes.md | 15 ------- .../motives/metas/architect-duplicate.md | 22 ----------- .../motives/metas/correct-package-meta.md | 19 --------- .../org/eolang/motives/metas/zero-version.md | 20 ---------- .../motives/misc/one-high-level-object.md | 27 ------------- .../test-object-is-not-verb-in-singular.md | 39 ------------------- .../names/object-does-not-match-filename.md | 22 ----------- .../eolang/motives/units/unit-test-missing.md | 1 - .../units/unit-test-without-live-file.md | 1 - 14 files changed, 1 insertion(+), 204 deletions(-) delete mode 100644 src/main/resources/org/eolang/motives/critical/same-line-names.md rename src/main/resources/org/eolang/motives/errors/{lt-incorrect-unlint.md => incorrect-unlint.md} (100%) delete mode 100644 src/main/resources/org/eolang/motives/errors/noname-attribute.md delete mode 100644 src/main/resources/org/eolang/motives/errors/signed-binding-indexes.md delete mode 100644 src/main/resources/org/eolang/motives/metas/architect-duplicate.md delete mode 100644 src/main/resources/org/eolang/motives/metas/correct-package-meta.md delete mode 100644 src/main/resources/org/eolang/motives/metas/zero-version.md delete mode 100644 src/main/resources/org/eolang/motives/misc/one-high-level-object.md delete mode 100644 src/main/resources/org/eolang/motives/misc/test-object-is-not-verb-in-singular.md delete mode 100644 src/main/resources/org/eolang/motives/names/object-does-not-match-filename.md delete mode 100644 src/main/resources/org/eolang/motives/units/unit-test-missing.md delete mode 100644 src/main/resources/org/eolang/motives/units/unit-test-without-live-file.md diff --git a/.vale.ini b/.vale.ini index 900f6ad54..ca48ffa90 100644 --- a/.vale.ini +++ b/.vale.ini @@ -26,8 +26,5 @@ Microsoft.Headings = No Microsoft.Contractions = No Google.Contractions = No -[src/main/resources/org/eolang/motives/critical/same-line-names.md] -Google.Units = No - [src/main/resources/org/eolang/motives/atoms/not-empty-atom.md] Google.Units = No diff --git a/src/main/java/org/eolang/lints/LtIncorrectUnlint.java b/src/main/java/org/eolang/lints/LtIncorrectUnlint.java index 12c570fa8..f7ac3ba7d 100644 --- a/src/main/java/org/eolang/lints/LtIncorrectUnlint.java +++ b/src/main/java/org/eolang/lints/LtIncorrectUnlint.java @@ -56,6 +56,6 @@ public Collection defects(final XML xmir) throws IOException { @Override public String motive() throws IOException { - return new MotiveFrom("errors", "lt-incorrect-unlint").asString(); + return new MotiveFrom("errors", "incorrect-unlint").asString(); } } diff --git a/src/main/resources/org/eolang/motives/critical/same-line-names.md b/src/main/resources/org/eolang/motives/critical/same-line-names.md deleted file mode 100644 index 90f536553..000000000 --- a/src/main/resources/org/eolang/motives/critical/same-line-names.md +++ /dev/null @@ -1,5 +0,0 @@ -# Same line names - -@todo #19:35min Document motives for `same-line-names` lint. - Currently, we don't have a test case for this `same-line-names` lint. - We should create a new test case, and then document a motive for this lint. diff --git a/src/main/resources/org/eolang/motives/errors/lt-incorrect-unlint.md b/src/main/resources/org/eolang/motives/errors/incorrect-unlint.md similarity index 100% rename from src/main/resources/org/eolang/motives/errors/lt-incorrect-unlint.md rename to src/main/resources/org/eolang/motives/errors/incorrect-unlint.md diff --git a/src/main/resources/org/eolang/motives/errors/noname-attribute.md b/src/main/resources/org/eolang/motives/errors/noname-attribute.md deleted file mode 100644 index 530032b38..000000000 --- a/src/main/resources/org/eolang/motives/errors/noname-attribute.md +++ /dev/null @@ -1,29 +0,0 @@ -# No name attribute - -Each object attribute must have a name. - -Incorrect: - -```eo -[] > foo - first - second -``` - -```eo -[args] > main - (stdout "Hello!").print -``` - -Correct: - -```eo -[] > foo - first > hi - second > hey -``` - -```eo -[args] > main - (stdout "Hello!").print > out -``` diff --git a/src/main/resources/org/eolang/motives/errors/signed-binding-indexes.md b/src/main/resources/org/eolang/motives/errors/signed-binding-indexes.md deleted file mode 100644 index 647316242..000000000 --- a/src/main/resources/org/eolang/motives/errors/signed-binding-indexes.md +++ /dev/null @@ -1,15 +0,0 @@ -# Signed binding indexes - -Binding indexes must not be signed numbers. - -Incorrect: - -```eo -object c:-1 d:-2 a:0 b:1 e:+12 -``` - -Correct: - -```eo -object c:1 d:2 a:0 b:1 e:12 -``` diff --git a/src/main/resources/org/eolang/motives/metas/architect-duplicate.md b/src/main/resources/org/eolang/motives/metas/architect-duplicate.md deleted file mode 100644 index 80405c9fb..000000000 --- a/src/main/resources/org/eolang/motives/metas/architect-duplicate.md +++ /dev/null @@ -1,22 +0,0 @@ -# Architect duplicate - -Each `.eo` file must have only one architect. - -Incorrect: - -```eo -+architect jeff@google.com -+architect ivan@google.com - -# Foo. -[] > foo -``` - -Correct: - -```eo -+architect jeff@google.com - -# Foo. -[] > foo -``` diff --git a/src/main/resources/org/eolang/motives/metas/correct-package-meta.md b/src/main/resources/org/eolang/motives/metas/correct-package-meta.md deleted file mode 100644 index 85445526b..000000000 --- a/src/main/resources/org/eolang/motives/metas/correct-package-meta.md +++ /dev/null @@ -1,19 +0,0 @@ -# Correct `+package` Meta - -The special meta `+package` must have a valid value. - -Incorrect: - -```eo -+package - -[] > foo -``` - -Correct: - -```eo -+package my.awesome.package - -[] > foo -``` diff --git a/src/main/resources/org/eolang/motives/metas/zero-version.md b/src/main/resources/org/eolang/motives/metas/zero-version.md deleted file mode 100644 index d772924ee..000000000 --- a/src/main/resources/org/eolang/motives/metas/zero-version.md +++ /dev/null @@ -1,20 +0,0 @@ -# Zero version - -Objects inside `src/(main|test)` must use version `0.0.0` during -development. - -Incorrect: - -```eo -+version 1.2.3 - -[] > foo -``` - -Correct: - -```eo -+version 0.0.0 - -[] > foo -``` diff --git a/src/main/resources/org/eolang/motives/misc/one-high-level-object.md b/src/main/resources/org/eolang/motives/misc/one-high-level-object.md deleted file mode 100644 index b541f6952..000000000 --- a/src/main/resources/org/eolang/motives/misc/one-high-level-object.md +++ /dev/null @@ -1,27 +0,0 @@ -# One high-level object - -Every `.eo` file must have only one high-level object inside. - -Incorrect: - -`app.eo`: - -```eo -# Foo. -[] > foo - 42 > @ - -# Bar. -[] > bar - 42 > @ -``` - -Correct: - -`app.eo`: - -```eo -# Foo. -[] > foo - 42 > @ -``` diff --git a/src/main/resources/org/eolang/motives/misc/test-object-is-not-verb-in-singular.md b/src/main/resources/org/eolang/motives/misc/test-object-is-not-verb-in-singular.md deleted file mode 100644 index 3416fa6f0..000000000 --- a/src/main/resources/org/eolang/motives/misc/test-object-is-not-verb-in-singular.md +++ /dev/null @@ -1,39 +0,0 @@ -# Test object isn't verb in singular - -Test object names must start with a verb in singular form. - -Incorrect: - -```eo -# Foo. -[] > foo - # This is test. - [] +> this-is-test - 42 > foo - - # This is test. - [] +> testing - 42 > foo - - # This is test. - [] +> itIsTrue - 42 > foo -``` - -Correct: - -```eo -# Foo -[] > foo - # This is test. - [] +> prints-data - 42 > foo - - # This is test. - [] +> works-as-expected - 42 > foo - - # This is test. - [] +> parses-dom - 42 > foo -``` diff --git a/src/main/resources/org/eolang/motives/names/object-does-not-match-filename.md b/src/main/resources/org/eolang/motives/names/object-does-not-match-filename.md deleted file mode 100644 index 211bcc200..000000000 --- a/src/main/resources/org/eolang/motives/names/object-does-not-match-filename.md +++ /dev/null @@ -1,22 +0,0 @@ -# Object doesn't match filename - -Each `.eo` file must have the same name as its contained object to avoid -confusing readers. - -Incorrect: - -`bar.eo`: - -```eo -# Foo. -[] > foo -``` - -Correct: - -`foo.eo`: - -```eo -# Foo. -[] > foo -``` diff --git a/src/main/resources/org/eolang/motives/units/unit-test-missing.md b/src/main/resources/org/eolang/motives/units/unit-test-missing.md deleted file mode 100644 index 8b1378917..000000000 --- a/src/main/resources/org/eolang/motives/units/unit-test-missing.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/main/resources/org/eolang/motives/units/unit-test-without-live-file.md b/src/main/resources/org/eolang/motives/units/unit-test-without-live-file.md deleted file mode 100644 index 8b1378917..000000000 --- a/src/main/resources/org/eolang/motives/units/unit-test-without-live-file.md +++ /dev/null @@ -1 +0,0 @@ - From c5f32da17526022fd8f6ac56d0dbf5d9c1bacca0 Mon Sep 17 00:00:00 2001 From: bibonix Date: Fri, 8 May 2026 00:57:11 +0000 Subject: [PATCH 3/3] #554: move motive-coverage check into PkMonoTest jtcop expects each test class to have a same-named production class (RuleEveryTestHasProductionClass) and present-tense test names (RulePresentTense). Fold the new motive-coverage assertion into PkMonoTest as linksEveryMotiveFileToALint() and drop the standalone MotivesAreUsedTest. --- .../org/eolang/lints/MotivesAreUsedTest.java | 72 ------------------- .../java/org/eolang/lints/PkMonoTest.java | 53 ++++++++++++++ 2 files changed, 53 insertions(+), 72 deletions(-) delete mode 100644 src/test/java/org/eolang/lints/MotivesAreUsedTest.java diff --git a/src/test/java/org/eolang/lints/MotivesAreUsedTest.java b/src/test/java/org/eolang/lints/MotivesAreUsedTest.java deleted file mode 100644 index 6b88efca7..000000000 --- a/src/test/java/org/eolang/lints/MotivesAreUsedTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2016-2026 Objectionary.com - * SPDX-License-Identifier: MIT - */ -package org.eolang.lints; - -import com.jcabi.log.Logger; -import io.github.secretx33.resourceresolver.PathMatchingResourcePatternResolver; -import io.github.secretx33.resourceresolver.Resource; -import java.util.Arrays; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; -import org.junit.jupiter.api.Test; - -/** - * Test that ensures every motive markdown under - * {@code org/eolang/motives/} is referenced by a registered lint. - * - *

Each lint exposes a motive document via its {@link Lint#motive()} method, - * loaded from {@code src/main/resources/org/eolang/motives/{dimension}/{name}.md}. - * If a markdown file lives under that tree but no lint claims it, the file is - * orphaned: it ships in the JAR, contributes to the build, and confuses - * future contributors who assume the lint exists. This test fails when such - * orphans appear.

- * - * @since 0.1.0 - */ -final class MotivesAreUsedTest { - - @Test - void everyMotiveFileIsLinkedToALint() throws Exception { - final Set names = StreamSupport.stream( - new PkMono().spliterator(), false - ).map(Lint::name).collect(Collectors.toSet()); - final List orphans = Arrays.stream( - new PathMatchingResourcePatternResolver().getResources( - "classpath*:org/eolang/motives/**/*.md" - ) - ).map(MotivesAreUsedTest::shortName) - .filter(name -> !names.contains(name)) - .distinct() - .sorted() - .collect(Collectors.toList()); - MatcherAssert.assertThat( - Logger.format( - "Motive files without a corresponding lint: %[list]s. Each .md under src/main/resources/org/eolang/motives/ must match the name() of a lint produced by PkMono.", - orphans - ), - orphans, - Matchers.empty() - ); - } - - /** - * Pull the lint name out of a motive resource filename. - * @param res Classpath resource for a motive markdown file - * @return Lint name (filename without {@code .md}) - */ - private static String shortName(final Resource res) { - final String filename = res.getFilename(); - if (filename == null) { - throw new IllegalStateException( - String.format("Resource %s has no filename", res) - ); - } - return filename.replaceAll("\\.md$", ""); - } -} diff --git a/src/test/java/org/eolang/lints/PkMonoTest.java b/src/test/java/org/eolang/lints/PkMonoTest.java index 8cd48fa20..798e9d0f9 100644 --- a/src/test/java/org/eolang/lints/PkMonoTest.java +++ b/src/test/java/org/eolang/lints/PkMonoTest.java @@ -4,13 +4,21 @@ */ package org.eolang.lints; +import com.jcabi.log.Logger; import com.tngtech.archunit.core.importer.ClassFileImporter; import com.tngtech.archunit.core.importer.ImportOption; import com.tngtech.archunit.lang.syntax.ArchRuleDefinition; import com.yegor256.Together; +import io.github.secretx33.resourceresolver.PathMatchingResourcePatternResolver; +import io.github.secretx33.resourceresolver.Resource; import java.io.IOException; import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import org.cactoos.list.ListOf; import org.cactoos.scalar.LengthOf; import org.cactoos.set.SetOf; @@ -78,6 +86,27 @@ void checksThatLintsCanBeUnlinted() { ); } + @Test + void linksEveryMotiveFileToALint() throws Exception { + final List orphans = Arrays.stream( + new PathMatchingResourcePatternResolver().getResources( + "classpath*:org/eolang/motives/**/*.md" + ) + ).map(PkMonoTest::shortName) + .filter(((Predicate) PkMonoTest.lintNames()::contains).negate()) + .distinct() + .sorted() + .collect(Collectors.toList()); + MatcherAssert.assertThat( + Logger.format( + "Motive files without a corresponding lint: %[list]s. Each .md under src/main/resources/org/eolang/motives/ must match the name() of a lint produced by PkMono.", + orphans + ), + orphans, + Matchers.empty() + ); + } + @Test @SuppressWarnings("JTCOP.RuleAssertionMessage") void staysInsideThePackage() { @@ -90,6 +119,30 @@ void staysInsideThePackage() { ); } + /** + * Snapshot of every lint name produced by {@link PkMono}. + * @return Set of lint names + */ + private static Set lintNames() { + return StreamSupport.stream(new PkMono().spliterator(), false) + .map(Lint::name).collect(Collectors.toSet()); + } + + /** + * Pull the lint name out of a motive resource filename. + * @param res Classpath resource for a motive markdown file + * @return Lint name (filename without {@code .md}) + */ + private static String shortName(final Resource res) { + final String filename = res.getFilename(); + if (filename == null) { + throw new IllegalStateException( + String.format("Resource %s has no filename", res) + ); + } + return filename.replaceAll("\\.md$", ""); + } + /** * Found decorated lint of specific lint. * @param decorate Lint