Skip to content

Commit 257f289

Browse files
rudolf101denis-fokin
authored andcommitted
Improve fuzzer & test exports/imports in JavaScript module (#1909)
(cherry picked from commit 4b401f0)
1 parent 45ed4c2 commit 257f289

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1114
-236
lines changed

utbot-cli-js/src/main/kotlin/org/utbot/cli/js/JsGenerateTestsCommand.kt

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import com.github.ajalt.clikt.parameters.options.*
66
import com.github.ajalt.clikt.parameters.types.choice
77
import mu.KotlinLogging
88
import org.utbot.cli.js.JsUtils.makeAbsolutePath
9-
import service.CoverageMode
9+
import service.coverage.CoverageMode
1010
import settings.JsDynamicSettings
1111
import settings.JsExportsSettings.endComment
1212
import settings.JsExportsSettings.startComment
@@ -82,6 +82,7 @@ class JsGenerateTestsCommand :
8282
val sourceFileAbsolutePath = makeAbsolutePath(sourceCodeFile)
8383
logger.info { "Generating tests for [$sourceFileAbsolutePath] - started" }
8484
val fileText = File(sourceCodeFile).readText()
85+
currentFileText = fileText
8586
val outputAbsolutePath = output?.let { makeAbsolutePath(it) }
8687
val testGenerator = JsTestGenerator(
8788
fileText = fileText,
@@ -118,36 +119,31 @@ class JsGenerateTestsCommand :
118119
}
119120
}
120121

