This is due to the possibility of simplifying the validation of ScalaCheck. ScalaCheck just sees your generator producing a string value. Whenever he finds a value that makes your property false, he tries to simplify that value. In your case, this simplifies it four times until the empty string ends, which still makes your property false.
So this is the expected, albeit confusing behavior. But you can improve the situation in three different ways.
You can choose a different data structure to represent your IP addresses. This will allow ScalaCheck to simplify your test cases in a smarter way. For example, use the following generator:
val addrs = Gen.listOfN(4, Gen.choose(0,255))
Now ScalaCheck knows that your generator generates only lists of length 4 and that it contains only numbers from 0 to 255. The process of simplifying the test case will take this into account, and not create values โโthat the generator could not be created from the beginning. Instead, you can convert the string to your property.
The second method is to add a filter directly to your generator, which tells ScalaCheck what the IP address string should look like. This filter is used during the simplification of the test case. Define a function that checks valid strings and attaches them to an existing generator as follows:
def validIP(ip: String): Boolean = ... val validAddrs = addrs.suchThat(validIP) forAll(validAddrs) { ... }
The third way is to simply disable the test case simplification function in general, using forAllNoShrink instead of forAll :
Prop.forAllNoShrink(addrs) { ... }
I should also note that the first two methods require the ScalaCheck> = 1.11.0 version to function properly.
UPDATE:
The length of the listOfN list is not actually followed by shrinking, due to https://github.com/rickynils/scalacheck/issues/89 . I hope this can be fixed in a future version of ScalaCheck.