5

I am using pholser's port. I have to generate strings matching a given pattern like \[a-zA-Z0-9\\.\\-\\\\;\\:\\_\\@\\[\\]\\^/\\|\\}\\{]* Length 40.

I extend the Generator class as:

public class InputGenerator extends Generator<TestData> {...}

It overloads a function:

publicTestData generate(SourceOfRandomness random, GenerationStatus status) {...}

Now, random has functions like nextDouble(), nextInt() but there is nothing for strings! How can I generate random strings matching the above pattern?

Frank Schmitt
  • 30,195
  • 12
  • 73
  • 107
Mangat Rai Modi
  • 5,397
  • 8
  • 45
  • 75
  • It's not really clear what you want to achieve. Do you want to know how to implement the method `public String generate(SourceOfRandomness random, GenerationStatus status)` to return a `String` which matches your pattern? – SubOptimal May 13 '15 at 12:45
  • Hi, generate(..) is actually an abstract function in Generator class of QuickCheck port on Java by Pholser. It is of generic type which will return an object of type "TestData". This object encapsulate several data objects like Double, Integer, String etc. random object has functions to get random values for double, integers but nothing for strings! I will update my answer! – Mangat Rai Modi May 13 '15 at 13:37

3 Answers3

5

Find below snippet for a custom generator which implement the generate(..) method to return a random string matching your posted pattern.

public class MyCharacterGenerator extends Generator<String> {

    private static final String LOWERCASE_CHARS = "abcdefghijklmnopqrstuvwxyz";
    private static final String UPPERCASE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final String NUMBERS = "0123456789";
    private static final String SPECIAL_CHARS = ".-\\;:_@[]^/|}{";
    private static final String ALL_MY_CHARS = LOWERCASE_CHARS
            + UPPERCASE_CHARS + NUMBERS + SPECIAL_CHARS;
    public static final int CAPACITY = 40;

    public MyCharacterGenerator () {
        super(String.class);
    }

    @Override
    public String generate(SourceOfRandomness random, GenerationStatus status) {
        StringBuilder sb = new StringBuilder(CAPACITY);
        for (int i = 0; i < CAPACITY; i++) {
            int randomIndex = random.nextInt(ALL_MY_CHARS.length());
            sb.append(ALL_MY_CHARS.charAt(randomIndex));
        }
        return sb.toString();
    }
}

edit A simple unit test to demonstrate the usage of the MyCharacterGenerator class.

import com.pholser.junit.quickcheck.ForAll;
import com.pholser.junit.quickcheck.From;
import static org.junit.Assert.assertTrue;
import org.junit.contrib.theories.Theories;
import org.junit.contrib.theories.Theory;
import org.junit.runner.RunWith;

@RunWith(Theories.class)
public class MyCharacterGeneratorTest {

    @Theory
    public void shouldHold(@ForAll @From(MyCharacterGenerator.class) String s) {
        // here you should add your unit test which uses the generated output
        // 
        // assertTrue(doMyUnitTest(s) == expectedResult);

        // the below lines only for demonstration and currently
        // check that the generated random has the expected
        // length and matches the expected pattern
        System.out.println("shouldHold(): " + s);
        assertTrue(s.length() == MyCharacterGenerator.CAPACITY);
        assertTrue(s.matches("[a-zA-Z0-9.\\-\\\\;:_@\\[\\]^/|}{]*"));
    }
}

sample output generated by shouldHold

shouldHold(): MD}o/LAkW/hbJVWPGdI;:RHpwo_T.lGs^DOFwu2.
shouldHold(): IT_O{8Umhkz{@PY:pmK6}Cb[Wc19GqGZjWVa@4li
shouldHold(): KQwpEz.CW28vy_/WJR3Lx2.tRC6uLIjOTQtYP/VR
shouldHold(): pc2_T4hLdZpK78UfcVmU\RTe9WaJBSGJ}5v@z[Z\
...
SubOptimal
  • 22,518
  • 3
  • 53
  • 69
  • 1
    Hey thanks for the help, So basically there is no library to form random string? I will just get random indices from my example string and fill it. – Mangat Rai Modi May 13 '15 at 14:52
  • 1
    @MangatRaiModi No there is no such `random.nextString()`. Because it would make not much sense. Because random what? Length? Charset? Possible characters within a charset? – SubOptimal May 13 '15 at 16:01
  • It could take two arguments: Length and Charset. However I might be wrong but it does make sense to me :( – Mangat Rai Modi May 15 '15 at 09:59
  • @MangatRaiModi You could create a `RandomString` class which for example implements a method `public static String next(int length, char[] chars)`. The method body itself could be similar to the `generate(..)` from my answer. – SubOptimal May 15 '15 at 10:42
  • Yup I did that. Thanks! – Mangat Rai Modi May 16 '15 at 07:59
  • @Muzietto Have a look to the added simple unit test. (btw. Thanks for fixing the typo). – SubOptimal Apr 05 '16 at 10:09
  • 1
    @SubOptimal - great stuff! Thank you. I am really struggling to understand the page about generators and this example is the only one around that's actually usable. – Marco Faustinelli Apr 05 '16 at 10:39
1

There is no random.nextString(), but there is a way to generate random strings within junit-quickcheck-generators library. You can access it when creating new generators using gen().type(String.class). However, it seems we don't have much control over it.

Here is a silly example of a StringBuilder generator to demonstrate how to use the String generator:

import com.pholser.junit.quickcheck.generator.GenerationStatus;
import com.pholser.junit.quickcheck.generator.Generator;
import com.pholser.junit.quickcheck.random.SourceOfRandomness;

public class StringBuilderGenerator extends Generator<StringBuilder> {

    public StringBuilderGenerator() {
        super(StringBuilder.class);
    }

    @Override
    public StringBuilder generate(SourceOfRandomness random, GenerationStatus status) {
        String s = gen().type(String.class).generate(random, status);
        return new StringBuilder(s);
    }
}
Helder Pereira
  • 5,522
  • 2
  • 35
  • 52
0

I just made a library that suppose to do what you want in a generic way: https://github.com/SimY4/coregex

Simple usage example:

import com.pholser.junit.quickcheck.Property;
import com.pholser.junit.quickcheck.runner.JUnitQuickcheck;
import org.junit.runner.RunWith;

import java.util.UUID;

import static org.junit.Assert.assertEquals;

@RunWith(JUnitQuickcheck.class)
public class CoregexGeneratorTest {
  @Property
  public void shouldGenerateMatchingUUIDString(
      @Regex("[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}")
          String uuid) {
    assertEquals(uuid, UUID.fromString(uuid).toString());
  }
}
Alex
  • 7,460
  • 2
  • 40
  • 51