Skip to content

Commit 4425639

Browse files
committed
fix: Improve Kotest character arbitrary generator
1 parent f1d2468 commit 4425639

File tree

1 file changed

+67
-21
lines changed

1 file changed

+67
-21
lines changed

fixture-monkey-kotest/src/main/kotlin/com/navercorp/fixturemonkey/kotest/KotestCharacterCombinableArbitrary.kt

Lines changed: 67 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,62 +20,108 @@ package com.navercorp.fixturemonkey.kotest
2020

2121
import com.navercorp.fixturemonkey.api.arbitrary.CharacterCombinableArbitrary
2222
import io.kotest.property.Arb
23-
import io.kotest.property.arbitrary.char
23+
import io.kotest.property.arbitrary.choice
2424
import io.kotest.property.arbitrary.filter
25+
import io.kotest.property.arbitrary.int
26+
import io.kotest.property.arbitrary.map
27+
import io.kotest.property.arbitrary.of
2528
import io.kotest.property.arbitrary.single
2629
import org.apiguardian.api.API
2730
import org.apiguardian.api.API.Status
2831
import java.util.function.Predicate
2932

3033
@API(since = "1.1.16", status = Status.EXPERIMENTAL)
31-
class KotestCharacterCombinableArbitrary(private val arb: Arb<Char> = Arb.char()) : CharacterCombinableArbitrary {
34+
class KotestCharacterCombinableArbitrary(
35+
private val arb: Arb<Char> = DEFAULT_CHAR_ARB
36+
) : CharacterCombinableArbitrary {
3237
override fun combined(): Char = arb.single()
3338

3439
override fun rawValue(): Char = this.combined()
3540

3641
override fun withRange(min: Char, max: Char): CharacterCombinableArbitrary =
37-
KotestCharacterCombinableArbitrary(Arb.char().filter { it in min..max })
42+
KotestCharacterCombinableArbitrary(charRange(min, max))
3843

3944
override fun alphabetic(): CharacterCombinableArbitrary =
40-
KotestCharacterCombinableArbitrary(Arb.char().filter { it.isLetter() })
45+
KotestCharacterCombinableArbitrary(Arb.choice(UPPERCASE_RANGE_ARB, LOWERCASE_RANGE_ARB))
4146

4247
override fun numeric(): CharacterCombinableArbitrary =
43-
KotestCharacterCombinableArbitrary(Arb.char().filter { it.isDigit() })
48+
KotestCharacterCombinableArbitrary(NUMERIC_RANGE_ARB)
4449

4550
override fun alphaNumeric(): CharacterCombinableArbitrary =
46-
KotestCharacterCombinableArbitrary(Arb.char().filter { it.isLetterOrDigit() })
51+
KotestCharacterCombinableArbitrary(Arb.choice(UPPERCASE_RANGE_ARB, LOWERCASE_RANGE_ARB, NUMERIC_RANGE_ARB))
4752

4853
override fun ascii(): CharacterCombinableArbitrary =
49-
KotestCharacterCombinableArbitrary(Arb.char().filter { it.code <= 127 })
54+
KotestCharacterCombinableArbitrary(charRange('\u0000', '\u007F'))
5055

5156
override fun uppercase(): CharacterCombinableArbitrary =
52-
KotestCharacterCombinableArbitrary(Arb.char().filter { it.isUpperCase() })
57+
KotestCharacterCombinableArbitrary(UPPERCASE_RANGE_ARB)
5358

5459
override fun lowercase(): CharacterCombinableArbitrary =
55-
KotestCharacterCombinableArbitrary(Arb.char().filter { it.isLowerCase() })
60+
KotestCharacterCombinableArbitrary(LOWERCASE_RANGE_ARB)
5661

5762
override fun korean(): CharacterCombinableArbitrary =
58-
KotestCharacterCombinableArbitrary(Arb.char().filter { it in ''..'' })
59-
60-
private fun isEmojiCodePoint(codePoint: Int): Boolean {
61-
return codePoint in 0x1F600..0x1F64F || // Emoticons
62-
codePoint in 0x1F300..0x1F5FF || // Misc Symbols and Pictographs
63-
codePoint in 0x1F680..0x1F6FF || // Transport and Map
64-
codePoint in 0x2600..0x26FF || // Misc symbols
65-
codePoint in 0x2700..0x27BF // Dingbats
66-
}
63+
KotestCharacterCombinableArbitrary(charRange('', ''))
6764

6865
override fun emoji(): CharacterCombinableArbitrary =
69-
KotestCharacterCombinableArbitrary(Arb.char().filter { isEmojiCodePoint(it.code) })
66+
KotestCharacterCombinableArbitrary(Arb.of(EMOJI_CHAR_CANDIDATES))
7067

7168
override fun whitespace(): CharacterCombinableArbitrary =
72-
KotestCharacterCombinableArbitrary(Arb.char().filter { it.isWhitespace() })
69+
KotestCharacterCombinableArbitrary(Arb.of(WHITESPACE_CHAR_CANDIDATES))
7370

7471
override fun filter(tries: Int, predicate: Predicate<Char>): CharacterCombinableArbitrary =
75-
KotestCharacterCombinableArbitrary(Arb.char().filter { predicate.test(it) })
72+
KotestCharacterCombinableArbitrary(arb.filter { predicate.test(it) })
7673

7774
override fun clear() {
7875
}
7976

8077
override fun fixed(): Boolean = false
78+
79+
companion object {
80+
private val DEFAULT_CHAR_ARB: Arb<Char> = charRange(Char.MIN_VALUE, Char.MAX_VALUE)
81+
private val UPPERCASE_RANGE_ARB: Arb<Char> = charRange('A', 'Z')
82+
private val LOWERCASE_RANGE_ARB: Arb<Char> = charRange('a', 'z')
83+
private val NUMERIC_RANGE_ARB: Arb<Char> = charRange('0', '9')
84+
private val EMOJI_CHAR_CANDIDATES: List<Char> = listOf(
85+
'\u263A', //
86+
'\u263B', //
87+
'\u2661', //
88+
'\u2665', //
89+
'\u2660', //
90+
'\u2663', //
91+
'\u2666', //
92+
'\u2600', //
93+
'\u2601', //
94+
'\u2602', //
95+
'\u2603', //
96+
'\u2604', //
97+
'\u2614', //
98+
'\u2615', //
99+
'\u260E', //
100+
'\u2618', //
101+
'\u2728', //
102+
'\u2764', //
103+
'\u2708', //
104+
'\u270C' //
105+
)
106+
private val WHITESPACE_CHAR_CANDIDATES: List<Char> = listOf(
107+
' ',
108+
'\t',
109+
'\n',
110+
'\r',
111+
'\u000B', // vertical tab
112+
'\u000C' // form feed
113+
)
114+
115+
private fun charRange(startInclusive: Char, endInclusive: Char): Arb<Char> {
116+
if (startInclusive == endInclusive) {
117+
return Arb.of(startInclusive)
118+
}
119+
val (min, max) = if (startInclusive <= endInclusive) {
120+
startInclusive to endInclusive
121+
} else {
122+
endInclusive to startInclusive
123+
}
124+
return Arb.int(min.code..max.code).map(Int::toChar)
125+
}
126+
}
81127
}

0 commit comments

Comments
 (0)