This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch maven-4.0.x
in repository https://gitbox.apache.org/repos/asf/maven.git
The following commit(s) were added to refs/heads/maven-4.0.x by this push:
new cd03d1b133 [#12340] Add DefaultLookupTest for lookupOptional (#12385)
cd03d1b133 is described below
commit cd03d1b133f318e8f486166f73256faa86eecb0a
Author: Gerd Aschemann <[email protected]>
AuthorDate: Tue Jul 7 18:18:11 2026 +0200
[#12340] Add DefaultLookupTest for lookupOptional (#12385)
The backported NPE fix for DefaultLookup.lookupOptional (#12340) —
switching Optional.of to Optional.ofNullable so a null component lookup
returns an empty Optional instead of throwing NullPointerException —
shipped without a regression test.
Add DefaultLookupTest in maven-core covering both lookupOptional overloads
across four scenarios: null component -> empty Optional (the guarded NPE
case), present component -> value, ComponentLookupException caused by
NoSuchElementException -> empty, and any other cause -> rethrown as
LookupException.
Co-authored-by: Gerd Aschemann <[email protected]>
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
---
.../maven/internal/impl/DefaultLookupTest.java | 151 +++++++++++++++++++++
1 file changed, 151 insertions(+)
diff --git
a/impl/maven-core/src/test/java/org/apache/maven/internal/impl/DefaultLookupTest.java
b/impl/maven-core/src/test/java/org/apache/maven/internal/impl/DefaultLookupTest.java
new file mode 100644
index 0000000000..986d9c618c
--- /dev/null
+++
b/impl/maven-core/src/test/java/org/apache/maven/internal/impl/DefaultLookupTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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.maven.internal.impl;
+
+import java.util.NoSuchElementException;
+import java.util.Optional;
+
+import org.apache.maven.api.services.LookupException;
+import org.codehaus.plexus.PlexusContainer;
+import
org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class DefaultLookupTest {
+
+ private PlexusContainer container;
+ private DefaultLookup lookup;
+
+ @BeforeEach
+ void setUp() {
+ container = mock(PlexusContainer.class);
+ lookup = new DefaultLookup(container);
+ }
+
+ /**
+ * Regression guard for the NPE fix (#12340): when the container returns
{@code null} for a type
+ * lookup, {@code lookupOptional} must wrap it with {@link
Optional#ofNullable} and return an empty
+ * {@link Optional} rather than throwing a {@link NullPointerException}
from {@code Optional.of}.
+ */
+ @Test
+ void lookupOptionalByTypeReturnsEmptyWhenContainerReturnsNull() throws
Exception {
+ when(container.lookup(String.class)).thenReturn(null);
+
+ Optional<String> result = lookup.lookupOptional(String.class);
+
+ assertTrue(result.isEmpty(), "expected empty Optional when container
returns null");
+ }
+
+ /**
+ * Same regression guard as above for the {@code (Class, String)} overload.
+ */
+ @Test
+ void lookupOptionalByTypeAndNameReturnsEmptyWhenContainerReturnsNull()
throws Exception {
+ when(container.lookup(String.class, "hint")).thenReturn(null);
+
+ Optional<String> result = lookup.lookupOptional(String.class, "hint");
+
+ assertTrue(result.isEmpty(), "expected empty Optional when container
returns null");
+ }
+
+ @Test
+ void lookupOptionalByTypeReturnsValueWhenPresent() throws Exception {
+ String value = "component";
+ when(container.lookup(String.class)).thenReturn(value);
+
+ Optional<String> result = lookup.lookupOptional(String.class);
+
+ assertTrue(result.isPresent());
+ assertSame(value, result.get());
+ }
+
+ @Test
+ void lookupOptionalByTypeAndNameReturnsValueWhenPresent() throws Exception
{
+ String value = "component";
+ when(container.lookup(String.class, "hint")).thenReturn(value);
+
+ Optional<String> result = lookup.lookupOptional(String.class, "hint");
+
+ assertTrue(result.isPresent());
+ assertSame(value, result.get());
+ }
+
+ /**
+ * A {@link ComponentLookupException} whose cause is a {@link
NoSuchElementException} signals an
+ * absent component and must be translated into an empty {@link Optional}.
+ */
+ @Test
+ void lookupOptionalReturnsEmptyOnNoSuchElementCause() throws Exception {
+ ComponentLookupException cle =
+ new ComponentLookupException(new NoSuchElementException(),
String.class.getName(), "");
+ assertSame(NoSuchElementException.class, cle.getCause().getClass(),
"test fixture precondition");
+ when(container.lookup(String.class)).thenThrow(cle);
+
+ Optional<String> result = lookup.lookupOptional(String.class);
+
+ assertFalse(result.isPresent(), "expected empty Optional when
component is absent");
+ }
+
+ /**
+ * Same as above for the {@code (Class, String)} overload, which has its
own catch/translate logic.
+ */
+ @Test
+ void lookupOptionalByTypeAndNameReturnsEmptyOnNoSuchElementCause() throws
Exception {
+ ComponentLookupException cle =
+ new ComponentLookupException(new NoSuchElementException(),
String.class.getName(), "hint");
+ assertSame(NoSuchElementException.class, cle.getCause().getClass(),
"test fixture precondition");
+ when(container.lookup(String.class, "hint")).thenThrow(cle);
+
+ Optional<String> result = lookup.lookupOptional(String.class, "hint");
+
+ assertFalse(result.isPresent(), "expected empty Optional when
component is absent");
+ }
+
+ /**
+ * A {@link ComponentLookupException} whose cause is NOT a {@link
NoSuchElementException} is
+ * rethrown as a {@link LookupException} rather than swallowed into an
empty {@link Optional}.
+ */
+ @Test
+ void lookupOptionalRethrowsOnNonNoSuchElementCause() throws Exception {
+ ComponentLookupException cle =
+ new ComponentLookupException(new
IllegalStateException("boom"), String.class.getName(), "");
+ when(container.lookup(String.class)).thenThrow(cle);
+
+ assertThrows(LookupException.class, () ->
lookup.lookupOptional(String.class));
+ }
+
+ /**
+ * Same as above for the {@code (Class, String)} overload.
+ */
+ @Test
+ void lookupOptionalByTypeAndNameRethrowsOnNonNoSuchElementCause() throws
Exception {
+ ComponentLookupException cle =
+ new ComponentLookupException(new
IllegalStateException("boom"), String.class.getName(), "hint");
+ when(container.lookup(String.class, "hint")).thenThrow(cle);
+
+ assertThrows(LookupException.class, () ->
lookup.lookupOptional(String.class, "hint"));
+ }
+}