Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
33dd8b1
add instrumentation for MongoCollection.deleteOne/deleteMany methods
jgaleotti Oct 9, 2026
b7969d6
add instrumentation for MongoCollection.updateOne/updateMany methods
jgaleotti Oct 9, 2026
08a8509
refactor: move method documentation to handleCount
jgaleotti Oct 9, 2026
fd25810
Merge branch 'mongo_countDocuments_instrumentation' into mongo_delete…
jgaleotti Oct 14, 2026
8e8eaa7
Merge remote-tracking branch 'origin/mongo_delete_instrumentation' in…
jgaleotti Oct 14, 2026
a5d3c88
add instrumentation for MongoCollection.updateOne/updateMany methods
jgaleotti Oct 9, 2026
8994f38
refactor: move method documentation to handleCount
jgaleotti Oct 9, 2026
9f4b743
add instrumentation for MongoCollection.deleteOne/deleteMany methods
jgaleotti Oct 9, 2026
6b7a0f2
Merge remote-tracking branch 'origin/mongo_update_instrumentation' in…
jgaleotti Oct 15, 2026
cb8865b
Merge remote-tracking branch 'origin/mongo_delete_instrumentation' in…
jgaleotti Oct 15, 2026
bbacf04
Merge branch 'mongo_countDocuments_instrumentation' into mongo_delete…
jgaleotti Oct 15, 2026
c8c367e
Merge branch 'mongo_delete_instrumentation' into mongo_update_instrum…
jgaleotti Oct 15, 2026
e0668d0
Introduced a `MongoCollectionPipelineUpdateClassReplacement` to handl…
jgaleotti Oct 15, 2026
5410b6f
- Refactor MongoCollection tests by introducing `MongoCollectionClass…
jgaleotti Oct 16, 2026
fad6705
Merge remote-tracking branch 'origin/master' into mongo_update_instru…
jgaleotti Oct 16, 2026
3116254
Refactor MongoCollection tests: remove unused imports and extend reus…
jgaleotti Oct 16, 2026
fbe4032
Merge remote-tracking branch 'origin/master' into mongo_update_instru…
jgaleotti Oct 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public static List<MethodReplacementClass> getList() {
new MessageBodyReaderClassReplacement(),
new MethodClassReplacement(),
new MongoCollectionClassReplacement(),
new MongoCollectionPipelineUpdateClassReplacement(),
new MongoTemplateClassReplacement(),
new OpenSearchClientClassReplacement(),
new Neo4JSessionClassReplacement(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public static long countDocuments(Object mongoCollection, @ThirdPartyCast(actual
return handleCount("countDocumentsClientSessionBsonCountOptions", mongoCollection, Arrays.asList(clientSession, filter, options), filter);
}


@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "deleteOneBson", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.DeleteResult")
public static Object deleteOne(Object mongoCollection, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter) {
return handleDelete("deleteOneBson", mongoCollection, Arrays.asList(filter), filter);
Expand Down Expand Up @@ -111,6 +112,47 @@ public static Object deleteMany(Object mongoCollection, @ThirdPartyCast(actualTy
return handleDelete("deleteManyClientSessionBsonDeleteOptions", mongoCollection, Arrays.asList(clientSession, filter, options), filter);
}


@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "updateOneBson", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.UpdateResult")
public static Object updateOne(Object mongoCollection, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object update) {
return handleUpdate("updateOneBson", mongoCollection, Arrays.asList(filter, update), filter);
}

@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "updateOneBsonUpdateOptions", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.UpdateResult")
public static Object updateOne(Object mongoCollection, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object update, @ThirdPartyCast(actualType = "com.mongodb.client.model.UpdateOptions") Object options) {
return handleUpdate("updateOneBsonUpdateOptions", mongoCollection, Arrays.asList(filter, update, options), filter);
}

@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "updateOneClientSessionBson", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.UpdateResult")
public static Object updateOne_EM_0(Object mongoCollection, @ThirdPartyCast(actualType = "com.mongodb.client.ClientSession") Object clientSession, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object update) {
return handleUpdate("updateOneClientSessionBson", mongoCollection, Arrays.asList(clientSession, filter, update), filter);
}

@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "updateOneClientSessionBsonUpdateOptions", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.UpdateResult")
public static Object updateOne(Object mongoCollection, @ThirdPartyCast(actualType = "com.mongodb.client.ClientSession") Object clientSession, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object update, @ThirdPartyCast(actualType = "com.mongodb.client.model.UpdateOptions") Object options) {
return handleUpdate("updateOneClientSessionBsonUpdateOptions", mongoCollection, Arrays.asList(clientSession, filter, update, options), filter);
}

@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "updateManyBson", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.UpdateResult")
public static Object updateMany(Object mongoCollection, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object update) {
return handleUpdate("updateManyBson", mongoCollection, Arrays.asList(filter, update), filter);
}

@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "updateManyBsonUpdateOptions", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.UpdateResult")
public static Object updateMany(Object mongoCollection, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object update, @ThirdPartyCast(actualType = "com.mongodb.client.model.UpdateOptions") Object options) {
return handleUpdate("updateManyBsonUpdateOptions", mongoCollection, Arrays.asList(filter, update, options), filter);
}

@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "updateManyClientSessionBson", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.UpdateResult")
public static Object updateMany_EM_0(Object mongoCollection, @ThirdPartyCast(actualType = "com.mongodb.client.ClientSession") Object clientSession, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object update) {
return handleUpdate("updateManyClientSessionBson", mongoCollection, Arrays.asList(clientSession, filter, update), filter);
}

@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "updateManyClientSessionBsonUpdateOptions", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.UpdateResult")
public static Object updateMany(Object mongoCollection, @ThirdPartyCast(actualType = "com.mongodb.client.ClientSession") Object clientSession, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object update, @ThirdPartyCast(actualType = "com.mongodb.client.model.UpdateOptions") Object options) {
return handleUpdate("updateManyClientSessionBsonUpdateOptions", mongoCollection, Arrays.asList(clientSession, filter, update, options), filter);
}

/**
* Processes the count handling based on the provided parameters.
*
Expand All @@ -137,32 +179,25 @@ private static Object handleDelete(String id, Object mongoCollection, List<Objec
return handleEagerQuery(id, mongoCollection, args, filter);
}

/**
* Handles the update of documents (updateOne/updateMany) in the provided collection.
*
* @param id The identifier of the replacement, used to retrieve the original update method.
* @param mongoCollection The collection in which the documents will be updated.
* @param args The arguments of the original update call.
* @param filter The filter conditions to locate the documents to update.
* @return The result of the update operation (an UpdateResult).
*/
private static Object handleUpdate(String id, Object mongoCollection, List<Object> args, Object filter) {
return handleEagerQuery(id, mongoCollection, args, filter);
}

/**
* Unlike find, operations like count and delete are executed eagerly,
* Unlike find, operations like count, delete and update are executed eagerly,
* so we know right away whether the filter was valid
*/
private static Object handleEagerQuery(String id, Object mongoCollection, List<Object> args, Object filter) {
long start = System.currentTimeMillis();
Method method = retrieveFindMethod(id, mongoCollection);
try {
Object result = method.invoke(mongoCollection, args.toArray());
long end = System.currentTimeMillis();
handleMongo(mongoCollection, filter, true, end - start);
return result;
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
long end = System.currentTimeMillis();
handleMongo(mongoCollection, filter, false, end - start);
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException(cause);
}
return handleEagerQuery(singleton, id, mongoCollection, args, filter);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package org.evomaster.client.java.instrumentation.coverage.methodreplacement.thirdpartyclasses;

import org.evomaster.client.java.instrumentation.coverage.methodreplacement.Replacement;
import org.evomaster.client.java.instrumentation.coverage.methodreplacement.ThirdPartyCast;
import org.evomaster.client.java.instrumentation.coverage.methodreplacement.UsageFilter;
import org.evomaster.client.java.instrumentation.shared.ReplacementCategory;
import org.evomaster.client.java.instrumentation.shared.ReplacementType;

import java.util.Arrays;
import java.util.List;

/**
* Replacements for the overloads of updateOne, updateMany and findOneAndUpdate that take an
* update pipeline (a list of stages) instead of a single update document.
* Those overloads were added to MongoCollection in driver 3.11.
* <p>
* This cannot be in {@link MongoCollectionClassReplacement}: all the original methods of a
* replacement class are resolved together, and they all must exist in the driver version
* in use. With older drivers, having these methods there would break all the other
* replacements for MongoCollection. Here, instead, the methods are resolved
* only if a call with a pipeline is replaced, which can only happen with drivers having it.
*/
public class MongoCollectionPipelineUpdateClassReplacement extends MongoOperationClassReplacement {

private static final MongoCollectionPipelineUpdateClassReplacement singleton = new MongoCollectionPipelineUpdateClassReplacement();

@Override
protected String getNameOfThirdPartyTargetClass() {
return "com.mongodb.client.MongoCollection";
}

/**
* Replacement for {@code MongoCollection.updateOne(Bson, List<? extends Bson>)}.
* <p>
* Present in the MongoDB Java driver from 3.11.0 (not present in 3.10.2).
*/
@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "updateOneBsonPipeline", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.UpdateResult")
public static Object updateOne(Object mongoCollection, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, List<?> pipeline) {
return handleEagerQuery(singleton, "updateOneBsonPipeline", mongoCollection, Arrays.asList(filter, pipeline), filter);
}

/**
* Replacement for {@code MongoCollection.updateOne(Bson, List<? extends Bson>, UpdateOptions)}.
* <p>
* Present in the MongoDB Java driver from 3.11.0 (not present in 3.10.2).
*/
@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "updateOneBsonPipelineUpdateOptions", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.UpdateResult")
public static Object updateOne(Object mongoCollection, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, List<?> pipeline, @ThirdPartyCast(actualType = "com.mongodb.client.model.UpdateOptions") Object options) {
return handleEagerQuery(singleton, "updateOneBsonPipelineUpdateOptions", mongoCollection, Arrays.asList(filter, pipeline, options), filter);
}

/**
* Replacement for {@code MongoCollection.updateOne(ClientSession, Bson, List<? extends Bson>)}.
* <p>
* Present in the MongoDB Java driver from 3.11.0 (not present in 3.10.2).
*/
@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "updateOneClientSessionBsonPipeline", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.UpdateResult")
public static Object updateOne(Object mongoCollection, @ThirdPartyCast(actualType = "com.mongodb.client.ClientSession") Object clientSession, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, List<?> pipeline) {
return handleEagerQuery(singleton, "updateOneClientSessionBsonPipeline", mongoCollection, Arrays.asList(clientSession, filter, pipeline), filter);
}

/**
* Replacement for {@code MongoCollection.updateOne(ClientSession, Bson, List<? extends Bson>, UpdateOptions)}.
* <p>
* Present in the MongoDB Java driver from 3.11.0 (not present in 3.10.2).
*/
@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "updateOneClientSessionBsonPipelineUpdateOptions", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.UpdateResult")
public static Object updateOne(Object mongoCollection, @ThirdPartyCast(actualType = "com.mongodb.client.ClientSession") Object clientSession, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, List<?> pipeline, @ThirdPartyCast(actualType = "com.mongodb.client.model.UpdateOptions") Object options) {
return handleEagerQuery(singleton, "updateOneClientSessionBsonPipelineUpdateOptions", mongoCollection, Arrays.asList(clientSession, filter, pipeline, options), filter);
}

/**
* Replacement for {@code MongoCollection.updateMany(Bson, List<? extends Bson>)}.
* <p>
* Present in the MongoDB Java driver from 3.11.0 (not present in 3.10.2).
*/
@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "updateManyBsonPipeline", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.UpdateResult")
public static Object updateMany(Object mongoCollection, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, List<?> pipeline) {
return handleEagerQuery(singleton, "updateManyBsonPipeline", mongoCollection, Arrays.asList(filter, pipeline), filter);
}

/**
* Replacement for {@code MongoCollection.updateMany(Bson, List<? extends Bson>, UpdateOptions)}.
* <p>
* Present in the MongoDB Java driver from 3.11.0 (not present in 3.10.2).
*/
@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "updateManyBsonPipelineUpdateOptions", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.UpdateResult")
public static Object updateMany(Object mongoCollection, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, List<?> pipeline, @ThirdPartyCast(actualType = "com.mongodb.client.model.UpdateOptions") Object options) {
return handleEagerQuery(singleton, "updateManyBsonPipelineUpdateOptions", mongoCollection, Arrays.asList(filter, pipeline, options), filter);
}

/**
* Replacement for {@code MongoCollection.updateMany(ClientSession, Bson, List<? extends Bson>)}.
* <p>
* Present in the MongoDB Java driver from 3.11.0 (not present in 3.10.2).
*/
@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "updateManyClientSessionBsonPipeline", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.UpdateResult")
public static Object updateMany(Object mongoCollection, @ThirdPartyCast(actualType = "com.mongodb.client.ClientSession") Object clientSession, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, List<?> pipeline) {
return handleEagerQuery(singleton, "updateManyClientSessionBsonPipeline", mongoCollection, Arrays.asList(clientSession, filter, pipeline), filter);
}

/**
* Replacement for {@code MongoCollection.updateMany(ClientSession, Bson, List<? extends Bson>, UpdateOptions)}.
* <p>
* Present in the MongoDB Java driver from 3.11.0 (not present in 3.10.2).
*/
@Replacement(replacingStatic = false, type = ReplacementType.TRACKER, id = "updateManyClientSessionBsonPipelineUpdateOptions", usageFilter = UsageFilter.ANY, category = ReplacementCategory.MONGO, castTo = "com.mongodb.client.result.UpdateResult")
public static Object updateMany(Object mongoCollection, @ThirdPartyCast(actualType = "com.mongodb.client.ClientSession") Object clientSession, @ThirdPartyCast(actualType = "org.bson.conversions.Bson") Object filter, List<?> pipeline, @ThirdPartyCast(actualType = "com.mongodb.client.model.UpdateOptions") Object options) {
return handleEagerQuery(singleton, "updateManyClientSessionBsonPipelineUpdateOptions", mongoCollection, Arrays.asList(clientSession, filter, pipeline, options), filter);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.evomaster.client.java.instrumentation.staticstate.ExecutionTracer;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;

Expand All @@ -23,6 +24,40 @@ protected static void handleMongo(Object mongoCollection, Object bson, boolean s
ExecutionTracer.addMongoInfo(info);
}

/**
* Executes an operation that, unlike find, is executed eagerly (eg, count, delete and update),
* so we know right away whether the filter was valid. The query is stored in any case.
*
* @param singleton the replacement class holding the replacement with the given id
* @param id the id of the replacement, used to retrieve the original method
* @param mongoCollection the collection on which the operation is executed
* @param args the arguments of the original call
* @param filter the query used in the operation
* @return the result of the original call
*/
protected static Object handleEagerQuery(ThirdPartyMethodReplacementClass singleton, String id, Object mongoCollection, List<Object> args, Object filter) {
long start = System.currentTimeMillis();
Method method = getOriginal(singleton, id, mongoCollection);
try {
Object result = method.invoke(mongoCollection, args.toArray());
long end = System.currentTimeMillis();
handleMongo(mongoCollection, filter, true, end - start);
return result;
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
long end = System.currentTimeMillis();
handleMongo(mongoCollection, filter, false, end - start);
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException(cause);
}
}

private static Class<?> extractDocumentsType(Object collection) {
try {
Expand Down
Loading
Loading