/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
package com.scytl.cryptolib.commons.utils.validations;

import static java.util.Arrays.copyOf;
import static java.util.Arrays.fill;

import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.regex.Pattern;

import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.test.tools.utils.CommonTestDataGenerator;

/**
 * Tests the validation of arguments.
 */
public class ValidateTest {

    static final private int MAX_INT_VALUE = 100;

    static final private BigInteger MAX_BIG_INTEGER_VALUE = BigInteger
        .valueOf(MAX_INT_VALUE);

    static final private int STRING_LENGTH = 20;

    static final private int NUM_BYTE_ELEMENTS = 100;

    static final private int NUM_INTEGER_ELEMENTS = 10;

    static final private int NUM_STRING_ELEMENTS = 10;

    static final private String TEST_INT_LABEL = "test int";

    static final private String TEST_BIG_INTEGER_LABEL =
        "test big integer";

    static final private String TEST_DOUBLE_LABEL = "test double";

    static final private String TEST_STRING_LABEL = "test string";

    static final private String TEST_CHAR_ARRAY_LABEL =
        "test character array";

    static final private String TEST_BYTE_ARRAY_LABEL = "test byte array";

    static final private String TEST_OBJECT_ARRAY_LABEL =
        "test object array";

    static final private String TEST_COLLECTION_LABEL = "test collection";

    static final private String TEST_MAP_LABEL = "test map";

    static final private String TEST_TIME_LABEL = "test time";

    static final private String ALLOWED_CHARACTER_SET =
        "abcdefghijklmnopqrstuvwxyz0ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789_-";

    static final private String ALLOWED_CHARACTER_SET_AS_REGEX =
        "[a-zA-Z0-9_-]+";

    static private int _testInt;

    static private BigInteger _testBigInteger;

    static private double _testDouble;

    static private String _testString;

    static private char[] _testCharArray;

    static private byte[] _testByteArray;

    static private Object[] _testObjectArray;

    static private Collection<?> _testCollection;

    static private Map<?, ?> _testMap;

    static private Pattern _allowedCharsPattern;

    static private Date _earlierTime;

    static private Date _laterTime;

    static char[] _whiteSpaceChars;
    
    private static final Random RANDOM = new Random();

