@SuppressWarnings("unused")
@Test
public void shouldNotCatchWhenCreatingADateWithLongOfZero() {
    try {
        Source<Date> testee = dates().withMilliseconds(0);
    } catch (IllegalArgumentException expected) {
        fail("Threw an exception for an acceptable input!");
    }
}

public static DatesDSL dates() {
    return new DatesDSL();
}

@Test
public void shouldGenerateLocalDateAtStartAndEndInclusive() {
    Source<LocalDate> testee = localDates().withDaysBetween(3245352, 729385723);
    assertThatSource(testee).generatesAllOf(LocalDate.ofEpochDay(3245352), LocalDate.ofEpochDay(729385723));
}

public static LocalDatesDSL localDates() {
    return new LocalDatesDSL();
}

@SuppressWarnings("unused")
@Test
public void shouldCatchWhenCreatingAnInclusiveLongIntervalForDatesWithMaxLessThanMin() {
    try {
        Source<Date> testee = dates().withMillisecondsBetween(342, 3);
        fail("Created a Date where max long is less than min long!");
    } catch (IllegalArgumentException expected) {
    }
}

@Test
public void shouldGenerateLocalDateMax() {
    Source<LocalDate> testee = localDates().withDays(7890789);
    assertThatSource(testee).generatesAllOf(LocalDate.ofEpochDay(7890789));
}

@SuppressWarnings("unused")
@Test
public void shouldCatchWhenCreatingADateOfNegativeLong() {
    try {
        Source<Date> testee = dates().withMilliseconds(-234);
        fail("Created a date with a negative number of milliseconds");
    } catch (IllegalArgumentException expected) {
        assertTrue("Expected exception message to relate to negative long argument", expected.getMessage().indexOf("not an accepted number of milliseconds") >= 0);
    }
}

@SuppressWarnings("unused")
@Test
public void shouldNotCatchWhenCreatingAnInclusiveLongIntervalForDatesWithMaxEqualToMin() {
    try {
        Source<Date> testee = dates().withMillisecondsBetween(352, 352);
    } catch (IllegalArgumentException expected) {
        fail("Threw an exception for an acceptable interval!");
    }
}

@SuppressWarnings("unused")
@Test
public void shouldNotCatchWhenCreatingALocalDateAtMaxEpochDayCount() {
    try {
        Source<LocalDate> testee = localDates().withDays(LOCAL_DATE_MAX_EPOCH_DAY_COUNT);
    } catch (IllegalArgumentException expected) {
        fail("Threw an exception for an acceptable input!");
    }
}

@SuppressWarnings("unused")
@Test
public void shouldNotCatchWhenCreatingAnInclusiveLongIntervalForDatesWithMinEqualToZero() {
    try {
        Source<Date> testee = dates().withMillisecondsBetween(0, 5);
    } catch (IllegalArgumentException expected) {
        fail("Threw an exception for an acceptable interval!");
    }
}

@SuppressWarnings("unused")
@Test
public void shouldNotCatchWhenCreatingALocalDateAtMinEpochDayCount() {
    try {
        Source<LocalDate> testee = localDates().withDays(LOCAL_DATE_MIN_EPOCH_DAY_COUNT);
    } catch (IllegalArgumentException expected) {
        fail("Threw an exception for an acceptable input!");
    }
}

@SuppressWarnings("unused")
@Test
public void shouldCatchWhenCreatingAnInclusiveLongIntervalForLocalDatesWithMinLessThanMinEpochDayCount() {
    try {
        Source<LocalDate> testee = localDates().withDaysBetween(LOCAL_DATE_MIN_EPOCH_DAY_COUNT - 1, LOCAL_DATE_MAX_EPOCH_DAY_COUNT + 1);
        fail("Created a Date where min long is less than zero!");
    } catch (IllegalArgumentException expected) {
    }
}

@SuppressWarnings("unused")
@Test
public void shouldNotCatchWhenCreatingAnInclusiveLongIntervalForLocalDatesWithMaxEqualToMin() {
    try {
        Source<LocalDate> testee = localDates().withDaysBetween(352, 352);
    } catch (IllegalArgumentException expected) {
        fail("Threw an exception for an acceptable interval!");
    }
}

@SuppressWarnings("unused")
@Test
public void shouldCatchWhenCreatingAnInclusiveLongIntervalForLocalDatesWithMaxLessThanMin() {
    try {
        Source<LocalDate> testee = localDates().withDaysBetween(342, 3);
        fail("Created a localDate where max long is less than min long!");
    } catch (IllegalArgumentException expected) {
    }
}

@SuppressWarnings("unused")
@Test
public void shouldCatchWhenCreatingALocalDateBelowMinEpochDayCount() {
    try {
        Source<LocalDate> testee = localDates().withDays(LOCAL_DATE_MIN_EPOCH_DAY_COUNT - 1);
        fail("Created a localDate with an improper value");
    } catch (IllegalArgumentException expected) {
        assertTrue("Expected exception message to relate to out of suitable range long argument", expected.getMessage().indexOf("The long value representing the number of days from the epoch must be bounded") >= 0);
    }
}

@SuppressWarnings("unused")
@Test
public void shouldCatchWhenCreatingALocalDateAboveMaxEpochDayCount() {
    try {
        Source<LocalDate> testee = localDates().withDays(LOCAL_DATE_MAX_EPOCH_DAY_COUNT + 1);
        fail("Created a localDate with an improper value");
    } catch (IllegalArgumentException expected) {
    }
}

@Test
public void shouldGenerateDateAtStartAndEndInclusive() {
    Source<Date> testee = dates().withMillisecondsBetween(3245352, 72938572398752l);
    assertThatSource(testee).generatesAllOf(new Date(3245352), new Date(72938572398752l));
}

@SuppressWarnings("unused")
@Test
public void shouldCatchWhenCreatingAnInclusiveLongIntervalForDatesWithMinLessThanZero() {
    try {
        Source<Date> testee = dates().withMillisecondsBetween(-5, 6);
        fail("Created a Date where min long is less than zero!");
    } catch (IllegalArgumentException expected) {
    }
}

@SuppressWarnings("unused")
@Test
public void shouldNotCatchWhenCreatingAnInclusiveLongIntervalForDatesWithMinEqualToMinEpochDayCount() {
    try {
        Source<LocalDate> testee = localDates().withDaysBetween(LOCAL_DATE_MIN_EPOCH_DAY_COUNT, LOCAL_DATE_MAX_EPOCH_DAY_COUNT);
    } catch (IllegalArgumentException expected) {
        fail("Threw an exception for an acceptable interval!");
    }
}

@Test
public void shouldGenerateDateMax() {
    Source<Date> testee = dates().withMilliseconds(7890789);
    assertThatSource(testee).generatesAllOf(new Date(7890789));
}


@Test
public void flatMapsContents() {
    Gen<Integer> testee = Generate.pick(Arrays.asList(1, 2)).flatMap(i -> Generate.range(0, i));
    assertThatGenerator(testee).generatesAllOf(0, 1, 2);
}

default <R> Gen<R> flatMap(Function<? super T, Gen<? extends R>> mapper) {
    return in -> mapper.apply(generate(in)).generate(in);
}


@Test
public void shouldGenerateFromLowerLimit() {
    XOrShiftPRNG testee = new XOrShiftPRNG(0);
    Set<Long> generated = new HashSet<>();
    for (int i = 0; i != 10; i++) {
        generated.add(testee.nextLong(Long.MIN_VALUE, Long.MIN_VALUE + 1));
    }
    assertThat(generated).contains(Long.MIN_VALUE);
    assertThat(generated).contains(Long.MIN_VALUE + 1);
}

@Override
public long nextLong(long startInclusive, long endInclusive) {
    if (startInclusive == Long.MIN_VALUE && endInclusive == Long.MAX_VALUE) {
        return nextLong();
    }
    final long temp = nextLong();
    final long temp2 = temp % ((endInclusive - startInclusive) + 1);
    if (temp2 < 0) {
        long t = temp2 + endInclusive + 1;
        return t;
    }
    long t = (temp2 + startInclusive);
    return t;
}

@Test
public void shouldGenerateUpToLongMax() {
    XOrShiftPRNG testee = new XOrShiftPRNG(0);
    long actual = testee.nextLong(Long.MAX_VALUE, Long.MAX_VALUE);
    assertThat(actual).isEqualTo(Long.MAX_VALUE);
}


@Test
public void boundedLengthClassArraysHaveBoundedLength() {
    qt().forAll(arrays().ofClass(dates().withMilliseconds(100), Date.class).withLengthBetween(1, 10)).check(is -> is.length >= 1 && is.length <= 10);
}

public <T> ArrayGeneratorBuilder<T> ofClass(Source<T> source, Class<T> c) {
    return new ArrayGeneratorBuilder<T>(source, c);
}

public <T> ArrayGeneratorBuilder<T> ofClass(Gen<T> source, Class<T> c) {
    return new ArrayGeneratorBuilder<T>(source, c);
}

@Test
public void boundedLengthStringArraysHaveBoundedLength() {
    qt().forAll(arrays().ofStrings(strings().allPossible().ofLength(2)).withLengthBetween(1, 10)).check(is -> is.length >= 1 && is.length <= 10);
}

public ArrayGeneratorBuilder<String> ofStrings(Source<String> source) {
    return new ArrayGeneratorBuilder<String>(source, String.class);
}

public ArrayGeneratorBuilder<String> ofStrings(Gen<String> source) {
    return new ArrayGeneratorBuilder<String>(source, String.class);
}

@Test
public void fixedLengthIntegerArraysAreFixedLength() {
    qt().forAll(arrays().ofIntegers(integers().all()).withLength(10)).check(is -> is.length == 10);
}

public Source<T[]> withLength(int length) {
    return withLengthBetween(length, length);
}

public Gen<T[]> withLength(int length) {
    return withLengthBetween(length, length);
}

@Test
public void fixedLengthStringArraysHaveFixedLength() {
    qt().forAll(arrays().ofStrings(strings().allPossible().ofLength(2)).withLength(3)).check(is -> is.length == 3);
}

@Test
public void fixedLengthClassArraysHaveFixedLength() {
    qt().forAll(arrays().ofClass(dates().withMilliseconds(100), Date.class).withLength(3)).check(is -> is.length == 3);
}

@Test
public void boundedLengthIntegerArraysHaveBoundedLengths() {
    qt().forAll(arrays().ofIntegers(integers().all()).withLengthBetween(1, 9)).check(is -> is.length >= 1 && is.length <= 9);
}

public ArrayGeneratorBuilder<Integer> ofIntegers(Source<Integer> source) {
    return new ArrayGeneratorBuilder<Integer>(source, Integer.class);
}

public ArrayGeneratorBuilder<Integer> ofIntegers(Gen<Integer> source) {
    return new ArrayGeneratorBuilder<Integer>(source, Integer.class);
}

@Test
public void boundedLengthCharacterArraysHaveBoundedLength() {
    qt().forAll(arrays().ofCharacters(characters().ascii()).withLengthBetween(1, 5)).check(is -> is.length >= 1 && is.length <= 5);
}

public ArrayGeneratorBuilder<Character> ofCharacters(Source<Character> source) {
    return new ArrayGeneratorBuilder<Character>(source, Character.class);
}

public ArrayGeneratorBuilder<Character> ofCharacters(Gen<Character> source) {
    return new ArrayGeneratorBuilder<Character>(source, Character.class);
}

@Test
public void fixedLengthCharacterArraysAreFixedLength() {
    qt().forAll(arrays().ofCharacters(characters().ascii()).withLength(5)).check(is -> is.length == 5);
}


@Test
public void bigDecimalsAreGeneratedWithRequestedScale() {
    Gen<BigDecimal> someBigDecimals = bigDecimals().ofBytes(64).withScale(3);
    qt().forAll(someBigDecimals).check(a -> a.scale() == 3);
}

public Source<BigDecimal> withScale(int scale) {
    return BigDecimals.randomWithScale(maxLengthOfBigIntegerByteArray, scale);
}

public Gen<BigDecimal> withScale(int scale) {
    return BigDecimals.randomWithScale(maxLengthOfBigIntegerByteArray, scale);
}


@Test
public void generatesOnlyValidCodepointsInBasicLatinRange() {
    qt().withExamples(100000).forAll(CodePoints.codePoints(BASIC_LATIN_FIRST_CODEPOINT, BASIC_LATIN_LAST_CODEPOINT)).check(i -> Character.isValidCodePoint(i));
}

static Source<Long> codePoints(int startInclusive, int endInclusive) {
    return codePoints(startInclusive, endInclusive, FIRST_NON_WHITESPACE_CHARACTER_IN_BLC);
}

public static Gen<Integer> codePoints(int startInclusive, int endInclusive) {
    return codePoints(startInclusive, endInclusive, FIRST_NON_WHITESPACE_CHARACTER_IN_BLC);
}

@Test
public void generatesOnlyValidCodepoints() {
    qt().withExamples(100000).forAll(CodePoints.codePoints(Character.MIN_CODE_POINT, Character.MAX_CODE_POINT)).check(i -> Character.isValidCodePoint(i));
}

@Test
public void generatesOnlyCodepointsWithinTheSuppliedRange() {
    qt().withExamples(100000).forAll(CodePoints.codePoints(BASIC_LATIN_FIRST_CODEPOINT, BASIC_LATIN_LAST_CODEPOINT)).check(i -> i >= BASIC_LATIN_FIRST_CODEPOINT && i <= BASIC_LATIN_LAST_CODEPOINT);
}


@Test
public void boundedSizeListsHaveBoundedSize() {
    qt().forAll(lists().of(integers().all()).ofSizeBetween(1, 2)).check(l -> l.size() >= 1 && l.size() <= 2);
}

