View Javadoc
1   /*
2    * (c) Copyright 2006-2020 by rapiddweller GmbH & Volker Bergmann. All rights reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, is permitted under the terms of the
6    * GNU General Public License.
7    *
8    * For redistributing this software or a derivative work under a license other
9    * than the GPL-compatible Free Software License as defined by the Free
10   * Software Foundation or approved by OSI, you must first obtain a commercial
11   * license to this software product from rapiddweller GmbH & Volker Bergmann.
12   *
13   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14   * WITHOUT A WARRANTY OF ANY KIND. ALL EXPRESS OR IMPLIED CONDITIONS,
15   * REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF
16   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE
17   * HEREBY EXCLUDED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24   * POSSIBILITY OF SUCH DAMAGE.
25   */
26  
27  package com.rapiddweller.benerator.test;
28  
29  import com.rapiddweller.benerator.Generator;
30  import com.rapiddweller.benerator.engine.DefaultBeneratorContext;
31  import com.rapiddweller.benerator.primitive.number.AbstractNonNullNumberGenerator;
32  import com.rapiddweller.benerator.wrapper.ProductWrapper;
33  import com.rapiddweller.common.ArrayFormat;
34  import com.rapiddweller.common.ArrayUtil;
35  import com.rapiddweller.common.BeanUtil;
36  import com.rapiddweller.common.CollectionUtil;
37  import com.rapiddweller.common.Converter;
38  import com.rapiddweller.common.IOUtil;
39  import com.rapiddweller.common.Resettable;
40  import com.rapiddweller.common.Validator;
41  import com.rapiddweller.common.collection.ObjectCounter;
42  import com.rapiddweller.common.converter.ToStringConverter;
43  import com.rapiddweller.common.validator.UniqueValidator;
44  import com.rapiddweller.model.data.Entity;
45  
46  import java.util.Collection;
47  import java.util.HashSet;
48  import java.util.Map;
49  import java.util.Set;
50  import java.util.concurrent.atomic.AtomicInteger;
51  
52  import static org.junit.Assert.assertEquals;
53  import static org.junit.Assert.assertFalse;
54  import static org.junit.Assert.assertNotNull;
55  import static org.junit.Assert.assertNull;
56  import static org.junit.Assert.assertTrue;
57  import static org.junit.Assert.fail;
58  
59  /**
60   * Provides methods for testing generators.<br/>
61   * <br/>
62   * Created: 15.11.2007 14:46:31
63   *
64   * @author Volker Bergmann
65   */
66  public abstract class GeneratorTest extends ModelTest {
67  
68    private final Converter<Object, String> formatter = new ToStringConverter();
69  
70    // helper methods for this and child classes -----------------------------------------------------------------------
71  
72    /**
73     * Initialize t.
74     *
75     * @param <T>       the type parameter
76     * @param <U>       the type parameter
77     * @param generator the generator
78     * @return the t
79     */
80    public <T extends Generator<U>, U> T initialize(T generator) {
81      generator.init(context);
82      return generator;
83    }
84  
85    /**
86     * Close.
87     *
88     * @param generator the generator
89     */
90    public void close(Generator<?> generator) {
91      IOUtil.close(generator);
92    }
93  
94    /**
95     * Sets current product.
96     *
97     * @param product     the product
98     * @param productName the product name
99     */
100   public void setCurrentProduct(Object product, String productName) {
101     ((DefaultBeneratorContext) context).setCurrentProduct(new ProductWrapper<>(product), productName);
102   }
103 
104   /**
105    * Print products.
106    *
107    * @param generator the generator
108    * @param n         the n
109    */
110   @SuppressWarnings({"unchecked", "rawtypes"})
111   public void printProducts(Generator<?> generator, int n) {
112     ProductWrapperoductWrapper.html#ProductWrapper">ProductWrapper wrapper = new ProductWrapper();
113     for (int i = 0; i < n; i++) {
114       ProductWrapper<?> tmp = generator.generate(wrapper);
115       if (tmp == null) {
116         System.out.println("<>");
117       } else {
118         System.out.println(formatter.convert(tmp.unwrap()));
119       }
120     }
121   }
122 
123   /**
124    * Count products map.
125    *
126    * @param <T>       the type parameter
127    * @param generator the generator
128    * @param n         the n
129    * @return the map
130    */
131   public static <T> Map<T, AtomicInteger> countProducts(Generator<T> generator, int n) {
132     ObjectCounter<T> counter = new ObjectCounter<>(Math.min(n, 1000));
133     ProductWrapper<T> wrapper = new ProductWrapper<>();
134     for (int i = 0; i < n; i++) {
135       wrapper = generator.generate(wrapper);
136       if (wrapper == null) {
137         fail("Generator unavailable after " + i + " of " + n + " invocations");
138       } else {
139         counter.count(wrapper.unwrap());
140       }
141     }
142     return counter.getCounts();
143   }
144 
145   /**
146    * Assert equal arrays.
147    *
148    * @param <T>      the type parameter
149    * @param expected the expected
150    * @param actual   the actual
151    */
152   protected static <T> void assertEqualArrays(T expected, T actual) {
153     ArrayFormat format = new ArrayFormat();
154     assertTrue("Expected " + format.format(expected) + ", found: " + format.format(actual),
155         ArrayUtil.equals(expected, actual));
156   }
157 
158   /**
159    * Expect generated sequence helper.
160    *
161    * @param <T>       the type parameter
162    * @param generator the generator
163    * @param products  the products
164    * @return the helper
165    */
166   @SafeVarargs
167   protected static <T> Helper expectGeneratedSequence(Generator<T> generator, T... products) {
168     expectGeneratedSequenceOnce(generator, products);
169     generator.reset();
170     expectGeneratedSequenceOnce(generator, products);
171     return new Helper(generator);
172   }
173 
174   /**
175    * Expect generated set helper.
176    *
177    * @param <T>         the type parameter
178    * @param generator   the generator
179    * @param invocations the invocations
180    * @param products    the products
181    * @return the helper
182    */
183   @SafeVarargs
184   protected final <T> Helper expectGeneratedSet(Generator<T> generator, int invocations, T... products) {
185     expectGeneratedSetOnce(generator, invocations, products);
186     generator.reset();
187     expectGeneratedSetOnce(generator, invocations, products);
188     return new Helper(generator);
189   }
190 
191   /**
192    * Expect uniquely generated set helper.
193    *
194    * @param <T>       the type parameter
195    * @param generator the generator
196    * @param products  the products
197    * @return the helper
198    */
199   @SafeVarargs
200   protected final <T> Helper expectUniquelyGeneratedSet(Generator<T> generator, T... products) {
201     expectUniquelyGeneratedSetOnce(generator, products);
202     generator.reset();
203     expectUniquelyGeneratedSetOnce(generator, products);
204     return new Helper(generator);
205   }
206 
207   /**
208    * Expect unique products helper.
209    *
210    * @param <T>       the type parameter
211    * @param generator the generator
212    * @param n         the n
213    * @return the helper
214    */
215   protected <T> Helper expectUniqueProducts(Generator<T> generator, int n) {
216     expectUniqueProductsOnce(generator, n);
217     generator.reset();
218     expectUniqueProductsOnce(generator, n);
219     return new Helper(generator);
220   }
221 
222   /**
223    * Expect generations helper.
224    *
225    * @param <T>        the type parameter
226    * @param generator  the generator
227    * @param n          the n
228    * @param validators the validators
229    * @return the helper
230    */
231   @SuppressWarnings({"unchecked", "rawtypes"})
232   protected <T> Helper expectGenerations(Generator<T> generator, int n, Validator... validators) {
233     expectGenerationsOnce(generator, n, validators);
234     generator.reset();
235     for (Validator<?> validator : validators) {
236       if (validator instanceof Resettable) {
237         ((Resettable) validator).reset();
238       }
239     }
240     expectGenerationsOnce(generator, n, validators);
241     return new Helper(generator);
242   }
243 
244   /**
245    * Expect unique generations helper.
246    *
247    * @param <T>       the type parameter
248    * @param generator the generator
249    * @param n         the n
250    * @return the helper
251    */
252   protected <T> Helper expectUniqueGenerations(Generator<T> generator, int n) {
253     expectUniqueGenerationsOnce(generator, n);
254     generator.reset();
255     expectUniqueGenerationsOnce(generator, n);
256     return new Helper(generator);
257   }
258 
259   /**
260    * Expect range.
261    *
262    * @param <T>       the type parameter
263    * @param generator the generator
264    * @param n         the n
265    * @param min       the min
266    * @param max       the max
267    */
268   protected <T extends Comparable<T>> void expectRange(Generator<T> generator, int n, T min, T max) {
269     expectRangeOnce(generator, n, min, max);
270     generator.reset();
271     expectRangeOnce(generator, n, min, max);
272     new Helper(generator);
273   }
274 
275   /**
276    * Format string.
277    *
278    * @param <T>     the type parameter
279    * @param product the product
280    * @return the string
281    */
282   protected <T> String format(T product) {
283     return ToStringConverter.convert(product, "[null]");
284   }
285 
286   /**
287    * Assert unavailable.
288    *
289    * @param generator the generator
290    */
291   @SuppressWarnings({"rawtypes", "unchecked"})
292   public static void assertUnavailable(Generator<?> generator) {
293     assertNull("Generator " + generator + " is expected to be unavailable", generator.generate(new ProductWrapper()));
294   }
295 
296   /**
297    * Assert available.
298    *
299    * @param generator the generator
300    */
301   public static void assertAvailable(Generator<?> generator) {
302     assertAvailable("Generator " + generator + " is expected to be available", generator);
303   }
304 
305   /**
306    * Assert available.
307    *
308    * @param message   the message
309    * @param generator the generator
310    */
311   @SuppressWarnings({"rawtypes", "unchecked"})
312   public static void assertAvailable(String message, Generator<?> generator) {
313     assertNotNull(message, generator.generate(new ProductWrapper()));
314   }
315 
316   // Number generator tests ------------------------------------------------------------------------------------------
317 
318   /**
319    * Check equal distribution.
320    *
321    * @param <T>            the type parameter
322    * @param generatorClass the generator class
323    * @param min            the min
324    * @param max            the max
325    * @param granularity    the granularity
326    * @param iterations     the iterations
327    * @param tolerance      the tolerance
328    * @param expectedValues the expected values
329    */
330   @SafeVarargs
331   public static <T extends Number> void checkEqualDistribution(
332       Class<? extends AbstractNonNullNumberGenerator<T>> generatorClass, T min, T max, T granularity,
333       int iterations, double tolerance, T... expectedValues) {
334     Set<T> expectedSet = CollectionUtil.toSet(expectedValues);
335     checkDistribution(generatorClass, min, max, granularity, iterations, true, tolerance, expectedSet);
336   }
337 
338   /**
339    * Check equal distribution.
340    *
341    * @param <T>            the type parameter
342    * @param generatorClass the generator class
343    * @param min            the min
344    * @param max            the max
345    * @param granularity    the granularity
346    * @param iterations     the iterations
347    * @param tolerance      the tolerance
348    * @param expectedSet    the expected set
349    */
350   public static <T extends Number> void checkEqualDistribution(
351       Class<? extends AbstractNonNullNumberGenerator<T>> generatorClass, T min, T max, T granularity,
352       int iterations, double tolerance, Set<T> expectedSet) {
353     checkDistribution(generatorClass, min, max, granularity, iterations, true, tolerance, expectedSet);
354   }
355 
356   private static <T extends Number> void checkDistribution(
357       Class<? extends AbstractNonNullNumberGenerator<T>> generatorClass, T min, T max, T granularity,
358       int iterations, boolean equalDistribution, double tolerance, Set<T> expectedSet) {
359     AbstractNonNullNumberGenerator<T> generator = BeanUtil.newInstance(generatorClass);
360     generator.setMin(min);
361     generator.setMax(max);
362     generator.setGranularity(granularity);
363     ObjectCounter<T> counter = new ObjectCounter<>(expectedSet != null ? expectedSet.size() : 10);
364     ProductWrapper<T> wrapper = new ProductWrapper<>();
365     for (int i = 0; i < iterations; i++) {
366       counter.count(generator.generate(wrapper).unwrap());
367     }
368     checkDistribution(counter, equalDistribution, tolerance, expectedSet);
369   }
370 
371   // unspecific generator tests --------------------------------------------------------------------------------------
372 
373   /**
374    * Check equal distribution.
375    *
376    * @param <E>         the type parameter
377    * @param generator   the generator
378    * @param iterations  the iterations
379    * @param tolerance   the tolerance
380    * @param expectedSet the expected set
381    */
382   public static <E> void checkEqualDistribution(
383       Generator<E> generator, int iterations, double tolerance, Set<E> expectedSet) {
384     checkDistribution(generator, iterations, true, tolerance, expectedSet);
385   }
386 
387   /**
388    * Check product set.
389    *
390    * @param <T>         the type parameter
391    * @param generator   the generator
392    * @param iterations  the iterations
393    * @param expectedSet the expected set
394    */
395   public static <T> void checkProductSet(Generator<T> generator, int iterations, Set<T> expectedSet) {
396     checkDistribution(generator, iterations, false, 0, expectedSet);
397   }
398 
399   private static <T> void checkDistribution(Generator<T> generator,
400                                             int iterations, boolean equalDistribution, double tolerance, Set<T> expectedSet) {
401     ObjectCounter<T> counter = new ObjectCounter<>(expectedSet != null ? expectedSet.size() : 10);
402     ProductWrapper<T> wrapper = new ProductWrapper<>();
403     for (int i = 0; i < iterations; i++) {
404       counter.count(generator.generate(wrapper).unwrap());
405     }
406     checkDistribution(counter, equalDistribution, tolerance, expectedSet);
407   }
408 
409   /**
410    * Expect relative weights.
411    *
412    * @param generator                the generator
413    * @param iterations               the iterations
414    * @param expectedValueWeightPairs the expected value weight pairs
415    */
416   @SuppressWarnings({"rawtypes", "unchecked"})
417   protected static void expectRelativeWeights(Generator<?> generator, int iterations, Object... expectedValueWeightPairs) {
418     ObjectCounter<Object> counter = new ObjectCounter<>(expectedValueWeightPairs.length / 2);
419     ProductWrapperoductWrapper.html#ProductWrapper">ProductWrapper wrapper = new ProductWrapper();
420     for (int i = 0; i < iterations; i++) {
421       wrapper = generator.generate(wrapper);
422       if (wrapper == null) {
423         fail("Generator unavailable after " + i + " of " + iterations + " invocations");
424       } else {
425         counter.count(wrapper.unwrap());
426       }
427     }
428     Set<Object> productSet = counter.objectSet();
429     double totalExpectedWeight = 0;
430     for (int i = 1; i < expectedValueWeightPairs.length; i += 2) {
431       totalExpectedWeight += ((Number) expectedValueWeightPairs[i]).doubleValue();
432     }
433 
434     for (int i = 0; i < expectedValueWeightPairs.length; i += 2) {
435       Object value = expectedValueWeightPairs[i];
436       if (totalExpectedWeight == 0) {
437         totalExpectedWeight = 0.00000001;
438       }
439       double expectedWeight = ((Number) expectedValueWeightPairs[i + 1]).doubleValue() / totalExpectedWeight;
440       if (expectedWeight > 0) {
441         assertTrue("Generated set does not contain value " + value, productSet.contains(value));
442         double measuredWeight = counter.getRelativeCount(value);
443         assertTrue("For value '" + value + "', weight " + expectedWeight + " is expected, but it is " + measuredWeight,
444             Math.abs(measuredWeight - expectedWeight) / expectedWeight < 0.15);
445       } else {
446         assertFalse("Generated contains value " + value + " though it has zero weight", productSet.contains(value));
447       }
448     }
449   }
450 
451   // collection checks -----------------------------------------------------------------------------------------------
452 
453   /**
454    * Check equal distribution.
455    *
456    * @param <E>         the type parameter
457    * @param collection  the collection
458    * @param tolerance   the tolerance
459    * @param expectedSet the expected set
460    */
461   public static <E> void checkEqualDistribution(Collection<E> collection, double tolerance, Set<E> expectedSet) {
462     checkDistribution(collection, true, tolerance, expectedSet);
463   }
464 
465   private static <E> void checkDistribution(Collection<E> collection,
466                                             boolean equalDistribution, double tolerance, Set<E> expectedSet) {
467     ObjectCounter<E> counter = new ObjectCounter<>(expectedSet != null ? expectedSet.size() : 10);
468     for (E object : collection) {
469       counter.count(object);
470     }
471     checkDistribution(counter, equalDistribution, tolerance, expectedSet);
472   }
473 
474   // counter checks --------------------------------------------------------------------------------------------------
475 
476   /**
477    * Check equal distribution.
478    *
479    * @param <E>         the type parameter
480    * @param counter     the counter
481    * @param tolerance   the tolerance
482    * @param expectedSet the expected set
483    */
484   public static <E> void checkEqualDistribution(
485       ObjectCounter<E> counter, double tolerance, Set<E> expectedSet) {
486     checkDistribution(counter, true, tolerance, expectedSet);
487   }
488 
489   private static <E> void checkDistribution(
490       ObjectCounter<E> counter, boolean equalDistribution, double tolerance, Set<E> expectedSet) {
491     if (equalDistribution) {
492       assertTrue("Distribution is not equal: " + counter, counter.equalDistribution(tolerance));
493     }
494     if (expectedSet != null) {
495       assertEquals(expectedSet, counter.objectSet());
496     }
497   }
498 
499   /**
500    * The type Helper.
501    */
502   public static class Helper {
503     private final Generator<?> generator;
504 
505     /**
506      * Instantiates a new Helper.
507      *
508      * @param generator the generator
509      */
510     public Helper(Generator<?> generator) {
511       this.generator = generator;
512     }
513 
514     /**
515      * With ceased availability.
516      */
517     public void withCeasedAvailability() {
518       try {
519         assertUnavailable(generator);
520       } finally {
521         generator.close();
522       }
523     }
524 
525     /**
526      * With continued availability.
527      */
528     public void withContinuedAvailability() {
529       assertAvailable(generator);
530     }
531   }
532 
533   // private helpers -------------------------------------------------------------------------------------------------
534 
535   /**
536    * Expect generated sequence once.
537    *
538    * @param <T>       the type parameter
539    * @param generator the generator
540    * @param products  the products
541    */
542   @SafeVarargs
543   protected static <T> void expectGeneratedSequenceOnce(Generator<T> generator, T... products) {
544     int count = 0;
545     ProductWrapper<T> wrapper = new ProductWrapper<>();
546     for (T expectedProduct : products) {
547       wrapper = generator.generate(wrapper);
548       assertNotNull("Generator is unavailable after generating " + count + " of " + products.length + " products: " + generator, wrapper);
549       T generatedProduct = wrapper.unwrap();
550       if (generatedProduct.getClass().isArray()) {
551         assertEqualArrays(expectedProduct, generatedProduct);
552       } else {
553         assertEquals(expectedProduct, generatedProduct);
554       }
555       count++;
556     }
557   }
558 
559   @SafeVarargs
560   private <T> void expectGeneratedSetOnce(Generator<T> generator, int invocations, T... expectedProducts) {
561     Set<T> expectedSet = CollectionUtil.toSet(expectedProducts);
562     Set<T> observedSet = new HashSet<>(expectedProducts.length);
563     ProductWrapper<T> wrapper = new ProductWrapper<>();
564     for (int i = 0; i < invocations; i++) {
565       wrapper = generator.generate(wrapper);
566       assertNotNull("Generator has gone unavailable. " +
567               "Generated only " + i + " of " + expectedProducts.length + " expected values: " + observedSet,
568           wrapper);
569       T generation = wrapper.unwrap();
570       if (logger.isDebugEnabled()) {
571         logger.debug("created " + format(generation));
572       }
573       assertTrue("The generated value '" + format(generation) + "' was not in the expected set: " + expectedSet,
574           expectedSet.contains(generation));
575       observedSet.add(generation);
576     }
577     assertEquals(expectedSet, observedSet);
578   }
579 
580   @SafeVarargs
581   private <T> void expectUniquelyGeneratedSetOnce(Generator<T> generator, T... expectedProducts) {
582     Set<T> expectedSet = CollectionUtil.toSet(expectedProducts);
583     UniqueValidator<Object> validator = new UniqueValidator<>();
584     ProductWrapper<T> wrapper = new ProductWrapper<>();
585     for (int i = 0; i < expectedProducts.length; i++) {
586       wrapper = generator.generate(wrapper);
587       assertNotNull("Generator has gone unavailable after " + i + " products, " +
588           "expected " + expectedProducts.length + " products. ", wrapper);
589       T product = wrapper.unwrap();
590       if (logger.isDebugEnabled()) {
591         logger.debug("created " + format(product));
592       }
593       assertTrue("Product is not unique: " + product, validator.valid(product));
594       assertTrue("The generated value '" + format(product) + "' was not in the expected set: "
595           + format(expectedSet), expectedSet.contains(product));
596     }
597   }
598 
599   private <T> void expectUniqueProductsOnce(Generator<T> generator, int n) {
600     UniqueValidator<T> validator = new UniqueValidator<>();
601     ProductWrapper<T> wrapper = new ProductWrapper<>();
602     for (int i = 0; i < n; i++) {
603       wrapper = generator.generate(wrapper);
604       assertNotNull("Generator is not available: " + generator, wrapper);
605       T product = wrapper.unwrap();
606       logger.debug("created: " + format(product));
607       assertTrue("Product is not unique: " + product, validator.valid(product));
608     }
609   }
610 
611   @SafeVarargs
612   private <T> void expectGenerationsOnce(Generator<T> generator, int n, Validator<T>... validators) {
613     ProductWrapper<T> wrapper = new ProductWrapper<>();
614     for (int i = 0; i < n; i++) {
615       wrapper = generator.generate(wrapper);
616       assertNotNull("Generator has gone unavailable before creating the required number of products, " +
617               "required " + n + " but was " + i,
618           wrapper);
619       T product = wrapper.unwrap();
620       if (logger.isDebugEnabled()) {
621         logger.debug("created " + format(product));
622       }
623       for (Validator<T> validator : validators) {
624         assertTrue("The generated value '" + format(product) + "' is not valid according to " + validator +
625                 ", failed after " + i + " generations",
626             validator.valid(product));
627       }
628     }
629   }
630 
631   @SafeVarargs
632   private <T> void expectUniqueGenerationsOnce(Generator<T> generator, int n, Validator<T>... validators) {
633     UniqueValidator<T> validator = new UniqueValidator<>();
634     ProductWrapper<T> wrapper = new ProductWrapper<>();
635     for (int i = 0; i < n; i++) {
636       wrapper = generator.generate(wrapper);
637       assertNotNull("Generator has gone unavailable before creating the required number of products ",
638           wrapper);
639       T product = wrapper.unwrap();
640       logger.debug("created " + format(product));
641       assertTrue("The generated value '" + format(product) + "' is not unique. Generator is " + generator,
642           validator.valid(product));
643     }
644   }
645 
646   /**
647    * Expect range once.
648    *
649    * @param <T>       the type parameter
650    * @param generator the generator
651    * @param n         the n
652    * @param min       the min
653    * @param max       the max
654    */
655   protected <T extends Comparable<T>> void expectRangeOnce(Generator<T> generator, int n, T min, T max) {
656     ProductWrapper<T> wrapper = new ProductWrapper<>();
657     for (int i = 0; i < n; i++) {
658       wrapper = generator.generate(wrapper);
659       assertNotNull(wrapper);
660       T product = wrapper.unwrap();
661       assertTrue("Generated value (" + product + ") is less than the configured minimum (" + min + ")", min.compareTo(product) <= 0);
662       assertTrue("Generated value (" + product + ") is greater than the configured maximum (" + max + ")", max.compareTo(product) >= 0);
663     }
664   }
665 
666   /**
667    * Assert components.
668    *
669    * @param entity         the entity
670    * @param nameValuePairs the name value pairs
671    */
672   public static void assertComponents(Entity entity, Object... nameValuePairs) {
673     for (int i = 0; i < nameValuePairs.length; i += 2) {
674       String name = (String) nameValuePairs[i];
675       Object expected = nameValuePairs[i + 1];
676       Object actual = entity.getComponent(name);
677       assertEquals("Unexpected value for component '" + name + "':", expected, actual);
678     }
679   }
680 
681 }