    @BeforeClass
    public static void setUp()
            throws NoSuchAlgorithmException, NoSuchProviderException,
            GeneralCryptoLibException {

        _testInt = RANDOM.nextInt(MAX_INT_VALUE);

        _testBigInteger = new BigInteger(_testInt, new Random());

        _testString =
            CommonTestDataGenerator.getAlphanumeric(STRING_LENGTH);

        _testCharArray = _testString.toCharArray();

        _testByteArray = new byte[NUM_BYTE_ELEMENTS];
        RANDOM.nextBytes(_testByteArray);

        _testObjectArray =
            generateRandomArray(NUM_INTEGER_ELEMENTS, MAX_INT_VALUE,
                NUM_STRING_ELEMENTS, STRING_LENGTH);

        _testCollection =
            generateRandomCollection(NUM_INTEGER_ELEMENTS, MAX_INT_VALUE,
                NUM_STRING_ELEMENTS, STRING_LENGTH);

        _testMap =
            generateRandomMap(NUM_INTEGER_ELEMENTS, MAX_INT_VALUE,
                NUM_STRING_ELEMENTS, STRING_LENGTH);

        _whiteSpaceChars = new char[STRING_LENGTH];
        fill(_whiteSpaceChars, ' ');

        _allowedCharsPattern =
            Pattern.compile(ALLOWED_CHARACTER_SET_AS_REGEX);

        _earlierTime = new Date(System.currentTimeMillis());

        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.YEAR, 1);
        _laterTime = calendar.getTime();
    }

    @Test
    public void whenDataIsValidThenNoExceptionsThrown()
            throws GeneralCryptoLibException {

        Validate.notNull(_testInt, TEST_INT_LABEL);
        Validate.notNull(_testDouble, TEST_DOUBLE_LABEL);
        Validate.notNull(_testString, TEST_STRING_LABEL);
        Validate.notNull(_testByteArray, TEST_BYTE_ARRAY_LABEL);
        Validate.notNull(_testObjectArray, TEST_OBJECT_ARRAY_LABEL);
        Validate.notNull(_testCollection, TEST_COLLECTION_LABEL);
        Validate.notNull(_testMap, TEST_MAP_LABEL);
        Validate.notNullOrBlank(_testString, TEST_STRING_LABEL);
        Validate.notNullOrBlank(_testCharArray, TEST_CHAR_ARRAY_LABEL);
        Validate.notNullOrEmpty(_testByteArray, TEST_BYTE_ARRAY_LABEL);
        Validate.notNullOrEmpty(_testObjectArray, TEST_OBJECT_ARRAY_LABEL);
        Validate.notNullOrEmpty(_testCollection, TEST_COLLECTION_LABEL);
        Validate.notNullOrEmpty(_testMap, TEST_MAP_LABEL);
        Validate.notNullOrEmptyAndNoNulls(_testObjectArray,
            TEST_OBJECT_ARRAY_LABEL);
        Validate.notNullOrEmptyAndNoNulls(_testCollection,
            TEST_COLLECTION_LABEL);
        Validate.notNullOrEmptyAndNoNulls(_testMap, TEST_MAP_LABEL);
        Validate.isPositive(1, TEST_INT_LABEL);
        Validate.equals(1, 1, TEST_INT_LABEL, TEST_INT_LABEL);
        Validate.equals(BigInteger.ONE, BigInteger.ONE,
            TEST_BIG_INTEGER_LABEL, TEST_BIG_INTEGER_LABEL);
        Validate.notLessThan(0, 0, TEST_INT_LABEL, TEST_INT_LABEL);
        Validate.notLessThan(BigInteger.ZERO, BigInteger.ZERO,
            TEST_BIG_INTEGER_LABEL, TEST_BIG_INTEGER_LABEL);
        Validate.notGreaterThan(MAX_INT_VALUE, MAX_INT_VALUE,
            TEST_INT_LABEL, TEST_INT_LABEL);
        Validate.notGreaterThan(MAX_BIG_INTEGER_VALUE,
            MAX_BIG_INTEGER_VALUE, TEST_BIG_INTEGER_LABEL,
            TEST_BIG_INTEGER_LABEL);
        Validate.inRange((MAX_INT_VALUE - 1), 0, MAX_INT_VALUE,
            TEST_INT_LABEL, TEST_INT_LABEL, TEST_INT_LABEL);
        Validate.inRange((MAX_BIG_INTEGER_VALUE.subtract(BigInteger.ONE)),
            BigInteger.ZERO, MAX_BIG_INTEGER_VALUE,
            TEST_BIG_INTEGER_LABEL, TEST_BIG_INTEGER_LABEL,
            TEST_BIG_INTEGER_LABEL);
        Validate.onlyContains(_testString, ALLOWED_CHARACTER_SET,
            TEST_STRING_LABEL);
        Validate.onlyContains(_testString, _allowedCharsPattern,
            TEST_STRING_LABEL);
        Validate.isAsciiPrintable(_testString, TEST_STRING_LABEL);
        Validate.isBefore(_earlierTime, _laterTime, TEST_TIME_LABEL,
            TEST_TIME_LABEL);
        Validate.isAfter(_laterTime, _earlierTime, TEST_TIME_LABEL,
            TEST_TIME_LABEL);
        Validate.notBefore(_laterTime, _earlierTime, TEST_TIME_LABEL,
            TEST_TIME_LABEL);
        Validate.notAfter(_earlierTime, _laterTime, TEST_TIME_LABEL,
            TEST_TIME_LABEL);
        Validate.notBefore(_earlierTime, _earlierTime, TEST_TIME_LABEL,
            TEST_TIME_LABEL);
        Validate.notAfter(_earlierTime, _earlierTime, TEST_TIME_LABEL,
            TEST_TIME_LABEL);
    }

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void whenNullPrimitiveCheckedForNullThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_INT_LABEL + " is null.");

        Validate.notNull(null, TEST_INT_LABEL);
    }

    @Test
    public void whenNullStringCheckedForNullThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_STRING_LABEL + " is null.");

        Validate.notNull((String) null, TEST_STRING_LABEL);
    }

    @Test
    public void whenNullStringCheckedForNullOrEmptyThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_STRING_LABEL + " is null.");

        Validate.notNullOrBlank((String) null, TEST_STRING_LABEL);
    }

    @Test
    public void whenEmptyStringCheckedForNullOrEmptyThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_STRING_LABEL + " is blank.");

        Validate.notNullOrBlank("", TEST_STRING_LABEL);
    }

    @Test
    public void whenWhiteSpaceStringCheckedForNullOrEmptyThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_STRING_LABEL + " is blank.");

        Validate.notNullOrBlank(new String(_whiteSpaceChars), TEST_STRING_LABEL);
    }

    @Test
    public void whenNullCharArrayCheckedForNullThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_CHAR_ARRAY_LABEL + " is null.");

        Validate.notNull((char[]) null, TEST_CHAR_ARRAY_LABEL);
    }

    @Test
    public void whenEmptyCharArrayCheckedForNullOrEmptyThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_CHAR_ARRAY_LABEL + " is blank.");

        Validate.notNullOrBlank("", TEST_CHAR_ARRAY_LABEL);
    }

    @Test
    public void whenWhiteCharArrayCheckedForNullOrEmptyThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_CHAR_ARRAY_LABEL + " is blank.");

        Validate.notNullOrBlank(_whiteSpaceChars, TEST_CHAR_ARRAY_LABEL);
    }

    @Test
    public void whenNullByteArrayCheckedForNullThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_BYTE_ARRAY_LABEL + " is null.");

        Validate.notNull((byte[]) null, TEST_BYTE_ARRAY_LABEL);
    }

    @Test
    public void whenNullByteArrayCheckedForNullOrEmptyThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_BYTE_ARRAY_LABEL + " is null.");

        Validate.notNullOrEmpty((byte[]) null, TEST_BYTE_ARRAY_LABEL);
    }

    @Test
    public void whenEmptyByteArrayCheckedForNullOrEmptyThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_BYTE_ARRAY_LABEL + " is empty.");

        Validate.notNullOrEmpty(new byte[0], TEST_BYTE_ARRAY_LABEL);
    }

    @Test
    public void whenNullObjectArrayCheckedForNullThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_OBJECT_ARRAY_LABEL + " is null.");

        Validate.notNull((Object[]) null, TEST_OBJECT_ARRAY_LABEL);
    }

    @Test
    public void whenNullObjectArrayCheckedForNullOrEmptyThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_OBJECT_ARRAY_LABEL + " is null.");

        Validate.notNullOrEmpty((Object[]) null, TEST_OBJECT_ARRAY_LABEL);
    }

    @Test
    public void whenEmptyObjectArrayCheckedForNullOrEmptyThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_OBJECT_ARRAY_LABEL + " is empty.");

        Validate.notNullOrEmpty(new Object[0], TEST_OBJECT_ARRAY_LABEL);
    }

    @Test
    public void whenObjectArrayWithNullElementCheckedForNullsThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        Object[] testObjectArrayWithNullElement =
            copyOf(_testObjectArray, _testObjectArray.length + 1);

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_OBJECT_ARRAY_LABEL
            + " contains one or more null elements.");

        Validate.notNullOrEmptyAndNoNulls(testObjectArrayWithNullElement,
            TEST_OBJECT_ARRAY_LABEL);
    }

    @Test
    public void whenNullCollectionCheckedForNullThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_COLLECTION_LABEL + " is null.");

        Validate.notNull((Collection<?>) null, TEST_COLLECTION_LABEL);
    }

    @Test
    public void whenNullCollectionCheckedForNullOrEmptyThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_COLLECTION_LABEL + " is null.");

        Validate.notNullOrEmpty((Collection<?>) null,
            TEST_COLLECTION_LABEL);
    }

    @Test
    public void whenEmptyCollectionCheckedForNullOrEmptyThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_COLLECTION_LABEL + " is empty.");

        Validate.notNullOrEmpty(new ArrayList<Object>(),
            TEST_COLLECTION_LABEL);
    }

    @Test
    public void whenCollectionWithNullElementCheckedForNullsThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        Collection<?> testCollectionWithNullElement =
            new ArrayList<Object>(_testCollection);
        testCollectionWithNullElement.add(null);

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_COLLECTION_LABEL
            + " contains one or more null elements.");

        Validate.notNullOrEmptyAndNoNulls(testCollectionWithNullElement,
            TEST_COLLECTION_LABEL);
    }

    @Test
    public void whenNullMapCheckedForNullThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_MAP_LABEL + " is null.");

        Validate.notNull((Map<?, ?>) null, TEST_MAP_LABEL);
    }

    @Test
    public void whenNullMapCheckedForNullOrEmptyThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_MAP_LABEL + " is null.");

        Validate.notNullOrEmpty((Map<?, ?>) null, TEST_MAP_LABEL);
    }

    @Test
    public void whenEmptyMapCheckedForNullOrEmptyThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_MAP_LABEL + " is empty.");

        Validate.notNullOrEmpty(new HashMap<Object, Object>(),
            TEST_MAP_LABEL);
    }

    @Test
    public void whenMapWithNullValueCheckedForNullsThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        Map<Object, Object> testMapWithNullValue =
            new HashMap<Object, Object>(_testMap);
        testMapWithNullValue.put("key for null value", null);

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_MAP_LABEL
            + " contains one or more null values.");

        Validate.notNullOrEmptyAndNoNulls(testMapWithNullValue,
            TEST_MAP_LABEL);
    }

    @Test
    public void whenIntNotPositiveThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        int notPositiveInt = 0;

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_INT_LABEL
            + " must be a positive integer; Found " + notPositiveInt);

        Validate.isPositive(notPositiveInt, TEST_INT_LABEL);
    }

    @Test
    public void whenBigIntegerNotEqualToRequiredValueThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        BigInteger arg = _testBigInteger;
        BigInteger value = _testBigInteger.add(BigInteger.ONE);
        String valueLabel = "int value label";

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_BIG_INTEGER_LABEL + " must be equal to "
            + valueLabel + ": " + value + "; Found " + arg);

        Validate.equals(arg, value, TEST_BIG_INTEGER_LABEL, valueLabel);
    }

    @Test
    public void whenIntNotEqualToRequiredValueThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        int arg = _testInt;
        int value = _testInt + 1;
        String valueLabel = "int value label";

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_INT_LABEL + " must be equal to "
            + valueLabel + ": " + value + "; Found " + arg);

        Validate.equals(arg, value, TEST_INT_LABEL, valueLabel);
    }

    @Test
    public void whenIntLessThanMinimumThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        int belowMinInt = -1;
        String minValueLabel = "min int value label";

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_INT_LABEL
            + " must be greater than or equal to " + minValueLabel + ": "
            + 0 + "; Found " + belowMinInt);

        Validate
            .notLessThan(belowMinInt, 0, TEST_INT_LABEL, minValueLabel);
    }

    @Test
    public void whenBigIntegerLessThanMinimumThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        BigInteger belowMinInt = BigInteger.valueOf(-1);
        String minValueLabel = "min int value label";

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_BIG_INTEGER_LABEL
            + " must be greater than or equal to " + minValueLabel + ": "
            + 0 + "; Found " + belowMinInt);

        Validate.notLessThan(belowMinInt, BigInteger.ZERO,
            TEST_BIG_INTEGER_LABEL, minValueLabel);
    }

    @Test
    public void whenIntGreaterThanMaximumThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        int aboveMaxInt = MAX_INT_VALUE + 1;
        String maxValueLabel = "max int value label";

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_INT_LABEL
            + " must be less than or equal to " + maxValueLabel + ": "
            + MAX_INT_VALUE + "; Found " + aboveMaxInt);

        Validate.notGreaterThan(aboveMaxInt, MAX_INT_VALUE,
            TEST_INT_LABEL, maxValueLabel);
    }

    @Test
    public void whenBigIntegerGreaterThanMaximumThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        BigInteger aboveMaxInt = MAX_BIG_INTEGER_VALUE.add(BigInteger.ONE);
        String maxValueLabel = "max int value label";

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_BIG_INTEGER_LABEL
            + " must be less than or equal to " + maxValueLabel + ": "
            + MAX_BIG_INTEGER_VALUE + "; Found " + aboveMaxInt);

        Validate.notGreaterThan(aboveMaxInt, MAX_BIG_INTEGER_VALUE,
            TEST_BIG_INTEGER_LABEL, maxValueLabel);
    }

    @Test
    public void whenIntLessThanMinimumInRangeThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        int belowMinInt = -1;
        String minValueLabel = "min int value label";
        String maxValueLabel = "max int value label";

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_INT_LABEL
            + " must be greater than or equal to " + minValueLabel + ": "
            + 0 + "; Found " + belowMinInt);

        Validate.inRange(belowMinInt, 0, MAX_INT_VALUE, TEST_INT_LABEL,
            minValueLabel, maxValueLabel);
    }

    @Test
    public void whenIntGreaterThanMaximumInRangeThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        int aboveMaxInt = MAX_INT_VALUE + 1;
        String minValueLabel = "min int value label";
        String maxValueLabel = "max int value label";

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_INT_LABEL
            + " must be less than or equal to " + maxValueLabel + ": "
            + MAX_INT_VALUE + "; Found " + aboveMaxInt);

        Validate.inRange(aboveMaxInt, 0, MAX_INT_VALUE, TEST_INT_LABEL,
            minValueLabel, maxValueLabel);
    }

    @Test
    public void whenBigIntegerLessThanMinimumInRangeThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        BigInteger belowMinInt = BigInteger.valueOf(-1);
        String minValueLabel = "min big integer value label";
        String maxValueLabel = "max big integer value label";

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_BIG_INTEGER_LABEL
            + " must be greater than or equal to " + minValueLabel + ": "
            + BigInteger.ZERO + "; Found " + belowMinInt);

        Validate.inRange(belowMinInt, BigInteger.ZERO,
            MAX_BIG_INTEGER_VALUE, TEST_BIG_INTEGER_LABEL, minValueLabel,
            maxValueLabel);
    }

    @Test
    public void whenBigIntegerGreaterThanMaximumInRangeThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        BigInteger aboveMaxInt = MAX_BIG_INTEGER_VALUE.add(BigInteger.ONE);
        String minValueLabel = "min int value label";
        String maxValueLabel = "max int value label";

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_BIG_INTEGER_LABEL
            + " must be less than or equal to " + maxValueLabel + ": "
            + MAX_BIG_INTEGER_VALUE + "; Found " + aboveMaxInt);

        Validate.inRange(aboveMaxInt, BigInteger.ZERO,
            MAX_BIG_INTEGER_VALUE, TEST_BIG_INTEGER_LABEL, minValueLabel,
            maxValueLabel);
    }

    @Test
    public void whenStringContainsDisallowedCharactersThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_STRING_LABEL
            + " contains characters outside of allowed set "
            + ALLOWED_CHARACTER_SET);

        Validate.onlyContains(_testString + "@", ALLOWED_CHARACTER_SET,
            TEST_STRING_LABEL);
    }

    @Test
    public void whenStringContainsNonAsciiPrintableCharactersThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_STRING_LABEL
            + " contains characters that are not ASCII printable.");

        Validate.isAsciiPrintable(_testString + "ä", TEST_STRING_LABEL);
    }

    @Test
    public void whenStringContainsDisallowedCharactersInRegexThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_STRING_LABEL
            + " contains characters outside of allowed set "
            + ALLOWED_CHARACTER_SET_AS_REGEX);

        Validate.onlyContains(_testString + "@", _allowedCharsPattern,
            TEST_STRING_LABEL);
    }

    @Test
    public void whenLaterTimeValidatedAsBeforeEarlierTimeThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        String earlierTimeLabel = "earlier value label";

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_TIME_LABEL + " " + _laterTime.toString()
            + " is not before " + earlierTimeLabel + " "
            + _earlierTime.toString());

        Validate.isBefore(_laterTime, _earlierTime, TEST_TIME_LABEL,
            earlierTimeLabel);
    }

    @Test
    public void whenEarlierTimeValidatedAsAfterLaterTimeThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        String laterTimeLabel = "later value label";

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_TIME_LABEL + " "
            + _earlierTime.toString() + " is not after " + laterTimeLabel
            + " " + _laterTime.toString());

        Validate.isAfter(_earlierTime, _laterTime, TEST_TIME_LABEL,
            laterTimeLabel);
    }

    @Test
    public void whenEarlerTimeValidatedAsNotBeforeLaterTimeThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        String laterTimeLabel = "later value label";

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_TIME_LABEL + " "
            + _earlierTime.toString() + " is before " + laterTimeLabel
            + " " + _laterTime.toString());

        Validate.notBefore(_earlierTime, _laterTime, TEST_TIME_LABEL,
            laterTimeLabel);
    }

    @Test
    public void whenLaterTimeValidatedAsNotAfterEarlierTimeThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        String earlierTimeLabel = "early value label";

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_TIME_LABEL + " " + _laterTime.toString()
            + " is after " + earlierTimeLabel + " "
            + _earlierTime.toString());

        Validate.notAfter(_laterTime, _earlierTime, TEST_TIME_LABEL,
            earlierTimeLabel);
    }

    @Test
    public void whenTimeValidatedAsBeforeSameTimeThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        String laterTimeLabel = "later value label";

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_TIME_LABEL + " "
            + _earlierTime.toString() + " is not before " + laterTimeLabel
            + " " + _earlierTime.toString());

        Validate.isBefore(_earlierTime, _earlierTime, TEST_TIME_LABEL,
            laterTimeLabel);
    }

    @Test
    public void whenTimeValidatedAsAfterSameTimeThenExpectedExceptionThrown()
            throws GeneralCryptoLibException {

        String earlierTimeLabel = "early value label";

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(TEST_TIME_LABEL + " "
            + _earlierTime.toString() + " is not after "
            + earlierTimeLabel + " " + _earlierTime.toString());

        Validate.isAfter(_earlierTime, _earlierTime, TEST_TIME_LABEL,
            earlierTimeLabel);
    }

    private static Collection<?> generateRandomCollection(
            final int numIntegerElements, final int numIntegerDigits,
            final int numStringElements, final int numStringCharacters)
            throws GeneralCryptoLibException {

        Object[] objArray =
            generateRandomArray(numIntegerElements, numIntegerDigits,
                numStringElements, numStringCharacters);

        return Arrays.asList(objArray);
    }

    private static Map<?, ?> generateRandomMap(
            final int numIntegerElements, final int numIntegerDigits,
            final int numStringElements, final int numStringCharacters)
            throws GeneralCryptoLibException {

        Map<Object, Object> objMap = new HashMap<>();
        for (int i = 0; i < numIntegerElements; i++) {
            String key = CommonTestDataGenerator
                .getAlphanumeric(numStringCharacters);
            Object value =
                Integer.valueOf(RANDOM.nextInt(numIntegerDigits));
            objMap.put(key, value);
        }
        for (int i = 0; i < numStringElements; i++) {
            String key = CommonTestDataGenerator
                .getAlphanumeric(numStringCharacters);
            Object value =
                Integer.valueOf(RANDOM.nextInt(numIntegerDigits));
            objMap.put(key, value);
        }

        return objMap;
    }

    private static Object[] generateRandomArray(
            final int numIntegerElements, final int numIntegerDigits,
            final int numStringElements, final int numStringCharacters)
            throws GeneralCryptoLibException {

        Object[] objArray =
            new Object[numIntegerElements + numStringElements];

        for (int i = 0; i < numIntegerElements; i++) {
            objArray[i] =
                Integer.valueOf(RANDOM.nextInt(numIntegerDigits));
        }
        for (int i = 0; i < numStringElements; i++) {
            objArray[numIntegerElements + i] = CommonTestDataGenerator
                .getAlphanumeric(numStringCharacters);
        }

        return objArray;
    }
}