public Source<List<T>> ofSizeBetween(int minimumSize, int maximumSize) {
    checkBoundedListArguments(minimumSize, maximumSize);
    return Lists.alternatingBoundedListsOf(source, minimumSize, maximumSize);
}

public Gen<List<T>> ofSizeBetween(int minimumSize, int maximumSize) {
    checkBoundedListArguments(minimumSize, maximumSize);
    return Lists.boundedListsOf(source, minimumSize, maximumSize);
}

@Test
public void listsAreOfSuppliedType() {
    Supplier<List<Integer>> collectionFactory = () -> new LinkedList<>();
    qt().forAll(lists().of(integers().all()).ofType(lists().createListCollector(collectionFactory)).ofSize(2)).check(l -> l instanceof LinkedList);
}

public Source<List<T>> ofSize(int size) {
    return ofSizeBetween(size, size);
}

public Gen<List<T>> ofSize(int size) {
    return ofSizeBetween(size, size);
}

@Test
public void fixedSizeListsHaveFixedSize() {
    qt().forAll(lists().of(integers().all()).ofSize(2)).check(l -> l.size() == 2);
}


@Test
public void negativeDoublesAreNegative() {
    qt().forAll(doubles().negative()).check(d -> d <= 0d);
}

public Gen<Double> negative() {
    return Doubles.negative();
}

@Test
public void positiveFloatsArePositive() {
    qt().forAll(floats().positive()).check(d -> d >= 0d);
}

public Gen<Float> positive() {
    return Floats.fromZeroToPositiveInfinity();
}

@Test
public void positiveDoublesArePositive() {
    qt().forAll(doubles().positive()).check(d -> d >= 0d);
}

public Gen<Double> positive() {
    return Doubles.positive();
}

@Test
public void constantSuppliesAConstant() {
    Supplier<Integer> s = () -> 42;
    qt().forAll(arbitrary().constant(s)).check(a -> a == 42);
}

public <T> Gen<T> constant(Supplier<T> constant) {
    return Generate.constant(constant);
}

@Test
public void negativeFloatsAreNegative() {
    qt().forAll(floats().negative()).check(d -> d <= 0d);
}

public Gen<Float> negative() {
    return Floats.fromNegativeInfinityToNegativeZero();
}


@Test
public void shouldDescribeArrayContentsUsingProvidedSource() {
    Gen<String> sourceWithCustomDescription = Generate.constant("x").describedAs(x -> "custom description for x");
    Gen<String[]> testee = Generate.arraysOf(sourceWithCustomDescription, String.class, 2, 2);
    String[] anArray = { "foo", "bar" };
    assertThat(testee.asString(anArray)).isEqualTo("[custom description for x, custom description for x]");
}

default Gen<T> describedAs(AsString<T> asString) {
    return new DescribingGenerator<>(this, asString);
}

@Test
public void shouldProvideReadableDescriptionOfByteArrays() {
    Gen<byte[]> testee = Generate.byteArrays(Generate.range(1, 2), Generate.bytes((byte) 1, (byte) 2, (byte) 0));
    assertThat(testee.asString(new byte[] { 1, 2 })).containsSequence("1, 2");
}

public static Gen<byte[]> byteArrays(Gen<Integer> sizes, Gen<Byte> contents) {
    Gen<byte[]> gen = td -> {
        int size = sizes.generate(td);
        byte[] bs = new byte[size];
        for (int i = 0; i != size; i++) {
            bs[i] = contents.generate(td);
        }
        return bs;
    };
    return gen.describedAs(Arrays::toString);
}

@Test
public void shouldProvideReadableDescriptionOfIntegerArrays() {
    Gen<int[]> testee = Generate.intArrays(Generate.range(1, 2), Generate.range(1, 2));
    assertThat(testee.asString(new int[] { 1, 2 })).containsSequence("1, 2");
}

public static Gen<int[]> intArrays(Gen<Integer> sizes, Gen<Integer> contents) {
    Gen<int[]> gen = td -> {
        int size = sizes.generate(td);
        int[] is = new int[size];
        for (int i = 0; i != size; i++) {
            is[i] = contents.generate(td);
        }
        return is;
    };
    return gen.describedAs(Arrays::toString);
}

@Test
public void shouldProvideReadableDescriptionOfTwoDimensionalIntegerArrays() {
    Gen<int[][]> testee = Generate.intArrays(Generate.range(1, 2), Generate.range(1, 2), Generate.range(1, 2));
    assertThat(testee.asString(new int[][] { { 1, 2 }, { 6, 12 } })).containsSequence("[1, 2], [6, 12]");
}

public static Gen<int[][]> intArrays(Gen<Integer> rows, Gen<Integer> cols, Gen<Integer> contents) {
    Gen<int[][]> gen = td -> {
        int w = rows.generate(td);
        int h = cols.generate(td);
        int[][] is = new int[w][h];
        for (int i = 0; i != w; i++) {
            for (int j = 0; j != h; j++) {
                is[i][j] = contents.generate(td);
            }
        }
        return is;
    };
    return gen.describedAs(a -> java.util.Arrays.stream(a).map(Arrays::toString).collect(Collectors.joining(", ", "[", "]")));
}


@Test
public void describesCodePoint() {
    Gen<Integer> testee = CodePoints.codePoints(Character.MIN_CODE_POINT, Character.MAX_CODE_POINT);
    assertThat(testee.asString(42)).isEqualTo("42");
}


@Test
public void fixedLengthStringsAreFixedLength() {
    qt().forAll(strings().allPossible().ofLength(100)).check(s -> s.length() == 100);
}

public Source<String> ofLength(int fixedLength) {
    return ofLengthBetween(fixedLength, fixedLength);
}

public Gen<String> ofLength(int fixedLength) {
    return ofLengthBetween(fixedLength, fixedLength);
}

@Test
public void boundedLengthStringsRespectsLengthBounds() {
    Gen<String> testee = strings().allPossible().ofLengthBetween(3, 200);
    qt().withExamples(100000).forAll(testee).check(s -> s.length() <= 200 && s.length() >= 3);
}

public Source<String> ofLengthBetween(int minLength, int maxLength) {
    ArgumentAssertions.checkArguments(minLength <= maxLength, "The minLength (%s) is longer than the maxLength(%s)", minLength, maxLength);
    ArgumentAssertions.checkArguments(minLength >= 0, "The length of a String cannot be negative; %s is not an accepted argument", minLength);
    return Strings.ofBoundedLengthStrings(minCodePoint, maxCodePoint, minLength, maxLength);
}

public Gen<String> ofLengthBetween(int minLength, int maxLength) {
    ArgumentAssertions.checkArguments(minLength <= maxLength, "The minLength (%s) is longer than the maxLength(%s)", minLength, maxLength);
    ArgumentAssertions.checkArguments(minLength >= 0, "The length of a String cannot be negative; %s is not an accepted argument", minLength);
    return Strings.ofBoundedLengthStrings(minCodePoint, maxCodePoint, minLength, maxLength);
}


@Test
public void correctsShrinkTargetWhenBelowMin() {
    Constraint testee = Constraint.between(1, 10).withShrinkPoint(0);
    assertThat(testee.shrinkTarget()).isEqualTo(OptionalLong.of(1));
}

OptionalLong shrinkTarget() {
    return shrinkTarget;
}

@Test
public void shrinkTargetDefaultsToZero() {
    assertThat(Constraint.between(0, 10).shrinkTarget()).isEqualTo(OptionalLong.of(0));
}

@Test
public void doesNotCreateNewObjectWhenShrinkTargetDoesNotChange() {
    Constraint testee = Constraint.between(0, 10).withShrinkPoint(5);
    assertSame(testee, testee.withShrinkPoint(5));
}

public static Constraint between(long min, long max) {
    if (min == 0 && max == 1) {
        return zeroToOne();
    }
    return new Constraint(min, max, correct(min, max, OptionalLong.of(0)));
}

@Test
public void usesSingletonForZeroToOne() {
    assertSame(Constraint.between(0, 1), Constraint.zeroToOne());
}

public static Constraint zeroToOne() {
    return ZERO_TO_ONE;
}

@Test
public void correctsShrinkTargetWhenAboveMax() {
    Constraint testee = Constraint.between(1, 10).withShrinkPoint(11);
    assertThat(testee.shrinkTarget()).isEqualTo(OptionalLong.of(10));
}

@Test
public void shrinkTargetCanBeUnset() {
    assertThat(Constraint.between(0, 10).withNoShrinkPoint().shrinkTarget()).isEqualTo(OptionalLong.empty());
}

@Test
public void setShrinkPointWhenWithinRange() {
    Constraint testee = Constraint.between(1, 10).withShrinkPoint(5);
    assertThat(testee.shrinkTarget()).isEqualTo(OptionalLong.of(5));
}

public Constraint withShrinkPoint(long shrinkPoint) {
    OptionalLong newShrinkPoint = OptionalLong.of(shrinkPoint);
    if (!newShrinkPoint.equals(shrinkTarget)) {
        return new Constraint(min, max, newShrinkPoint);
    } else {
        return this;
    }
}

@Test
public void noConstraintAllowsAnyLong() {
    assertThat(Constraint.none().min()).isEqualTo(Long.MIN_VALUE);
    assertThat(Constraint.none().max()).isEqualTo(Long.MAX_VALUE);
}

public static Constraint none() {
    return NONE;
}


@Test
public void capturesConsumedLongs() {
    testee.store(3l, Constraint.none());
    testee.store(6l, Constraint.none());
    testee.store(9l, Constraint.none());
    testee.store(12l, Constraint.none());
    assertThat(testee.current()).containsExactly(3l, 6l, 9l, 12l);
}

void store(long l, Constraint constraint) {
    addToBuffer(l);
    storeConstraints(constraint);
}

@Test
public void capturesConsumedLongsWhenBufferMustBeResized() {
    for (long l = 0; l != 128; l++) {
        testee.store(l, Constraint.none());
    }
    assertThat(testee.current()[0]).isEqualTo(0);
    assertThat(testee.current()[127]).isEqualTo(127);
}

@Test
public void produces8ByteArrayWhenContainsSingleLong() {
    testee.store(42l, Constraint.none());
    assertThat(testee.bytes()).hasSize(8);
}

@Test
public void producesEmptyByteArrayWhenNoValues() {
    assertThat(testee.bytes()).isEmpty();
}

byte[] bytes() {
    return Arrays.copyOf(buffer.array(), buffer.position());
}


@Test
public void onlyCapturesPrecursorsThatPassCompoundAssumptions() {
    Gen<Pair<Long, Long>> joined = Generate.longRange(0l, 5l).assuming(l -> l != 3).map((a, b) -> Pair.of(a, b)).assuming(p -> p._1 == 2).assuming(p -> p._2 != 0);
    Pair<Long, Long> value = joined.generate(testee);
    assertThat(testee.capturedPrecursor().current()).containsExactly(value._1, value._2);
}

Precursor capturedPrecursor() {
    return precursor;
}


@Test
public void alwaysGeneratesRHSOfMixWhenWeightingIs100() {
    Gen<Integer> testee = Generate.constant(1).mix(Generate.constant(2), 100);
    assertThatGenerator(testee).generatesAllOf(2);
}

default Gen<T> mix(Gen<T> rhs, int weight) {
    return prng -> {
        long picked = prng.next(Constraint.between(0, 99));
        if (picked >= weight) {
            return this.generate(prng);
        }
        return rhs.generate(prng);
    };
}

@Test
public void alwaysGeneratesLHSOfMixWhenWeightingIs0() {
    Gen<Integer> testee = Generate.constant(1).mix(Generate.constant(2), 0);
    assertThatGenerator(testee).generatesAllOf(1);
}

@Test
public void producesValuesFromBothSidesWhenWeightingIs5050() {
    Gen<Integer> testee = Generate.constant(1).mix(Generate.constant(2), 50);
    assertThatGenerator(testee).generatesAllOf(1, 2);
}


@Test
public void shouldNotShrinkFalse() {
    Source<Boolean> testee = Booleans.generate();
    assertThatSource(testee).cannotShrink(false);
}

static Source<Boolean> generate() {
    return Arbitrary.pick(Arrays.asList(false, true));
}

@Test
public void shouldShrinkTrueToFalse() {
    Source<Boolean> testee = Booleans.generate();
    assertThatSource(testee).shrinksValueTo(true, false);
}

@Test
public void shouldGenerateBothOptions() {
    Source<Boolean> testee = Booleans.generate();
    assertThatSource(testee).generatesAllOf(true, false);
}


@Test
public void shouldGenerateBooleansAsExpected() {
    Source<Boolean> testee = booleans().all();
    assertThatSource(testee).generatesAllOf(true, false);
}

public static BooleansDSL booleans() {
    return new BooleansDSL();
}


@Test
public void shouldFalisyWhenAssertionsThrownAfterTypeConversion() {
    qt().forAll(arbitrary().sequence(1, 2, 3, 4, 5, 6, 7), arbitrary().sequence(1, 2, 3, 4, 5, 6, 7)).as((a, b) -> a.toString() + b).checkAssert(a -> assertEquals("11", a));
    verifier.isFalsifiedByException();
}

public <T> Subject1<T> as(BiFunction<A, B, T> mapping) {
    return new MappingTheoryBuilder<>(state, combine(), precursor -> assumptions.test(precursor._1, precursor._2), precursor -> mapping.apply(precursor._1, precursor._2), t -> t.toString());
}

public <T> Subject1<T> as(BiFunction<A, B, T> mapping) {
    return new MappingTheoryBuilder<>(this.state, combine(), precursor -> this.assumptions.test(precursor._1, precursor._2), precursor -> mapping.apply(precursor._1, precursor._2), t -> t.toString());
}


@Test
public void shouldFalisyWhenAssertionsThrownAfterTypeConversion() {
    qt().forAll(arbitrary().sequence(1, 2, 3, 4, 5, 6, 7), arbitrary().sequence(1, 2, 3, 4, 5, 6, 7), arbitrary().sequence(1, 2, 3, 4, 5, 6, 7)).as((a, b, c) -> a.toString() + b + c).checkAssert(a -> assertEquals("111", a));
    verifier.isFalsifiedByException();
}

