Skip to content

Commit 5c15b95

Browse files
authored
Support fuzzing for regular expressions in Python (#2298)
* Support fuzzing for regular expressions * Include random to PythonMethodDescription * Replace regex by startsWith and endsWith, use substring instead of drop and dropLast * Use random from description * Add empty string
1 parent ff11586 commit 5c15b95

File tree

5 files changed

+69
-12
lines changed

5 files changed

+69
-12
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import re
2+
import time
3+
4+
5+
def check_regex(string: str) -> bool:
6+
pattern = r"'(''|\\\\|\\'|[^'])*'"
7+
if re.match(pattern, string):
8+
return True
9+
return False
10+
11+
12+
def timetest():
13+
t = time.time()
14+
print(check_regex("\'\J/\\\\\\'\\N''''P'''\'x'L''\'';'\'N\$'\\\'\'\`''D\\''�='''m\\\\\'\'\\–H\\No'F'(''U]\'V"))
15+
print((time.time() - t))
16+
17+
if __name__ == '__main__':
18+
timetest()

utbot-python/src/main/kotlin/org/utbot/python/PythonEngine.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import org.utbot.python.newtyping.pythonTypeRepresentation
2525
import org.utbot.python.utils.camelToSnakeCase
2626
import org.utbot.summary.fuzzer.names.TestSuggestedInfo
2727
import java.net.ServerSocket
28+
import kotlin.random.Random
2829

2930
private val logger = KotlinLogging.logger {}
3031

@@ -290,7 +291,8 @@ class PythonEngine(
290291
parameters,
291292
fuzzedConcreteValues,
292293
pythonTypeStorage,
293-
Trie(Instruction::id)
294+
Trie(Instruction::id),
295+
Random(0),
294296
)
295297

296298
if (parameters.isEmpty()) {

utbot-python/src/main/kotlin/org/utbot/python/fuzzing/PythonApi.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import org.utbot.python.fuzzing.provider.*
1212
import org.utbot.python.fuzzing.provider.utils.isAny
1313
import org.utbot.python.newtyping.*
1414
import org.utbot.python.newtyping.general.Type
15+
import kotlin.random.Random
1516

1617
private val logger = KotlinLogging.logger {}
1718

@@ -27,6 +28,7 @@ class PythonMethodDescription(
2728
val concreteValues: Collection<PythonFuzzedConcreteValue> = emptyList(),
2829
val pythonTypeStorage: PythonTypeStorage,
2930
val tracer: Trie<Instruction, *>,
31+
val random: Random,
3032
) : Description<Type>(parameters)
3133

3234
sealed interface FuzzingExecutionFeedback

utbot-python/src/main/kotlin/org/utbot/python/fuzzing/provider/StrValueProvider.kt

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@ package org.utbot.python.fuzzing.provider
33
import org.utbot.fuzzing.Seed
44
import org.utbot.fuzzing.ValueProvider
55
import org.utbot.fuzzing.seeds.KnownValue
6+
import org.utbot.fuzzing.seeds.RegexValue
67
import org.utbot.fuzzing.seeds.StringValue
78
import org.utbot.python.framework.api.python.PythonTree
89
import org.utbot.python.framework.api.python.util.pythonStrClassId
910
import org.utbot.python.fuzzing.PythonFuzzedConcreteValue
1011
import org.utbot.python.fuzzing.PythonFuzzedValue
1112
import org.utbot.python.fuzzing.PythonMethodDescription
1213
import org.utbot.python.fuzzing.provider.utils.generateSummary
14+
import org.utbot.python.fuzzing.provider.utils.isPattern
1315
import org.utbot.python.fuzzing.provider.utils.transformQuotationMarks
16+
import org.utbot.python.fuzzing.provider.utils.transformRawString
1417
import org.utbot.python.newtyping.general.Type
1518
import org.utbot.python.newtyping.pythonTypeName
1619

@@ -19,22 +22,33 @@ object StrValueProvider : ValueProvider<Type, PythonFuzzedValue, PythonMethodDes
1922
return type.pythonTypeName() == pythonStrClassId.canonicalName
2023
}
2124

22-
private fun getStrConstants(concreteValues: Collection<PythonFuzzedConcreteValue>): List<StringValue> {
25+
private fun getStrConstants(concreteValues: Collection<PythonFuzzedConcreteValue>): List<String> {
2326
return concreteValues
2427
.filter { accept(it.type) }
2528
.map {
26-
StringValue((it.value as String).transformQuotationMarks())
29+
val value = it.value as String
30+
value.transformRawString()
31+
}
32+
.map {
33+
it.transformQuotationMarks()
2734
}
2835
}
2936

3037
override fun generate(description: PythonMethodDescription, type: Type) = sequence {
3138
val strConstants = getStrConstants(description.concreteValues) + listOf(
32-
StringValue("pythön"),
33-
StringValue("abcdefghijklmnopqrst"),
34-
StringValue("foo"),
35-
StringValue(""),
39+
"pythön",
40+
"foo",
41+
"",
3642
)
37-
strConstants.forEach { yieldStrings(it) { value } }
43+
strConstants.forEach { yieldStrings(StringValue(it)) { value } }
44+
45+
strConstants
46+
.filter {
47+
it.isPattern()
48+
}
49+
.forEach {
50+
yieldStrings(RegexValue(it, description.random), StringValue::value)
51+
}
3852
}
3953

4054
private suspend fun <T : KnownValue<T>> SequenceScope<Seed<Type, PythonFuzzedValue>>.yieldStrings(value: T, block: T.() -> Any) {
@@ -45,4 +59,4 @@ object StrValueProvider : ValueProvider<Type, PythonFuzzedValue, PythonMethodDes
4559
)
4660
})
4761
}
48-
}
62+
}
Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package org.utbot.python.fuzzing.provider.utils
22

3+
import java.util.regex.Pattern
4+
import java.util.regex.PatternSyntaxException
5+
36
fun String.transformQuotationMarks(): String {
47

58
val doubleQuotationMarks = this.startsWith("\"") && this.endsWith("\"")
6-
val oneQuotationMarks = this.startsWith("'") && this.endsWith("'")
9+
// val oneQuotationMarks = this.startsWith("'") && this.endsWith("'")
710

811
val tripleDoubleQuotationMarks = this.startsWith("\"\"\"") && this.endsWith("\"\"\"")
912
val tripleOneQuotationMarks = this.startsWith("'''") && this.endsWith("'''")
@@ -12,9 +15,27 @@ fun String.transformQuotationMarks(): String {
1215
return this.drop(3).dropLast(3)
1316
}
1417

15-
if (oneQuotationMarks || doubleQuotationMarks) {
18+
if (doubleQuotationMarks) {
1619
return this.drop(1).dropLast(1)
1720
}
1821

1922
return this
20-
}
23+
}
24+
25+
fun String.transformRawString(): String {
26+
val rawStringWithDoubleQuotationMarks = this.startsWith("r\"") && this.endsWith("\"")
27+
val rawStringWithOneQuotationMarks = this.startsWith("r'") && this.endsWith("'")
28+
return if (rawStringWithOneQuotationMarks || rawStringWithDoubleQuotationMarks) {
29+
this.substring(2, this.length-1)
30+
} else {
31+
this
32+
}
33+
}
34+
35+
fun String.isPattern(): Boolean {
36+
return try {
37+
Pattern.compile(this); true
38+
} catch (_: PatternSyntaxException) {
39+
false
40+
}
41+
}

0 commit comments

Comments
 (0)