This is an automated email from the ASF dual-hosted git repository. jamesbognar pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/juneau.git
commit c24f4004ef374fc54c2a7286a5b0787909d756f2 Author: James Bognar <[email protected]> AuthorDate: Mon May 11 15:44:33 2026 -0400 test: fill in coverage gaps for inject and builder classes; fix Javadoc links and remove CreatableBeanStore --- .../juneau/commons/inject/CreatableBeanStore.java | 272 -------------- .../java/org/apache/juneau/annotation/Named.java | 2 +- .../httppart/bean/RequestBeanPropertyMeta.java | 2 +- .../httppart/bean/ResponseBeanPropertyMeta.java | 2 +- .../java/org/apache/juneau/rest/RestContext.java | 2 +- .../juneau/commons/inject/BasicBeanStore_Test.java | 416 +++++++++++++++++++++ .../commons/inject/BeanInstantiator_Test.java | 64 ++++ .../apache/juneau/encoders/EncoderSet_Test.java | 93 +++++ .../httppart/bean/HttpPartBeanMeta_Test.java | 163 ++++++++ .../org/apache/juneau/parser/ParserSet_Test.java | 158 ++++++++ .../client/remote/RemoteOperationArg_Test.java | 75 ++++ .../juneau/serializer/SerializerSet_Test.java | 183 +++++++++ 12 files changed, 1156 insertions(+), 276 deletions(-) diff --git a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/CreatableBeanStore.java b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/CreatableBeanStore.java deleted file mode 100644 index 86df9c7b5b..0000000000 --- a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/CreatableBeanStore.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * 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.juneau.commons.inject; - -import static org.apache.juneau.commons.utils.AssertionUtils.*; -import static org.apache.juneau.commons.utils.Utils.*; - -import java.util.*; -import java.util.concurrent.*; -import java.util.function.*; - -/** - * A bean store that provides convenient access to {@link BeanInstantiator} instances for creating beans. - * - * <p> - * This class extends {@link BasicBeanStore} and adds methods to manage {@link BeanInstantiator} instances - * for different bean types. Creators are lazily created and cached for efficient reuse. - * - * <h5 class='section'>Example:</h5> - * <p class='bjava'> - * <jc>// Create a creatable bean store</jc> - * CreatableBeanStore <jv>store</jv> = <jk>new</jk> CreatableBeanStore(<jk>null</jk>); - * - * <jc>// Get or create a creator for MyBean</jc> - * BeanInstantiator<MyBean> <jv>creator</jv> = <jv>store</jv>.getCreator(MyBean.<jk>class</jk>); - * - * <jc>// Use the creator to create a bean</jc> - * MyBean <jv>bean</jv> = <jv>creator</jv>.create(); - * - * <jc>// Explicitly add a creator (optional, getCreator will create if missing)</jc> - * <jv>store</jv>.addCreator(MyOtherBean.<jk>class</jk>); - * </p> - * - * <h5 class='section'>See Also:</h5><ul> - * <li class='jc'>{@link BasicBeanStore} - * <li class='jc'>{@link BeanInstantiator} - * </ul> - */ -@SuppressWarnings({ - "java:S115" // Constants use UPPER_snakeCase convention -}) -public class CreatableBeanStore extends BasicBeanStore { - - // Argument name constants for assertArgNotNull - private static final String ARG_beanType = "beanType"; - - private final ConcurrentHashMap<Class<?>, BeanInstantiator<?>> creators = new ConcurrentHashMap<>(); - private final Object enclosingInstance; - - /** - * Constructor. - * - * @param parent The parent bean store. Can be <jk>null</jk>. Bean searches are performed recursively up this parent chain. - * @param enclosingInstance The enclosing instance object to use when instantiating inner classes. Can be <jk>null</jk>. - */ - public CreatableBeanStore(BeanStore parent, Object enclosingInstance) { - super(parent); - this.enclosingInstance = enclosingInstance; - } - - /** - * Same as {@link #addCreator(Class)} but returns the creator instead of this object for fluent calls. - * - * @param <T> The bean type. - * @param beanType The bean type to create a creator for. Cannot be <jk>null</jk>. - * @return The creator that was created and stored. - */ - public <T> BeanInstantiator<T> add(Class<T> beanType) { - assertArgNotNull(ARG_beanType, beanType); - var creator = BeanInstantiator.of(beanType, this, null, enclosingInstance).build(); - creators.put(beanType, creator); - return creator; - } - - /** - * Same as {@link #addCreator(Class, String)} but returns the creator instead of this object for fluent calls. - * - * @param <T> The bean type. - * @param beanType The bean type to create a creator for. Cannot be <jk>null</jk>. - * @param name The bean name. Can be <jk>null</jk>. - * @return The creator that was created and stored. - */ - public <T> BeanInstantiator<T> add(Class<T> beanType, String name) { - assertArgNotNull(ARG_beanType, beanType); - var creator = BeanInstantiator.of(beanType, this, name, enclosingInstance).build(); - creators.put(beanType, creator); - return creator; - } - - /** - * Creates and stores a {@link BeanInstantiator} for the specified bean type. - * - * <p> - * If a creator for this type already exists, it is replaced with a new one. - * - * <h5 class='section'>Example:</h5> - * <p class='bjava'> - * <jc>// Create and store a creator</jc> - * <jv>store</jv>.addCreator(MyBean.<jk>class</jk>); - * - * <jc>// Get the creator</jc> - * BeanInstantiator<MyBean> <jv>creator</jv> = <jv>store</jv>.getCreator(MyBean.<jk>class</jk>); - * </p> - * - * @param <T> The bean type. - * @param beanType The bean type to create a creator for. Cannot be <jk>null</jk>. - * @return This object. - */ - public <T> CreatableBeanStore addCreator(Class<T> beanType) { - assertArgNotNull(ARG_beanType, beanType); - var creator = BeanInstantiator.of(beanType, this, null, enclosingInstance).build(); - creators.put(beanType, creator); - return this; - } - - /** - * Creates and stores a {@link BeanInstantiator} for the specified bean type with a name. - * - * <p> - * If a creator for this type already exists, it is replaced with a new one. - * - * <h5 class='section'>Example:</h5> - * <p class='bjava'> - * <jc>// Create and store a creator with a name</jc> - * <jv>store</jv>.addCreator(MyBean.<jk>class</jk>, <js>"myBean"</js>); - * - * <jc>// Get the creator</jc> - * BeanInstantiator<MyBean> <jv>creator</jv> = <jv>store</jv>.getCreator(MyBean.<jk>class</jk>, <js>"myBean"</js>); - * </p> - * - * @param <T> The bean type. - * @param beanType The bean type to create a creator for. Cannot be <jk>null</jk>. - * @param name The bean name. Can be <jk>null</jk>. - * @return This object. - */ - public <T> CreatableBeanStore addCreator(Class<T> beanType, String name) { - assertArgNotNull(ARG_beanType, beanType); - var creator = BeanInstantiator.of(beanType, this, name, enclosingInstance).build(); - creators.put(beanType, creator); - return this; - } - - /** - * Returns the {@link BeanInstantiator} for the specified bean type, creating it if it doesn't exist. - * - * <p> - * If a creator for this type doesn't exist, a new one is created with this bean store configured - * and stored for future use. - * - * <h5 class='section'>Example:</h5> - * <p class='bjava'> - * <jc>// Get or create a creator</jc> - * BeanInstantiator<MyBean> <jv>creator</jv> = <jv>store</jv>.getCreator(MyBean.<jk>class</jk>); - * - * <jc>// Use the creator to create a bean</jc> - * MyBean <jv>bean</jv> = <jv>creator</jv>.create(); - * </p> - * - * @param <T> The bean type. - * @param beanType The bean type to get a creator for. Cannot be <jk>null</jk>. - * @return The creator for the specified bean type. Never <jk>null</jk>. - */ - @SuppressWarnings({ - "unchecked" // Type erasure requires cast to BeanInstantiator<T> - }) - public <T> BeanInstantiator<T> getCreator(Class<T> beanType) { - assertArgNotNull(ARG_beanType, beanType); - return (BeanInstantiator<T>)creators.computeIfAbsent(beanType, k -> BeanInstantiator.of((Class<T>)k, this, null, enclosingInstance).build()); - } - - /** - * Returns the {@link BeanInstantiator} for the specified bean type with a name, creating it if it doesn't exist. - * - * <p> - * If a creator for this type doesn't exist, a new one is created with this bean store configured - * and stored for future use. - * - * <h5 class='section'>Example:</h5> - * <p class='bjava'> - * <jc>// Get or create a creator with a name</jc> - * BeanInstantiator<MyBean> <jv>creator</jv> = <jv>store</jv>.getCreator(MyBean.<jk>class</jk>, <js>"myBean"</js>); - * - * <jc>// Use the creator to create a bean</jc> - * MyBean <jv>bean</jv> = <jv>creator</jv>.create(); - * </p> - * - * @param <T> The bean type. - * @param beanType The bean type to get a creator for. Cannot be <jk>null</jk>. - * @param name The bean name. Can be <jk>null</jk>. - * @return The creator for the specified bean type. Never <jk>null</jk>. - */ - @SuppressWarnings({ - "unchecked" // Type erasure requires cast to BeanInstantiator<T> - }) - public <T> BeanInstantiator<T> getCreator(Class<T> beanType, String name) { - assertArgNotNull(ARG_beanType, beanType); - return (BeanInstantiator<T>)creators.computeIfAbsent(beanType, k -> BeanInstantiator.of((Class<T>)k, this, name, enclosingInstance).build()); - } - - /** - * Resolves a bean supplier by checking creators for matching bean or builder types. - * - * <p> - * This method searches through all registered creators to find one that can create the requested bean type. - * It checks both the creator's bean type and any builder types associated with the creator. - * - * <p> - * If no matching creator is found, delegates to the parent store's resolve method. - * - * @param <T> The bean type. - * @param beanType The bean type to resolve. - * @param name The bean name. Can be <jk>null</jk>. - * @return The supplier, or {@link Optional#empty()} if no matching creator or supplier exists. - */ - @Override - @SuppressWarnings({ - "unchecked" // Type erasure requires cast for supplier resolution - }) - protected <T> Optional<Supplier<T>> resolve(Class<T> beanType, String name) { - // First check if there's a creator for the exact bean type - var creator = creators.get(beanType); - if (nn(creator)) { - return opt(() -> (T)creator.run()); - } - - // Check all creators for matching bean types (for inheritance) - for (var entry : creators.entrySet()) { - var creatorBeanType = entry.getKey(); - var c = entry.getValue(); - - // Check if creator's bean type is assignable to the requested type - // (i.e., creator creates a subtype that can be cast to requested type) - if (nn(creatorBeanType) && beanType.isAssignableFrom(creatorBeanType)) { - return opt(() -> (T)c.run()); - } - - // Check builder types - if requested type matches a builder type, return the creator - var builderTypes = safeOpt(c::getBuilderTypes); - if (builderTypes.isPresent()) { - for (var builderType : builderTypes.get()) { - if (nn(builderType) && builderType.is(beanType)) { - return opt(() -> (T)c.getBuilder()); - } - } - } - } - - // If not found, delegate to parent - return super.resolve(beanType, name); - } - - @Override - public CreatableBeanStore clear() { - super.clear(); - creators.clear(); - return this; - } -} diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Named.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Named.java index 92438e7d2b..def1f6d446 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Named.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Named.java @@ -61,7 +61,7 @@ import java.lang.annotation.*; * * <h5 class='section'>See Also:</h5><ul> * <li class='ja'>{@link Name} - * <li class='jc'>{@link BeanStore} + * <li class='jc'>{@link org.apache.juneau.commons.inject.BeanStore} * </ul> */ @Documented diff --git a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/httppart/bean/RequestBeanPropertyMeta.java b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/httppart/bean/RequestBeanPropertyMeta.java index 3c33d2f8bf..943c797789 100644 --- a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/httppart/bean/RequestBeanPropertyMeta.java +++ b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/httppart/bean/RequestBeanPropertyMeta.java @@ -114,7 +114,7 @@ public class RequestBeanPropertyMeta { * * @return The HTTP part name, or <jk>null</jk> if it doesn't have a part name. */ - public String getPartName() { return schema == null ? null : schema.getName(); } + public String getPartName() { return schema.getName(); } /** * Returns the HTTP part type for this property (query parameter for example). diff --git a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanPropertyMeta.java b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanPropertyMeta.java index 9dec884706..59a167db4c 100644 --- a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanPropertyMeta.java +++ b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanPropertyMeta.java @@ -112,7 +112,7 @@ public class ResponseBeanPropertyMeta { * * @return The HTTP part name, or <jk>null</jk> if it doesn't have a part name. */ - public Optional<String> getPartName() { return opt(schema == null ? null : schema.getName()); } + public Optional<String> getPartName() { return opt(schema.getName()); } /** * Returns the HTTP part type for this property (query parameter for example). diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java index d6ba0deca1..25b538a18d 100644 --- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java +++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java @@ -2228,7 +2228,7 @@ public class RestContext extends Context { * * <p> * Per-op {@link RestOpArg} instances are resolved separately at per-op setup via the bean store - * (see {@link #findRestOperationArgs(Method, WritableBeanStore)}); this getter returns the class list + * (see {@link #findRestOperationArgs(Method, BeanStore)}); this getter returns the class list * that drives that resolution. * * <h5 class='section'>See Also:</h5><ul> diff --git a/juneau-utest/src/test/java/org/apache/juneau/commons/inject/BasicBeanStore_Test.java b/juneau-utest/src/test/java/org/apache/juneau/commons/inject/BasicBeanStore_Test.java index a84558d888..73a5822e51 100644 --- a/juneau-utest/src/test/java/org/apache/juneau/commons/inject/BasicBeanStore_Test.java +++ b/juneau-utest/src/test/java/org/apache/juneau/commons/inject/BasicBeanStore_Test.java @@ -23,6 +23,8 @@ import java.util.*; import java.util.stream.Stream; import org.apache.juneau.*; +import org.apache.juneau.annotation.Named; +import org.apache.juneau.commons.reflect.*; import org.junit.jupiter.api.*; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -1107,5 +1109,419 @@ class BasicBeanStore_Test extends TestBase { // "d" only in defaults. assertEquals("default-d", beans.get("d").getName()); } + + // ========================================================================= + // T - hasDefaultSupplier / getDefaultSupplier (uncovered paths) + // ========================================================================= + + @Test + void t01_hasDefaultSupplier_unnamed_delegatesToNamed() { + var store = new BasicBeanStore(null); + assertFalse(store.hasDefaultSupplier(TestBean.class)); + store.addDefaultSupplier(TestBean.class, () -> new TestBean("x")); + assertTrue(store.hasDefaultSupplier(TestBean.class)); + } + + @Test + void t02_hasDefaultSupplier_named_notRegistered_returnsFalse() { + var store = new BasicBeanStore(null); + assertFalse(store.hasDefaultSupplier(TestBean.class, "missing")); + } + + @Test + void t06_hasDefaultSupplier_named_typeMapExistsButKeyAbsent_returnsFalse() { + var store = new BasicBeanStore(null); + // Register a default for the unnamed variant so typeMap is non-null + store.addDefaultSupplier(TestBean.class, () -> new TestBean("unnamed")); + // Now check a different (named) key that was never registered + assertFalse(store.hasDefaultSupplier(TestBean.class, "unregistered-name")); + } + + @Test + void t03_getDefaultSupplier_unnamed_delegatesToNamed() { + var store = new BasicBeanStore(null); + assertTrue(store.getDefaultSupplier(TestBean.class).isEmpty()); + store.addDefaultSupplier(TestBean.class, () -> new TestBean("via-default")); + assertEquals("via-default", store.getDefaultSupplier(TestBean.class).orElseThrow().get().getName()); + } + + @Test + void t04_getDefaultSupplier_named_typeMapNull_returnsEmpty() { + var store = new BasicBeanStore(null); + assertTrue(store.getDefaultSupplier(TestBean.class, "nope").isEmpty()); + } + + @Test + void t05_getDefaultSupplier_named_supplierNull_returnsEmpty() { + var store = new BasicBeanStore(null); + store.addDefaultSupplier(TestBean.class, () -> new TestBean("other"), "other"); + assertTrue(store.getDefaultSupplier(TestBean.class, "missing").isEmpty()); + } + + // ========================================================================= + // U - createBeanFromMethod edge cases + // ========================================================================= + + static class U_Resource { + public static TestBean makeBean(String msg) { return new TestBean(msg); } + public static TestBean throwingFactory() { throw new RuntimeException("factory-boom"); } + } + + @Test + void u01_createBeanFromMethod_passClass_callsStaticMethod() { + var store = new BasicBeanStore(null); + store.addBean(String.class, "hello"); + var result = store.createBeanFromMethod(TestBean.class, U_Resource.class, null); + assertTrue(result.isPresent()); + assertEquals("hello", result.get().getName()); + } + + @Test + void u02_createBeanFromMethod_throwingFactory_wrapsBeanCreationException() { + var store = new BasicBeanStore(null); + assertThrows(BeanCreationException.class, + () -> store.createBeanFromMethod(TestBean.class, U_Resource.class, m -> m.getNameSimple().equals("throwingFactory"))); + } + + static class U_InstanceResource { + public TestBean makeBean(String msg) { return new TestBean(msg); } + } + + @Test + void u03_createBeanFromMethod_passInstance_callsInstanceMethod() { + var store = new BasicBeanStore(null); + store.addBean(String.class, "from-instance"); + var result = store.createBeanFromMethod(TestBean.class, new U_InstanceResource(), null); + assertTrue(result.isPresent()); + assertEquals("from-instance", result.get().getName()); + } + + static class U_NonStaticResource { + public TestBean makeBean() { return new TestBean("non-static"); } + } + + @Test + void u04_createBeanFromMethod_passClass_nonStaticMethodSkipped_returnsEmpty() { + // When passing a Class (resource=null), non-static methods are excluded (line 578 false branch). + var store = new BasicBeanStore(null); + var result = store.createBeanFromMethod(TestBean.class, U_NonStaticResource.class, null); + assertTrue(result.isEmpty()); + } + + @Test + void u05_createBeanFromMethod_methodHasUnresolvableParams_returnsEmpty() { + // makeBean needs a String but none is in store → canResolveAllParameters false branch + var store = new BasicBeanStore(null); + var result = store.createBeanFromMethod(TestBean.class, U_Resource.class, m -> m.getNameSimple().equals("makeBean")); + assertTrue(result.isEmpty()); + } + + // ========================================================================= + // V - toString / properties (defaults list, non-BasicBeanStore overriding parent) + // ========================================================================= + + @Test + void v01_toString_withDefaults_includesDefaultsSection() { + var store = new BasicBeanStore(null); + store.addDefaultSupplier(TestBean.class, () -> new TestBean("default")); + var s = store.toString(); + assertNotNull(s); + assertTrue(s.contains("defaults")); + } + + @Test + void v02a_toString_basicBeanStoreOverridingParent_usesProperties() { + var overriding = new BasicBeanStore(null); + overriding.addBean(TestBean.class, new TestBean("override-val")); + var store = new BasicBeanStore(null, overriding); + var s = store.toString(); + assertNotNull(s); + } + + @Test + void v02_toString_nonBasicOverridingParent_usesStringForm() { + var overriding = new BeanStore() { + @Override public <T> Optional<java.util.function.Supplier<T>> getBeanSupplier(Class<T> t) { return Optional.empty(); } + @Override public <T> Optional<java.util.function.Supplier<T>> getBeanSupplier(Class<T> t, String n) { return Optional.empty(); } + @Override public boolean hasBean(Class<?> t) { return false; } + @Override public boolean hasBean(Class<?> t, String n) { return false; } + @Override public <T> Optional<T> getBean(Class<T> t) { return Optional.empty(); } + @Override public <T> Optional<T> getBean(Class<T> t, String n) { return Optional.empty(); } + @Override public <T> java.util.Map<String, T> getBeansOfType(Class<T> t) { return java.util.Map.of(); } + }; + var store = new BasicBeanStore(null, overriding); + var s = store.toString(); + assertNotNull(s); + } + + // ========================================================================= + // W - WritableBeanStore.add() convenience defaults + // BasicBeanStore overrides add(), so we need a minimal wrapper that + // delegates only addBean() in order to exercise the interface defaults. + // ========================================================================= + + /** Minimal WritableBeanStore that does NOT override add(), relying on interface defaults. */ + static class MinimalWritable implements WritableBeanStore { + private final BasicBeanStore delegate = new BasicBeanStore(null); + + @Override public <T> WritableBeanStore addBean(Class<T> t, T b) { return delegate.addBean(t, b); } + @Override public <T> WritableBeanStore addBean(Class<T> t, T b, String n) { return delegate.addBean(t, b, n); } + @Override public <T> WritableBeanStore addSupplier(Class<T> t, java.util.function.Supplier<T> s) { return delegate.addSupplier(t, s); } + @Override public <T> WritableBeanStore addSupplier(Class<T> t, java.util.function.Supplier<T> s, String n) { return delegate.addSupplier(t, s, n); } + @Override public <T> WritableBeanStore addDefaultSupplier(Class<T> t, java.util.function.Supplier<T> s) { return delegate.addDefaultSupplier(t, s); } + @Override public <T> WritableBeanStore addDefaultSupplier(Class<T> t, java.util.function.Supplier<T> s, String n) { return delegate.addDefaultSupplier(t, s, n); } + @Override public WritableBeanStore clear() { return delegate.clear(); } + @Override public boolean hasDefaultSupplier(Class<?> t) { return delegate.hasDefaultSupplier(t); } + @Override public boolean hasDefaultSupplier(Class<?> t, String n) { return delegate.hasDefaultSupplier(t, n); } + @Override public <T> WritableBeanStore addBeanType(Class<T> t, Class<? extends T> i) { return delegate.addBeanType(t, i); } + @Override public <T> Optional<java.util.function.Supplier<T>> getBeanSupplier(Class<T> t) { return delegate.getBeanSupplier(t); } + @Override public <T> Optional<java.util.function.Supplier<T>> getBeanSupplier(Class<T> t, String n) { return delegate.getBeanSupplier(t, n); } + @Override public boolean hasBean(Class<?> t) { return delegate.hasBean(t); } + @Override public boolean hasBean(Class<?> t, String n) { return delegate.hasBean(t, n); } + @Override public <T> Optional<T> getBean(Class<T> t) { return delegate.getBean(t); } + @Override public <T> Optional<T> getBean(Class<T> t, String n) { return delegate.getBean(t, n); } + @Override public <T> java.util.Map<String, T> getBeansOfType(Class<T> t) { return delegate.getBeansOfType(t); } + } + + @Test + void w01_writableBeanStore_add_unnamed_returnsBean() { + var store = new MinimalWritable(); + var bean = new TestBean("fluent"); + var returned = store.add(TestBean.class, bean); + assertSame(bean, returned); + assertSame(bean, store.getBean(TestBean.class).orElseThrow()); + } + + @Test + void w02_writableBeanStore_add_named_returnsBean() { + var store = new MinimalWritable(); + var bean = new TestBean("fluent-named"); + var returned = store.add(TestBean.class, bean, "myBean"); + assertSame(bean, returned); + assertSame(bean, store.getBean(TestBean.class, "myBean").orElseThrow()); + } + + // ========================================================================= + // X - BeanStore default methods: outer-instance, Optional, named-qualifier + // ========================================================================= + + interface X_Iface { + TestBean create(Object outer, String msg); + TestBean createNamed(@Named("msg") String msg); + TestBean createOptional(Optional<String> optMsg); + TestBean createMissingNamed(@Named("absent") String msg); + TestBean createWithOptionalFirst(Optional<String> optFirst, @Named("extra") String extra); + } + + @Test + void x01_hasAllParams_outerInstanceSkippedAtPosition0() throws Exception { + var store = new BasicBeanStore(null); + store.addBean(String.class, "hello"); + var outer = new Object(); + var m = MethodInfo.of(X_Iface.class.getMethod("create", Object.class, String.class)); + assertTrue(store.hasAllParams(m, outer)); + } + + @Test + void x02_hasAllParams_optionalParamAlwaysSatisfied() throws Exception { + var store = new BasicBeanStore(null); + var m = MethodInfo.of(X_Iface.class.getMethod("createOptional", Optional.class)); + assertTrue(store.hasAllParams(m, null)); + } + + @Test + void x03_hasAllParams_namedQualifier_present() throws Exception { + var store = new BasicBeanStore(null); + store.addBean(String.class, "hi", "msg"); + var m = MethodInfo.of(X_Iface.class.getMethod("createNamed", String.class)); + assertTrue(store.hasAllParams(m, null)); + } + + @Test + void x04_hasAllParams_namedQualifier_absent_returnsFalse() throws Exception { + var store = new BasicBeanStore(null); + var m = MethodInfo.of(X_Iface.class.getMethod("createMissingNamed", String.class)); + assertFalse(store.hasAllParams(m, null)); + } + + @Test + void x05_getParams_outerInstanceInjectedAtPosition0() throws Exception { + var store = new BasicBeanStore(null); + store.addBean(String.class, "world"); + var outer = new Object(); + var m = MethodInfo.of(X_Iface.class.getMethod("create", Object.class, String.class)); + var params = store.getParams(m, outer); + assertSame(outer, params[0]); + assertEquals("world", params[1]); + } + + @Test + void x06_getParams_namedQualifier_resolved() throws Exception { + var store = new BasicBeanStore(null); + store.addBean(String.class, "named-val", "msg"); + var m = MethodInfo.of(X_Iface.class.getMethod("createNamed", String.class)); + var params = store.getParams(m, null); + assertEquals("named-val", params[0]); + } + + @Test + void x07_getParams_optionalParam_wrappedInOptional() throws Exception { + var store = new BasicBeanStore(null); + store.addBean(String.class, "opt-val"); + var m = MethodInfo.of(X_Iface.class.getMethod("createOptional", Optional.class)); + var params = store.getParams(m, null); + assertTrue(params[0] instanceof Optional); + assertEquals("opt-val", ((Optional<?>) params[0]).orElse(null)); + } + + @Test + void x08_getMissingParams_outerAndOptionalSkipped() throws Exception { + var store = new BasicBeanStore(null); + store.addBean(String.class, "present"); + var outer = new Object(); + var m = MethodInfo.of(X_Iface.class.getMethod("create", Object.class, String.class)); + assertNull(store.getMissingParams(m, outer)); + } + + @Test + void x09_getMissingParams_namedQualifier_absent_listed() throws Exception { + var store = new BasicBeanStore(null); + var m = MethodInfo.of(X_Iface.class.getMethod("createMissingNamed", String.class)); + var missing = store.getMissingParams(m, null); + assertNotNull(missing); + assertTrue(missing.contains("absent")); + } + + static class SimpleNoArgBean { + public SimpleNoArgBean() {} + } + + @Test + void x10_beanStore_instantiate_createsBean() { + var store = new BasicBeanStore(null); + var bean = store.instantiate(SimpleNoArgBean.class); + assertNotNull(bean); + } + + @Test + void x11_beanStore_createBeanFromMethod_twoArgOverload_delegatesToFullOverload() { + var store = new BasicBeanStore(null); + store.addBean(String.class, "world"); + var result = store.createBeanFromMethod(TestBean.class, U_Resource.class); + assertTrue(result.isPresent()); + assertEquals("world", result.get().getName()); + } + + @Test + void x12_getMissingParams_namedQualifier_present_notListedAsMissing() throws Exception { + var store = new BasicBeanStore(null); + store.addBean(String.class, "hi", "msg"); + var m = MethodInfo.of(X_Iface.class.getMethod("createNamed", String.class)); + assertNull(store.getMissingParams(m, null)); + } + + /** Minimal BeanStore that uses all BeanStore defaults (not overridden). */ + static class MinimalBeanStore implements BeanStore { + private final BasicBeanStore delegate = new BasicBeanStore(null); + @Override public <T> Optional<java.util.function.Supplier<T>> getBeanSupplier(Class<T> t) { return delegate.getBeanSupplier(t); } + @Override public <T> Optional<java.util.function.Supplier<T>> getBeanSupplier(Class<T> t, String n) { return delegate.getBeanSupplier(t, n); } + @Override public boolean hasBean(Class<?> t) { return delegate.hasBean(t); } + @Override public boolean hasBean(Class<?> t, String n) { return delegate.hasBean(t, n); } + @Override public <T> Optional<T> getBean(Class<T> t) { return delegate.getBean(t); } + @Override public <T> Optional<T> getBean(Class<T> t, String n) { return delegate.getBean(t, n); } + @Override public <T> java.util.Map<String, T> getBeansOfType(Class<T> t) { return delegate.getBeansOfType(t); } + } + + @Test + void x13_beanStore_defaultCreateBeanFromMethod_returnsEmpty() { + var store = new MinimalBeanStore(); + var result = store.createBeanFromMethod(TestBean.class, U_Resource.class, null); + assertTrue(result.isEmpty()); + } + + @Test + void x14_beanStore_defaultCreateBeanFromMethod_twoArg_returnsEmpty() { + var store = new MinimalBeanStore(); + var result = store.createBeanFromMethod(TestBean.class, U_Resource.class); + assertTrue(result.isEmpty()); + } + + @Test + void x15_beanStore_defaultGetBeanType_returnsEmpty() { + var store = new MinimalBeanStore(); + assertTrue(store.getBeanType(TestBean.class).isEmpty()); + } + + interface X_BasicIface { + TestBean create(String msg); + TestBean createMissingUnnamed(Integer missingInt); + } + + @Test + void x16_hasAllParams_unnamedParam_absent_returnsFalse() throws Exception { + var store = new BasicBeanStore(null); + // no Integer bean in store + var m = MethodInfo.of(X_BasicIface.class.getMethod("createMissingUnnamed", Integer.class)); + assertFalse(store.hasAllParams(m, null)); + } + + @Test + void x17_getMissingParams_unnamedBean_absent_listed() throws Exception { + var store = new BasicBeanStore(null); + // Integer is not in the store + var m = MethodInfo.of(X_BasicIface.class.getMethod("createMissingUnnamed", Integer.class)); + var missing = store.getMissingParams(m, null); + assertNotNull(missing); + assertTrue(missing.contains("Integer")); + } + + @Test + void x18_getMissingParams_optionalParamAtNonZero_skipped() throws Exception { + // createWithOptionalFirst: i=0 → Optional (D path of condition), i=1 → named "extra" + var store = new BasicBeanStore(null); + store.addBean(String.class, "val", "extra"); + var m = MethodInfo.of(X_Iface.class.getMethod("createWithOptionalFirst", Optional.class, String.class)); + // Optional at i=0 is always satisfied (skipped); named "extra" is in store → null + assertNull(store.getMissingParams(m, null)); + } + + @Test + void x19_getMissingParams_outerNonNull_typeNoMatch_paramCheckedNormally() throws Exception { + // i=0, nn(outer)=true, !pt.isInstance(outer) → falls through to check bean + var store = new BasicBeanStore(null); + store.addBean(Integer.class, 42); + var outer = "type-mismatch"; // outer is String, param[0] is Integer → no skip + var m = MethodInfo.of(X_BasicIface.class.getMethod("createMissingUnnamed", Integer.class)); + // Integer IS in store → no missing params + assertNull(store.getMissingParams(m, outer)); + } + + // ========================================================================= + // Y - BeanCreationException constructors + // ========================================================================= + + @Test + void y01_beanCreationException_messageOnly() { + var ex = new BeanCreationException("msg"); + assertEquals("msg", ex.getMessage()); + assertNull(ex.getCause()); + } + + @Test + void y02_beanCreationException_causeOnly() { + var cause = new RuntimeException("cause"); + var ex = new BeanCreationException(cause); + assertSame(cause, ex.getCause()); + } + + @Test + void y03_beanCreationException_messageAndCause_coveredByU02() { + // BeanCreationException(String, Throwable) is exercised by u02 above; + // verify direct construction too for completeness. + var cause = new IllegalStateException("boom"); + var ex = new BeanCreationException("wrapper", cause); + assertEquals("wrapper", ex.getMessage()); + assertSame(cause, ex.getCause()); + } } diff --git a/juneau-utest/src/test/java/org/apache/juneau/commons/inject/BeanInstantiator_Test.java b/juneau-utest/src/test/java/org/apache/juneau/commons/inject/BeanInstantiator_Test.java index 2bdcc6e104..6d6951df54 100644 --- a/juneau-utest/src/test/java/org/apache/juneau/commons/inject/BeanInstantiator_Test.java +++ b/juneau-utest/src/test/java/org/apache/juneau/commons/inject/BeanInstantiator_Test.java @@ -4118,5 +4118,69 @@ class BeanInstantiator_Test extends TestBase { } } + //==================================================================================================== + // U - Convenience statics: createOrNull, optionalOf, createOrDefault + //==================================================================================================== + @Nested + @DisplayName("U - Convenience statics") + class U_convenienceStatics { + + public static class U_Bean { + final String value; + public U_Bean() { this.value = "created"; } + } + + // --- createOrNull --- + + @Test + @DisplayName("U01 - createOrNull(null) returns null") + void u01_createOrNull_null() { + assertNull(BeanInstantiator.createOrNull(null)); + } + + @Test + @DisplayName("U02 - createOrNull(Class) instantiates the bean") + void u02_createOrNull_nonNull() { + var bean = BeanInstantiator.createOrNull(U_Bean.class); + assertNotNull(bean); + assertEquals("created", bean.value); + } + + // --- optionalOf --- + + @Test + @DisplayName("U03 - optionalOf(null) returns Optional.empty()") + void u03_optionalOf_null() { + assertTrue(BeanInstantiator.optionalOf(null).isEmpty()); + } + + @Test + @DisplayName("U04 - optionalOf(Class) returns Optional containing the bean") + void u04_optionalOf_nonNull() { + var opt = BeanInstantiator.optionalOf(U_Bean.class); + assertTrue(opt.isPresent()); + assertEquals("created", opt.get().value); + } + + // --- createOrDefault --- + + @Test + @DisplayName("U05 - createOrDefault(null, default) returns the default") + void u05_createOrDefault_null() { + var fallback = new U_Bean(); + var result = BeanInstantiator.createOrDefault(null, fallback); + assertSame(fallback, result); + } + + @Test + @DisplayName("U06 - createOrDefault(Class, default) instantiates the bean") + void u06_createOrDefault_nonNull() { + var fallback = new U_Bean(); + var result = BeanInstantiator.createOrDefault(U_Bean.class, fallback); + assertNotNull(result); + assertNotSame(fallback, result, "Should create a new instance, not return the default"); + } + } + } diff --git a/juneau-utest/src/test/java/org/apache/juneau/encoders/EncoderSet_Test.java b/juneau-utest/src/test/java/org/apache/juneau/encoders/EncoderSet_Test.java index d7871f8437..fbbd0cc8fa 100755 --- a/juneau-utest/src/test/java/org/apache/juneau/encoders/EncoderSet_Test.java +++ b/juneau-utest/src/test/java/org/apache/juneau/encoders/EncoderSet_Test.java @@ -21,6 +21,7 @@ import static org.apache.juneau.junit.bct.BctAssertions.*; import static org.junit.jupiter.api.Assertions.*; import org.apache.juneau.*; +import org.apache.juneau.commons.inject.*; import org.junit.jupiter.api.*; class EncoderSet_Test extends TestBase { @@ -114,4 +115,96 @@ class EncoderSet_Test extends TestBase { return a("E5"); } } + + //==================================================================================================== + // Builder edge-case coverage + //==================================================================================================== + + @Test void b01_builder_addInstancesDirectly() { + var instance = new E1(); + var s = EncoderSet.create().add(instance).build(); + assertInstanceOf(E1.class, s.getEncoder("E1")); + } + + @Test void b02_builder_addNoInheritClearsEntries() { + var sb = EncoderSet.create().add(E1.class, E2.class); + assertEquals(2, sb.inner().size()); + // Adding NoInherit triggers clear() on the first pass; E1 and E2 are gone + sb.add(EncoderSet.NoInherit.class, E3.class); + assertTrue(sb.inner().stream().noneMatch(x -> x.equals(E1.class))); + assertTrue(sb.inner().stream().noneMatch(x -> x.equals(E2.class))); + } + + @Test void b03_builder_addInvalidClassThrows() { + assertThrows(IllegalArgumentException.class, () -> + EncoderSet.create().add(String.class)); + } + + @Test void b04_builder_setWithInherit() { + var sb = EncoderSet.create().add(E1.class, E2.class); + // Inherit preserves existing entries and inserts them + sb.set(E3.class, EncoderSet.Inherit.class); + var s = sb.build(); + var codings = s.getSupportedEncodings(); + assertTrue(codings.contains("E3")); + assertTrue(codings.contains("E1")); + assertTrue(codings.contains("E2")); + } + + @Test void b05_builder_setWithInvalidClassThrows() { + assertThrows(IllegalArgumentException.class, () -> + EncoderSet.create().set(String.class)); + } + + @Test void b06_builder_implBypassesBuild() { + var preset = EncoderSet.create().add(E1.class).build(); + var s = EncoderSet.create().impl(preset).build(); + assertSame(preset, s); + } + + @Test void b07_builder_inner_returnsEntries() { + var sb = EncoderSet.create().add(E1.class); + var inner = sb.inner(); + assertFalse(inner.isEmpty()); + } + + @Test void b08_builder_isEmpty() { + var sb = EncoderSet.create(); + assertTrue(sb.isEmpty()); + sb.add(E1.class); + assertFalse(sb.isEmpty()); + } + + @Test void b09_builder_toStringWithClass() { + var sb = EncoderSet.create().add(E1.class); + var s = sb.toString(); + assertTrue(s.contains("class:")); + } + + @Test void b10_builder_toStringWithInstance() { + var sb = EncoderSet.create().add(new E1()); + var s = sb.toString(); + assertTrue(s.contains("object:")); + } + + @Test void b11_builder_toStringWithNull() { + var sb = EncoderSet.create(); + sb.inner().add(null); + var s = sb.toString(); + assertTrue(s.contains("null")); + } + + @Test void b12_builder_create_withBeanStore() { + var bs = new BasicBeanStore(null); + var sb = EncoderSet.create(bs); + assertSame(bs, sb.beanStore()); + } + + @Test void b13_builder_copy_isIndependent() { + var sb1 = EncoderSet.create().add(E1.class); + var sb2 = sb1.copy(); + sb2.add(E2.class); + // sb1 should not have E2 + assertFalse(sb1.inner().stream().anyMatch(x -> x.equals(E2.class))); + } } \ No newline at end of file diff --git a/juneau-utest/src/test/java/org/apache/juneau/httppart/bean/HttpPartBeanMeta_Test.java b/juneau-utest/src/test/java/org/apache/juneau/httppart/bean/HttpPartBeanMeta_Test.java new file mode 100644 index 0000000000..031c71243c --- /dev/null +++ b/juneau-utest/src/test/java/org/apache/juneau/httppart/bean/HttpPartBeanMeta_Test.java @@ -0,0 +1,163 @@ +/* + * 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.juneau.httppart.bean; + +import static org.apache.juneau.commons.httppart.HttpPartType.*; +import static org.junit.jupiter.api.Assertions.*; + +import java.util.*; + +import org.apache.juneau.*; +import org.apache.juneau.commons.httppart.*; +import org.apache.juneau.commons.reflect.*; +import org.apache.juneau.http.annotation.*; +import org.apache.juneau.httppart.*; +import org.apache.juneau.uon.*; +import org.junit.jupiter.api.*; + +/** + * Tests for uncovered getter paths in RequestBeanPropertyMeta and ResponseBeanPropertyMeta. + */ +class HttpPartBeanMeta_Test { + + // -------------------------------------------------- + // RequestBeanPropertyMeta + // -------------------------------------------------- + + static class ReqInterface { + @Query("q") public String getQuery() { return "q-value"; } + @Header("h") public String getHeader() { return "h-value"; } + } + + private RequestBeanPropertyMeta buildReqMeta(String methodName, HttpPartSerializer serializer, HttpPartParser parser) throws Exception { + var m = MethodInfo.of(ReqInterface.class.getMethod(methodName)); + var b = RequestBeanPropertyMeta.create(QUERY, Query.class, m); + return b.build(serializer, parser); + } + + @Test + void req01_getGetter_returnsMethod() throws Exception { + var meta = buildReqMeta("getQuery", null, null); + assertEquals("getQuery", meta.getGetter().getName()); + } + + @Test + void req02_getPartType_returnsQueryType() throws Exception { + var meta = buildReqMeta("getQuery", null, null); + assertEquals(QUERY, meta.getPartType()); + } + + @Test + void req03_getSchema_returnsNonNull() throws Exception { + var meta = buildReqMeta("getQuery", null, null); + assertNotNull(meta.getSchema()); + assertEquals("q", meta.getPartName()); + } + + @Test + void req04_getParser_parserNull_returnsDefault() throws Exception { + var meta = buildReqMeta("getQuery", null, null); + var session = mock(HttpPartParserSession.class); + assertSame(session, meta.getParser(session)); + } + + @Test + void req04b_getParser_parserNotNull_returnsParserSession() throws Exception { + // Pass a real parser as the default; schema has no parser class, so meta.parser = the default parser + var meta = buildReqMeta("getQuery", null, UonParser.DEFAULT); + var result = meta.getParser(null); + assertNotNull(result); + } + + @Test + void req05_getSerializer_noSchemaSerializer_returnsEmpty() throws Exception { + var meta = buildReqMeta("getQuery", null, null); + assertFalse(meta.getSerializer().isPresent()); + } + + // -------------------------------------------------- + // ResponseBeanPropertyMeta + // -------------------------------------------------- + + static class RespInterface { + @Header("x-status") public int getStatus() { return 200; } + @Content public String getBody() { return "body"; } + } + + private ResponseBeanPropertyMeta buildRespMeta(String methodName) throws Exception { + var m = MethodInfo.of(RespInterface.class.getMethod(methodName)); + var b = ResponseBeanPropertyMeta.create(HEADER, m); + return b.build(Optional.empty(), Optional.empty()); + } + + @Test + void resp01_getGetter_returnsMethod() throws Exception { + var meta = buildRespMeta("getStatus"); + assertEquals("getStatus", meta.getGetter().getName()); + } + + @Test + void resp02_getPartType_returnsHeaderType() throws Exception { + var meta = buildRespMeta("getStatus"); + assertEquals(HEADER, meta.getPartType()); + } + + @Test + void resp03_getSchema_returnsNonNull() throws Exception { + var meta = buildRespMeta("getStatus"); + assertNotNull(meta.getSchema()); + } + + @Test + void resp04_getPartName_withSchema_returnsName() throws Exception { + var schema = HttpPartSchema.create().name("x-status").build(); + var m = MethodInfo.of(RespInterface.class.getMethod("getStatus")); + var b = ResponseBeanPropertyMeta.create(HEADER, schema, m); + var meta = b.build(Optional.empty(), Optional.empty()); + assertTrue(meta.getPartName().isPresent()); + assertEquals("x-status", meta.getPartName().orElseThrow()); + } + + @Test + void resp05_getParser_noSchemaParser_returnsEmpty() throws Exception { + var meta = buildRespMeta("getStatus"); + assertFalse(meta.getParser().isPresent()); + } + + @Test + void resp06_getSerializer_noSchemaSerializer_returnsEmpty() throws Exception { + var meta = buildRespMeta("getStatus"); + assertFalse(meta.getSerializer().isPresent()); + } + + @Test + void resp07_builderName_setsName() throws Exception { + // ResponseBeanPropertyMeta.Builder.name() is a package-private setter — cover it here. + var m = MethodInfo.of(RespInterface.class.getMethod("getStatus")); + var b = ResponseBeanPropertyMeta.create(HEADER, m); + b.name("overridden"); // exercises the name() setter (lines 54-56) + var meta = b.build(Optional.empty(), Optional.empty()); + assertNotNull(meta); + } + + // Minimal mock to avoid requiring full HttpPartParserSession instantiation + private static HttpPartParserSession mock(Class<HttpPartParserSession> cls) { + return new HttpPartParserSession() { + @Override public <T> T parse(HttpPartType partType, HttpPartSchema schema, String in, ClassMeta<T> type) { return null; } + }; + } +} diff --git a/juneau-utest/src/test/java/org/apache/juneau/parser/ParserSet_Test.java b/juneau-utest/src/test/java/org/apache/juneau/parser/ParserSet_Test.java index b8400a8a23..7e93143078 100755 --- a/juneau-utest/src/test/java/org/apache/juneau/parser/ParserSet_Test.java +++ b/juneau-utest/src/test/java/org/apache/juneau/parser/ParserSet_Test.java @@ -20,6 +20,8 @@ import static org.apache.juneau.junit.bct.BctAssertions.*; import static org.junit.jupiter.api.Assertions.*; import org.apache.juneau.*; +import org.apache.juneau.commons.http.MediaType; +import org.apache.juneau.commons.inject.*; import org.apache.juneau.json.*; import org.junit.jupiter.api.*; @@ -73,4 +75,160 @@ class ParserSet_Test extends TestBase { public static class P3 extends JsonParser { public P3(JsonParser.Builder b) { super(b.consumes("text/3")); }} public static class P4 extends JsonParser { public P4(JsonParser.Builder b) { super(b.consumes("text/4,text/4a"));} } public static class P5 extends JsonParser { public P5(JsonParser.Builder b) { super(b.consumes("text/5"));}} + + public static class SimpleParser extends JsonParser { + public SimpleParser() { + super(JsonParser.create().consumes("text/simple")); + } + } + + //==================================================================================================== + // Builder edge-case coverage + //==================================================================================================== + + @Test void b01_builder_addInstancesDirectly() { + var instance = new P1(JsonParser.create().consumes("text/1")); + var s = ParserSet.create().add(instance).build(); + assertInstanceOf(P1.class, s.getParser("text/1")); + } + + @Test void b02_builder_clear_removesAllEntries() { + var sb = ParserSet.create().add(P1.class, P2.class); + assertEquals(2, sb.inner().size()); + sb.clear(); + assertTrue(sb.inner().isEmpty()); + } + + @Test void b03_builder_addInvalidClassThrows() { + assertThrows(RuntimeException.class, () -> + ParserSet.create().add(String.class)); + } + + @Test void b04_builder_setWithInherit() { + var sb = ParserSet.create().add(P1.class, P2.class); + sb.set(ParserSet.Inherit.class, P3.class); + var s = sb.build(); + assertInstanceOf(P1.class, s.getParser("text/1")); + assertInstanceOf(P2.class, s.getParser("text/2")); + assertInstanceOf(P3.class, s.getParser("text/3")); + } + + @Test void b05_builder_setWithInvalidClassThrows() { + assertThrows(RuntimeException.class, () -> + ParserSet.create().set(String.class)); + } + + @Test void b06_builder_implBypassesBuild() { + var preset = ParserSet.create().add(P1.class).build(); + var s = ParserSet.create().impl(preset).build(); + assertSame(preset, s); + } + + @Test void b07_builder_inner_returnsEntries() { + var sb = ParserSet.create().add(P1.class); + assertFalse(sb.inner().isEmpty()); + } + + @Test void b08_builder_toStringWithBuilder() { + var sb = ParserSet.create().add(JsonParser.class); + assertTrue(sb.toString().contains("builder:")); + } + + @Test void b09_builder_toStringWithInstance() { + var sb = ParserSet.create().add(new P1(JsonParser.create().consumes("text/1"))); + assertTrue(sb.toString().contains("parser:")); + } + + @Test void b10_builder_toStringWithNull() { + var sb = ParserSet.create(); + sb.inner().add(null); + assertTrue(sb.toString().contains("null")); + } + + @Test void b11_builder_create_withBeanStore() { + var bs = new BasicBeanStore(null); + var sb = ParserSet.create(bs); + assertSame(bs, sb.beanStore()); + } + + @Test void b12_builder_copy_isIndependent() { + var sb1 = ParserSet.create().add(P1.class); + var sb2 = sb1.copy(); + sb2.add(P2.class); + assertTrue(sb2.build().getParser("text/2") instanceof P2); + assertNull(sb1.build().getParser("text/2")); + } + + @Test void b13_builder_beanContext_propagatesToBuilders() { + var bcb = BeanContext.create(); + var sb = ParserSet.create().add(JsonParser.class); + sb.beanContext(bcb); + assertNotNull(sb.beanStore()); + } + + @Test void b14_builder_beanContext_consumer_withNonNullBcBuilder() { + boolean[] called = {false}; + var sb = ParserSet.create().add(JsonParser.class); + sb.beanContext(BeanContext.create()); + sb.beanContext((BeanContext.Builder b) -> called[0] = true); + assertTrue(called[0]); + } + + @Test void b15_builder_beanContext_consumer_withNullBcBuilder_isNoop() { + boolean[] called = {false}; + var sb = ParserSet.create().add(JsonParser.class); + // bcBuilder is null by default — consumer should NOT be called + sb.beanContext((BeanContext.Builder b) -> called[0] = true); + assertFalse(called[0]); + } + + @Test void b16_builder_forEachRP_actsOnReaderParsers() { + int[] count = {0}; + ParserSet.create().add(JsonParser.class).forEachRP(b -> count[0]++); + assertEquals(1, count[0]); + } + + @Test void b17_builder_forEachISP_noAction_whenNoISPEntries() { + int[] count = {0}; + ParserSet.create().add(JsonParser.class).forEachISP(b -> count[0]++); + assertEquals(0, count[0]); + } + + @Test void b18_builder_copy_withBcBuilder_propagatesBcBuilder() { + var sb = ParserSet.create().add(JsonParser.class); + sb.beanContext(BeanContext.create()); + var copy = sb.copy(); + assertNotNull(copy.beanStore()); + } + + @Test void b19_builder_addAfterBcBuilder_propagatesContext() { + var sb = ParserSet.create(); + sb.beanContext(BeanContext.create()); + sb.add(JsonParser.class); + assertFalse(sb.inner().isEmpty()); + } + + @Test void b20_parserSet_copy_returnsNewBuilder() { + var s = ParserSet.create().add(P1.class).build(); + var copy = s.copy(); + assertInstanceOf(P1.class, copy.build().getParser("text/1")); + } + + @Test void b21_getParser_byMediaType() { + var s = ParserSet.create().add(P1.class).build(); + assertInstanceOf(P1.class, s.getParser(MediaType.of("text/1"))); + assertNull(s.getParser(MediaType.of("text/unknown"))); + } + + @Test void b22_add_parserWithNoArgConstructor_instantiatesDirectly() { + var s = ParserSet.create().add(SimpleParser.class).build(); + assertInstanceOf(SimpleParser.class, s.getParser("text/simple")); + } + + @Test void b23_copy_withParserInstance_coversNonBuilderBranch() { + var instance = new SimpleParser(); + var sb = ParserSet.create().add(instance); + var copy = sb.copy(); + assertFalse(copy.inner().isEmpty()); + } } \ No newline at end of file diff --git a/juneau-utest/src/test/java/org/apache/juneau/rest/client/remote/RemoteOperationArg_Test.java b/juneau-utest/src/test/java/org/apache/juneau/rest/client/remote/RemoteOperationArg_Test.java new file mode 100644 index 0000000000..4f5976b70d --- /dev/null +++ b/juneau-utest/src/test/java/org/apache/juneau/rest/client/remote/RemoteOperationArg_Test.java @@ -0,0 +1,75 @@ +/* + * 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.juneau.rest.client.remote; + +import static org.apache.juneau.commons.httppart.HttpPartType.*; +import static org.junit.jupiter.api.Assertions.*; + +import org.apache.juneau.commons.reflect.*; +import org.apache.juneau.http.annotation.*; +import org.junit.jupiter.api.*; + +/** + * Tests for the {@link RemoteOperationArg#create(ParameterInfo)} factory, + * specifically the branches not yet covered by integration tests. + */ +class RemoteOperationArg_Test { + + // Interface whose single method covers the PathRemainder branch + interface PathRemainderIface { + void get(@PathRemainder String remainder); + } + + @Test + void a01_create_pathRemainder_returnsPATHWithSlashStar() throws Exception { + var m = MethodInfo.of(PathRemainderIface.class.getMethod("get", String.class)); + var pi = m.getParameter(0); + var arg = RemoteOperationArg.create(pi); + assertNotNull(arg, "create() should return non-null for @PathRemainder param"); + assertEquals(PATH, arg.getPartType()); + assertEquals("/*", arg.getName()); + } + + // Verify null is returned for unannotated parameters + interface NoAnnotationIface { + void get(String plain); + } + + @Test + void a02_create_noAnnotation_returnsNull() throws Exception { + var m = MethodInfo.of(NoAnnotationIface.class.getMethod("get", String.class)); + var pi = m.getParameter(0); + assertNull(RemoteOperationArg.create(pi)); + } + + // Verify @Path branch (already covered upstream, but check getName / getSchema) + interface PathIface { + void get(@Path("id") String id); + } + + @Test + void a03_create_path_returnsCorrectArg() throws Exception { + var m = MethodInfo.of(PathIface.class.getMethod("get", String.class)); + var pi = m.getParameter(0); + var arg = RemoteOperationArg.create(pi); + assertNotNull(arg); + assertEquals(PATH, arg.getPartType()); + assertEquals("id", arg.getName()); + assertEquals(0, arg.getIndex()); + assertNotNull(arg.getSchema()); + } +} diff --git a/juneau-utest/src/test/java/org/apache/juneau/serializer/SerializerSet_Test.java b/juneau-utest/src/test/java/org/apache/juneau/serializer/SerializerSet_Test.java index 60a451e511..eb423e1a30 100755 --- a/juneau-utest/src/test/java/org/apache/juneau/serializer/SerializerSet_Test.java +++ b/juneau-utest/src/test/java/org/apache/juneau/serializer/SerializerSet_Test.java @@ -20,6 +20,8 @@ import static org.apache.juneau.junit.bct.BctAssertions.*; import static org.junit.jupiter.api.Assertions.*; import org.apache.juneau.*; +import org.apache.juneau.commons.http.MediaType; +import org.apache.juneau.commons.inject.*; import org.apache.juneau.json.*; import org.junit.jupiter.api.*; @@ -145,4 +147,185 @@ class SerializerSet_Test extends TestBase { super(builder.accept("*/*")); } } + + public static class SimpleSerializer extends JsonSerializer { + public SimpleSerializer() { + super(JsonSerializer.create().accept("text/simple")); + } + } + + //==================================================================================================== + // Builder edge-case coverage + //==================================================================================================== + + @Test void b01_builder_addInstancesDirectly() { + var instance = new SB1(JsonSerializer.create().accept("text/1")); + var s = SerializerSet.create().add(instance).build(); + assertInstanceOf(SB1.class, s.getSerializer("text/1")); + } + + @Test void b02_builder_addInvalidClassThrows() { + assertThrows(RuntimeException.class, () -> + SerializerSet.create().add(String.class)); + } + + @Test void b03_builder_setWithInherit() { + var sb = SerializerSet.create().add(SB1.class, SB2.class); + sb.set(SerializerSet.Inherit.class, SB3.class); + var s = sb.build(); + assertInstanceOf(SB1.class, s.getSerializer("text/1")); + assertInstanceOf(SB2.class, s.getSerializer("text/2")); + assertInstanceOf(SB3.class, s.getSerializer("text/3")); + } + + @Test void b04_builder_setWithInvalidClassThrows() { + assertThrows(RuntimeException.class, () -> + SerializerSet.create().set(String.class)); + } + + @Test void b05_builder_implBypassesBuild() { + var preset = SerializerSet.create().add(SB1.class).build(); + var s = SerializerSet.create().impl(preset).build(); + assertSame(preset, s); + } + + @Test void b06_builder_inner_returnsEntries() { + var sb = SerializerSet.create().add(SB1.class); + assertFalse(sb.inner().isEmpty()); + } + + @Test void b07_builder_toStringWithBuilder() { + var sb = SerializerSet.create().add(JsonSerializer.class); + assertTrue(sb.toString().contains("builder:")); + } + + @Test void b08_builder_toStringWithInstance() { + var sb = SerializerSet.create().add(new SB1(JsonSerializer.create().accept("text/1"))); + assertTrue(sb.toString().contains("serializer:")); + } + + @Test void b09_builder_toStringWithNull() { + var sb = SerializerSet.create(); + sb.inner().add(null); + assertTrue(sb.toString().contains("null")); + } + + @Test void b10_builder_create_withBeanStore() { + var bs = new BasicBeanStore(null); + var sb = SerializerSet.create(bs); + assertSame(bs, sb.beanStore()); + } + + @Test void b11_builder_copy_isIndependent() { + var sb1 = SerializerSet.create().add(SB1.class); + var sb2 = sb1.copy(); + sb2.add(SB2.class); + assertNotNull(sb2.build().getSerializer("text/2")); + assertNull(sb1.build().getSerializer("text/2")); + } + + @Test void b12_builder_beanContext_propagatesToBuilders() { + var bcb = BeanContext.create(); + var sb = SerializerSet.create().add(JsonSerializer.class); + sb.beanContext(bcb); + assertNotNull(sb.beanStore()); + } + + @Test void b13_builder_beanContext_consumer_withNonNullBcBuilder() { + boolean[] called = {false}; + var sb = SerializerSet.create().add(JsonSerializer.class); + sb.beanContext(BeanContext.create()); + sb.beanContext((BeanContext.Builder b) -> called[0] = true); + assertTrue(called[0]); + } + + @Test void b14_builder_beanContext_consumer_withNullBcBuilder_isNoop() { + boolean[] called = {false}; + var sb = SerializerSet.create().add(JsonSerializer.class); + sb.beanContext((BeanContext.Builder b) -> called[0] = true); + assertFalse(called[0]); + } + + @Test void b15_builder_forEachWS_actsOnWriterSerializers() { + int[] count = {0}; + SerializerSet.create().add(JsonSerializer.class).forEachWS(b -> count[0]++); + assertEquals(1, count[0]); + } + + @Test void b16_builder_forEachOSS_noAction_whenNoOSSEntries() { + int[] count = {0}; + SerializerSet.create().add(JsonSerializer.class).forEachOSS(b -> count[0]++); + assertEquals(0, count[0]); + } + + @Test void b17_builder_copy_withBcBuilder_propagatesBcBuilder() { + var sb = SerializerSet.create().add(JsonSerializer.class); + sb.beanContext(BeanContext.create()); + var copy = sb.copy(); + assertNotNull(copy.beanStore()); + } + + @Test void b18_builder_addAfterBcBuilder_propagatesContext() { + var sb = SerializerSet.create(); + sb.beanContext(BeanContext.create()); + sb.add(JsonSerializer.class); + assertFalse(sb.inner().isEmpty()); + } + + @Test void b19_serializerSet_copy_returnsNewBuilder() { + var s = SerializerSet.create().add(SB1.class).build(); + var copy = s.copy(); + assertInstanceOf(SB1.class, copy.build().getSerializer("text/1")); + } + + @Test void b20_getSerializer_byMediaType() { + var s = SerializerSet.create().add(SB1.class).build(); + assertInstanceOf(SB1.class, s.getSerializer(MediaType.of("text/1"))); + assertNull(s.getSerializer(MediaType.of("text/unknown"))); + } + + @Test void b21_getSerializer_nullMediaType_returnsNull() { + var s = SerializerSet.create().add(SB1.class).build(); + assertNull(s.getSerializer((MediaType) null)); + } + + @Test void b22_builder_clear_removesAllEntries() { + var sb = SerializerSet.create().add(SB1.class, SB2.class); + assertEquals(2, sb.inner().size()); + sb.clear(); + assertTrue(sb.inner().isEmpty()); + } + + @Test void b23_getSerializerMatch_byMediaType() { + var s = SerializerSet.create().add(SB1.class).build(); + assertNotNull(s.getSerializerMatch(MediaType.of("text/1"))); + } + + @Test void b24_getSerializerMatch_nullString_returnsNull() { + var s = SerializerSet.create().add(SB1.class).build(); + assertNull(s.getSerializerMatch((String) null)); + } + + @Test void b25_getWriterSerializer_byMediaType() { + var s = SerializerSet.create().add(SB1.class).build(); + assertInstanceOf(SB1.class, s.getWriterSerializer(MediaType.of("text/1"))); + } + + @Test void b26_add_serializerWithNoArgConstructor_instantiatesDirectly() { + var s = SerializerSet.create().add(SimpleSerializer.class).build(); + assertInstanceOf(SimpleSerializer.class, s.getSerializer("text/simple")); + } + + @Test void b27_copy_withSerializerInstance_coversNonBuilderBranch() { + var instance = new SimpleSerializer(); + var sb = SerializerSet.create().add(instance); + var copy = sb.copy(); + assertFalse(copy.inner().isEmpty()); + } + + @Test void b28_getStreamSerializer_withNoMatch_returnsNull() { + var s = SerializerSet.create().add(SB1.class).build(); + assertNull(s.getStreamSerializer(MediaType.of("text/unknown"))); + assertNull(s.getStreamSerializer("text/unknown")); + } } \ No newline at end of file