public <T> Subject1<T> as(Function3<A, B, C, T> mapping) {
    return new MappingTheoryBuilder<>(state, combine(), precursor -> assumptions.test(precursor._1, precursor._2, precursor._3), tuple -> mapping.apply(tuple._1, tuple._2, tuple._3), t -> t.toString());
}

public <T> Subject1<T> as(Function3<A, B, C, T> mapping) {
    return new MappingTheoryBuilder<>(this.state, combine(), precursor -> this.assumptions.test(precursor._1, precursor._2, precursor._3), tuple -> mapping.apply(tuple._1, tuple._2, tuple._3), t -> t.toString());
}


@Test
public void shouldFalisyWhenAssertionsThrownAfterTypeConversion() {
    qt().forAll(arbitrary().sequence(1, 2, 3, 4, 5, 6, 7), arbitrary().sequence(1, 2, 3, 4, 5, 6, 7), arbitrary().sequence(1, 2, 3, 4, 5, 6, 7), arbitrary().sequence(1, 2, 3, 4, 5, 6, 7)).as((a, b, c, d) -> a.toString() + b + c + d).checkAssert(a -> assertEquals("111a", a));
    verifier.isFalsifiedByException();
}

public <T> Subject1<T> as(Function4<A, B, C, D, T> mapping) {
    return new MappingTheoryBuilder<>(state, combine(), precursor -> assumptions.test(precursor._1, precursor._2, precursor._3, precursor._4), tuple -> mapping.apply(tuple._1, tuple._2, tuple._3, tuple._4), t -> t.toString());
}

public <T> Subject1<T> as(Function4<A, B, C, D, T> mapping) {
    return new MappingTheoryBuilder<>(this.state, combine(), precursor -> this.assumptions.test(precursor._1, precursor._2, precursor._3, precursor._4), tuple -> mapping.apply(tuple._1, tuple._2, tuple._3, tuple._4), t -> t.toString());
}


@Test
public void shouldMapValues() {
    assertThat(Pair.of(1, 2).map(i -> "A" + i, i -> "B" + i)).isEqualTo(Pair.of("A1", "B2"));
}

public <A1, B1> Pair<A1, B1> map(Function<A, A1> fa, Function<B, B1> fb) {
    return Pair.of(fa.apply(_1), fb.apply(_2));
}


@Test
public void shouldMapValues() {
    assertThat(Tuple3.of(1, 2, 3).map(i -> "A" + i, i -> "B" + i, i -> "C" + i)).isEqualTo(Tuple3.of("A1", "B2", "C3"));
}

public <A1, B1, C1> Tuple3<A1, B1, C1> map(Function<A, A1> fa, Function<B, B1> fb, Function<C, C1> fc) {
    return Tuple3.of(fa.apply(_1), fb.apply(_2), fc.apply(_3));
}


@Test
public void shouldMapValues() {
    assertThat(Tuple4.of(1, 2, 3, 4).map(i -> "A" + i, i -> "B" + i, i -> "C" + i, i -> "D" + i)).isEqualTo(Tuple4.of("A1", "B2", "C3", "D4"));
}

public <A1, B1, C1, D1> Tuple4<A1, B1, C1, D1> map(Function<A, A1> fa, Function<B, B1> fb, Function<C, C1> fc, Function<D, D1> fd) {
    return Tuple4.of(fa.apply(_1), fb.apply(_2), fc.apply(_3), fd.apply(_4));
}


@Test
public void shouldMapValues() {
    assertThat(Tuple5.of(1, 2, 3, 4, 5).map(i -> "A" + i, i -> "B" + i, i -> "C" + i, i -> "D" + i, i -> "E" + i)).isEqualTo(Tuple5.of("A1", "B2", "C3", "D4", "E5"));
}

public <A1, B1, C1, D1, E1> Tuple5<A1, B1, C1, D1, E1> map(Function<A, A1> fa, Function<B, B1> fb, Function<C, C1> fc, Function<D, D1> fd, Function<E, E1> fe) {
    return Tuple5.of(fa.apply(_1), fb.apply(_2), fc.apply(_3), fd.apply(_4), fe.apply(_5));
}


@Test
public void shouldRetainCustomToStringForAirity2() {
    try {
        qt().forAll(arbitrary().constant(1).describedAs(l -> "EXPECTED" + l), arbitrary().constant(2).describedAs(l -> "EXPECTED" + l)).check((a, b) -> false);
        failIfReached();
    } catch (final AssertionError error) {
        assertThat(error).hasMessageContaining("EXPECTED1, EXPECTED2");
    }
}

public Source<T> describedAs(AsString<T> asString) {
    return new Source<>(this.prngToValue, this.shrink, asString);
}

@Test
public void shouldPrintReadableRepresentationOfArrays() {
    try {
        qt().withFixedSeed(5).forAll(arrays().ofStrings(strings().ascii().ofLength(1)).withLength(1)).check(i -> false);
        failIfReached();
    } catch (final AssertionError error) {
        assertThat(error).hasMessageContaining(Arrays.deepToString(new String[] { "!" }));
    }
}

public <A> TheoryBuilder<A, A> forAll(final Source<A> values) {
    return new TheoryBuilder<>(state, values, a -> true, x -> x, a -> a.toString());
}

public <A> TheoryBuilder<A> forAll(final Source<A> values) {
    return new TheoryBuilder<>(state, values, a -> true);
}

@Test
public void shouldRetainCustomToStringWhenConvertingWithPrecursor() {
    try {
        qt().forAll(arbitrary().constant(42).describedAs(l -> "EXPECTED" + l)).asWithPrecursor(i -> "astring").check((a, b) -> false);
        failIfReached();
    } catch (final AssertionError error) {
        assertThat(error).hasMessageContaining("EXPECTED42");
    }
}

public <N> Subject2<P, N> asWithPrecursor(Function<T, N> mapping) {
    Generator<Pair<P, N>> g = (prng, step) -> {
        P p = this.ps.next(prng, step);
        T t = this.conversion.apply(p);
        return Pair.of(p, mapping.apply(t));
    };
    Shrink<Pair<P, N>> s = (original, context) -> ps.shrink(original._1, context).map(p -> Pair.of(p, conversion.andThen(mapping).apply(p)));
    Source<Pair<P, N>> gen = Source.of(g).withShrinker(s);
    return new PrecursorTheoryBuilder1<P, N>(state, gen, assumptions, a -> a.toString(), b -> b.toString());
}

public <T> Subject2<A, T> asWithPrecursor(Function<A, T> mapping) {
    return asWithPrecursor(mapping, t -> t.toString());
}

@Test
public void shouldRetainCustomToStringWhenConvertingWithPrecursorForAirity3() {
    try {
        qt().forAll(arbitrary().constant(1).describedAs(l -> "EXPECTED" + l), arbitrary().constant(2).describedAs(l -> "EXPECTED" + l), arbitrary().constant(3).describedAs(l -> "EXPECTED" + l)).asWithPrecursor((a, b, c) -> "astring").check((a, b, c, d) -> false);
        failIfReached();
    } catch (final AssertionError error) {
        assertThat(error).hasMessageContaining("EXPECTED1, EXPECTED2, EXPECTED3");
    }
}

public <T> Subject4<A, B, C, T> asWithPrecursor(Function3<A, B, C, T> mapping) {
    Shrink<Tuple4<A, B, C, T>> shrink = (original, context) -> combineShrinks().shrink(Tuple3.of(original._1, original._2, original._3), context).map(precursor -> precursor.extend(mapping));
    Source<Tuple4<A, B, C, T>> gen = Source.of(generatePrecursorValueTuple(mapping)).withShrinker(shrink);
    return new PrecursorTheoryBuilder3<A, B, C, T>(state, gen, assumptions, a -> a.toString(), b -> b.toString(), c -> c.toString(), t -> t.toString());
}

public <T> Subject4<A, B, C, T> asWithPrecursor(Function3<A, B, C, T> mapping) {
    return this.asWithPrecursor(mapping, t -> t.toString());
}

@Test
public void shouldRetainCustomToStringForAirity1() {
    try {
        qt().forAll(arbitrary().constant(42).describedAs(l -> "EXPECTED" + l)).check(l -> false);
        failIfReached();
    } catch (final AssertionError error) {
        assertThat(error).hasMessageContaining("EXPECTED42");
    }
}

@Test
public void shouldRetainCustomToStringForAirity4() {
    try {
        qt().forAll(arbitrary().constant(1).describedAs(l -> "EXPECTED" + l), arbitrary().constant(2).describedAs(l -> "EXPECTED" + l), arbitrary().constant(3).describedAs(l -> "EXPECTED" + l), arbitrary().constant(4).describedAs(l -> "EXPECTED" + l)).check((a, b, c, d) -> false);
        failIfReached();
    } catch (final AssertionError error) {
        assertThat(error).hasMessageContaining("EXPECTED1, EXPECTED2, EXPECTED3, EXPECTED4");
    }
}

@Test
public void shouldRetainCustomToStringWhenConvertingWithPrecursorForAirity2() {
    try {
        qt().forAll(arbitrary().constant(1).describedAs(l -> "EXPECTED" + l), arbitrary().constant(2).describedAs(l -> "EXPECTED" + l)).asWithPrecursor((a, b) -> "astring").check((a, b, c) -> false);
        failIfReached();
    } catch (final AssertionError error) {
        assertThat(error).hasMessageContaining("EXPECTED1, EXPECTED2");
    }
}

public <T> Subject3<A, B, T> asWithPrecursor(BiFunction<A, B, T> mapping) {
    Generator<Tuple3<A, B, T>> g = (prng, step) -> {
        A a = this.as.next(prng, step);
        B b = this.bs.next(prng, step);
        return Tuple3.of(a, b, mapping.apply(a, b));
    };
    Shrink<Tuple3<A, B, T>> s = (original, context) -> joinShrunkStreams().shrink(Pair.of(original._1, original._2), context).map(p -> Tuple3.of(p._1, p._2, mapping.apply(p._1, p._2)));
    Source<Tuple3<A, B, T>> gen = Source.of(g).withShrinker(s);
    return new PrecursorTheoryBuilder2<A, B, T>(state, gen, assumptions, a -> a.toString(), b -> b.toString(), t -> t.toString());
}

public <T> Subject3<A, B, T> asWithPrecursor(BiFunction<A, B, T> mapping) {
    return this.asWithPrecursor(mapping, t -> t.toString());
}

@Test
public void shouldRetainCustomToStringWhenConvertingWithPrecursorForAirity4() {
    try {
        qt().forAll(arbitrary().constant(1).describedAs(l -> "EXPECTED" + l), arbitrary().constant(2).describedAs(l -> "EXPECTED" + l), arbitrary().constant(3).describedAs(l -> "EXPECTED" + l), arbitrary().constant(4).describedAs(l -> "EXPECTED" + l)).asWithPrecursor((a, b, c, d) -> "astring").check((a, b, c, d, e) -> false);
        failIfReached();
    } catch (final AssertionError error) {
        assertThat(error).hasMessageContaining("EXPECTED1, EXPECTED2, EXPECTED3, EXPECTED4");
    }
}

public <T> Subject5<A, B, C, D, T> asWithPrecursor(Function4<A, B, C, D, T> mapping) {
    Shrink<Tuple5<A, B, C, D, T>> shrink = (original, context) -> combineShrinks().shrink(Tuple4.of(original._1, original._2, original._3, original._4), context).map(precursor -> precursor.extend(mapping));
    Source<Tuple5<A, B, C, D, T>> gen = Source.of(generatePrecursorValueTuple(mapping)).withShrinker(shrink);
    return new PrecursorTheoryBuilder4<A, B, C, D, T>(state, gen, assumptions, a -> a.toString(), b -> b.toString(), c -> c.toString(), d -> d.toString(), t -> t.toString());
}

public <T> Subject5<A, B, C, D, T> asWithPrecursor(Function4<A, B, C, D, T> mapping) {
    return asWithPrecursor(mapping, t -> t.toString());
}

@Test
public void shouldRetainCustomToStringForAirity3() {
    try {
        qt().forAll(arbitrary().constant(1).describedAs(l -> "EXPECTED" + l), arbitrary().constant(2).describedAs(l -> "EXPECTED" + l), arbitrary().constant(3).describedAs(l -> "EXPECTED" + l)).check((a, b, c) -> false);
        failIfReached();
    } catch (final AssertionError error) {
        assertThat(error).hasMessageContaining("EXPECTED1, EXPECTED2, EXPECTED3");
    }
}


@Test
public void shouldDescribeArrayContents() {
    Source<Integer[]> testee = Arrays.arraysOf(Integers.range(0, 1), Integer.class, 2);
    Integer[] anArray = { 1, 2, 3 };
    assertThat(testee.asString(anArray)).isEqualTo("[1, 2, 3]");
}

@SuppressWarnings("unchecked")
static <T> Source<T[]> arraysOf(Source<T> values, Class<T> c, int length) {
    return Lists.listsOf(values, Lists.arrayListCollector(), length).as(l -> l.toArray((T[]) Array.newInstance(c, length)), a -> java.util.Arrays.asList(a));
}

static <T> Source<T[]> arraysOf(Source<T> values, Class<T> c, int length) {
    return arraysOf(values, c, length, length);
}


@Test
public void testMapping() throws IOException {
    SourceMap source = new SourceMap(new String(Files.readAllBytes(new File("src/test/resources/bundle.js.map").toPath())));
    source.eachMapping(System.out::println);
    System.out.println(source.getMapping(44, 0));
    System.out.println(source.getMapping(60, 0));
    System.out.println(source.getMapping(80, 0));
}

