001// Generated by delombok at Sun Jul 30 17:21:56 UTC 2023
002package de.cuioss.test.jsf.converter;
003
004import static org.junit.jupiter.api.Assertions.assertEquals;
005import static org.junit.jupiter.api.Assertions.assertThrows;
006import static org.junit.jupiter.api.Assertions.fail;
007import javax.faces.component.UIComponent;
008import javax.faces.component.html.HtmlInputText;
009import javax.faces.context.FacesContext;
010import javax.faces.convert.Converter;
011import javax.faces.convert.ConverterException;
012import org.junit.jupiter.api.BeforeEach;
013import org.junit.jupiter.api.Test;
014import de.cuioss.test.generator.junit.EnableGeneratorController;
015import de.cuioss.test.jsf.config.ComponentConfigurator;
016import de.cuioss.test.jsf.config.decorator.ComponentConfigDecorator;
017import de.cuioss.test.jsf.junit5.EnableJsfEnvironment;
018import de.cuioss.test.jsf.junit5.JsfEnabledTestEnvironment;
019import de.cuioss.test.valueobjects.objects.ConfigurationCallBackHandler;
020import de.cuioss.test.valueobjects.objects.impl.DefaultInstantiator;
021import de.cuioss.tools.reflect.MoreReflection;
022// owolff we need to migrate this aspect later
023/**
024 * Base class for testing implementations of {@link Converter} within a Junit 5
025 * context
026 * <h3>Setup</h3>
027 * <p>
028 * The actual test must provide {@link EnableJsfEnvironment}, for the basic
029 * test-infrastructure. See the class-documentation for details.
030 * </p>
031 * <p>
032 * {@link #initConverter()}: Instantiates the concrete {@link Converter} using
033 * reflection. After this the method calls {@link #configure(Object)} that can
034 * be used for further configuration of the {@link Converter}
035 * </p>
036 * <p>
037 * In case you want you want to create the {@link Converter} yourself you can
038 * overwrite {@link #getConverter()}
039 * </p>
040 * <p>
041 * In case you want a pass another {@link UIComponent} as parameter to the
042 * {@link Converter} you can overwrite {@link #getComponent()}
043 * </p>
044 * <h3>Test-Methods</h3> The core test-methods are:
045 * <ul>
046 * <li>{@link #shouldFailOnInvalidObjects()}</li>
047 * <li>{@link #shouldFailOnInvalidStrings()}</li>
048 * <li>{@link #shouldPassOnValidObjects()}</li>
049 * <li>{@link #shouldPassOnValidStrings()}</li>
050 * <li>{@link #shouldRoundTripValidData()}</li>
051 * </ul>
052 * They call {@link #populate(TestItems)} in oder to create corresponding
053 * test-data. The implementation is in the actual test-class.
054 * <h3>API-Test</h3> The api as defined within {@link Converter} is tested with
055 * the methods
056 * <ul>
057 * <li>{@link #shouldFailOnNullComponentOnGetAsObject()}</li>
058 * <li>{@link #shouldFailOnNullComponentOnGetAsString()}</li>
059 * <li>{@link #shouldFailOnNullFacesContextOnGetAsObject()}</li>
060 * <li>{@link #shouldFailOnNullFacesContextOnGetAsString()}</li>
061 * <li>{@link #shouldReturnEmptyStringOnNullValue()}</li>
062 * </ul>
063 * <h3>Example</h3> Shows all variants of dealing with {@link TestItems}
064 *
065 * <pre>
066 * <code>
067 * &#64;EnableJsfEnvironment
068 * class IntegerConverterTest extends AbstractConverterTest&lt;IntegerConverter, Integer&gt; {
069 *
070 * &#64;Override
071 * public void populate(final TestItems&lt;Integer&gt; testItems) {
072 * testItems.addRoundtripValues("1", "122", "2132121").addInvalidString("a")
073 * .addInvalidStringWithMessage("a", "javax.faces.converter.IntegerConverter.INTEGER")
074 * .addInvalidObject(Boolean.TRUE)
075 * .addInvalidObjectWithMessage(Boolean.FALSE, "javax.faces.converter.STRING")
076 * .addValidString("13").addValidStringWithObjectResult("17", Integer.valueOf(17))
077 * .addValidObject(Integer.valueOf(2))
078 * .addValidObjectWithStringResult(Integer.valueOf(14), "14");
079 * }
080 * </code>
081 * </pre>
082 *
083 * @param <C> identifying the concrete {@link Converter} to be tested.
084 * @param <T> identifying the type of elements to be used for values to be given
085 *            to the {@link Converter}
086 *
087 * @author Oliver Wolff
088 */
089@SuppressWarnings({"rawtypes", "unchecked"})
090@EnableGeneratorController
091public abstract class AbstractConverterTest<C extends Converter, T> extends JsfEnabledTestEnvironment implements ConfigurationCallBackHandler<C>, ComponentConfigurator {
092    private static final String SHOULD_HAVE_THROWN_CONVERTER_EXCEPTION = "Should have thrown ConverterException for invalid Value: ";
093    private UIComponent component = new HtmlInputText();
094    private C converter;
095    private TestItems<T> testItems;
096
097    /**
098     * Instantiates and initially configures a concrete {@link Converter}
099     */
100    @BeforeEach
101    protected void initConverter() {
102        final Class<C> klazz = MoreReflection.extractFirstGenericTypeArgument(getClass());
103        converter = new DefaultInstantiator<>(klazz).newInstance();
104        configure(converter);
105        testItems = new TestItems<>();
106        populate(testItems);
107    }
108
109    /**
110     * Populates the test-items
111     *
112     * @param testItems to be populated, is never null
113     */
114    public abstract void populate(TestItems<T> testItems);
115
116    /**
117     * Callback method for interacting with the {@link ComponentConfigDecorator} at
118     * the correct time.<br>
119     * This method provide <b>extension point</b> to prepare needed test environment
120     * for your converter test. For example :
121     *
122     * <pre>
123     *    // register some converter
124     *    decorator.registerConverter(MyRequiredConverter.class);
125     *    // register UIComponent
126     *      ...
127     * </pre>
128     *
129     * @param decorator {@link ComponentConfigDecorator} is never null
130     */
131    @Override
132    public void configureComponents(final ComponentConfigDecorator decorator) {
133        // default do nothing
134    }
135
136    /**
137     * Checks the api contract regarding {@code null} as parameter for
138     * {@link UIComponent}, see
139     * {@link Converter#getAsObject(FacesContext, UIComponent, String)}
140     */
141    @Test
142    void shouldFailOnNullComponentOnGetAsObject() {
143        assertThrows(NullPointerException.class, () -> getConverter().getAsObject(getFacesContext(), null, null));
144    }
145
146    /**
147     * Checks the api contract regarding {@code null} as parameter for
148     * {@link FacesContext}, see
149     * {@link Converter#getAsObject(FacesContext, UIComponent, String)}
150     */
151    @Test
152    void shouldFailOnNullFacesContextOnGetAsObject() {
153        assertThrows(NullPointerException.class, () -> getConverter().getAsObject(null, getComponent(), null));
154    }
155
156    /**
157     * Checks the api contract regarding {@code null} as parameter for
158     * {@link UIComponent}, see
159     * {@link Converter#getAsString(FacesContext, UIComponent, Object)}
160     */
161    @Test
162    void shouldFailOnNullComponentOnGetAsString() {
163        assertThrows(NullPointerException.class, () -> getConverter().getAsString(getFacesContext(), null, null));
164    }
165
166    /**
167     * Checks the api contract regarding {@code null} as parameter for
168     * {@link FacesContext}, see
169     * {@link Converter#getAsString(FacesContext, UIComponent, Object)}
170     */
171    @Test
172    void shouldFailOnNullFacesContextOnGetAsString() {
173        assertThrows(NullPointerException.class, () -> getConverter().getAsString(null, getComponent(), null));
174    }
175
176    /**
177     * Checks the api contract regarding {@code null} as parameter for the actual
178     * value, see {@link Converter#getAsString(FacesContext, UIComponent, Object)}
179     */
180    @Test
181    void shouldReturnEmptyStringOnNullValue() {
182        assertEquals("", getConverter().getAsString(getFacesContext(), getComponent(), null));
183    }
184
185    /**
186     * Core test for converter testing. It collects the test-data using
187     * {@link TestItems} and iterates them for the individual test. For each element
188     * there will be called the method
189     * {@link Converter#getAsObject(javax.faces.context.FacesContext, javax.faces.component.UIComponent, String)},
190     * with the result again
191     * {@link Converter#getAsString(javax.faces.context.FacesContext, javax.faces.component.UIComponent, Object)}
192     * with the result being checked against the initial value.
193     */
194    @Test
195    void shouldRoundTripValidData() {
196        for (final String value : getTestItems().getRoundtripValues()) {
197            final var converted = (T) getConverter().getAsObject(getFacesContext(), getComponent(), value);
198            final var roundTripped = getConverter().getAsString(getFacesContext(), getComponent(), converted);
199            assertEquals(value, roundTripped);
200        }
201    }
202
203    /**
204     * Tests the method
205     * {@link Converter#getAsString(javax.faces.context.FacesContext, javax.faces.component.UIComponent, Object)}
206     * with invalid objects, derived by {@link TestItems}
207     */
208    @Test
209    void shouldFailOnInvalidObjects() {
210        for (final ConverterTestItem<T> item : getTestItems().getInvalidObjectTestItems()) {
211            try {
212                getConverter().getAsString(getFacesContext(), getComponent(), item.getTestValue());
213                fail(SHOULD_HAVE_THROWN_CONVERTER_EXCEPTION + item);
214            } catch (final ConverterException e) {
215                verifyExpectedErrorMessage(item, e);
216            }
217        }
218    }
219
220    private void verifyExpectedErrorMessage(final ConverterTestItem<T> item, final ConverterException e) {
221        // Check message
222        if (null != item.getErrorMessage()) {
223            assertEquals(item.getErrorMessage(), e.getFacesMessage().getSummary(), "Wrong error message detected. TestItem was : " + item);
224        }
225    }
226
227    /**
228     * Tests the method
229     * {@link Converter#getAsString(javax.faces.context.FacesContext, javax.faces.component.UIComponent, Object)}
230     * with valid objects, derived by {@link TestItems}
231     */
232    @Test
233    void shouldPassOnValidObjects() {
234        for (final ConverterTestItem<T> item : getTestItems().getValidObjectTestItems()) {
235            final var result = getConverter().getAsString(getFacesContext(), getComponent(), item.getTestValue());
236            if (null != item.getStringValue()) {
237                assertEquals(item.getStringValue(), result, item.toString());
238            }
239        }
240    }
241
242    /**
243     * Tests the method
244     * {@link Converter#getAsObject(javax.faces.context.FacesContext, javax.faces.component.UIComponent, String)}
245     * with invalid objects, derived by {@link TestItems}
246     */
247    @Test
248    void shouldFailOnInvalidStrings() {
249        for (final ConverterTestItem<T> item : getTestItems().getInvalidStringTestItems()) {
250            try {
251                getConverter().getAsObject(getFacesContext(), getComponent(), item.getStringValue());
252                fail(SHOULD_HAVE_THROWN_CONVERTER_EXCEPTION + item);
253            } catch (final ConverterException e) {
254                verifyExpectedErrorMessage(item, e);
255            }
256        }
257    }
258
259    /**
260     * Tests the method
261     * {@link Converter#getAsObject(javax.faces.context.FacesContext, javax.faces.component.UIComponent, String)}
262     * with valid String, derived by {@link TestItems}
263     */
264    @Test
265    void shouldPassOnValidStrings() {
266        for (final ConverterTestItem<T> item : getTestItems().getValidStringTestItems()) {
267            final var result = (T) getConverter().getAsObject(getFacesContext(), getComponent(), item.getStringValue());
268            if (null != item.getTestValue()) {
269                assertEquals(item.getTestValue(), result, item.toString());
270            }
271        }
272    }
273
274    @java.lang.SuppressWarnings("all")
275    @lombok.Generated
276    public UIComponent getComponent() {
277        return this.component;
278    }
279
280    @java.lang.SuppressWarnings("all")
281    @lombok.Generated
282    public void setComponent(final UIComponent component) {
283        this.component = component;
284    }
285
286    @java.lang.SuppressWarnings("all")
287    @lombok.Generated
288    public C getConverter() {
289        return this.converter;
290    }
291
292    @java.lang.SuppressWarnings("all")
293    @lombok.Generated
294    protected TestItems<T> getTestItems() {
295        return this.testItems;
296    }
297}