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 * </ul>
058 *
059 * @author borettim
060 *
061 */
062public interface TestSuite extends Assert, Assume, Matchers {
063
064    /**
065     * A static field that is a testsuite (to avoid implementing TestSuite in
066     * test, in the rare case when it may be required).
067     * <p>
068     * The main use case is to access the stream functionnalities from a
069     * {@link Parameters &#64;Parameters} annotated method, has this method must
070     * be static.
071     */
072    static TestSuite DSL = new TestSuite() {
073    };
074
075    /**
076     * Build a before testrule.
077     * <p>
078     * The passed runnable will be used before each test. The exact location of
079     * the execution is depending on where this used on the testRule chain.
080     * <p>
081     * In the much simple case (just one method to be executed before each
082     * test), the syntax is :
083     * 
084     * <pre>
085     * &#064;Rule
086     * public TestRule rule = before(this::beforeMethodName);
087     * </pre>
088     * 
089     * @param befores
090     *            the befores
091     * @return {@link TestRule the rule chain}.
092     * @see Rule
093     */
094    default TestRule before(Runnable... befores) {
095        return Arrays.stream(befores).map(TestRule::before)
096                .reduce((prev, next) -> prev.around(next)).get();
097    }
098
099    /**
100     * Build a after testrule.
101     * <p>
102     * The passed runnable will be used after each test. The exact location of
103     * the execution is depending on where this used on the testRule chain.
104     * <p>
105     * In the much simple case (just one method to be executed after each test),
106     * the syntax is :
107     * 
108     * <pre>
109     * &#064;Rule
110     * public TestRule rule = after(this::afterMethodName);
111     * </pre>
112     * 
113     * @param afters
114     *            the afters
115     * @return {@link TestRule the rule chain}.
116     * @see Rule
117     */
118    default TestRule after(Runnable... afters) {
119        return Arrays.stream(afters).map(TestRule::after)
120                .reduce((prev, next) -> prev.around(next)).get();
121    }
122
123    /**
124     * Create a rule to support mockito.
125     * <p>
126     * This provide a way to setup Mockito before each test.
127     * 
128     * @return {@link TestRule the rule chain}.
129     * @see Rule
130     */
131    default TestRule mockitoRule() {
132        return TestRule.mockitoRule();
133    }
134
135    /**
136     * Produces a new rule for the temporary folder.
137     * <p>
138     * As the {@link TemporaryFolder} rule provides several methods that are
139     * required for the test, except in the case when only this rule is
140     * required, a direct usage in the rule DSL is not adapted.
141     * 
142     * For instance, assuming that it is required to mix a before and the
143     * {@link TemporaryFolder} rule, the code will look like :
144     * 
145     * <pre>
146     * private TemporaryFolder temporary = temporaryFolder();
147     * 
148     * &#064;Rule
149     * public TestRule rule = before(this::beforeMethodName).around(temporary);
150     * </pre>
151     * 
152     * This is required to ensure that the method of the {@link TemporaryFolder}
153     * object can be used (using the field named <code>temporary</code>).
154     * 
155     * @return the temporary folder rule.
156     * @see Rule
157     */
158    default TemporaryFolder temporaryFolder() {
159        return temporaryFolderBuilder().build();
160    }
161
162    /**
163     * Produces a new rule builder for the temporary folder.
164     * <p>
165     * As the {@link TemporaryFolder} rule provides several methods that are
166     * required for the test, except in the case when only this rule is
167     * required, a direct usage in the rule DSL is not adapted.
168     * 
169     * For instance, assuming that it is required to mix a before and the
170     * {@link TemporaryFolder} rule, the code will look like :
171     * 
172     * <pre>
173     * private TemporaryFolder temporary = temporaryFolderBuilder().build();
174     * 
175     * &#064;Rule
176     * public TestRule rule = before(this::beforeMethodName).around(temporary);
177     * </pre>
178     * 
179     * This is required to ensure that the method of the {@link TemporaryFolder}
180     * object can be used (using the field named <code>temporary</code>).
181     * <p>
182     * The builder provide several capabilities to create initial folder
183     * structure at the same time than the temporary folder itself.
184     * 
185     * @return the temporary folder rule builder.
186     * @see Rule
187     */
188    default TemporaryFolderBuilder temporaryFolderBuilder() {
189        return new TemporaryFolderImpl.TemporaryFolderBuilderImpl();
190    }
191
192    /**
193     * Create a rule to restore some system properties after the test
194     * 
195     * @param propertiesName
196     *            the properties to be restored
197     * @return {@link TestRule the rule chain}.
198     * @see Rule
199     */
200    default TestRule systemPropertiesRule(String... propertiesName) {
201        return new SystemPropertiesRule(propertiesName);
202    }
203
204    /**
205     * Set a property before the run and ensure correct restore.
206     * 
207     * @param propertyName
208     *            the name of the property
209     * @param propertyValue
210     *            the value of the property
211     * @return {@link TestRule the rule chain}.
212     * @see Rule
213     */
214    default TestRule systemProperty(String propertyName,
215            Supplier<String> propertyValue) {
216        return SystemPropertiesRule.setSystemPropertyBeforeTestAndRestoreAfter(
217                propertyName, propertyValue);
218    }
219
220    /**
221     * Set a property before the run and ensure correct restore.
222     * 
223     * @param propertyName
224     *            the name of the property
225     * @param propertyValue
226     *            the value of the property
227     * @return {@link TestRule the rule chain}.
228     * @see Rule
229     */
230    default TestRule systemProperty(String propertyName, String propertyValue) {
231        return SystemPropertiesRule.setSystemPropertyBeforeTestAndRestoreAfter(
232                propertyName, propertyValue);
233    }
234
235    /**
236     * Start building a Parameter Mapper function, with an initial converter.
237     * <p>
238     * Not specified index are considered transformed by identity function.
239     * 
240     * @param idx
241     *            The parameter index
242     * @param mapFunction
243     *            the function to be applied
244     * @return the function on the parameter array
245     * @param <T>
246     *            The input type for the function
247     * @param <R>
248     *            the result type for the function
249     */
250    default <T, R> StreamParametersMapFunction<T> parametersMap(int idx,
251            Function<T, R> mapFunction) {
252        return StreamParametersMapFunction.map(idx, mapFunction);
253    }
254
255    /**
256     * Start building a Parameter Mapper function, assuming that the input are
257     * String, and using the type of the {@link Parameter &#64;Parameter} field.
258     * <p>
259     * Fields not supported will not be mapped and must be handled manually,
260     * using {@link StreamParametersMapFunction#andMap(int, Function) andMap}
261     * method to avoid any unexpected error.
262     * <p>
263     * The goal of this method is to provide a way to receive so generic
264     * parameter and not having to care about typing. Let's take for example the
265     * following use case :
266     * <ul>
267     * <li>As an input for the test parameters, a CSV file is used. In this
268     * case, a framework like <a
269     * href="http://opencsv.sourceforge.net/">OpenCSV</a> can be used to load
270     * all the parameters, but they are all String.</li>
271     * <li>On the produced stream, the
272     * {@link java.util.stream.Stream#map(Function) map} method can be used to
273     * transform the received data into another format. Here, using the result
274     * of this method as parameter of the map method will ensure the
275     * transformation of the String to the right type, for simple type.</li>
276     * <li>For undetected field type, it is possible to use the method
277     * {@link StreamParametersMapFunction#andMap(int, Function) andMap} (on the
278     * returned object of this method), to add manual transformation.</li>
279     * </ul>
280     * <p>
281     * In this context, as the {@link Parameters &#64;Parameters} annotated
282     * method must be static, access to this method can be done using
283     * {@link #DSL DSL.} prefix.
284     * 
285     * @param testClass
286     *            the testClass, as this method is to be used in static mode.
287     * @return the function on the parameter array
288     * @see <a href="./helpers/doc-files/convertedType.html">Supported automated
289     *      conversion</a>
290     */
291    default StreamParametersMapFunction<String> stringToParameterMap(
292            Class<?> testClass) {
293        return StreamParametersMapFunction.stringToParameterMap(testClass);
294    }
295
296    /**
297     * Provide a way to add a field to each parameter line.
298     * 
299     * @param field
300     *            The field to be added.
301     * @return the function that can be used on the stream (
302     *         {@link java.util.stream.Stream#map(Function)}).
303     * @since 0.1.0
304     * @param <T>
305     *            The object type to be added.
306     */
307    default <T> Function<Object[], Object[]> addFieldToEachEntry(T field) {
308        return StreamParametersMapFunction.addFieldToEachEntry(field);
309    }
310
311    /**
312     * Provide a filter for stream parameters, to keep only parameters accepted
313     * by a matcher.
314     * 
315     * @param matcher
316     *            the matcher
317     * @return the stream filter
318     */
319    default Predicate<Object[]> parametersFilterUsingMatcher(
320            Matcher<Object[]> matcher) {
321        return matcherPredicate(matcher);
322    }
323
324    /**
325     * Expose a matcher as a predicate.
326     * 
327     * @param matcher
328     *            the matcher.
329     * @return the predicate
330     * @param <T>
331     *            The target object type
332     */
333    default <T> Predicate<T> matcherPredicate(Matcher<T> matcher) {
334        return matcher::matches;
335    }
336}