public String toString() {
    return "Mapping " + generatedLine + ":" + generatedColumn + " -> " + sourceFileName + ":" + sourceLine + ":" + sourceColumn;
}


@Test(timeout = 30000)
public void deployUnderSubdirectoryAndPathsStillBeCorrect(TestContext ctx) {
    final Async async = ctx.async();
    rule.vertx().deployVerticle("mjs:./lib/main.spec.mjs", deploy -> {
        if (deploy.failed()) {
            deploy.cause().printStackTrace();
        }
        ctx.assertTrue(deploy.succeeded());
        rule.vertx().setTimer(1000L, t -> rule.vertx().undeploy(deploy.result(), undeploy -> {
            ctx.assertTrue(undeploy.succeeded());
            async.complete();
        }));
    });
}

@Override
public final void createVerticle(String verticleName, ClassLoader classLoader, Promise<Callable<Verticle>> promise) {
    final String fsVerticleName = VerticleFactory.removePrefix(verticleName);
    if (">".equals(fsVerticleName)) {
        promise.complete(() -> {
            final Runtime runtime = createRuntime(engine);
            return new REPLVerticle(runtime);
        });
    } else {
        promise.complete(() -> {
            final Runtime runtime = createRuntime(engine);
            return createVerticle(runtime, fsVerticleName);
        });
    }
}

@Override
public final void createVerticle(String verticleName, ClassLoader classLoader, Promise<Callable<Verticle>> promise) {
    final String strippedName = toNixPath(VerticleFactory.removePrefix(verticleName));
    if (">".equals(strippedName)) {
        promise.complete(() -> {
            final Runtime runtime = createRuntime(engine);
            return new REPLVerticle(runtime);
        });
    } else {
        promise.complete(() -> {
            final Runtime runtime = createRuntime(engine);
            return createVerticle(runtime, mainScript(strippedName));
        });
    }
}


@Test(timeout = 30000)
public void testThatGlobalObjectsArePresentMJS(TestContext ctx) {
    final Async async = ctx.async();
    rule.vertx().deployVerticle("mjs:globals-present.js", deploy -> {
        if (deploy.failed()) {
            ctx.fail(deploy.cause());
        } else {
            ctx.assertTrue(deploy.succeeded());
            rule.vertx().setTimer(1000L, t -> rule.vertx().undeploy(deploy.result(), undeploy -> {
                ctx.assertTrue(undeploy.succeeded());
                async.complete();
            }));
        }
    });
}

@Test(timeout = 30000)
public void testThatGlobalObjectsArePresentJS(TestContext ctx) {
    final Async async = ctx.async();
    rule.vertx().deployVerticle("js:globals-present.js", deploy -> {
        if (deploy.failed()) {
            ctx.fail(deploy.cause());
        } else {
            ctx.assertTrue(deploy.succeeded());
            rule.vertx().setTimer(1000L, t -> rule.vertx().undeploy(deploy.result(), undeploy -> {
                ctx.assertTrue(undeploy.succeeded());
                async.complete();
            }));
        }
    });
}


@Test
void checkThreadsDialogURLWithState() {
    FacebookClient client = new DefaultThreadsClient(Version.LATEST);
    String loginDialogUrlString = client.getLoginDialogUrl("1234", "http://www.example.com", new ScopeBuilder(true), "state3456");
    assertThat(loginDialogUrlString).isEqualTo("https://www.threads.net/oauth/authorize?client_id=1234&redirect_uri=http%3A%2F%2Fwww.example.com&state=state3456&response_type=code");
}

@Override
public String getLoginDialogUrl(String appId, String redirectUri, ScopeBuilder scope, String state, Parameter... parameters) {
    List<Parameter> parameterList = new ArrayList<>();
    Collections.addAll(parameterList, parameters);
    parameterList.add(Parameter.with("response_type", CODE));
    return getGenericLoginDialogUrl(appId, redirectUri, scope, () -> getFacebookEndpointUrls().getThreadsBaseEndpoint() + "/oauth/authorize", state, parameterList);
}

@Test
void checkThreadsDialogURLAdditionalParameters() {
    FacebookClient client = new DefaultThreadsClient(Version.LATEST);
    String loginDialogUrlString = client.getLoginDialogUrl("1234", "http://www.example.com", new ScopeBuilder(true), Parameter.with("state", "abcd"));
    assertThat(loginDialogUrlString).isEqualTo("https://www.threads.net/oauth/authorize?client_id=1234&redirect_uri=http%3A%2F%2Fwww.example.com&state=abcd&response_type=code");
}

@Override
public String getLoginDialogUrl(String appId, String redirectUri, ScopeBuilder scope, Parameter... parameters) {
    return getLoginDialogUrl(appId, redirectUri, scope, null, parameters);
}


@Test
void checkInstagramDialogURLAdditionalParameters() {
    FacebookClient client = new DefaultFacebookClient(Version.LATEST);
    String loginDialogUrlString = client.getInstagramLoginDialogUrl("1234", "http://www.example.com", new ScopeBuilder(true), Parameter.with("state", "abcd"));
    assertThat(loginDialogUrlString).isEqualTo("https://api.instagram.com/oauth/authorize?client_id=1234&redirect_uri=http%3A%2F%2Fwww.example.com&state=abcd&response_type=code");
}

@Override
public String getInstagramLoginDialogUrl(String instagramAppId, String redirectUri, ScopeBuilder scope, Parameter... parameters) {
    List<Parameter> parameterList = new ArrayList<>();
    Collections.addAll(parameterList, parameters);
    parameterList.add(Parameter.with("response_type", "code"));
    return getGenericLoginDialogUrl(instagramAppId, redirectUri, scope, () -> getFacebookEndpointUrls().getInstagramApiEndpoint() + "/oauth/authorize", parameterList);
}

@Test
void checkInstagramDialogURL() {
    FacebookClient client = new DefaultFacebookClient(Version.LATEST);
    String loginDialogUrlString = client.getInstagramLoginDialogUrl("1234", "http://www.example.com", new ScopeBuilder(true));
    assertThat(loginDialogUrlString).isEqualTo("https://api.instagram.com/oauth/authorize?client_id=1234&redirect_uri=http%3A%2F%2Fwww.example.com&response_type=code");
}


@Test
public void containsMatch() {
    Trie trie = Trie.builder().removeOverlaps().addKeyword("ab").addKeyword("cba").addKeyword("ababc").build();
    assertTrue(trie.containsMatch("ababcbab"));
}

public boolean containsMatch(CharSequence text) {
    Emit firstMatch = firstMatch(text);
    return firstMatch != null;
}

@Test
public void recipesFirstMatch() {
    Trie trie = Trie.builder().addKeyword("veal").addKeyword("cauliflower").addKeyword("broccoli").addKeyword("tomatoes").build();
    Emit firstMatch = trie.firstMatch("2 cauliflowers, 3 tomatoes, 4 slices of veal, 100g broccoli");
    checkEmit(firstMatch, 2, 12, "cauliflower");
}

public Emit firstMatch(CharSequence text) {
    if (!trieConfig.isAllowOverlaps()) {
        Collection<Emit> parseText = parseText(text);
        if (parseText != null && !parseText.isEmpty()) {
            return parseText.iterator().next();
        }
    } else {
        State currentState = this.rootState;
        for (int position = 0; position < text.length(); position++) {
            Character character = text.charAt(position);
            if (trieConfig.isCaseInsensitive()) {
                character = Character.toLowerCase(character);
            }
            currentState = getState(currentState, character);
            Collection<String> emitStrs = currentState.emit();
            if (emitStrs != null && !emitStrs.isEmpty()) {
                for (String emitStr : emitStrs) {
                    final Emit emit = new Emit(position - emitStr.length() + 1, position, emitStr);
                    if (trieConfig.isOnlyWholeWords()) {
                        if (!isPartialMatch(text, emit)) {
                            return emit;
                        }
                    } else {
                        return emit;
                    }
                }
            }
        }
    }
    return null;
}

@Test
public void variousKeywordsFirstMatch() {
    Trie trie = Trie.builder().addKeyword("abc").addKeyword("bcd").addKeyword("cde").build();
    Emit firstMatch = trie.firstMatch("bcd");
    checkEmit(firstMatch, 0, 2, "bcd");
}

@Test
public void misleadingTestFirstMatch() {
    Trie trie = Trie.builder().addKeyword("hers").build();
    Emit firstMatch = trie.firstMatch("h he her hers");
    checkEmit(firstMatch, 9, 12, "hers");
}

@Test
public void caseInsensitiveFirstMatch() {
    Trie trie = Trie.builder().caseInsensitive().addKeyword("turning").addKeyword("once").addKeyword("again").addKeyword("börkü").build();
    Emit firstMatch = trie.firstMatch("TurninG OnCe AgAiN BÖRKÜ");
    checkEmit(firstMatch, 0, 6, "turning");
}

@Test
public void partialMatchFirstMatch() {
    Trie trie = Trie.builder().onlyWholeWords().addKeyword("sugar").build();
    Emit firstMatch = trie.firstMatch("sugarcane sugarcane sugar canesugar");
    checkEmit(firstMatch, 20, 24, "sugar");
}

@Test
public void ushersTestFirstMatch() {
    Trie trie = Trie.builder().addKeyword("hers").addKeyword("his").addKeyword("she").addKeyword("he").build();
    Emit firstMatch = trie.firstMatch("ushers");
    checkEmit(firstMatch, 2, 3, "he");
}

@Test
public void nonOverlappingFirstMatch() {
    Trie trie = Trie.builder().removeOverlaps().addKeyword("ab").addKeyword("cba").addKeyword("ababc").build();
    Emit firstMatch = trie.firstMatch("ababcbab");
    checkEmit(firstMatch, 0, 4, "ababc");
}

@Test
public void keywordAndTextAreTheSameFirstMatch() {
    Trie trie = Trie.builder().addKeyword("abc").build();
    Emit firstMatch = trie.firstMatch("abc");
    checkEmit(firstMatch, 0, 2, "abc");
}

@Test
public void unicodeIssueBug8ReportedByDwyerkFirstMatch() {
    String target = "LİKE THIS";
    Trie trie = Trie.builder().caseInsensitive().onlyWholeWords().addKeyword("this").build();
    assertEquals("THIS", target.substring(5, 9));
    Emit firstMatch = trie.firstMatch(target);
    checkEmit(firstMatch, 5, 8, "this");
}

@Test
public void textIsLongerThanKeywordFirstMatch() {
    Trie trie = Trie.builder().addKeyword("abc").build();
    Emit firstMatch = trie.firstMatch(" abc");
    checkEmit(firstMatch, 1, 3, "abc");
}


@Test
public void tokenizeFullSentence() {
    Trie trie = new Trie();
    trie.addKeyword("Alpha");
    trie.addKeyword("Beta");
    trie.addKeyword("Gamma");
    Collection<Token> tokens = trie.tokenize("Hear: Alpha team first, Beta from the rear, Gamma in reserve");
    assertEquals(7, tokens.size());
    Iterator<Token> tokensIt = tokens.iterator();
    assertEquals("Hear: ", tokensIt.next().getFragment());
    assertEquals("Alpha", tokensIt.next().getFragment());
    assertEquals(" team first, ", tokensIt.next().getFragment());
    assertEquals("Beta", tokensIt.next().getFragment());
    assertEquals(" from the rear, ", tokensIt.next().getFragment());
    assertEquals("Gamma", tokensIt.next().getFragment());
    assertEquals(" in reserve", tokensIt.next().getFragment());
}

public Collection<Token> tokenize(String text) {
    Collection<Token> tokens = new ArrayList<Token>();
    Collection<Emit> collectedEmits = parseText(text);
    int lastCollectedPosition = -1;
    for (Emit emit : collectedEmits) {
        if (emit.getStart() - lastCollectedPosition > 1) {
            tokens.add(createFragment(emit, text, lastCollectedPosition));
        }
        tokens.add(createMatch(emit, text));
        lastCollectedPosition = emit.getEnd();
    }
    if (text.length() - lastCollectedPosition > 1) {
        tokens.add(createFragment(null, text, lastCollectedPosition));
    }
    return tokens;
}

@Test
public void tokenizeTokensInSequence() {
    Trie trie = new Trie();
    trie.addKeyword("Alpha");
    trie.addKeyword("Beta");
    trie.addKeyword("Gamma");
    Collection<Token> tokens = trie.tokenize("Alpha Beta Gamma");
    assertEquals(5, tokens.size());
}


@Test
public void ushersTestWithCapitalKeywords() {
    Trie trie = Trie.builder().caseInsensitive().addKeyword("HERS").addKeyword("HIS").addKeyword("SHE").addKeyword("HE").build();
    Collection<Emit> emits = trie.parseText("ushers");
    assertEquals(3, emits.size());
    Iterator<Emit> iterator = emits.iterator();
    checkEmit(iterator.next(), 2, 3, "he");
    checkEmit(iterator.next(), 1, 3, "she");
    checkEmit(iterator.next(), 2, 5, "hers");
}

public TrieBuilder addKeyword(String keyword) {
    trie.addKeyword(keyword);
    return this;
}


@Test
public void caseInsensitive() {
    Trie trie = new Trie().caseInsensitive();
    trie.addKeyword("turning");
    trie.addKeyword("once");
    trie.addKeyword("again");
    trie.addKeyword("börkü");
    Collection<Emit> emits = trie.parseText("TurninG OnCe AgAiN BÖRKÜ");
    assertEquals(4, emits.size());
    Iterator<Emit> it = emits.iterator();
    checkEmit(it.next(), 0, 6, "turning");
    checkEmit(it.next(), 8, 11, "once");
    checkEmit(it.next(), 13, 17, "again");
    checkEmit(it.next(), 19, 23, "börkü");
}

public Trie caseInsensitive() {
    this.trieConfig.setCaseInsensitive(true);
    return this;
}


