001/**
002 * Powerunit - A JDK1.8 test framework
003 * Copyright (C) 2014 Mathieu Boretti.
004 *
005 * This file is part of Powerunit
006 *
007 * Powerunit is free software: you can redistribute it and/or modify
008 * it under the terms of the GNU General Public License as published by
009 * the Free Software Foundation, either version 3 of the License, or
010 * (at your option) any later version.
011 *
012 * Powerunit is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
015 * GNU General Public License for more details.
016 *
017 * You should have received a copy of the GNU General Public License
018 * along with Powerunit. If not, see <http://www.gnu.org/licenses/>.
019 */
020package ch.powerunit;
021
022import java.util.Arrays;
023import java.util.function.Function;
024import java.util.function.Predicate;
025import java.util.function.Supplier;
026
027import org.hamcrest.Matcher;
028
029import ch.powerunit.helpers.StreamParametersMapFunction;
030import ch.powerunit.rules.SystemPropertiesRule;
031import ch.powerunit.rules.TemporaryFolder;
032import ch.powerunit.rules.TemporaryFolder.TemporaryFolderBuilder;
033import ch.powerunit.rules.impl.TemporaryFolderImpl;
034
035/**
036 * This is the interface to be implemented by test class, in order to have
037 * access to the test DSL.
038 * <p>
039 * This interface is only used to provide the DSL capabilities for the test ; It
040 * is not used as a marker by the framework.
041 * <p>
042 * Several functionalities are provided here :
043 * <ul>
044 * <li>Assertion capabilities (assertion on Object, Iterable, Function, Piece of
045 * code).</li>
046 * <li>Matchers to be used as end parameter of the assertion.</li>
047 * <li><code>{@link #before(Runnable...)}</code> to setup a before action.</li>
048 * <li><code>{@link #after(Runnable...)}</code> to setup a after action.</li>
049 * <li><code>{@link #mockitoRule()}</code> to setup mockito.</li>
050 * <li><code>{@link #systemPropertiesRule(String...)}</code> to restore
051 * properties system after test ; Several others method related to
052 * systemProperties exist.</li>
053 * <li><code>{@link #temporaryFolder()}</code> to support temporary folder.</li>
054 * <li><code>{@link #parametersMap(int, Function)}</code> to create Map function
055 * to be used on the stream to be provided by the {@link Parameters
056 * &#64;Parameters}. Other variants of this method may exist.</li>
057 * <li>Severals method of the form <code>tester...</code> (for instance
058 * <code>{@link #testerOfMatcher(Class)}</code>) that can be used with the
059 * <code>@{@link TestDelegate}</code> annotation to test some standard classes
060 * (like Matchers, Comparator, Function, etc).
061 * </ul>
062 *
063 * @author borettim
064 *
065 */
066public interface TestSuite extends Assert, Assume, Matchers,
067                TestFrameworkSupport {
068
069        /**
070         * A static field that is a testsuite (to avoid implementing TestSuite in
071         * test, in the rare case when it may be required).
072         * <p>
073         * The main use case is to access the stream functionnalities from a
074         * {@link Parameters &#64;Parameters} annotated method, has this method must
075         * be static.
076         */
077        static TestSuite DSL = new TestSuite() {
078        };
079
080        /**
081         * Build a before testrule.
082         * <p>
083         * The passed runnable will be used before each test. The exact location of
084         * the execution is depending on where this used on the testRule chain.
085         * <p>
086         * In the much simple case (just one method to be executed before each
087         * test), the syntax is :
088         * 
089         * <pre>
090         * &#064;Rule
091         * public TestRule rule = before(this::beforeMethodName);
092         * </pre>
093         * 
094         * @param befores
095         *            the befores
096         * @return {@link TestRule the rule chain}.
097         * @see Rule
098         */
099        default TestRule before(Runnable... befores) {
100                return Arrays.stream(befores).map(TestRule::before)
101                                .reduce((prev, next) -> prev.around(next)).get();
102        }
103
104        /**
105         * Build a after testrule.
106         * <p>
107         * The passed runnable will be used after each test. The exact location of
108         * the execution is depending on where this used on the testRule chain.
109         * <p>
110         * In the much simple case (just one method to be executed after each test),
111         * the syntax is :
112         * 
113         * <pre>
114         * &#064;Rule
115         * public TestRule rule = after(this::afterMethodName);
116         * </pre>
117         * 
118         * @param afters
119         *            the afters
120         * @return {@link TestRule the rule chain}.
121         * @see Rule
122         */
123        default TestRule after(Runnable... afters) {
124                return Arrays.stream(afters).map(TestRule::after)
125                                .reduce((prev, next) -> prev.around(next)).get();
126        }
127
128        /**
129         * Create a rule to support mockito.
130         * <p>
131         * This provide a way to setup Mockito before each test.
132         * 
133         * @return {@link TestRule the rule chain}.
134         * @see Rule
135         */
136        default TestRule mockitoRule() {
137                return TestRule.mockitoRule();
138        }
139
140        /**
141         * Produces a new rule for the temporary folder.
142         * <p>
143         * As the {@link TemporaryFolder} rule provides several methods that are
144         * required for the test, except in the case when only this rule is
145         * required, a direct usage in the rule DSL is not adapted.
146         * 
147         * For instance, assuming that it is required to mix a before and the
148         * {@link TemporaryFolder} rule, the code will look like :
149         * 
150         * <pre>
151         * private TemporaryFolder temporary = temporaryFolder();
152         * 
153         * &#064;Rule
154         * public TestRule rule = before(this::beforeMethodName).around(temporary);
155         * </pre>
156         * 
157         * This is required to ensure that the method of the {@link TemporaryFolder}
158         * object can be used (using the field named <code>temporary</code>).
159         * 
160         * @return the temporary folder rule.
161         * @see Rule
162         */
163        default TemporaryFolder temporaryFolder() {
164                return temporaryFolderBuilder().build();
165        }
166
167        /**
168         * Produces a new rule builder for the temporary folder.
169         * <p>
170         * As the {@link TemporaryFolder} rule provides several methods that are
171         * required for the test, except in the case when only this rule is
172         * required, a direct usage in the rule DSL is not adapted.
173         * 
174         * For instance, assuming that it is required to mix a before and the
175         * {@link TemporaryFolder} rule, the code will look like :
176         * 
177         * <pre>
178         * private TemporaryFolder temporary = temporaryFolderBuilder().build();
179         * 
180         * &#064;Rule
181         * public TestRule rule = before(this::beforeMethodName).around(temporary);
182         * </pre>
183         * 
184         * This is required to ensure that the method of the {@link TemporaryFolder}
185         * object can be used (using the field named <code>temporary</code>).
186         * <p>
187         * The builder provide several capabilities to create initial folder
188         * structure at the same time than the temporary folder itself.
189         * 
190         * @return the temporary folder rule builder.
191         * @see Rule
192         */
193        default TemporaryFolderBuilder temporaryFolderBuilder() {
194                return new TemporaryFolderImpl.TemporaryFolderBuilderImpl();
195        }
196
197        /**
198         * Create a rule to restore some system properties after the test
199         * 
200         * @param propertiesName
201         *            the properties to be restored
202         * @return {@link TestRule the rule chain}.
203         * @see Rule
204         */
205        default TestRule systemPropertiesRule(String... propertiesName) {
206                return new SystemPropertiesRule(propertiesName);
207        }
208
209        /**
210         * Set a property before the run and ensure correct restore.
211         * 
212         * @param propertyName
213         *            the name of the property
214         * @param propertyValue
215         *            the value of the property
216         * @return {@link TestRule the rule chain}.
217         * @see Rule
218         */
219        default TestRule systemProperty(String propertyName,
220                        Supplier<String> propertyValue) {
221                return SystemPropertiesRule.setSystemPropertyBeforeTestAndRestoreAfter(
222                                propertyName, propertyValue);
223        }
224
225        /**
226         * Set a property before the run and ensure correct restore.
227         * 
228         * @param propertyName
229         *            the name of the property
230         * @param propertyValue
231         *            the value of the property
232         * @return {@link TestRule the rule chain}.
233         * @see Rule
234         */
235        default TestRule systemProperty(String propertyName, String propertyValue) {
236                return SystemPropertiesRule.setSystemPropertyBeforeTestAndRestoreAfter(
237                                propertyName, propertyValue);
238        }
239
240        /**
241         * Start building a Parameter Mapper function, with an initial converter.
242         * <p>
243         * Not specified index are considered transformed by identity function.
244         * 
245         * @param idx
246         *            The parameter index
247         * @param mapFunction
248         *            the function to be applied
249         * @return the function on the parameter array
250         * @param <T>
251         *            The input type for the function
252         * @param <R>
253         *            the result type for the function
254         */
255        default <T, R> StreamParametersMapFunction<T> parametersMap(int idx,
256                        Function<T, R> mapFunction) {
257                return StreamParametersMapFunction.map(idx, mapFunction);
258        }
259
260        /**
261         * Start building a Parameter Mapper function, assuming that the input are
262         * String, and using the type of the {@link Parameter &#64;Parameter} field.
263         * <p>
264         * Fields not supported will not be mapped and must be handled manually,
265         * using {@link StreamParametersMapFunction#andMap(int, Function) andMap}
266         * method to avoid any unexpected error.
267         * <p>
268         * The goal of this method is to provide a way to receive so generic
269         * parameter and not having to care about typing. Let's take for example the
270         * following use case :
271         * <ul>
272         * <li>As an input for the test parameters, a CSV file is used. In this
273         * case, a framework like <a
274         * href="http://opencsv.sourceforge.net/">OpenCSV</a> can be used to load
275         * all the parameters, but they are all String.</li>
276         * <li>On the produced stream, the
277         * {@link java.util.stream.Stream#map(Function) map} method can be used to
278         * transform the received data into another format. Here, using the result
279         * of this method as parameter of the map method will ensure the
280         * transformation of the String to the right type, for simple type.</li>
281         * <li>For undetected field type, it is possible to use the method
282         * {@link StreamParametersMapFunction#andMap(int, Function) andMap} (on the
283         * returned object of this method), to add manual transformation.</li>
284         * </ul>
285         * <p>
286         * In this context, as the {@link Parameters &#64;Parameters} annotated
287         * method must be static, access to this method can be done using
288         * {@link #DSL DSL.} prefix.
289         * 
290         * @param testClass
291         *            the testClass, as this method is to be used in static mode.
292         * @return the function on the parameter array
293         * @see <a href="./helpers/doc-files/convertedType.html">Supported automated
294         *      conversion</a>
295         */
296        default StreamParametersMapFunction<String> stringToParameterMap(
297                        Class<?> testClass) {
298                return StreamParametersMapFunction.stringToParameterMap(testClass);
299        }
300
301        /**
302         * Provide a way to add a field to each parameter line.
303         * 
304         * @param field
305         *            The field to be added.
306         * @return the function that can be used on the stream (
307         *         {@link java.util.stream.Stream#map(Function)}).
308         * @since 0.1.0
309         * @param <T>
310         *            The object type to be added.
311         */
312        default <T> Function<Object[], Object[]> addFieldToEachEntry(T field) {
313                return StreamParametersMapFunction.addFieldToEachEntry(field);
314        }
315
316        /**
317         * Provide a filter for stream parameters, to keep only parameters accepted
318         * by a matcher.
319         * 
320         * @param matcher
321         *            the matcher
322         * @return the stream filter
323         */
324        default Predicate<Object[]> parametersFilterUsingMatcher(
325                        Matcher<Object[]> matcher) {
326                return matcherPredicate(matcher);
327        }
328
329        /**
330         * Expose a matcher as a predicate.
331         * 
332         * @param matcher
333         *            the matcher.
334         * @return the predicate
335         * @param <T>
336         *            The target object type
337         */
338        default <T> Predicate<T> matcherPredicate(Matcher<T> matcher) {
339                return matcher::matches;
340        }
341}