Lucene's test runner decides what is a test from the method name alone.
XYRectangle checks would fail if they ran. Select the still to open it at full size. Generated with AI, checked against the code.TestXYRectangledeclares 7 public no-arg test methods, all with assertions. Two are spelledtesInvalidMinMaxXandtesInvalidMinMaxY: onetshort.- Run
./gradlew :lucene:core:test --tests org.apache.lucene.geo.TestXYRectangle. You expect 7 tests; gradle runs 5. Asking forTestXYRectangle.tesInvalidMinMaxXby name ends with "no tests matched the provided --tests filters". - Why: Lucene's runner takes only public no-arg methods whose names start with lowercase
test, or that carry@Test. Two more methods miss it:TestOperations.TestOptionalOptionalAcceptsState0(23 of 24 run) andTestCommonGramsFilter.TestFirstAndLastStopWord(10 of 11). - Rename the two
XYRectanglemethods and both fail: they check the message for5 > 4, butnew XYRectangle(5, 4, 3, 4)reportsminX must be lower than maxX, got 5.0 > 4.0because the bounds are floats. The library is right; the checks were stale from the day they were written (2020). - The cost: no running test builds an
XYRectanglewith reversed bounds, so a regression in that check would go unnoticed. The other two dead methods repeat checks that running tests already make.
Only Lucene's own test suite: normal test runs, local or CI, skip the four methods: the TestXYRectangle pair in every release since 8.5.0, TestOptionalOptionalAcceptsState0 since 10.2.0 and TestFirstAndLastStopWord since 4.0.0-alpha. No shipped jar, API or index is affected. The real loss is coverage of XYRectangle's reversed-bounds IllegalArgumentException, which no running test checks.
Mechanism, terms and numbers462 words
Lucene's test runner treats a method as a test only if it is public, non-static, takes no arguments and its name starts with lowercase test, or if it carries @Test. Four methods in Lucene's own test sources miss that by one letter: TestXYRectangle.tesInvalidMinMaxX and tesInvalidMinMaxY, TestOperations.TestOptionalOptionalAcceptsState0 and TestCommonGramsFilter.TestFirstAndLastStopWord. Every test run skips them silently: gradle runs 5, 23 and 10 tests for the three classes instead of 7, 24 and 11. Renamed, the two TestXYRectangle methods fail, because they look for 5 > 4 in a message that reads minX must be lower than maxX, got 5.0 > 4.0. No shipped code is wrong; the real loss is that XYRectangle's reversed-bounds IllegalArgumentException has had no running test since it was added in 2020.
LuceneTestCase registers two method providers, @TestMethodProviders({LuceneJUnit3MethodProvider.class, JUnit4MethodProvider.class}) (LuceneTestCase.java:114). The first keeps a method only if m.getName().startsWith("test"), it is public and non-static, and it takes no parameters (LuceneJUnit3MethodProvider.java:39-42); the second keeps @Test methods. The class javadoc says the same: "Any test method with a testXXX prefix is considered a test case. Any test method annotated with [org.junit.Test] is considered a test case." (LuceneTestCase.java:90-91). Anything else is silently ignored.
| method | where | what it checks |
|---|---|---|
tesInvalidMinMaxX | TestXYRectangle.java:26 | new XYRectangle(5, 4, 3, 4) throws IllegalArgumentException whose message contains 5 > 4 (:33) |
tesInvalidMinMaxY | TestXYRectangle.java:37 | new XYRectangle(4, 5, 5, 4) throws, message contains 5 > 4 (:44) |
TestOptionalOptionalAcceptsState0 | TestOperations.java:574 | optional(optional(a)) for a(ba)* is a minimal DFA with the same language |
TestFirstAndLastStopWord | TestCommonGramsFilter.java:276 | "the of" through CommonGramsQueryFilter gives ["the_of"] |
None has @Test, @Ignore, @AwaitsFix, @Nightly or a comment saying it is disabled, and no code calls them.
XYRectangle(float minX, float maxX, float minY, float maxY) throws new IllegalArgumentException("minX must be lower than maxX, got " + minX + " > " + maxX) (and the minY twin) from a plain if (XYRectangle.java:36-44). Java prints the float 5 as 5.0, so the message is got 5.0 > 4.0 and contains("5 > 4") is false. LUCENE-9218 made the bounds floats in the same commit that added the exception and these two tests, so the checks never matched in any version.
Lucene's build turns off error-prone's JUnit3TestNotRun ("we don't use junit3") and JUnit4TestNotRun ("noisy") (ErrorPronePlugin.java:542, :548); per error-prone's documentation the former looks only at junit.framework.TestCase subclasses anyway, and whether the latter would flag these methods under Lucene's RandomizedRunner is unknown (neither was tested). The JUnit 5 base class checks test* methods for @Test (LuceneTestCaseJupiter.java:608-634), but only for Jupiter tests and only for names that already start with test.
Exposure and likelihood236 words
- Who. Lucene developers and CI. No user configuration or API is involved, and test sources are not in the released jars.
- What is lost. The two
TestXYRectanglemethods are the only tests that build anXYRectanglewith reversed bounds; every othernew XYRectangle(...)in the test trees passes ordered bounds. If that check regressed (for example back to anassert, as it was before 8.5.0), no running test would notice. - What is not lost.
TestFirstAndLastStopWord("the of"gives["the_of"]) repeats the case that the runningtestCommonGramsQueryFilteralready checks with two common words in the other order ("of the"gives["of_the"],TestCommonGramsFilter.java:105).TestOptionalOptionalAcceptsState0adds nothing beyond the runningtestOptionalAcceptsState0andtestOptionalOptional. Both dead bodies pass when run under new names, so they hide no product bug. - Scale. Two name scans find exactly these four: the finder's over every
lucene/**/src/testtree, sandbox included (public no-argvoidmethods whose names start withtes,tset,tets,tst,Test,TESTorTsetbut not withtest), and an independent verifier's over the non-sandbox trees (Test[A-Z]*,tes[^t]*,tset*,tets*,tst*,est[A-Z]*). They look for near-miss spellings only. Public no-arg methods that never run on purpose do exist, for example the compile-onlyTestAnalyzers._testStandardConstants(LUCENE-1150 comment,@SuppressWarnings("unused")). - Library behaviour. Correct:
XYRectanglerejects reversed bounds with assertions on and off;Operations.optionalandCommonGramsQueryFilterwork.
One JUnit class with one test. It asks JUnit for the runner of each of the three unmodified classes, reads which tests that runner would run, and lists every public no-arg instance method that is missing. Nothing in the three classes is executed, and nothing is random.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import org.apache.lucene.analysis.commongrams.TestCommonGramsFilter;
import org.apache.lucene.geo.TestXYRectangle;
import org.apache.lucene.tests.util.LuceneTestCase;
import org.apache.lucene.util.automaton.TestOperations;
import org.junit.runner.Description;
import org.junit.runner.Request;
public class TestMisnamedTestMethodsNeverRun extends LuceneTestCase {
public void testEveryPublicNoArgMethodIsRunAsATest() {
List<String> notRun = new ArrayList<>();
for (Class<?> c :
List.of(TestXYRectangle.class, TestOperations.class, TestCommonGramsFilter.class)) {
List<String> run = new ArrayList<>();
for (Description d : Request.aClass(c).getRunner().getDescription().getChildren()) {
run.add(d.isSuite() ? d.getDisplayName() : d.getMethodName());
}
for (Method m : c.getDeclaredMethods()) {
if (Modifier.isPublic(m.getModifiers())
&& Modifier.isStatic(m.getModifiers()) == false
&& m.getParameterCount() == 0
&& run.contains(m.getName()) == false) {
notRun.add(c.getSimpleName() + "#" + m.getName());
}
}
}
notRun.sort(null);
// expected: every public no-arg test method runs; actual: the four misnamed ones are skipped
assertEquals(List.of(), notRun);
}
}
# $LUCENE: a built checkout; $REPRO: the directory holding the test; $DEPS: junit 4.13.2, randomizedtesting-runner 2.9.2, hamcrest 3.0
CP=$LUCENE/lucene/core/build/libs/lucene-core-11.0.0-SNAPSHOT.jar:$LUCENE/lucene/codecs/build/libs/lucene-codecs-11.0.0-SNAPSHOT.jar:$LUCENE/lucene/test-framework/build/libs/lucene-test-framework-11.0.0-SNAPSHOT.jar:$LUCENE/lucene/analysis/common/build/libs/lucene-analysis-common-11.0.0-SNAPSHOT.jar:$DEPS
javac -nowarn -d out -cp "$CP" $REPRO/org/apache/lucene/TestMisnamedTestMethodsNeverRun.java \
$LUCENE/lucene/core/src/test/org/apache/lucene/geo/TestXYRectangle.java \
$LUCENE/lucene/core/src/test/org/apache/lucene/util/automaton/TestOperations.java \
$LUCENE/lucene/analysis/common/src/test/org/apache/lucene/analysis/commongrams/TestCommonGramsFilter.java
java -ea -Dtests.seed=DEADBEEF -cp "out:$CP" org.junit.runner.JUnitCore org.apache.lucene.TestMisnamedTestMethodsNeverRun
# assertions off, same result: java -da -Dtests.asserts=false ...
- Expected
- An empty list: every public no-arg method of the three classes is run as a test.
- Observed
[TestCommonGramsFilter#TestFirstAndLastStopWord, TestOperations#TestOptionalOptionalAcceptsState0, TestXYRectangle#tesInvalidMinMaxX, TestXYRectangle#tesInvalidMinMaxY], with assertions on and off, on main and on 10.6.0.
More files, environment, full output359 words
The test compiles the checkout's own, unmodified TestXYRectangle, TestOperations and TestCommonGramsFilter and calls Request.aClass(c).getRunner().getDescription() for each: that builds Lucene's RandomizedRunner (from LuceneTestCase's @RunWith) and lists the tests it would run, without running them. Every declared public, non-static, no-arg method that is not in that list is reported. Apart from their test methods the three classes declare no public no-arg instance methods, so the list should be empty (true for these three classes; it is not a general rule). d.isSuite() covers -Dtests.iters > 1, where the runner groups each method's repetitions under a suite named after the method (checked with 5 iterations).
The test classes of two modules are compiled together, so the command runs outside gradle, against the checkout's built jars of lucene-core, -codecs, -test-framework and -analysis-common plus junit 4.13.2, randomizedtesting-runner 2.9.2 and hamcrest 3.0 ($DEPS, the versions in gradle/libs.versions.toml).
Without the test: on a stock checkout, ./gradlew :lucene:core:test --tests org.apache.lucene.geo.TestXYRectangle reports 5 tests for a class with 7 test-shaped methods, and --tests 'org.apache.lucene.geo.TestXYRectangle.tesInvalidMinMaxX' fails the build with "1 test task executed but no tests matched the provided --tests filters?".
No separate regression test is suggested: once renamed, the four methods are running tests themselves. The minimal test passes with the fix.
| lucene | main 645d4fe14a20 (11.0.0-SNAPSHOT); release 10.6.0 fails the same way with its own test sources |
|---|---|
| jdk | OpenJDK 25.0.3 (build 25.0.3+9-LTS) |
| os | Linux aarch64 (the only platform run) |
# the command above, main 645d4fe14a20, seed DEADBEEF, -ea (-da identical)
java.lang.AssertionError: expected:<[]> but was:<[TestCommonGramsFilter#TestFirstAndLastStopWord, TestOperations#TestOptionalOptionalAcceptsState0, TestXYRectangle#tesInvalidMinMaxX, TestXYRectangle#tesInvalidMinMaxY]>
FAILURES!!!
Tests run: 1, Failures: 1
# gradle, stock checkout
<testsuite name="org.apache.lucene.geo.TestXYRectangle" tests="5" skipped="0" failures="0" errors="0"
<testsuite name="org.apache.lucene.util.automaton.TestOperations" tests="23" skipped="0" failures="0" errors="0"
<testsuite name="org.apache.lucene.analysis.commongrams.TestCommonGramsFilter" tests="10" skipped="0" failures="0" errors="0"
$ ./gradlew :lucene:core:test --tests 'org.apache.lucene.geo.TestXYRectangle.tesInvalidMinMaxX'
> 1 test task executed but no tests matched the provided --tests filters?
# gradle, tes -> test only (message checks unchanged)
TestXYRectangle > testInvalidMinMaxX FAILED
java.lang.AssertionError
at org.apache.lucene.geo.TestXYRectangle.testInvalidMinMaxX(TestXYRectangle.java:33)
TestXYRectangle > testInvalidMinMaxY FAILED
java.lang.AssertionError
at org.apache.lucene.geo.TestXYRectangle.testInvalidMinMaxY(TestXYRectangle.java:44)
7 tests completed, 2 failed
# the messages those checks look at: minX must be lower than maxX, got 5.0 > 4.0 / minY must be lower than maxY, got 5.0 > 4.0- Written as tests: javadoc such as "maxX must be gte minX", assertion-only bodies, no callers, no
@Ignoreor@AwaitsFix. - Misnamed at creation, next to correctly named siblings: #14218 split a running
testOptionalinto four methods and named oneTestOptional.... - Lucene's JUnit 5 base class checks
test*methods "so that they're not silently ignored".
The full argument216 words
- Written as tests.
tesInvalidMinMaxX/Ycarry the javadoc "maxX must be gte minX" / "maxY must be gte minY";TestFirstAndLastStopWordcarries "Test CommonGramsQueryFilter when first and last words are stopwords."; all four bodies are assertions only (expectThrows,assertMinimalDFA,assertTokenStreamContents), and no code calls them. Lucene's test code disables tests on purpose with 49 active@AwaitsFix/@Ignoreannotations; none of the four has a marker. - History shows typos. LUCENE-9218 (
4a54ffb553ad, apache/lucene-solr#1252, 2020) createdTestXYRectanglewith the twotesmethods next to four correctly named siblings (testNaN,testPositiveInf,testNegativeInf,testEqualsAndHashCode). #14218 (de1ed71261d5, 2025, "Clean up more dead states creation by Automata/Operations"; its commit message lists "split some large tests into smaller tests for easier management") split the runningtestOptionalinto four methods; three gottest, one gotTest.TestFirstAndLastStopWordcame with SOLR-1674 (2009) and kept its name through the move into Lucene (LUCENE-2413, 2010) and the 2021 class rename (LUCENE-8626). - Precedent. LUCENE-2037 (
db067550e89, 2010) added a check because "i found lots of tests that simply forgot to add @Test and were never ran"; a 2010 commit (d1ecb22f8a9) renamedtesStopwordsParsingtotestStopwordsParsing;LuceneTestCaseJupiterenforces@Testontest*methods "so that they're not silently ignored" (LuceneTestCaseJupiter.java:610).
The devil's-advocate check154 words
Strongest case for "not a bug": the runner and XYRectangle work exactly as documented; LuceneTestCase never promises to run every public no-arg method, and a public method may be a helper; the exact exception text is not a contract; two of the four methods are redundant. The first cross-model reviewer (refute lens) returned "not real" on these grounds, at medium confidence and partly on procedure (it could not run Java).
Why it fails: nobody claims the runner is wrong; the defect is in the test sources. The same reviewer wrote that "static review nevertheless strongly indicates accidental test-name typos" and that the renames and message fixes "look appropriate". A helper would not carry test javadoc and assertion-only bodies, and nothing marks the methods as disabled. The exact text matters only because the tests' own assertion checks it. Redundancy narrows the impact but does not make the names intended. Verdict: a minor, test-only defect (adjudication).
Recommended Rename the four methods; correct the two expected messages (validated)
Rename the four methods to test... and change both TestXYRectangle checks to contains("5.0 > 4.0"): 6 lines in 3 test files. With it the classes run 7, 24 and 11 tests and pass, the geo, automaton and commongrams suites pass (453 tests), and the minimal test passes. Renaming alone leaves 2 failures.
--- TestXYRectangle.java
- public void tesInvalidMinMaxX() {
+ public void testInvalidMinMaxX() {
- assertTrue(expected.getMessage().contains("5 > 4"));
+ assertTrue(expected.getMessage().contains("5.0 > 4.0"));
(the same two changes in tesInvalidMinMaxY)
--- TestOperations.java
- public void TestOptionalOptionalAcceptsState0() {
+ public void testOptionalOptionalAcceptsState0() {
--- TestCommonGramsFilter.java
- public void TestFirstAndLastStopWord() throws Exception {
+ public void testFirstAndLastStopWord() throws Exception {All options and trade-offs4 options ยท 411 words
One fix was validated with gradle in a scratch worktree of 645d4fe14a20; the alternatives were not run.
Rename tesInvalidMinMaxX, tesInvalidMinMaxY, TestOptionalOptionalAcceptsState0 and TestFirstAndLastStopWord to testInvalidMinMaxX, testInvalidMinMaxY, testOptionalOptionalAcceptsState0 and testFirstAndLastStopWord, and in TestXYRectangle change both contains("5 > 4") to contains("5.0 > 4.0"). 3 files, +6/-6, test sources only.
--- TestXYRectangle.java
- public void tesInvalidMinMaxX() {
+ public void testInvalidMinMaxX() {
- assertTrue(expected.getMessage().contains("5 > 4"));
+ assertTrue(expected.getMessage().contains("5.0 > 4.0"));
(the same two changes in tesInvalidMinMaxY)
--- TestOperations.java
- public void TestOptionalOptionalAcceptsState0() {
+ public void testOptionalOptionalAcceptsState0() {
--- TestCommonGramsFilter.java
- public void TestFirstAndLastStopWord() throws Exception {
+ public void testFirstAndLastStopWord() throws Exception {Trade-offs
None for users: no library code changes. Both parts are needed: renaming without the message change makes TestXYRectangle fail 2 of 7. No CHANGES entry is strictly needed.
What the checks validated
- gradle
--no-daemon, seed DEADBEEF, OpenJDK 25.0.3:TestXYRectangle,TestOperationsandTestCommonGramsFilterrun 7, 24 and 11 tests (stock 5, 23, 10), 0 failures, with-Ptests.asserts=trueandfalse. - Closest suites, 0 failures:
org.apache.lucene.geo.*(17 classes, 195 tests, 7 skipped),org.apache.lucene.util.automaton.*(15 classes, 239 tests, 2 skipped),org.apache.lucene.analysis.commongrams.*(3 classes, 19 tests). - The minimal test passes against the fixed sources with assertions on and off.
- Renaming only:
TestXYRectangle7 tests, 2 failures (TestXYRectangle.java:33,:44).
assertEquals("minX must be lower than maxX, got 5.0 > 4.0", expected.getMessage()) and the minY twin.
Trade-offs
Stricter, but ties the tests to the full wording; the recommended option keeps the original substring-style check and is the smaller diff.
TestFirstAndLastStopWord and TestOptionalOptionalAcceptsState0 add no coverage that running tests do not already give.
Trade-offs
Defensible, but renaming is a smaller, less debatable change and keeps the explicit cases their authors wrote. The TestXYRectangle pair must be renamed either way.
A check in LuceneTestCase, like the Jupiter guard (LuceneTestCaseJupiter.java:608-634), that fails on public no-arg instance methods named tes... or Test... but not test....
Trade-offs
A heuristic: a legitimate public helper with such a name would trip it (the near-miss scans found none besides these four). Turning on error-prone's JUnit3TestNotRun would likely not help, since it looks only at junit.framework.TestCase subclasses; whether JUnit4TestNotRun would flag these methods in classes run by Lucene's RandomizedRunner was not tested.
Code reading, no formal method. While an AI tool was checking XYRectangle's input validation in a sweep of assert-only input checks, it noticed the tes prefix; two independent name scans of the test trees found the same four. Runs of the unmodified and the renamed classes confirmed it.
The method in full163 words
- Context. A hunt unit swept Lucene for input checks that exist only as
assert. ReadingTestXYRectangleto see howXYRectangle's bounds checks are tested, the finder noticed thattesInvalidMinMaxXandtesInvalidMinMaxYcannot run. - Widening. The finder's scan of every
lucene/**/src/testtree, sandbox included, for public no-argvoidmethods whose names start withtes,tset,tets,tst,Test,TESTorTsetbut not withtestfound exactly four. An independent verifier's scan of the non-sandbox test trees with its own patterns (Test[A-Z]*,tes[^t]*,tset*,tets*,tst*,est[A-Z]*) found the same four. Both match name patterns only. - Confirmation. The unmodified classes ran 5, 23 and 10 tests with none of the four; renamed copies ran 42 tests with only the two
XYRectanglechecks failing; the corrected checks passed. - Tier. Non-formal: the first signal was reading a test and the widening was a text scan; no model or bounded check was involved.
No prior report: GitHub issues and PRs (with the migrated JIRA archive), commits, the dev@, java-user@, issues@ and commits@ lists and downstream repositories were searched on 2026-10-29; each method name returns 0 GitHub results (re-checked 2026-11-01).
Searches5 searches
Related, none the same bug: #16027 (the JUnit 5 base class needs @Test), #9672 and #11374 (test class naming and discovery), #3112 (LUCENE-2037, the 2010 @Test check), and LFV-389, tests that lost @Test (a different root). The introducing PRs (apache/lucene-solr#1252, #14218) have no comment on these tests.
One silent signal that is not a report: an outside Kotlin port (nehemiaharchives/lucene-kmp#97, 2025-06-13) runs the two XYRectangle methods and expects 5.0 > 4.0 without comment; nothing reached apache/lucene.
| Where | When | How | Found |
|---|---|---|---|
| Scripted searches, 3 runs | 2026-10-29 | 46 GitHub issue/PR, 9 commit, 4 downstream, 60 mailing-list (dev@, java-user@, issues@, commits@), 12 JIRA-archive and 6 web queries | 0 high-overlap hits; 2 medium hits share only the title term testOperations |
| GitHub, manual (apache/lucene) | 2026-10-29 | 38 phrasings ("was never running", "test method" typo, "start with test", "dead test", "public void tes", ...) | nothing about these methods or a misnamed-test sweep |
| GitHub-wide issues and commits | 2026-10-29; re-checked 2026-11-01 | each of the four method names; "misnamed test" | 0 |
| Open PRs | 2026-10-29 | changed-file lists of all 415 open apache/lucene PRs | only #16911 (deletes every file on branch_10x) touches one of the files |
| Upstream main | 2026-11-01 | the three files at f815e679bf6c | all four names and both "5 > 4" checks unchanged |
Each method was misnamed in the commit that created it: the TestXYRectangle pair in LUCENE-9218 (4a54ffb553ad, 2020, first released in 8.5.0), the TestOperations one in #14218 (de1ed71261d5, 2025, 10.2.0), TestFirstAndLastStopWord in a 2009 Solr test moved into Lucene in 2010 (4.0.0-alpha). All four are on main and in 10.6.0.
Versions tested6 rows
TestXYRectanglepair:4a54ffb553ad(LUCENE-9218, apache/lucene-solr#1252, by iverase, 2020-02-14; branch_8x backportca3319cdbce1). The same commit made the bounds floats and replacedXYRectangle'sassert minX <= maxXwith theIllegalArgumentException, so the"5 > 4"checks never matched. Only reformatting touched the bodies later.TestOperationsmethod:de1ed71261d5(#14218, by rmuir, 2025-02-13; branch_10x backportb74580c51f0c) split the runningtestOptionalinto four methods, one misnamed.TestFirstAndLastStopWord:b105beef661a(SOLR-1674, 2009-12-21) in Solr'sCommonGramsFilterTest; moved into Lucene by1d1331fa033f(LUCENE-2413, 2010-05-04); the 2021 class rename (LUCENE-8626) kept the method name.- None of the four has run in any Lucene release. 8.8.2 to 8.11.3 are inferred (same branch, between affected 8.8.1 and 8.11.4).
| Version or commit | Verdict | Result |
|---|---|---|
main 645d4fe14a20; upstream main f815e679bf6c | affected | all four skipped: minimal test fails with -ea and -da; gradle 5/23/10 tests (upstream main read, not run) |
| 10.6.0 | affected | minimal test fails on the release jars with the tag's own test sources |
| 10.2.0 to 10.5.2 | affected | all four present; runner skips them on 10.2.0 and 10.5.2; stale checks fail on 10.5.2 |
| 8.5.0 to 10.1.0 | 3 of 4 | the TestXYRectangle pair and TestFirstAndLastStopWord; runner skips them on 8.5.0 and 9.12.3; stale checks fail on 8.5.0, 8.11.4, 9.12.3; on 10.1.0 a related state-0 check runs inside testOptional |
| 4.0.0-alpha to 8.4.1 | 1 of 4 | only the redundant TestFirstAndLastStopWord (skipped on 4.0.0); 8.4.1 has no XYRectangle exception to test |
| 3.x and older | not affected | the CommonGrams test is only in Solr's tests |
Independent reproducers, a reviewer from another model family (OpenAI GPT-5.6) with lenses, a devil's-advocate check and an adjudication looked at it. All agree on the cause; the refute lens said "not real" on procedure but called the names accidental typos. Adjudication: confirmed, high confidence, minor and test-only.
Checks and verdicts238 words
Each check ran separately on main 645d4fe14a20, except the cross-model reviews: they could not run code and read an earlier checkout, ea57360912ed, whose copies of the relevant files are identical (a separate run re-ran the repro).
| Check | Verdict | What it found |
|---|---|---|
| Finder | reproduced | Unmodified classes run 5/23/10 tests; renamed copies 42 tests, only the two XYRectangle checks fail; corrected checks pass. |
| Refute lens (OpenAI GPT-5.6) | not real (medium), procedural | Runner and XYRectangle work as documented; it could not run Java; agreed on the root cause and called the methods accidental typos; a separate run re-ran the repro: 2 of 2 fail. |
| Reproducer | real | Own discovery check: the four never run on main (-ea, -da) and 10.5.2, three on 9.12.3; own stale-check repro with passing controls; the other two bodies pass; own scan finds exactly four. |
| Works-as-designed lens (OpenAI GPT-5.6) | real (medium) | Intended-behaviour argument "moderate"; root cause agreed. |
| Devil's advocate | real, narrow scope | Typos, not intent; two of the four are redundant; re-run 2 of 2 fail with -ea and -da. |
| Adjudication | confirmed (high), minor, test-only | Re-ran 2 of 2 fail; corrections: only the two XYRectangle checks are lost coverage, and the never-run proof is runner discovery. |
| Minimal repro and brief | verified; fix validated | One-test class fails on main (-ea, -da) and 10.6.0, passes with the fix; gradle confirms 5/23/10 stock and 7/24/11 fixed. |
The diagram as a still image, generated with AI and checked against the code. It is under 10 MB, the limit for a GitHub attachment.
- PNG 438 KB
Finding record, Lucene and upstream sources, repro files and diagram notes1,185 words
- Registry title
- Four Lucene test methods never run because their names do not start with lowercase 'test' (TestXYRectangle.tesInvalidMinMaxX/Y, TestOperations.TestOptionalOptionalAcceptsState0, TestCommonGramsFilter.TestFirstAndLastStopWord); the two TestXYRectangle ones would also fail if run, because they look for '5 > 4' while XYRectangle reports '5.0 > 4.0', so XYRectangle's reversed-bounds check has had no running test since 2020
- Area
- tests / test-method discovery (core geo TestXYRectangle, core automaton TestOperations, analysis-common TestCommonGramsFilter)
- Priority
- Low, rank 1219: It only affects Lucene's own test suite, where 4 misnamed test methods (in practice one untested XYRectangle reversed-bounds check) silently never run.
- Impact class
- minor
- Impact note
- Only Lucene's own test sources, in every release since 8.5.0 (the oldest of the four methods since 4.0.0-alpha): every test run silently skips the four methods; no shipped jar, API or index behaviour is wrong, two of the four duplicate running tests, and the real loss is that XYRectangle's min > max IllegalArgumentException has no running test, so a future regression there would go unnoticed.
- Summary
- You run Lucene's test suite and expect every method written as a test to run. Four are named 'tes...' or 'Test...' instead of 'test...', so Lucene's runner, which picks only public no-arg methods whose names start with lowercase 'test' (or that carry @Test), silently skips TestXYRectangle.tesInvalidMinMaxX/Y, TestOperations.TestOptionalOptionalAcceptsState0 and TestCommonGramsFilter.TestFirstAndLastStopWord. Run under 'test...' names, the two TestXYRectangle methods fail, because they look for '5 > 4' while XYRectangle correctly reports 'minX must be lower than maxX, got 5.0 > 4.0'; the library itself behaves correctly.
- Introduced
- 4a54ffb553ad (apache/lucene-solr#1252, LUCENE-9218, by iverase; branch_8x backport ca3319cdbce1) for TestXYRectangle.tesInvalidMinMaxX/Y, created misnamed and with the '5 > 4' checks in the same commit that added XYRectangle's float bounds and its min > max IllegalArgumentException [read]; de1ed71261d5 (apache/lucene#14218, by rmuir; branch_10x backport b74580c51f0c) for TestOperations.TestOptionalOptionalAcceptsState0, split out of the running testOptional [read]; b105beef661a (SOLR-1674, in the Solr test), moved into the Lucene tree by 1d1331fa033f (LUCENE-2413) for TestCommonGramsFilter.TestFirstAndLastStopWord [read]. Each method was created with its final name; none was renamed into this state later [read, history clone] (2020-02-14 (TestXYRectangle pair); 2025-02-13 (TestOperations method); 2009-12-21 in the Solr test, 2010-05-04 in the Lucene tree (TestCommonGramsFilter method))
- First affected version
- 8.5.0 for the TestXYRectangle pair, the only real coverage loss [exec: 8.5.0 stock classes do not run them, the checks fail 2 of 2]; 10.2.0 for the TestOperations method [exec discovery]; 4.0.0-alpha for TestFirstAndLastStopWord, the oldest [read; exec discovery on 4.0.0]
- Affected versions
- main 645d4fe14a20 [exec: the unmodified TestXYRectangle, TestOperations and TestCommonGramsFilter run 5, 23 and 10 tests and none of the four methods; the two TestXYRectangle checks fail 2 of 2 with -ea and -da]; 10.6.0 (4ae946e5a9b) [exec: the minimal test fails on the release jars with the tag's own test sources, -ea and -da; main() form of the two checks on its core jar, both messages 'got 5.0 > 4.0']; branch_10x 657014b3c579 [read]; 10.5.2 [exec: four not run, checks fail]; 9.12.3 [exec: the three methods that exist there not run, checks fail]; 8.11.4 and 8.5.0 [exec: checks fail; 8.5.0 discovery: three not run]; 10.2.0 [exec: TestOperations method not run]; 4.0.0 [exec: TestFirstAndLastStopWord not run]; every release tag from 8.5.0 to 10.5.2 has the TestXYRectangle pair and every one from 4.0.0-alpha has TestFirstAndLastStopWord [read]. Not affected: 8.4.1 for the TestXYRectangle pair (no such test and no IllegalArgumentException; XYRectangle only asserted) [exec]; 10.1.0 for the TestOperations method (absent; testOptional held an earlier related check and ran) [exec]
- Status
- documented (brief, checks and a per-finding doc exist)
- Verification
- Confirmed by AI-run checks: an independent repro, a cross-model review that disputed it (the adjudication ruled it real), an intended-behaviour check, a prior-report search and a skeptical audit; not yet triaged by a person.
- Found by
- non-formal: code reading
- Upstream and prior reports
- none found (2026-10-29: three scripted search runs over GitHub issues and PRs incl. the migrated JIRA archive, commits, downstream repositories, the dev@, java-user@, issues@ and commits@ lists and JIRA, 60 manual GitHub searches, 6 web queries and the changed-file lists of 415 open apache/lucene PRs; GitHub-wide issue and commit search for each of the four method names: 0). Related items, none the same bug: apache/lucene-solr#1252 and apache/lucene#14218 (the introducing PRs, no comment on the tests), #16027 (JUnit 5 base class needs @Test), #9672 and #11374 (test class naming and discovery), LFV-389 (a different never-run-tests root). The only open PR touching one of the files, #16911, deletes branch_10x. Follow-up re-check 2026-10-29: the four names still return 0 issues/PRs, no issue or PR created since 2026-10-26 matches 'TestXYRectangle', 'misnamed test', 'test method name typo' or 'never run tests', no open PR created since 2026-10-27 touches the three files, and upstream main HEAD is still 645d4fe14a20 (author date 2026-10-28). Not fixed upstream. Update 2026-11-02: #16911 (open when searched) merged 2026-10-29 as 48cf3f515d6, which emptied branch_10x to a README tombstone (10.6 is the last 10.x minor; 10.x fixes go to branch_10_6); it fixes nothing here.
- Lucene commit tested
- main
645d4fe14a20(2026-10-28): the minimal test fails with assertions on and off (standalone and with a plainjavac/javacommand); gradle in a scratch worktree runs 5/23/10 tests stock and 7/24/11 with the fix; the 10.6.0 release jars with that tag's own test sources fail the same way; older releases are listed under history. Upstream mainf815e679bf6c(2026-11-01) was read, not run: all four names are unchanged.
- Finding record
a Lucene main checkout (645d4fe14a20): lucene/core/src/test/org/apache/lucene/geo/TestXYRectangle.java:25-45 (tesInvalidMinMaxX at :26, tesInvalidMinMaxY at :37, contains("5 > 4") at :33 and :44); lucene/core/src/java/org/apache/lucene/geo/XYRectangle.java:36-44 (float constructor, unconditional IllegalArgumentException with the bounds concatenated as floats); lucene/core/src/test/org/apache/lucene/util/automaton/TestOperations.java:574; lucene/analysis/common/src/test/org/apache/lucene/analysis/commongrams/TestCommonGramsFilter.java:275-276; lucene/test-framework/src/java/org/apache/lucene/tests/util/LuceneJUnit3MethodProvider.java:39 (m.getName().startsWith("test")); lucene/test-framework/src/java/org/apache/lucene/tests/util/LuceneTestCase.java:88-91 ('Any test method with a testXXX prefix is considered a test case'); build-tools/build-infra/src/main/java/org/apache/lucene/gradle/plugins/java/ErrorPronePlugin.java:542, :548 (JUnit3TestNotRun and JUnit4TestNotRun off)
- The finding in 30 seconds
a Lucene main checkout (645d4fe14a20) lucene/test-framework/src/java/org/apache/lucene/tests/util/LuceneTestCase.java:90-91, :114a Lucene main checkout (645d4fe14a20) lucene/test-framework/src/java/org/apache/lucene/tests/util/LuceneJUnit3MethodProvider.java:39-42a Lucene main checkout (645d4fe14a20) lucene/core/src/test/org/apache/lucene/geo/TestXYRectangle.java:25-45; lucene/core/src/test/org/apache/lucene/util/automaton/TestOperations.java:574-588; lucene/analysis/common/src/test/org/apache/lucene/analysis/commongrams/TestCommonGramsFilter.java:275-283a Lucene main checkout (645d4fe14a20) lucene/core/src/java/org/apache/lucene/geo/XYRectangle.java:36-44a Lucene main checkout (645d4fe14a20) build-tools/build-infra/src/main/java/org/apache/lucene/gradle/plugins/java/ErrorPronePlugin.java:542, :548a Lucene main checkout (645d4fe14a20) lucene/test-framework/src/java/org/apache/lucene/tests/util/LuceneTestCaseJupiter.java:608-634
- Who is affected
a Lucene main checkout (645d4fe14a20) lucene/analysis/common/src/test/org/apache/lucene/analysis/commongrams/TestCommonGramsFilter.java:87-136 (testCommonGramsQueryFilter, 'of the' at :105)a Lucene main checkout (645d4fe14a20) grep of 'new XYRectangle(' in the test trees (only TestXYRectangle.java:31 and :42 pass reversed bounds)a Lucene main checkout (645d4fe14a20) lucene/analysis/common/src/test/org/apache/lucene/analysis/core/TestAnalyzers.java:101-104 (_testStandardConstants, compile-only by design)
- Minimal repro
a Lucene main checkout (645d4fe14a20) gradle/libs.versions.toml (junit 4.13.2, randomizedtesting 2.9.2, hamcrest 3.0)
- Why it looks unintended
a Lucene main checkout (645d4fe14a20) lucene/core/src/test/org/apache/lucene/geo/TestXYRectangle.java:25-45; lucene/analysis/common/src/test/org/apache/lucene/analysis/commongrams/TestCommonGramsFilter.java:275-276a Lucene main checkout (645d4fe14a20) git grep -nE 'AwaitsFix\(|@Ignore\b' (without @IgnoreRandomChains mentions): 52 lines, 3 of them commented out (TestMappingCharFilter.java:225, :253; BaseKnnVectorQueryTestCase.java:596), so 49 active annotationsa Lucene main checkout (645d4fe14a20) lucene/test-framework/src/java/org/apache/lucene/tests/util/LuceneTestCaseJupiter.java:610
- History
a Lucene clone: git grep of the four names at releases/lucene/10.6.0
- TestMisnamedTestMethodsNeverRun.java (one test, test sources only):
repro/LFV-1013/src/org/apache/lucene/TestMisnamedTestMethodsNeverRun.java(53 lines)
Made in parallel with this doc from the mechanism in the brief; the PNG is the summary frame.
assets/LFV-1013/bug.png(438 KB)