@Test
public void testIsMyTypeVersionAbsent() {
    document.setRootElement(new Element("rss"));
    assertTrue(parser.isMyType(document));
}

@Override
public boolean isMyType(final Document document) {
    return rootElementMatches(document) && versionMatches(document);
}

@Override
public boolean isMyType(final Document document) {
    return rootElementMatches(document) && (versionMatches(document) || versionAbsent(document));
}


@Test
public void testFindWorkspace() throws Exception {
    assertNotNull(service);
    final ClientWorkspace ws = (ClientWorkspace) service.findWorkspace("adminblog");
    if (ws != null) {
        final ClientCollection col = (ClientCollection) ws.findCollection(null, "entry");
        final ClientEntry entry = col.createEntry();
        entry.setTitle("NPE on submitting order query");
        entry.setContent("This is a <b>bad</b> one!", Content.HTML);
        col.addEntry(entry);
        final ClientEntry saved = col.getEntry(entry.getEditURI());
        assertNotNull(saved);
        saved.remove();
        boolean failed = false;
        try {
            col.getEntry(saved.getEditURI());
        } catch (final ProponoException e) {
            failed = true;
        }
        assertTrue(failed);
    }
}

public Workspace findWorkspace(final String title) {
    for (final Object element : workspaces) {
        final Workspace ws = (Workspace) element;
        if (title.equals(ws.getTitle())) {
            return ws;
        }
    }
    return null;
}


@Test
public void test() {
    try {
        String expectedJson = "{\"chatId\":\"12345\",\"messageThreadId\":null,\"medias\":[{\"media\":\"attach://321.png\",\"caption_entities\":[],\"type\":\"photo\"}," + "{\"media\":\"attach://123.png\",\"caption_entities\":[],\"type\":\"photo\"}],\"replyToMessageId\":null,\"disableNotification\":null," + "\"allowSendingWithoutReply\":null,\"protectContent\":null,\"replyParameters\":null,\"businessConnectionId\":null," + "\"method\":\"sendMediaGroup\"}";
        InputStream is = new ByteArrayInputStream("RandomFileContent".getBytes());
        InputStream is2 = new ByteArrayInputStream("RandomFileContent2".getBytes());
        SendMediaGroup sendMediaGroup = SendMediaGroup.builder().chatId("12345").media(InputMediaPhoto.builder().media(is, "321.png").build()).media(InputMediaPhoto.builder().media(is2, "123.png").build()).build();
        String json = mapper.writeValueAsString(sendMediaGroup);
        assertEquals(expectedJson, json);
    } catch (Exception e) {
        fail(e);
    }
}

@JsonIgnore
public B media(@NonNull InputStream mediaStream, @NonNull String fileName) {
    this.newMediaStream = mediaStream;
    this.isNewMedia = true;
    this.mediaName = fileName;
    this.media = "attach://" + fileName;
    return self();
}


@Test
public void canTurnOffAbilitiesThroughProperties() {
    Properties properties = new Properties();
    try {
        properties.load(new FileInputStream(filename));
        toggle = new CustomToggle().config(properties);
    } catch (IOException e) {
        System.out.println("No such file");
    }
    customBot = new DefaultBot(EMPTY, EMPTY, db, toggle);
    customBot.onRegister();
    assertFalse(customBot.abilities().containsKey(DefaultAbilities.CLAIM));
}

public CustomToggle config(Properties properties) {
    for (String key : properties.stringPropertyNames()) {
        String value = properties.getProperty(key);
        key = key.toLowerCase();
        for (Property p : Property.values()) {
            if (key.equals(p.key())) {
                String ability = key.split("\\.")[1];
                if (key.contains("enabled") && value.equalsIgnoreCase("false")) {
                    this.turnOff(ability);
                } else if (key.contains("toggle")) {
                    this.toggle(ability, value);
                }
            }
        }
    }
    return this;
}

@Test
public void canProcessAbilitiesThroughProperties() {
    Properties properties = new Properties();
    try {
        properties.load(new FileInputStream(filename));
        toggle = new CustomToggle().config(properties);
    } catch (IOException e) {
        System.out.println("No such file");
    }
    customBot = new DefaultBot(EMPTY, EMPTY, db, toggle);
    customBot.onRegister();
    String targetName = "restrict";
    assertTrue(customBot.abilities().containsKey(targetName));
}


@Test
public void testSecretGenerated() {
    DefaultSecretGenerator generator160 = new DefaultSecretGenerator();
    String secret160 = generator160.generate();
    assertNotNull(secret160);
    assertEquals(32, secret160.length());
    DefaultSecretGenerator generator80 = new DefaultSecretGenerator(80);
    String secret80 = generator80.generate();
    assertNotNull(secret80);
    assertEquals(16, secret80.length());
}

public String generate() {
    return new String(encoder.encode(getRandomBytes()));
}


@Test
public void voidTestMimeType() {
    assertEquals("image/png", new ZxingPngQrGenerator().getImageMimeType());
}

public String getImageMimeType() {
    return "image/png";
}


@Test
public void allImplementationsAreAvailable() {
    assertTrue(Java8Base64.isAvailable());
}

static boolean isAvailable() {
    try {
        Class.forName("java.util.Base64", false, Java8Base64.class.getClassLoader());
        return true;
    } catch (ClassNotFoundException cnfE) {
        return false;
    }
}


@Test
public void testParseBuckets() {
    JsonObject termsAggregationJson = new Gson().fromJson(termsAggregationContent, JsonObject.class);
    TermsAggregation termsAggregation = new TermsAggregation("termsAggregation", termsAggregationJson);
    List<Entry> buckets = termsAggregation.getBuckets();
    assertNotNull(buckets);
    assertEquals(2, buckets.size());
    Entry entryWithKeyAsString = buckets.get(0);
    assertEquals(EXPECTED_KEY_VALUE, entryWithKeyAsString.getKey());
    assertEquals(EXPECTED_KEY_AS_STRING_VALUE, entryWithKeyAsString.getKeyAsString());
    assertTrue(EXPECTED_DOC_COUNT_VALUE == entryWithKeyAsString.getCount());
    Entry entryWithoutKeyAsString = buckets.get(1);
    assertEquals(EXPECTED_KEY_VALUE, entryWithoutKeyAsString.getKey());
    assertEquals(EXPECTED_KEY_VALUE, entryWithoutKeyAsString.getKeyAsString());
    assertTrue(EXPECTED_DOC_COUNT_VALUE == entryWithoutKeyAsString.getCount());
}

public String getKeyAsString() {
    return keyAsString;
}


@Test
public void reroute() throws JSONException {
    List<RerouteCommand> moveCommands = new LinkedList<RerouteCommand>();
    moveCommands.add(new RerouteMove("index1", 1, "node1", "node2"));
    moveCommands.add(new RerouteCancel("index2", 1, "node2", true));
    moveCommands.add(new RerouteAllocate("index3", 1, "node3", false));
    Reroute reroute = new Reroute.Builder(moveCommands).build();
    assertEquals("/_cluster/reroute", reroute.getURI());
    assertEquals("POST", reroute.getRestMethodName());
    String expectedData = "{ \"commands\": [" + "{ \"move\": { \"index\": \"index1\", \"shard\": 1, \"from_node\": \"node1\", \"to_node\": \"node2\" } }, " + "{ \"cancel\": { \"index\": \"index2\", \"shard\": 1, \"node\": \"node2\", \"allow_primary\": true } }," + "{ \"allocate\": { \"index\": \"index3\", \"shard\": 1, \"node\": \"node3\", \"allow_primary\": false } }" + "] }";
    JSONAssert.assertEquals(expectedData, reroute.getData(new Gson()), false);
}

@Override
public Reroute build() {
    return new Reroute(this);
}


@Test
public void reroute() throws JSONException {
    List<RerouteCommand> moveCommands = new LinkedList<RerouteCommand>();
    moveCommands.add(new RerouteMove("index1", 1, "node1", "node2"));
    moveCommands.add(new RerouteMove("index2", 1, "node2", "node1"));
    Reroute reroute = new Reroute.Builder(moveCommands).build();
    assertEquals("/_cluster/reroute", reroute.getURI());
    assertEquals("POST", reroute.getRestMethodName());
    String expectedData = "{ \"commands\": [" + "{ \"move\": { \"index\": \"index1\", \"shard\": 1, \"from_node\": \"node1\", \"to_node\": \"node2\" } }, " + "{ \"move\": { \"index\": \"index2\", \"shard\": 1, \"from_node\": \"node2\", \"to_node\": \"node1\" } }" + "] }";
    JSONAssert.assertEquals(expectedData, reroute.getData(new Gson()), false);
}


@Test
public void allowPrimaryFalse() throws JSONException {
    RerouteAllocate allocateReplica = new RerouteAllocate("index1", 1, "node1", false);
    assertEquals(allocateReplica.getType(), "allocate");
    String actualJson = new Gson().toJson(allocateReplica.getData());
    String expectedJson = "{\"index\":\"index1\", \"shard\": 1, \"node\": \"node1\", \"allow_primary\": false}";
    JSONAssert.assertEquals(actualJson, expectedJson, false);
}

@Override
public String getType() {
    return "allocate";
}

@Test
public void allowPrimaryTrue() throws JSONException {
    RerouteAllocate allocateReplica = new RerouteAllocate("index1", 1, "node1", true);
    assertEquals(allocateReplica.getType(), "allocate");
    String actualJson = new Gson().toJson(allocateReplica.getData());
    String expectedJson = "{\"index\":\"index1\", \"shard\": 1, \"node\": \"node1\", \"allow_primary\": true}";
    JSONAssert.assertEquals(actualJson, expectedJson, false);
}


@Test
public void allowPrimaryTrue() throws JSONException {
    RerouteCancel rerouteCancel = new RerouteCancel("index1", 1, "node1", true);
    assertEquals(rerouteCancel.getType(), "cancel");
    String actualJson = new Gson().toJson(rerouteCancel.getData());
    String expectedJson = "{\"index\":\"index1\", \"shard\": 1, \"node\": \"node1\", \"allow_primary\": true}";
    JSONAssert.assertEquals(actualJson, expectedJson, false);
}

@Override
public String getType() {
    return "cancel";
}

@Test
public void allowPrimaryFalse() throws JSONException {
    RerouteCancel rerouteCancel = new RerouteCancel("index1", 1, "node1", false);
    assertEquals(rerouteCancel.getType(), "cancel");
    String actualJson = new Gson().toJson(rerouteCancel.getData());
    String expectedJson = "{\"index\":\"index1\", \"shard\": 1, \"node\": \"node1\", \"allow_primary\": false}";
    JSONAssert.assertEquals(actualJson, expectedJson, false);
}


@Test
public void move() throws JSONException {
    RerouteMove rerouteMove = new RerouteMove("index1", 1, "node1", "node2");
    assertEquals(rerouteMove.getType(), "move");
    String actualJson = new Gson().toJson(rerouteMove.getData());
    String expectedJson = "{\"index\":\"index1\", \"shard\": 1, \"from_node\": \"node1\", \"to_node\": \"node2\"}";
    JSONAssert.assertEquals(actualJson, expectedJson, false);
}

@Override
public String getType() {
    return "move";
}


@Test
public void testUriGenerationWhenRemovingStatsFields() throws Exception {
    Action action = new Stats.Builder().flush(true).indexing(true).indexing(false).build();
    assertEquals("_all/_stats/flush", URLDecoder.decode(action.getURI()));
}

public Builder indexing(boolean indexing) {
    return setParameter("indexing", indexing);
}

public Builder indexing(boolean indexing) {
    return toggleApiParameter("indexing", indexing);
}

@Test
public void testUriGenerationWithStatsFields() throws Exception {
    Action action = new Stats.Builder().flush(true).indexing(true).search(true, "group1", "group2").build();
    assertEquals("_all/_stats/flush,indexing,search?groups=group1,group2", URLDecoder.decode(action.getURI()));
}


@Test
public void equalsReturnsTrueForSameMappings() {
    Reindex reindex1 = new Reindex.Builder(source, dest).conflicts("proceed").build();
    Reindex reindex2 = new Reindex.Builder(source, dest).conflicts("proceed").build();
    assertEquals(reindex1, reindex2);
}

public Reindex build() {
    return new Reindex(this);
}

@Test
public void testQueryStringGeneration() throws Exception {
    Reindex reindex = new Reindex.Builder(source, dest).waitForCompletion(true).waitForActiveShards(1).timeout(5000L).requestsPerSecond(5d).build();
    String generatedURI = reindex.getURI();
    String expectedURI = "/_reindex?wait_for_completion=true&wait_for_active_shards=1&timeout=5000&requests_per_second=5.0";
    assertEquals(expectedURI, generatedURI);
}

@Test
public void equalsReturnsFalseForDifferentMappings() {
    Reindex reindex1 = new Reindex.Builder(source, dest).waitForCompletion(true).build();
    Reindex reindex2 = new Reindex.Builder(source, dest).waitForCompletion(false).build();
    assertNotEquals(reindex1, reindex2);
}

@Test
public void testDataGeneration() throws Exception {
    Reindex reindex = new Reindex.Builder(source, dest).conflicts("proceed").build();
    String generatedData = reindex.getData(new GsonBuilder().create());
    String expectedData = "{\"conflicts\":\"proceed\",\"dest\":{\"index\":\"destIndex\"},\"source\":{\"index\":\"sourceIndex\"}}";
    JSONAssert.assertEquals(expectedData, generatedData, false);
}

@Test
public void testBasicUriGeneration() {
    Reindex reindex = new Reindex.Builder(source, dest).build();
    assertEquals("POST", reindex.getRestMethodName());
    assertEquals("/_reindex", reindex.getURI());
}