121-
private fun manageExports(exports: List<String>) {
122-
val exportSection = exports.joinToString("\n") { "exports.$it = $it" }
122+
// Needed for continuous exports managing
123+
private var currentFileText = ""
124+
125+
private fun manageExports(swappedText: (String?, String) -> String) {
123126
val file = File(sourceCodeFile)
124-
val fileText = file.readText()
125127
when {
126-
fileText.contains(exportSection) -> {}
127128

128-
fileText.contains(startComment) && !fileText.contains(exportSection) -> {
129+
currentFileText.contains(startComment) -> {
129130
val regex = Regex("$startComment((\\r\\n|\\n|\\r|.)*)$endComment")
130-
regex.find(fileText)?.groups?.get(1)?.value?.let { existingSection ->
131-
val exportRegex = Regex("exports[.](.*) =")
132-
val existingExports = existingSection.split("\n").filter { it.contains(exportRegex) }
133-
val existingExportsSet = existingExports.map { rawLine ->
134-
exportRegex.find(rawLine)?.groups?.get(1)?.value ?: throw IllegalStateException()
135-
}.toSet()
136-
val resultSet = existingExportsSet + exports.toSet()
137-
val resSection = resultSet.joinToString("\n") { "exports.$it = $it" }
138-
val swappedText = fileText.replace(existingSection, "\n$resSection\n")
139-
file.writeText(swappedText)
131+
regex.find(currentFileText)?.groups?.get(1)?.value?.let { existingSection ->
132+
val newText = swappedText(existingSection, currentFileText)
133+
file.writeText(newText)
134+
currentFileText = newText
140135
}
141136
}
142137

143138
else -> {
144139
val line = buildString {
145-
append("\n$startComment\n")
146-
append(exportSection)
147-
append("\n$endComment")
140+
append("\n$startComment")
141+
append(swappedText(null, currentFileText))
142+
append(endComment)
148143
}
149144
file.appendText(line)
145+
currentFileText = file.readText()
150146
}
151147
}
152148
}
153-
}
149+
}

utbot-intellij-js/src/main/kotlin/org/utbot/intellij/plugin/language/js/CoverageModeButtons.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package org.utbot.intellij.plugin.language.js
22

3-
import service.CoverageMode
43
import javax.swing.ButtonGroup
54
import javax.swing.JRadioButton
5+
import service.coverage.CoverageMode
66

77
object CoverageModeButtons {
88

utbot-intellij-js/src/main/kotlin/org/utbot/intellij/plugin/language/js/JsDialogProcessor.kt

Lines changed: 40 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,13 @@ object JsDialogProcessor {
144144

145145
private fun createDialog(jsTestsModel: JsTestsModel?) = jsTestsModel?.let { JsDialogWindow(it) }
146146

147+
private fun unblockDocument(project: Project, document: Document) {
148+
PsiDocumentManager.getInstance(project).apply {
149+
commitDocument(document)
150+
doPostponedOperationsAndUnblockDocument(document)
151+
}
152+
}
153+
147154
private fun createTests(model: JsTestsModel, containingFilePath: String, editor: Editor?, contents: String) {
148155
val normalizedContainingFilePath = containingFilePath.replace(File.separator, "/")
149156
(object : Task.Backgroundable(model.project, "Generate tests") {
@@ -154,7 +161,9 @@ object JsDialogProcessor {
154161
model.testSourceRoot!!
155162
)
156163
val testFileName = normalizedContainingFilePath.substringAfterLast("/").replace(Regex(".js"), "Test.js")
157-
val testGenerator = JsTestGenerator(fileText = contents,
164+
currentFileText = model.file.getContent()
165+
val testGenerator = JsTestGenerator(
166+
fileText = contents,
158167
sourceFilePath = normalizedContainingFilePath,
159168
projectPath = model.project.basePath?.replace(File.separator, "/")
160169
?: throw IllegalStateException("Can't access project path."),
@@ -208,44 +217,6 @@ object JsDialogProcessor {
208217

209218
private fun JSFile.getContent(): String = this.viewProvider.contents.toString()
210219

211-
private fun manageExports(
212-
editor: Editor?, project: Project, model: JsTestsModel, exports: List<String>
213-
) {
214-
AppExecutorUtil.getAppExecutorService().submit {
215-
invokeLater {
216-
val exportSection = exports.joinToString("\n") { "exports.$it = $it" }
217-
val fileText = model.file.getContent()
218-
when {
219-
fileText.contains(exportSection) -> {}
220-
221-
fileText.contains(startComment) && !fileText.contains(exportSection) -> {
222-
val regex = Regex("$startComment((\\r\\n|\\n|\\r|.)*)$endComment")
223-
regex.find(fileText)?.groups?.get(1)?.value?.let { existingSection ->
224-
val exportRegex = Regex("exports[.](.*) =")
225-
val existingExports = existingSection.split("\n").filter { it.contains(exportRegex) }
226-
val existingExportsSet = existingExports.map { rawLine ->
227-
exportRegex.find(rawLine)?.groups?.get(1)?.value ?: throw IllegalStateException()
228-
}.toSet()
229-
val resultSet = existingExportsSet + exports.toSet()
230-
val resSection = resultSet.joinToString("\n") { "exports.$it = $it" }
231-
val swappedText = fileText.replace(existingSection, "\n$resSection\n")
232-
project.setNewText(editor, model.containingFilePath, swappedText)
233-
}
234-
}
235-
236-
else -> {
237-
val line = buildString {
238-
append("\n$startComment\n")
239-
append(exportSection)
240-
append("\n$endComment")
241-
}
242-
project.setNewText(editor, model.containingFilePath, fileText + line)
243-
}
244-
}
245-
}
246-
}
247-
}
248-
249220
private fun Project.setNewText(editor: Editor?, filePath: String, text: String) {
250221
editor?.let {
251222
runWriteAction {
@@ -261,12 +232,38 @@ object JsDialogProcessor {
261232
} ?: run {
262233
File(filePath).writeText(text)
263234
}
235+
currentFileText = text
264236
}
265237

266-
private fun unblockDocument(project: Project, document: Document) {
267-
PsiDocumentManager.getInstance(project).apply {
268-
commitDocument(document)
269-
doPostponedOperationsAndUnblockDocument(document)
238+
// Needed for continuous exports managing
239+
private var currentFileText = ""
240+
241+
private fun manageExports(
242+
editor: Editor?,
243+
project: Project,
244+
model: JsTestsModel,
245+
swappedText: (String?, String) -> String
246+
) {
247+
AppExecutorUtil.getAppExecutorService().submit {
248+
invokeLater {
249+
when {
250+
currentFileText.contains(startComment) -> {
251+
val regex = Regex("$startComment((\\r\\n|\\n|\\r|.)*)$endComment")
252+
regex.find(currentFileText)?.groups?.get(1)?.value?.let { existingSection ->
253+
val newText = swappedText(existingSection, currentFileText)
254+
project.setNewText(editor, model.containingFilePath, newText)
255+
}
256+
}
257+
258+
else -> {
259+
val line = buildString {
260+
append("\n")
261+
appendLine(swappedText(null, currentFileText))
262+
}
263+
project.setNewText(editor, model.containingFilePath, currentFileText + line)
264+
}
265+
}
266+
}
270267
}
271268
}
272269
}

utbot-intellij-js/src/main/kotlin/org/utbot/intellij/plugin/language/js/JsTestsModel.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import com.intellij.openapi.module.Module
66
import com.intellij.openapi.project.Project
77
import org.utbot.framework.codegen.domain.TestFramework
88
import org.utbot.intellij.plugin.models.BaseTestsModel
9-
import service.CoverageMode
9+
import service.coverage.CoverageMode
1010
import settings.JsTestGenerationSettings.defaultTimeout
1111

1212
class JsTestsModel(

utbot-js/build.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ dependencies {
2929
testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1")
3030
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1")
3131
api(project(":utbot-framework"))
32-
api(project(":utbot-fuzzers"))
3332
// https://mvnrepository.com/artifact/com.google.javascript/closure-compiler
3433
implementation("com.google.javascript:closure-compiler:v20221102")
3534

utbot-js/samples/arrays.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
function simpleArray(arr) {
2+
if (arr[0] === 5) {
3+
return 5
4+
}
5+
return 1
6+
}
7+
8+
simpleArray([0, 2])
9+
10+
class ObjectParameter {
11+
12+
constructor(a) {
13+
this.first = a
14+
}
15+
16+
performAction(value) {
17+
return 2 * value
18+
}
19+
}
20+
21+
function arrayOfObjects(arr) {
22+
if (arr[0].first === 2) {
23+
return 1
24+
}
25+
return 10
26+
}
27+
28+
let arr = []
29+
arr[0] = new ObjectParameter(10)
30+
arrayOfObjects(arr)

utbot-js/samples/mapStructure.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Maps in JavaScript are untyped, so only maps with basic key/value types are feasible to support
2+
function simpleMap(map, compareValue) {
3+
if (map.get("a") === compareValue) {
4+
return 5
5+
}
6+
return 1
7+
}
8+
9+
const map1 = new Map()
10+
map1.set("b", 3.0)
11+
simpleMap(map1, 5)

utbot-js/samples/setStructure.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Sets in JavaScript are untyped, so only sets with basic value types are feasible to support
2+
function setTest(set, checkValue) {
3+
if (set.has(checkValue)) {
4+
return 5
5+
}
6+
return 1
7+
}
8+
9+
let s = new Set()
10+
s.add(5)
11+
s.add(6)
12+
setTest(s, 4)

0 commit comments

Comments
 (0)