Skip to content

Commit e74328f

Browse files
authored
Merge branch 'master' into feature/dynamosa-3
2 parents 300ba05 + e1bc4bc commit e74328f

6 files changed

Lines changed: 217 additions & 7 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,9 @@ Examples of Fortune 500 companies using _EvoMaster_ are:
198198

199199
![](docs/img/video-player-flaticon.png)
200200

201-
* A [45-minute talk given at TestCon'25](https://www.youtube.com/watch?v=uKKRo3LrNiw&list=PLqYhGsQ9iSEoXaRmW9WQjjXJK_1NbLlZ6&index=15) on Fuzz Testing Web APIs gives an overview of what can be expected from this kind of fuzzers.
201+
* A [45-minute talk given at TestCon'25](https://www.youtube.com/watch?v=uKKRo3LrNiw&list=PLqYhGsQ9iSEoXaRmW9WQjjXJK_1NbLlZ6&index=15) on Fuzz Testing Web APIs gives an overview of what can be expected from this kind of fuzzers.
202+
A [shorter version (16 minutes)](https://www.youtube.com/watch?v=iJdhVzGedjM)
203+
was given at Nordic APIs 2025 Platform Summit.
202204

203205
* A [short video](https://youtu.be/3mYxjgnhLEo) (5 minutes)
204206
shows the use of _EvoMaster_ on one of the

core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -406,10 +406,24 @@ abstract class EnterpriseIndividual(
406406
* if [relativePosition] = -1, append the [actions] at the end
407407
*/
408408
fun addInitializingDbActions(relativePosition: Int=-1, actions: List<Action>){
409-
if (relativePosition < 0) {
410-
addChildrenToGroup(getLastIndexOfDbActionToAdd(), actions, GroupsOfChildren.INITIALIZATION_SQL)
411-
} else{
412-
addChildrenToGroup(getFirstIndexOfDbActionToAdd()+relativePosition, actions, GroupsOfChildren.INITIALIZATION_SQL)
409+
/*
410+
SQL actions representing existing data must ALWAYS be at the beginning.
411+
Recall those are only used for FKs.
412+
*/
413+
val (existing, others) = actions.partition { it is SqlAction && it.representExistingData }
414+
415+
if(existing.isNotEmpty()){
416+
addChildrenToGroup(getFirstIndexOfDbActionToAdd(), existing, GroupsOfChildren.INITIALIZATION_SQL)
417+
//TODO shall we check for duplications???
418+
}
419+
420+
if(others.isNotEmpty()) {
421+
val pos = if (relativePosition < 0) {
422+
getLastIndexOfDbActionToAdd()
423+
} else {
424+
getFirstIndexOfDbActionToAdd() + relativePosition
425+
}
426+
addChildrenToGroup(pos, others, GroupsOfChildren.INITIALIZATION_SQL)
413427
}
414428
}
415429

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
package org.evomaster.core.problem.rest.service
2+
3+
import org.evomaster.core.problem.enterprise.SampleType
4+
import org.evomaster.core.problem.rest.data.HttpVerb
5+
import org.evomaster.core.problem.rest.data.RestCallAction
6+
import org.evomaster.core.problem.rest.data.RestIndividual
7+
import org.evomaster.core.problem.rest.data.RestPath
8+
import org.evomaster.core.search.gene.numeric.IntegerGene
9+
import org.evomaster.core.search.gene.placeholder.ImmutableDataHolderGene
10+
import org.evomaster.core.search.gene.sql.SqlPrimaryKeyGene
11+
import org.evomaster.core.sql.SqlAction
12+
import org.evomaster.core.sql.schema.Column
13+
import org.evomaster.core.sql.schema.ColumnDataType
14+
import org.evomaster.core.sql.schema.Table
15+
import org.evomaster.core.sql.schema.TableId
16+
import org.junit.jupiter.api.Assertions.assertEquals
17+
import org.junit.jupiter.api.Assertions.assertFalse
18+
import org.junit.jupiter.api.Assertions.assertTrue
19+
import org.junit.jupiter.api.Test
20+
21+
class RestIndividualBuilderTest {
22+
23+
@Test
24+
fun mergeCombinesMainActionsAndKeepsOrder() {
25+
// create two simple RestCallAction with no parameters
26+
val callA = RestCallAction("idA", HttpVerb.GET, RestPath("/a"), mutableListOf())
27+
val callB = RestCallAction("idB", HttpVerb.GET, RestPath("/b"), mutableListOf())
28+
29+
// build individuals from single actions
30+
val first = RestIndividual(actions = mutableListOf(callA), sampleType = SampleType.RANDOM)
31+
val second = RestIndividual(actions = mutableListOf(callB), sampleType = SampleType.RANDOM)
32+
33+
// merge
34+
val merged = RestIndividualBuilder.merge(first, second)
35+
36+
// main executable actions should contain both actions in order
37+
val mains = merged.seeMainExecutableActions()
38+
assertEquals(2, mains.size)
39+
assertEquals(callA.getName(), mains[0].getName())
40+
assertEquals(callB.getName(), mains[1].getName())
41+
42+
// merged total actions should equal sum of both (no initializing actions present)
43+
val before = first.seeAllActions().size + second.seeAllActions().size
44+
assertEquals(before, merged.seeAllActions().size)
45+
}
46+
47+
@Test
48+
fun mergeInitActionsAndKeepsOrder() {
49+
// create two simple RestCallAction with no parameters
50+
val callA = RestCallAction("idA", HttpVerb.GET, RestPath("/a"), mutableListOf())
51+
val callB = RestCallAction("idB", HttpVerb.GET, RestPath("/b"), mutableListOf())
52+
53+
// create a simple table/column for sql initialization
54+
val col = Column(
55+
"ID",
56+
ColumnDataType.INT,
57+
primaryKey = true,
58+
databaseType = org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType.H2
59+
)
60+
val table1 = Table(TableId("T1"), setOf(col), emptySet())
61+
val table2 = Table(TableId("T2"), setOf(col), emptySet())
62+
63+
val sql1 = SqlAction(table1, setOf(col), 1L, computedGenes = listOf(IntegerGene("ID", 1)))
64+
val sql2 = SqlAction(table2, setOf(col), 1L, computedGenes = listOf(IntegerGene("ID", 2)))
65+
66+
// build individuals from single actions with initialization SQL
67+
val first = RestIndividual(actions = mutableListOf(callA), sampleType = SampleType.RANDOM)
68+
val second = RestIndividual(actions = mutableListOf(callB), sampleType = SampleType.RANDOM)
69+
70+
first.addInitializingDbActions(0, listOf(sql1))
71+
second.addInitializingDbActions(0, listOf(sql2))
72+
73+
// merge
74+
val merged = RestIndividualBuilder.merge(first, second)
75+
76+
// main executable actions should contain both actions in order
77+
val mains = merged.seeMainExecutableActions()
78+
assertEquals(2, mains.size)
79+
assertEquals(callA.getName(), mains[0].getName())
80+
assertEquals(callB.getName(), mains[1].getName())
81+
82+
// initializing SQL actions should contain both sql1 and sql2
83+
val inits = merged.seeInitializingActions().filterIsInstance<SqlAction>()
84+
assertEquals(2, inits.size)
85+
// check their table names preserved and order: first's sql then second's sql
86+
assertEquals("T1", inits[0].table.name)
87+
assertEquals("T2", inits[1].table.name)
88+
89+
// merged total actions should be sum of both initial individuals
90+
val before = first.seeAllActions().size + second.seeAllActions().size
91+
assertEquals(before, merged.seeAllActions().size)
92+
}
93+
94+
@Test
95+
fun mergeInitActionsWithExistingDataAndKeepsOrder() {
96+
// create two simple RestCallAction with no parameters
97+
val callA = RestCallAction("idA", HttpVerb.GET, RestPath("/a"), mutableListOf())
98+
val callB = RestCallAction("idB", HttpVerb.GET, RestPath("/b"), mutableListOf())
99+
100+
// create a simple table/column for sql initialization
101+
val col = Column(
102+
"ID",
103+
ColumnDataType.INT,
104+
primaryKey = true,
105+
databaseType = org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType.H2
106+
)
107+
val table1 = Table(TableId("T1"), setOf(col), emptySet())
108+
val table2 = Table(TableId("T2"), setOf(col), emptySet())
109+
110+
111+
val sql1 = SqlAction(
112+
table1,
113+
setOf(col),
114+
1L,
115+
computedGenes = listOf(
116+
SqlPrimaryKeyGene(
117+
"ID",
118+
TableId("T1"),
119+
ImmutableDataHolderGene("ID", "1", inQuotes = false),
120+
uniqueId = 1L
121+
)
122+
),
123+
representExistingData = true
124+
)
125+
val sql2 = SqlAction(
126+
table1,
127+
setOf(col),
128+
1L,
129+
computedGenes = listOf(IntegerGene("ID", 2)),
130+
representExistingData = false
131+
)
132+
133+
val sql3 = SqlAction(
134+
table2,
135+
setOf(col),
136+
1L,
137+
computedGenes = listOf(
138+
SqlPrimaryKeyGene(
139+
"ID", TableId("T2"),
140+
ImmutableDataHolderGene("ID", "3", inQuotes = false),
141+
uniqueId = 2L
142+
)
143+
),
144+
representExistingData = true
145+
)
146+
val sql4 = SqlAction(
147+
table2,
148+
setOf(col),
149+
1L,
150+
computedGenes = listOf(IntegerGene("ID", 4)),
151+
representExistingData = false
152+
)
153+
154+
// build individuals from single actions with initialization SQL
155+
val first = RestIndividual(actions = mutableListOf(callA), sampleType = SampleType.RANDOM)
156+
val second = RestIndividual(actions = mutableListOf(callB), sampleType = SampleType.RANDOM)
157+
158+
first.addInitializingDbActions(0, listOf(sql1, sql2))
159+
second.addInitializingDbActions(0, listOf(sql3, sql4))
160+
161+
// merge
162+
val merged = RestIndividualBuilder.merge(first, second)
163+
164+
// main executable actions should contain both actions in order
165+
val mains = merged.seeMainExecutableActions()
166+
assertEquals(2, mains.size)
167+
assertEquals(callA.getName(), mains[0].getName())
168+
assertEquals(callB.getName(), mains[1].getName())
169+
170+
// initializing SQL actions should contain both sql1 and sql2
171+
val inits = merged.seeInitializingActions().filterIsInstance<SqlAction>()
172+
assertEquals(4, inits.size)
173+
/*
174+
check their table names preserved and order: first's sql then second's sql.
175+
however, existing data is always at the beginning
176+
*/
177+
assertTrue(inits[0].representExistingData)
178+
assertTrue(inits[1].representExistingData)
179+
assertFalse(inits[2].representExistingData)
180+
assertFalse(inits[3].representExistingData)
181+
assertEquals("T1", inits[2].table.name)
182+
assertEquals("T2", inits[3].table.name)
183+
184+
// merged total actions should be sum of both initial individuals
185+
val before = first.seeAllActions().size + second.seeAllActions().size
186+
assertEquals(before, merged.seeAllActions().size)
187+
}
188+
189+
190+
}

docs/publications.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ Also, some of these papers provides full replication packages, which are linked
1313

1414
## Recent arXiv Technical Reports, not Peer-Reviewed (Yet)
1515

16+
* P. Garrett, J. P. Galeotti, A. Arcuri, A. Poth, O. Rrjolli.
17+
*Generating REST API Tests With Descriptive Names*.
18+
[[arxiv]()]
19+
1620
* O. Sahin, M. Zhang, A. Arcuri.
1721
*WFC/WFD: Web Fuzzing Commons, Dataset and Guidelines to Support Experimentation in REST API Fuzzing*.
1822
[[arxiv](https://arxiv.org/abs/2509.01612)]
@@ -35,12 +39,12 @@ Also, some of these papers provides full replication packages, which are linked
3539
* A. Golmohammadi, M. Zhang, A. Arcuri.
3640
*Tools and Benchmarks Evolve: What is their Impact on Parameter Tuning in SBSE Experiments?*.
3741
Empirical Software Engineering (EMSE).
38-
(To appear)
42+
[[PDF](publications/2025_emse_tuning.pdf)]
3943

4044
* F. F. Susilo.
4145
*Human-Centered Evaluation of REST API Fuzzing Tools: Bridging Academia and Industry*.
4246
IEEE/ACM International Conference on Automated Software Engineering (ASE), Doctoral Symposium.
43-
(To appear)
47+
[[PDF](publications/2025_ase_ds.pdf)]
4448

4549
* A. Arcuri, O. Sahin, M. Zhang.
4650
*Fuzzing for Detecting Access Policy Violations in REST APIs*.

docs/publications/2025_ase_ds.pdf

87.6 KB
Binary file not shown.
7.81 MB
Binary file not shown.

0 commit comments

Comments
 (0)