@Test
public void testBasicUriGenerationWithWaitIfOngoing() {
    Flush flush = new Flush.Builder().addIndex("twitter").waitIfOngoing().build();
    assertEquals("POST", flush.getRestMethodName());
    assertEquals("twitter/_flush?wait_if_ongoing=true", flush.getURI());
}

public Builder waitIfOngoing() {
    return waitIfOngoing(true);
}

@Test
public void testBasicUriGenerationWithForce() {
    Flush flush = new Flush.Builder().addIndex("twitter").force().build();
    assertEquals("POST", flush.getRestMethodName());
    assertEquals("twitter/_flush?force=true", flush.getURI());
}

public Builder force() {
    return force(true);
}


@Test
public void shouldFetchVersionFromValidResponse() {
    assertEquals(Long.valueOf(2), validResult.getVersion());
}

public Long getVersion() {
    return getAsLong(jsonObject.get("_version"));
}


@Test
public void testGetMaxScoreWhenMissing() {
    SearchResult searchResult = new SearchResult(new Gson());
    searchResult.setSucceeded(true);
    searchResult.setJsonString(json);
    searchResult.setJsonObject(new JsonParser().parse(json).getAsJsonObject());
    searchResult.setPathToResult("hits/hits/_source");
    Float maxScore = searchResult.getMaxScore();
    assertNull(maxScore);
    TermsAggregation termsAgg = searchResult.getAggregations().getTermsAggregation("test");
    assertEquals(new Long(1), termsAgg.getDocCountErrorUpperBound());
    assertEquals(new Long(2), termsAgg.getSumOtherDocCount());
    List<Entry> termBuckets = termsAgg.getBuckets();
    for (Entry entry : termBuckets) {
        TopHitsAggregation topHitAgg = entry.getTopHitsAggregation("top_hits_test");
        List<String> hits = topHitAgg.getSourceAsStringList();
        assertEquals(2, hits.size());
    }
}

public TopHitsAggregation getTopHitsAggregation(String aggName) {
    return jsonRoot.has(aggName) ? new TopHitsAggregation(aggName, jsonRoot.getAsJsonObject(aggName)) : null;
}


@Test(expected = IllegalArgumentException.class)
public void preemptiveAuthWithoutCredentials() {
    new HttpClientConfig.Builder("localhost").setPreemptiveAuth(new HttpHost("localhost", 80, "http")).build();
    fail("Builder should have thrown an exception if preemptive authentication is set without setting credentials");
}

public Builder setPreemptiveAuth(HttpHost targetHost) {
    this.preemptiveAuthTargetHost = targetHost;
    return this;
}

@Test
public void preemptiveAuth() {
    String hostName = "targetHost";
    int port = 80;
    HttpClientConfig httpClientConfig = new HttpClientConfig.Builder("localhost").defaultCredentials("someUser", "somePassword").setPreemptiveAuth(new HttpHost(hostName, port, "http")).build();
    assertEquals(hostName, httpClientConfig.getPreemptiveAuthTargetHost().getHostName());
    assertEquals(port, httpClientConfig.getPreemptiveAuthTargetHost().getPort());
}


@Test
public void reroute() throws JSONException {
    List<RerouteCommand> moveCommands = new LinkedList<RerouteCommand>();
    moveCommands.add(new RerouteMove("index1", 1, "node1", "node2"));
    moveCommands.add(new RerouteCancel("index2", 1, "node2", true));
    moveCommands.add(new RerouteAllocate("index3", 1, "node3", false));
    Reroute reroute = new Reroute.Builder(moveCommands).build();
    assertEquals("/_cluster/reroute", reroute.getURI());
    assertEquals("POST", reroute.getRestMethodName());
    String expectedData = "{ \"commands\": [" + "{ \"move\": { \"index\": \"index1\", \"shard\": 1, \"from_node\": \"node1\", \"to_node\": \"node2\" } }, " + "{ \"cancel\": { \"index\": \"index2\", \"shard\": 1, \"node\": \"node2\", \"allow_primary\": true } }," + "{ \"allocate\": { \"index\": \"index3\", \"shard\": 1, \"node\": \"node3\", \"allow_primary\": false } }" + "] }";
    JSONAssert.assertEquals(expectedData, reroute.getData(new Gson()), false);
}


@Test
public void allowPrimaryFalse() throws JSONException {
    RerouteAllocate allocateReplica = new RerouteAllocate("index1", 1, "node1", false);
    assertEquals(allocateReplica.getType(), "allocate");
    String actualJson = new Gson().toJson(allocateReplica.getData());
    String expectedJson = "{\"index\":\"index1\", \"shard\": 1, \"node\": \"node1\", \"allow_primary\": false}";
    JSONAssert.assertEquals(actualJson, expectedJson, false);
}

@Test
public void allowPrimaryTrue() throws JSONException {
    RerouteAllocate allocateReplica = new RerouteAllocate("index1", 1, "node1", true);
    assertEquals(allocateReplica.getType(), "allocate");
    String actualJson = new Gson().toJson(allocateReplica.getData());
    String expectedJson = "{\"index\":\"index1\", \"shard\": 1, \"node\": \"node1\", \"allow_primary\": true}";
    JSONAssert.assertEquals(actualJson, expectedJson, false);
}


@Test
public void allowPrimaryTrue() throws JSONException {
    RerouteCancel rerouteCancel = new RerouteCancel("index1", 1, "node1", true);
    assertEquals(rerouteCancel.getType(), "cancel");
    String actualJson = new Gson().toJson(rerouteCancel.getData());
    String expectedJson = "{\"index\":\"index1\", \"shard\": 1, \"node\": \"node1\", \"allow_primary\": true}";
    JSONAssert.assertEquals(actualJson, expectedJson, false);
}

@Test
public void allowPrimaryFalse() throws JSONException {
    RerouteCancel rerouteCancel = new RerouteCancel("index1", 1, "node1", false);
    assertEquals(rerouteCancel.getType(), "cancel");
    String actualJson = new Gson().toJson(rerouteCancel.getData());
    String expectedJson = "{\"index\":\"index1\", \"shard\": 1, \"node\": \"node1\", \"allow_primary\": false}";
    JSONAssert.assertEquals(actualJson, expectedJson, false);
}


@Test
public void move() throws JSONException {
    RerouteMove rerouteMove = new RerouteMove("index1", 1, "node1", "node2");
    assertEquals(rerouteMove.getType(), "move");
    String actualJson = new Gson().toJson(rerouteMove.getData());
    String expectedJson = "{\"index\":\"index1\", \"shard\": 1, \"from_node\": \"node1\", \"to_node\": \"node2\"}";
    JSONAssert.assertEquals(actualJson, expectedJson, false);
}


@Test
public void shouldSetApplicationJsonHeader() {
    Cat cat = new Cat.NodesBuilder().build();
    assertEquals("application/json", cat.getHeader("accept"));
    assertEquals("application/json", cat.getHeader("content-type"));
}

@Override
public Cat build() {
    return new Cat(this);
}

@Test
public void shouldGenerateValidUriWhenParameterGiven() {
    Cat cat = new Cat.NodesBuilder().setParameter("v", "true").build();
    assertEquals("_cat/nodes?v=true", cat.getURI());
}

@Test
public void shouldGenerateValidUriWhenIndexNotGiven() {
    Cat cat = new Cat.NodesBuilder().build();
    assertEquals("_cat/nodes", cat.getURI());
}


@Test
public void shouldSetApplicationJsonHeader() {
    Cat cat = new Cat.ShardsBuilder().build();
    assertEquals("application/json", cat.getHeader("accept"));
    assertEquals("application/json", cat.getHeader("content-type"));
}

@Test
public void shouldGenerateValidUriWhenIndexNotGiven() {
    Cat cat = new Cat.ShardsBuilder().build();
    assertEquals("_cat/shards", cat.getURI());
}

@Test
public void shouldGenerateValidUriWhenIndexGiven() {
    Cat cat = new Cat.ShardsBuilder().addIndex("testIndex").build();
    assertEquals("_cat/shards/testIndex", cat.getURI());
}

@Test
public void shouldGenerateValidUriWhenParameterGiven() {
    Cat cat = new Cat.ShardsBuilder().setParameter("v", "true").build();
    assertEquals("_cat/shards?v=true", cat.getURI());
}


@Test
public void multiSearchWithExtraParameters() throws JSONException {
    String expectedData = " {\"index\" : \"twitter\", " + "\"search_type\" : \"query_then_fetch\"," + " \"routing\" : \"testRoute\"," + " \"ignore_unavailable\" : \"true\"," + " \"allow_no_indices\" : \"true\", " + "\"expand_wildcards\" : \"true\"}\n" + "{\"query\" : {\"match_all\" : {}}}\n";
    Search search = new Search.Builder("{\"query\" : {\"match_all\" : {}}}").addIndex("twitter").setParameter(Parameters.ROUTING, "testRoute").setSearchType(SearchType.QUERY_THEN_FETCH).setParameter(Parameters.IGNORE_UNAVAILABLE, true).setParameter(Parameters.ALLOW_NO_INDICES, true).setParameter(Parameters.EXPAND_WILDCARDS, true).build();
    Search search2 = new Search.Builder("{\"query\" : {\"match_all\" : {}}}").build();
    MultiSearch multiSearch = new MultiSearch.Builder(search).addSearch(search2).build();
    assertEquals("POST", multiSearch.getRestMethodName());
    assertEquals("/_msearch", multiSearch.getURI());
    JSONAssert.assertEquals(expectedData, multiSearch.getData(null), false);
}

@Override
public String getData(Gson gson) {
    StringBuilder sb = new StringBuilder();
    for (Search search : searches) {
        sb.append("{\"index\" : \"").append(search.getIndex());
        if (!StringUtils.isBlank(search.getType())) {
            sb.append("\", \"type\" : \"").append(search.getType());
        }
        sb.append(getParameter(search, "ignore_unavailable"));
        sb.append(getParameter(search, "allow_no_indices"));
        sb.append(getParameter(search, "expand_wildcards"));
        final String query = NEWLINE_MATCHER.removeFrom(search.getData(gson));
        sb.append("\"}\n").append(query).append("\n");
    }
    return sb.toString();
}

@Override
public String getData(Gson gson) {
    StringBuilder sb = new StringBuilder();
    for (Search search : searches) {
        sb.append("{\"index\" : \"").append(search.getIndex());
        if (!StringUtils.isBlank(search.getType())) {
            sb.append("\", \"type\" : \"").append(search.getType());
        }
        sb.append(getParameter(search, Parameters.IGNORE_UNAVAILABLE));
        sb.append(getParameter(search, Parameters.ALLOW_NO_INDICES));
        sb.append(getParameter(search, Parameters.EXPAND_WILDCARDS));
        sb.append(getParameter(search, Parameters.ROUTING));
        sb.append(getParameter(search, Parameters.SEARCH_TYPE));
        final String query = NEWLINE_MATCHER.removeFrom(search.getData(gson));
        sb.append("\"}\n").append(query).append("\n");
    }
    return sb.toString();
}


@Test
public void equalsReturnsFalseForDifferentIndex() {
    ForceMerge forceMerge1 = new ForceMerge.Builder().addIndex("twitter").build();
    ForceMerge forceMerge2 = new ForceMerge.Builder().addIndex("myspace").build();
    assertNotEquals(forceMerge1, forceMerge2);
}

@Override
public ForceMerge build() {
    return new ForceMerge(this);
}

@Test
public void equalsReturnsTrueForSameIndex() {
    ForceMerge forceMerge1 = new ForceMerge.Builder().addIndex("twitter").build();
    ForceMerge forceMerge1Duplicate = new ForceMerge.Builder().addIndex("twitter").build();
    assertEquals(forceMerge1, forceMerge1Duplicate);
}

@Test
public void testBasicUriGeneration() {
    ForceMerge forceMerge = new ForceMerge.Builder().addIndex("twitter").build();
    assertEquals("POST", forceMerge.getRestMethodName());
    assertEquals("twitter/_forcemerge", forceMerge.getURI());
}


@Test
public void testBasicUriGenerationWithAlias() {
    AliasExists aliasExists = new AliasExists.Builder().alias("aliasName").build();
    assertEquals("HEAD", aliasExists.getRestMethodName());
    assertEquals("_all/_alias/aliasName", aliasExists.getURI());
}

@Override
public AliasExists build() {
    return new AliasExists(this, alias);
}

@Test
public void testBasicUriGenerationWithAliasAndIndex() {
    AliasExists aliasExists = new AliasExists.Builder().addIndex("indexName").alias("aliasName").build();
    assertEquals("HEAD", aliasExists.getRestMethodName());
    assertEquals("indexName/_alias/aliasName", aliasExists.getURI());
}

@Test
public void testBasicUriGenerationWithIndex() {
    AliasExists aliasExists = new AliasExists.Builder().addIndex("indexName").build();
    assertEquals("HEAD", aliasExists.getRestMethodName());
    assertEquals("indexName/_alias/*", aliasExists.getURI());
}

@Test
public void testBasicUriGeneration() {
    AliasExists aliasExists = new AliasExists.Builder().build();
    assertEquals("HEAD", aliasExists.getRestMethodName());
    assertEquals("_all/_alias/*", aliasExists.getURI());
}


@Test
public void getURIWithoutIndexAndType() {
    assertEquals("_all/_update_by_query", new UpdateByQuery.Builder(null).build().getURI());
}

@Override
public UpdateByQuery build() {
    return new UpdateByQuery(this);
}

@Test
public void getURIWithMultipleIndexAndTypes() {
    assertEquals("twitter%2Csearchbox/tweet%2Cjest/_update_by_query", new UpdateByQuery.Builder(null).addIndex("twitter").addIndex("searchbox").addType("tweet").addType("jest").build().getURI());
}

@Test
public void getURIWithOnlyMultipleIndex() {
    assertEquals("twitter%2Csearchbox/_update_by_query", new UpdateByQuery.Builder(null).addIndex("twitter").addIndex("searchbox").build().getURI());
}

@Test
public void equals() {
    UpdateByQuery deleteUserKramer = new UpdateByQuery.Builder("{\"user\":\"kramer\"}").addIndex("twitter").addIndex("searchbox").addType("tweet").addType("jest").build();
    UpdateByQuery deleteUserKramerDuplicate = new UpdateByQuery.Builder("{\"user\":\"kramer\"}").addIndex("twitter").addIndex("searchbox").addType("tweet").addType("jest").build();
    assertEquals(deleteUserKramer, deleteUserKramerDuplicate);
}

@Override
public boolean equals(Object obj) {
    if (obj == null) {
        return false;
    }
    if (obj == this) {
        return true;
    }
    if (obj.getClass() != getClass()) {
        return false;
    }
    return super.equals(obj);
}

@Test
public void equalsReturnsFalseForDifferentQueries() {
    UpdateByQuery deleteUserKramer = new UpdateByQuery.Builder("{\"user\":\"kramer\"}").addIndex("twitter").addIndex("searchbox").addType("tweet").addType("jest").build();
    UpdateByQuery deleteUserJerry = new UpdateByQuery.Builder("{\"user\":\"jerry\"}").addIndex("twitter").addIndex("searchbox").addType("tweet").addType("jest").build();
    assertNotEquals(deleteUserKramer, deleteUserJerry);
}

@Test
public void getURIWithOnlyOneIndex() {
    assertEquals("twitter/_update_by_query", new UpdateByQuery.Builder(null).addIndex("twitter").build().getURI());
}

@Test
public void getURIWithOnlyMultipleType() {
    assertEquals("_all/tweet%2Cjest/_update_by_query", new UpdateByQuery.Builder(null).addType("tweet").addType("jest").build().getURI());
}

@Test
public void getURIWithOneIndexAndOneType() {
    assertEquals("twitter/tweet/_update_by_query", new UpdateByQuery.Builder(null).addIndex("twitter").addType("tweet").build().getURI());
}


@Test
public void shouldFetchVersionFromValidResponse() {
    assertEquals(Long.valueOf(2), validResult.getVersion());
}


@Test(expected = IllegalArgumentException.class)
public void preemptiveAuthWithoutCredentials() {
    new HttpClientConfig.Builder("localhost").setPreemptiveAuth(new HttpHost("localhost", 80, "http")).build();
    fail("Builder should have thrown an exception if preemptive authentication is set without setting credentials");
}

@Test
public void preemptiveAuth() {
    String hostName = "targetHost";
    int port = 80;
    HttpClientConfig httpClientConfig = new HttpClientConfig.Builder("localhost").defaultCredentials("someUser", "somePassword").setPreemptiveAuth(new HttpHost(hostName, port, "http")).build();
    assertEquals(hostName, httpClientConfig.getPreemptiveAuthTargetHost().getHostName());
    assertEquals(port, httpClientConfig.getPreemptiveAuthTargetHost().getPort());
}


@Test
public void equalsReturnsFalseForDifferentIndex() {
    ForceMerge forceMerge1 = new ForceMerge.Builder().addIndex("twitter").build();
    ForceMerge forceMerge2 = new ForceMerge.Builder().addIndex("myspace").build();
    assertNotEquals(forceMerge1, forceMerge2);
}

@Test
public void equalsReturnsTrueForSameIndex() {
    ForceMerge forceMerge1 = new ForceMerge.Builder().addIndex("twitter").build();
    ForceMerge forceMerge1Duplicate = new ForceMerge.Builder().addIndex("twitter").build();
    assertEquals(forceMerge1, forceMerge1Duplicate);
}

@Test
public void testBasicUriGeneration() {
    ForceMerge forceMerge = new ForceMerge.Builder().addIndex("twitter").build();
    assertEquals("POST", forceMerge.getRestMethodName());
    assertEquals("twitter/_forcemerge", forceMerge.getURI());
}


@Test
public void testRepository() {
    String repository = "leeseohoo";
    DeleteSnapshotRepository deleteSnapshotRepository = new DeleteSnapshotRepository.Builder(repository).build();
    assertEquals("DELETE", deleteSnapshotRepository.getRestMethodName());
    assertEquals("/_snapshot/leeseohoo", deleteSnapshotRepository.getURI());
}

@Override
public DeleteSnapshotRepository build() {
    return new DeleteSnapshotRepository(this);
}


@Test
public void testGetMaxScoreWhenMissing() {
    SearchResult searchResult = new SearchResult(new Gson());
    searchResult.setSucceeded(true);
    searchResult.setJsonString(json);
    searchResult.setJsonObject(new JsonParser().parse(json).getAsJsonObject());
    searchResult.setPathToResult("hits/hits/_source");
    Float maxScore = searchResult.getMaxScore();
    assertNull(maxScore);
    TermsAggregation termsAgg = searchResult.getAggregations().getTermsAggregation("test");
    assertEquals(new Long(1), termsAgg.getDocCountErrorUpperBound());
    assertEquals(new Long(2), termsAgg.getSumOtherDocCount());
    List<Entry> termBuckets = termsAgg.getBuckets();
    for (Entry entry : termBuckets) {
        TopHitsAggregation topHitAgg = entry.getTopHitsAggregation("top_hits_test");
        List<String> hits = topHitAgg.getSourceAsStringList();
        assertEquals(2, hits.size());
    }
}


@Test
public void byteSize_of_hello_world_is_11() throws Exception {
    assertThat(Snippets.byteSize("Hello World")).isEqualTo(11);
}

public static int byteSize(String input) {
    return input.getBytes().length;
}

@Test
public void byteSize_of_smiley_is_4() throws Exception {
    int length = Snippets.byteSize("\uD83D\uDE00");
    assertThat(length).isEqualTo(4);
}


@Test
public void getCurrentWorkingDirectoryPath_test() throws Exception {
    assertThat(Snippets.getCurrentWorkingDirectoryPath()).isNotBlank();
}

public static String getCurrentWorkingDirectoryPath() {
    return FileSystems.getDefault().getPath("").toAbsolutePath().toString();
}


@Test
public void min_of_array_containing_10_1_and_5_is_10() throws Exception {
    OptionalInt min = Snippets.arrayMin(new int[] { 10, 1, 5 });
    assertThat(min).hasValue(1);
}

public static OptionalInt arrayMin(int[] numbers) {
    return Arrays.stream(numbers).min();
}


@Test
public void max_of_array_containing_10_1_and_5_is_10() throws Exception {
    OptionalInt max = Snippets.arrayMax(new int[] { 10, 1, 5 });
    assertThat(max).hasValue(10);
}

public static OptionalInt arrayMax(int[] numbers) {
    return Arrays.stream(numbers).max();
}


@Test
public void toKebabCase_test() throws Exception {
    assertThat(Snippets.toKebabCase("camelCase")).isEqualTo("camel-case");
    assertThat(Snippets.toKebabCase("some text")).isEqualTo("some-text");
    assertThat(Snippets.toKebabCase("some-mixed_string With spaces_underscores-and-hyphens")).isEqualTo("some-mixed-string-with-spaces-underscores-and-hyphens");
    assertThat(Snippets.toKebabCase("AllThe-small Things")).isEqualTo("all-the-small-things");
    assertThat(Snippets.toKebabCase("IAmListeningToFMWhileLoadingDifferentURLOnMyBrowserAndAlsoEditingXMLAndHTML")).isEqualTo("i-am-listening-to-fm-while-loading-different-url-on-my-browser-and-also-editing-xml-and-html");
}

public static String toKebabCase(String input) {
    Matcher matcher = Pattern.compile("[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+").matcher(input);
    List<String> matchedParts = new ArrayList<>();
    while (matcher.find()) {
        matchedParts.add(matcher.group(0));
    }
    return matchedParts.stream().map(String::toLowerCase).collect(Collectors.joining("-"));
}


@Test
public void countVowels_test() throws Exception {
    assertThat(Snippets.countVowels("foobar")).isEqualTo(3);
}

public static int countVowels(String input) {
    return input.replaceAll("[^aeiouAEIOU]", "").length();
}


@Test
public void distinct_remove_all_duplicate_values_from_an_array() throws Exception {
    int[] distinct = Snippets.distinctValuesOfArray(new int[] { 1, 2, 2, 3, 4, 4, 5 });
    assertThat(distinct).isEqualTo(new int[] { 1, 2, 3, 4, 5 });
}

public static int[] distinctValuesOfArray(int[] elements) {
    return Arrays.stream(elements).distinct().toArray();
}


@Test
public void isAbsoluteUrl_test() throws Exception {
    assertThat(Snippets.isAbsoluteUrl("https://google.com")).isTrue();
    assertThat(Snippets.isAbsoluteUrl("ftp://www.myserver.net")).isTrue();
    assertThat(Snippets.isAbsoluteUrl("/foo/bar")).isFalse();
}

public static boolean isAbsoluteUrl(String url) {
    return Pattern.compile("^[a-z][a-z0-9+.-]*:").matcher(url).find();
}


@Test
public void drop_elements__return_all_elements_when_all_elements_match_the_condition() throws Exception {
    int[] elements = Snippets.dropElements(new int[] { 1, 2, 3, 4 }, i -> i <= 4);
    assertThat(elements).isEqualTo(new int[] { 1, 2, 3, 4 });
}

public static int[] dropElements(int[] elements, IntPredicate condition) {
    while (elements.length > 0 && !condition.test(elements[0])) {
        elements = Arrays.copyOfRange(elements, 1, elements.length);
    }
    return elements;
}

@Test
public void drop_elements_returns_empty_array_when_no_element_match_the_condition() throws Exception {
    int[] elements = Snippets.dropElements(new int[] { 1, 2, 3, 4 }, i -> i < 1);
    assertThat(elements).isEmpty();
}

@Test
public void drop_elements_less_than_3() throws Exception {
    int[] elements = Snippets.dropElements(new int[] { 1, 2, 3, 4 }, i -> i >= 3);
    assertThat(elements).isEqualTo(new int[] { 3, 4 });
}


@Test
public void deepFlatten_flatten_a_deeply_nested_array() throws Exception {
    int[] flatten = Snippets.deepFlatten(new Object[] { 1, new Object[] { 2 }, new Object[] { 3, 4, 5 } });
    assertThat(flatten).isEqualTo(new int[] { 1, 2, 3, 4, 5 });
}

public static int[] deepFlatten(Object[] input) {
    return Arrays.stream(input).flatMapToInt(o -> {
        if (o instanceof Object[]) {
            return Arrays.stream(deepFlatten((Object[]) o));
        }
        return IntStream.of((Integer) o);
    }).toArray();
}


@Test
public void toCamelCase_test() throws Exception {
    assertThat(Snippets.toCamelCase("some_database_field_name")).isEqualTo("someDatabaseFieldName");
    assertThat(Snippets.toCamelCase("Some label that needs to be camelized")).isEqualTo("someLabelThatNeedsToBeCamelized");
    assertThat(Snippets.toCamelCase("some-java-property")).isEqualTo("someJavaProperty");
    assertThat(Snippets.toCamelCase("some-mixed_string with spaces_underscores-and-hyphens")).isEqualTo("someMixedStringWithSpacesUnderscoresAndHyphens");
}

public static String toCamelCase(String input) {
    Matcher matcher = Pattern.compile("[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+").matcher(input);
    List<String> matchResult = new ArrayList<>();
    while (matcher.find()) {
        matchResult.add(matcher.group(0));
    }
    String s = matchResult.stream().map(x -> x.substring(0, 1).toUpperCase() + x.substring(1).toLowerCase()).collect(Collectors.joining());
    return s.substring(0, 1).toLowerCase() + s.substring(1);
}


@Test
public void palindrome_test() throws Exception {
    assertThat(Snippets.isPalindrome("taco cat")).isTrue();
    assertThat(Snippets.isPalindrome("abc")).isFalse();
}

public static boolean isPalindrome(String input) {
    String s = input.toLowerCase().replaceAll("[\\W_]", "");
    return Objects.equals(s, new StringBuilder(s).reverse().toString());
}


@Test
public void zipObject_test_1() throws Exception {
    Map<String, Object> map = Snippets.zipObject(new String[] { "a", "b", "c" }, new Integer[] { 1, 2 });
    assertThat(map).containsOnly(new SimpleEntry<>("a", 1), new SimpleEntry<>("b", 2), new SimpleEntry<>("c", null));
}

public static Map<String, Object> zipObject(String[] props, Object[] values) {
    return IntStream.range(0, props.length).mapToObj(i -> new SimpleEntry<>(props[i], i < values.length ? values[i] : null)).collect(HashMap::new, (m, v) -> m.put(v.getKey(), v.getValue()), HashMap::putAll);
}

@Test
public void zipObject_test_2() throws Exception {
    Map<String, Object> map = Snippets.zipObject(new String[] { "a", "b" }, new Integer[] { 1, 2, 3 });
    assertThat(map).containsOnly(new SimpleEntry<>("a", 1), new SimpleEntry<>("b", 2));
}


@Test
public void initializeArrayWithRange_from_1_to_5() throws Exception {
    int[] numbers = Snippets.initializeArrayWithRange(5, 1);
    assertThat(numbers).isEqualTo(new int[] { 1, 2, 3, 4, 5 });
}

public static int[] initializeArrayWithRange(int end, int start) {
    return IntStream.rangeClosed(start, end).toArray();
}


@Test
public void difference_between_array_with_1_2_3_and_array_with_1_2_3_is_empty_array() throws Exception {
    int[] difference = Snippets.difference(new int[] { 1, 2, 3 }, new int[] { 1, 2, 3 });
    assertThat(difference).isEmpty();
}

public static int[] difference(int[] first, int[] second) {
    Set<Integer> set = Arrays.stream(second).boxed().collect(Collectors.toSet());
    return Arrays.stream(first).filter(v -> !set.contains(v)).toArray();
}

@Test
public void difference_between_array_with_1_2_3_and_array_with_1_2_4_is_3() throws Exception {
    int[] difference = Snippets.difference(new int[] { 1, 2, 3 }, new int[] { 1, 2, 4 });
    assertThat(difference).isEqualTo(new int[] { 3 });
}


@Test
public void isEven_test() throws Exception {
    assertThat(Snippets.isEven(1)).isFalse();
    assertThat(Snippets.isEven(2)).isTrue();
    assertThat(Snippets.isEven(3)).isFalse();
    assertThat(Snippets.isEven(4)).isTrue();
    assertThat(Snippets.isEven(-1)).isFalse();
}

public static boolean isEven(final int value) {
    return (value & 0b1) == 0;
}


@Test
public void escapeRegex_test() throws Exception {
    assertThat(Snippets.escapeRegExp("(test)")).isEqualTo("\\Q(test)\\E");
}

public static String escapeRegExp(String input) {
    return Pattern.quote(input);
}


@Test
public void intersection_between_two_arrays() throws Exception {
    int[] elements = Snippets.intersection(new int[] { 1, 2, 3 }, new int[] { 4, 3, 2 });
    assertThat(elements).isEqualTo(new int[] { 2, 3 });
}

public static int[] intersection(int[] first, int[] second) {
    Set<Integer> set = Arrays.stream(second).boxed().collect(Collectors.toSet());
    return Arrays.stream(first).filter(set::contains).toArray();
}


@Test
public void isLowerCase_test() throws Exception {
    assertThat(Snippets.isLowerCase("abc")).isTrue();
    assertThat(Snippets.isLowerCase("a3@$")).isTrue();
    assertThat(Snippets.isLowerCase("Ab4")).isFalse();
}

public static boolean isLowerCase(String input) {
    return Objects.equals(input, input.toLowerCase());
}


@Test
public void filterNonUnique_return_empty_array_when_all_duplicated() throws Exception {
    int[] elements = Snippets.filterNonUnique(new int[] { 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 });
    assertThat(elements).isEmpty();
}

public static int[] filterNonUnique(int[] elements) {
    return Arrays.stream(elements).filter(el -> indexOf(elements, el) == lastIndexOf(elements, el)).toArray();
}

@Test
public void filterNonUnique_return_same_array_when_all_unique() throws Exception {
    int[] elements = Snippets.filterNonUnique(new int[] { 1, 2, 3, 4, 5 });
    assertThat(elements).isEqualTo(new int[] { 1, 2, 3, 4, 5 });
}

@Test
public void filterNonUnique_return_unique_elements() throws Exception {
    int[] elements = Snippets.filterNonUnique(new int[] { 1, 2, 2, 3, 4, 4, 5 });
    assertThat(elements).isEqualTo(new int[] { 1, 3, 5 });
}


@Test
public void randomInts_test() throws Exception {
    assertThat(Snippets.randomInts(5, 100, 200)).hasSize(5);
}

public static int[] randomInts(int total, int start, int end) {
    return ThreadLocalRandom.current().ints(total, start, end).toArray();
}


@Test
public void randomInts_test() throws Exception {
    System.out.println(Arrays.toString(Snippets.randomInts(5, 100, 200)));
}


@Test
public void truncateString_test() throws Exception {
    assertThat(Snippets.truncateString("boomerang", 7)).isEqualTo("boom...");
}

public static String truncateString(String input, int num) {
    return input.length() > num ? input.substring(0, num > 3 ? num - 3 : num) + "..." : input;
}


@Test
public void capitalizeEveryWord_test() throws Exception {
    assertThat(Snippets.capitalizeEveryWord("hello world!")).isEqualTo("Hello World!");
}

public static String capitalizeEveryWord(final String input) {
    return Pattern.compile("\\b(?=\\w)").splitAsStream(input).map(w -> capitalize(w, false)).collect(Collectors.joining());
}

@Test
public void capitalize_test() throws Exception {
    assertThat(Snippets.capitalize("fooBar", false)).isEqualTo("FooBar");
    assertThat(Snippets.capitalize("fooBar", true)).isEqualTo("Foobar");
}

public static String capitalize(String input, boolean lowerRest) {
    return input.substring(0, 1).toUpperCase() + (lowerRest ? input.substring(1, input.length()).toLowerCase() : input.substring(1, input.length()));
}


@Test
public void toSnakeCase_test() throws Exception {
    assertThat(Snippets.toSnakeCase("camelCase")).isEqualTo("camel_case");
    assertThat(Snippets.toSnakeCase("some text")).isEqualTo("some_text");
    assertThat(Snippets.toSnakeCase("some-mixed_string With spaces_underscores-and-hyphens")).isEqualTo("some_mixed_string_with_spaces_underscores_and_hyphens");
    assertThat(Snippets.toSnakeCase("AllThe-small Things")).isEqualTo("all_the_small_things");
    assertThat(Snippets.toSnakeCase("IAmListeningToFMWhileLoadingDifferentURLOnMyBrowserAndAlsoEditingXMLAndHTML")).isEqualTo("i_am_listening_to_fm_while_loading_different_url_on_my_browser_and_also_editing_xml_and_html");
}

public static String toSnakeCase(String input) {
    Matcher matcher = Pattern.compile("[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+").matcher(input);
    List<String> matchedParts = new ArrayList<>();
    while (matcher.find()) {
        matchedParts.add(matcher.group(0));
    }
    return matchedParts.stream().map(String::toLowerCase).collect(Collectors.joining("_"));
}


@Test
public void findNextPositivePowerOfTwo_test() throws Exception {
    assertThat(Snippets.findNextPositivePowerOfTwo(-1)).isEqualTo(1);
    assertThat(Snippets.findNextPositivePowerOfTwo(3)).isEqualTo(4);
    assertThat(Snippets.findNextPositivePowerOfTwo(31)).isEqualTo(32);
    assertThat(Snippets.findNextPositivePowerOfTwo(32)).isEqualTo(32);
}

public static int findNextPositivePowerOfTwo(int value) {
    return 1 << (32 - Integer.numberOfLeadingZeros(value - 1));
}


@Test
public void reverseString_test() throws Exception {
    assertThat(Snippets.reverseString("foobar")).isEqualTo("raboof");
}

public static String reverseString(String input) {
    return new StringBuilder(input).reverse().toString();
}


@Test
public void flatten_flat_one_level_array() throws Exception {
    int[] flatten = Snippets.flatten(new Object[] { 1, new int[] { 2 }, 3, 4 });
    assertThat(flatten).isEqualTo(new int[] { 1, 2, 3, 4 });
}

public static int[] flatten(Object[] elements) {
    return Arrays.stream(elements).flatMapToInt(el -> el instanceof int[] ? Arrays.stream((int[]) el) : IntStream.of((int) el)).toArray();
}


@Test
public void countOccurrences_counts_occurrences_of_a_value() throws Exception {
    long count = Snippets.countOccurrences(new int[] { 1, 1, 2, 1, 2, 3 }, 1);
    assertThat(count).isEqualTo(3);
}

public static long countOccurrences(int[] numbers, int value) {
    return Arrays.stream(numbers).filter(number -> number == value).count();
}


@Test
public void sortCharactersInString_test() throws Exception {
    assertThat(Snippets.sortCharactersInString("cabbage")).isEqualTo("aabbceg");
}

public static String sortCharactersInString(String input) {
    return Arrays.stream(input.split("")).sorted().collect(Collectors.joining());
}


@Test
public void mask_test() throws Exception {
    assertThat(Snippets.mask("1234567890", 4, "*")).isEqualTo("******7890");
    assertThat(Snippets.mask("1234567890", 3, "*")).isEqualTo("*******890");
    assertThat(Snippets.mask("1234567890", -4, "*")).isEqualTo("1234******");
}

public static String mask(String input, int num, String mask) {
    int length = input.length();
    return num > 0 ? input.substring(0, length - num).replaceAll(".", mask) + input.substring(length - num) : input.substring(0, Math.negateExact(num)) + input.substring(Math.negateExact(num), length).replaceAll(".", mask);
}


@Test
public void everyNth_return_every_2nd_element() throws Exception {
    int[] elements = Snippets.everyNth(new int[] { 1, 2, 3, 4, 5, 6 }, 2);
    assertThat(elements).isEqualTo(new int[] { 2, 4, 6 });
}

public static int[] everyNth(int[] elements, int nth) {
    return IntStream.range(0, elements.length).filter(i -> i % nth == nth - 1).map(i -> elements[i]).toArray();
}

@Test
public void dropRight_remove_n_elements_from_right() throws Exception {
    int[] elements = Snippets.dropRight(new int[] { 1, 2, 3 }, 1);
    assertThat(elements).isEqualTo(new int[] { 1, 2 });
    elements = Snippets.dropRight(new int[] { 1, 2, 3 }, 2);
    assertThat(elements).isEqualTo(new int[] { 1 });
    elements = Snippets.dropRight(new int[] { 1, 2, 3 }, 3);
    assertThat(elements).isEmpty();
    elements = Snippets.dropRight(new int[] { 1, 2, 3 }, 42);
    assertThat(elements).isEmpty();
}

public static int[] dropRight(int[] elements, int n) {
    if (n < 0) {
        throw new IllegalArgumentException("n is less than 0");
    }
    return n < elements.length ? Arrays.copyOfRange(elements, 0, elements.length - n) : new int[0];
}


@Test
public void testGetQueryStatements_2() {
    List<String> sqls = JdbcQueryServiceHelper.getQueryStatements("aaa");
    Assert.assertEquals(1, sqls.size());
    Assert.assertEquals(sqls.get(0), "aaa");
}

public static List<String> getQueryStatements(String sql) {
    if (sql == null || sql.isEmpty()) {
        return Collections.emptyList();
    }
    if (!sql.contains(";")) {
        return Arrays.asList(sql);
    }
    List<String> statements = new ArrayList<>();
    String[] sqlArray = sql.split(";");
    for (String s : sqlArray) {
        String t = s.trim();
        if (!t.isEmpty()) {
            statements.add(t + ";");
        }
    }
    return statements;
}

@Test
public void testParseSqlStatementWithParams_1() {
    String sql = "SELECT * FROM table " + "WHERE 1=1 {{AND field IN (:fields)}} {{AND time > :startTime}} AND user='2' {{AND status =:status }} AND column3='2'";
    String expected = "SELECT * FROM table WHERE 1=1 AND field IN (:fields) AND time > :startTime AND user='2'  AND column3='2'";
    Map<String, Object> params = new HashMap<>();
    params.put("fields", "a, b, c");
    params.put("startTime", "12-01");
    Assert.assertEquals(expected, JdbcQueryServiceHelper.parseSqlStatementWithParams(sql, params));
}

public static String parseSqlStatementWithParams(String sql, Map<String, Object> params) {
    StringBuilder sb = new StringBuilder();
    char[] s = sql.toCharArray();
    int i = 0;
    while (i < s.length) {
        if (s[i] == '{' && (i + 1 < s.length) && s[i + 1] == '{') {
            int j = i + 2;
            while (j < s.length) {
                if (s[j] == '}' && (j + 1 < s.length) && s[j + 1] == '}') {
                    String clause = sql.substring(i + 2, j);
                    boolean hasParam = false;
                    for (Map.Entry<String, Object> entry : params.entrySet()) {
                        if (clause.contains(":" + entry.getKey())) {
                            hasParam = true;
                            break;
                        }
                    }
                    if (hasParam) {
                        sb.append(clause);
                    }
                    i = j + 2;
                    break;
                }
                j++;
            }
        }
        if (i < s.length) {
            sb.append(s[i]);
            i++;
        }
    }
    return sb.toString();
}

@Test
public void testParseSqlStatementWithParams_3() {
    String sql = "SELECT * FROM table WHERE 1=1 {{AND field IN (:fields)}}";
    String expected = "SELECT * FROM table WHERE 1=1 ";
    Assert.assertEquals(expected, JdbcQueryServiceHelper.parseSqlStatementWithParams(sql, new HashMap<>()));
}

@Test
public void testParseSqlStatementWithParams_2() {
    String sql = "SELECT * FROM table";
    Assert.assertEquals(sql, JdbcQueryServiceHelper.parseSqlStatementWithParams(sql, new HashMap<>()));
    sql = "SELECT * FROM table WHERE a='{{{'";
    Assert.assertEquals(sql, JdbcQueryServiceHelper.parseSqlStatementWithParams(sql, new HashMap<>()));
    sql = "SELECT * FROM table WHERE a='}'";
    Assert.assertEquals(sql, JdbcQueryServiceHelper.parseSqlStatementWithParams(sql, new HashMap<>()));
}

@Test
public void testGetQueryStatements_1() {
    Assert.assertEquals(0, JdbcQueryServiceHelper.getQueryStatements("").size());
    Assert.assertEquals(0, JdbcQueryServiceHelper.getQueryStatements(null).size());
}

@Test
public void testGetQueryStatements_5() {
    List<String> sqls = JdbcQueryServiceHelper.getQueryStatements("aaa;bbb;     ");
    Assert.assertEquals(2, sqls.size());
    Assert.assertEquals(sqls.get(0), "aaa;");
    Assert.assertEquals(sqls.get(1), "bbb;");
}

@Test
public void testGetQueryStatements_4() {
    List<String> sqls = JdbcQueryServiceHelper.getQueryStatements("aaa;bbb;");
    Assert.assertEquals(2, sqls.size());
    Assert.assertEquals(sqls.get(0), "aaa;");
    Assert.assertEquals(sqls.get(1), "bbb;");
}

@Test
public void testGetQueryStatements_3() {
    List<String> sqls = JdbcQueryServiceHelper.getQueryStatements("aaa;");
    Assert.assertEquals(1, sqls.size());
    Assert.assertEquals(sqls.get(0), "aaa;");
}