From 2e5b448af43ae88126a5de1ec2dfb45bf06fa948 Mon Sep 17 00:00:00 2001 From: Juan Pablo Galeotti Date: Fri, 21 Apr 2023 17:24:19 -0300 Subject: [PATCH 001/345] e2e tests for @Min @Max JPA annotations --- .../core/database/DbActionGeneBuilder.kt | 2 +- .../core/database/SqlInsertBuilder.kt | 99 +++++++++++-------- .../examples/spring/db/jpa/EntityJPAData.java | 29 +++++- .../src/main/resources/sql/entityjpa.sql | 4 +- 4 files changed, 88 insertions(+), 46 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt b/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt index b9c899d08c..860c960414 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt @@ -685,7 +685,7 @@ class DbActionGeneBuilder { */ fun buildLikeRegexGene(geneName: String, likePatterns: List, databaseType: DatabaseType): RegexGene { return when (databaseType) { - DatabaseType.POSTGRES, DatabaseType.MYSQL -> buildPostgresMySQLLikeRegexGene(geneName, likePatterns) + DatabaseType.POSTGRES, DatabaseType.MYSQL, DatabaseType.H2 -> buildPostgresMySQLLikeRegexGene(geneName, likePatterns) //TODO: support other database SIMILAR_TO check expressions else -> throw UnsupportedOperationException( "Must implement LIKE expressions for database %s".format( diff --git a/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt b/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt index 7eb0a92d4b..82ab5d905c 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt @@ -281,7 +281,7 @@ class SqlInsertBuilder( private fun mergeConstraints(column: Column, extra: ExtraConstraintsDto): Column { Lazy.assert { matchJpaName(column.name, extra.columnName) } - val mergedIsNullable = if (!column.nullable) { + val mergedIsNullable = if (!column.nullable || extra.constraints.isNotBlank ?: false) { false } else if (extra.constraints.isNullable == null) { column.nullable @@ -578,43 +578,43 @@ class SqlInsertBuilder( * test cases manually for EM */ fun createSqlInsertionAction( - tableName: String, - /** - * Which columns to create data for. Default is all, ie *. - * Notice that more columns might be added, eg, to satisfy non-null - * and PK constraints - */ - columnNames: Set = setOf("*"), - /** - * used to avoid infinite recursion - */ - history: MutableList = mutableListOf(), - /** - * When adding new insertions due to FK constraints, specify if - * should get all columns for those new insertions, or just the minimal - * needed to satisfy all the constraints - */ - forceAll: Boolean = false, - /** - * whether to use extra constraints identified in the business logic - */ - useExtraSqlDbConstraints : Boolean = false, - /** - * whether to enable single insertion for table - * - * in order to insert one row to the table, - * it might need to create its fk tables, - * and its fk table might have further fk tables as well. - * eg, - * D -> B -> A - * D -> C -> A - * to insert a row to D, - * if we do enable single insertion for table, - * the insertions will ABCD (C and B refer to the same A) - * otherwise, they will be ABACD - * - */ - enableSingleInsertionForTable : Boolean = false + tableName: String, + /** + * Which columns to create data for. Default is all, ie *. + * Notice that more columns might be added, eg, to satisfy non-null + * and PK constraints + */ + columnNames: Set = setOf("*"), + /** + * used to avoid infinite recursion + */ + history: MutableList = mutableListOf(), + /** + * When adding new insertions due to FK constraints, specify if + * should get all columns for those new insertions, or just the minimal + * needed to satisfy all the constraints + */ + forceAll: Boolean = false, + /** + * whether to use extra constraints identified in the business logic + */ + useExtraSqlDbConstraints: Boolean = false, + /** + * whether to enable single insertion for table + * + * in order to insert one row to the table, + * it might need to create its fk tables, + * and its fk table might have further fk tables as well. + * eg, + * D -> B -> A + * D -> C -> A + * to insert a row to D, + * if we do enable single insertion for table, + * the insertions will ABCD (C and B refer to the same A) + * otherwise, they will be ABACD + * + */ + enableSingleInsertionForTable: Boolean = false ): List { history.add(tableName) @@ -666,9 +666,23 @@ class SqlInsertBuilder( } val pre = if (forceAll) { - createSqlInsertionAction(target, setOf("*"), history, true, useExtraSqlDbConstraints, enableSingleInsertionForTable) + createSqlInsertionAction( + target, + setOf("*"), + history, + true, + useExtraSqlDbConstraints, + enableSingleInsertionForTable + ) } else { - createSqlInsertionAction(target, setOf(), history, false, useExtraSqlDbConstraints, enableSingleInsertionForTable) + createSqlInsertionAction( + target, + setOf(), + history, + false, + useExtraSqlDbConstraints, + enableSingleInsertionForTable + ) } actions.addAll(0, pre) } @@ -676,9 +690,10 @@ class SqlInsertBuilder( log.trace("create insertions and current size is {}", actions.size) } - if (enableSingleInsertionForTable && actions.size > 1){ + if (enableSingleInsertionForTable && actions.size > 1) { val removed = actions.filterIndexed { index, dbAction -> - (index > 0 && (index < actions.size-1 || actions.size == 2)) && actions.subList(0, index-1).any { a-> a.table.name.equals(dbAction.table.name,ignoreCase = true ) } + (index > 0 && (index < actions.size - 1 || actions.size == 2)) && actions.subList(0, index - 1) + .any { a -> a.table.name.equals(dbAction.table.name, ignoreCase = true) } } if (removed.isNotEmpty()) actions.removeAll(removed) diff --git a/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java b/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java index 3c79f44a60..348499999b 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java +++ b/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java @@ -2,8 +2,7 @@ import javax.persistence.*; -import javax.validation.constraints.Min; -import javax.validation.constraints.Max; +import javax.validation.constraints.*; @Entity @Table(name = "ExistingTable") @@ -32,4 +31,30 @@ public int getX1() { public void setX1(int x1) { this.x1 = x1; } + + + + @NotBlank + @Column(name="notblank") + private String notblank; + + public String getNotBlank() { + return notblank; + } + + public void setNotBlank(String notBlank) { + this.notblank = notBlank; + } + + //@Email + @NotNull + private String email; + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } } diff --git a/e2e-tests/spring-rest-openapi-v2/src/main/resources/sql/entityjpa.sql b/e2e-tests/spring-rest-openapi-v2/src/main/resources/sql/entityjpa.sql index 1be0f3db3f..cca50d81f3 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/main/resources/sql/entityjpa.sql +++ b/e2e-tests/spring-rest-openapi-v2/src/main/resources/sql/entityjpa.sql @@ -1,4 +1,6 @@ create table IF NOT EXISTS existing_table ( id integer primary key, - x1 integer + x1 integer, + notBlank varchar, + email varchar not null ); \ No newline at end of file From c306b96c5a605e5e827d67d0215e7852d7b70bc6 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Wed, 26 Apr 2023 12:31:33 +0200 Subject: [PATCH 002/345] option to collect all covered target info --- .../controller/EmbeddedSutController.java | 5 + .../controller/ExternalSutController.java | 8 + .../controller/internal/EMController.java | 147 ++++++++++-------- .../controller/internal/SutController.java | 3 + .../InstrumentationController.java | 12 ++ .../java/instrumentation/TargetInfo.java | 14 ++ .../external/AgentController.java | 11 ++ .../instrumentation/external/Command.java | 2 +- .../external/ServerController.java | 20 +++ 9 files changed, 160 insertions(+), 62 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/EmbeddedSutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/EmbeddedSutController.java index 297127c65c..f251203f18 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/EmbeddedSutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/EmbeddedSutController.java @@ -55,6 +55,11 @@ public final List getTargetInfos(Collection ids){ return InstrumentationController.getTargetInfos(ids); } + @Override + public final List getAllCoveredTargetInfos(){ + return InstrumentationController.getAllCoveredTargetInfos(); + } + @Override public final List getAdditionalInfoList(){ return InstrumentationController.getAdditionalInfoList(); diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java index 0331d61a31..e05f7e86c1 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java @@ -394,6 +394,14 @@ public final List getTargetInfos(Collection ids) { return serverController.getTargetsInfo(ids); } + @Override + public final List getAllCoveredTargetInfos(){ + checkInstrumentation(); + return serverController.getAllCoveredTargetsInfo(); + } + + + @Override public final List getAdditionalInfoList(){ checkInstrumentation(); diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java index f126e33a71..15f10969be 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java @@ -434,8 +434,16 @@ public Response getTestResults( String idList, @QueryParam("killSwitch") @DefaultValue("false") boolean killSwitch, + @QueryParam("allCovered") @DefaultValue("false") + boolean allCovered, @Context HttpServletRequest httpServletRequest) { + if(allCovered && !idList.isEmpty()){ + String msg = "Cannot specify to collect all covered targets and also at same time specify some targets manually: " + idList; + SimpleLogger.warn(msg); + return Response.status(400).entity(WrappedResponseDto.withError(msg)).build(); + } + // notify that actions execution is done. noKillSwitch(() -> sutController.setExecutingAction(false)); @@ -444,24 +452,36 @@ public Response getTestResults( try { TestResultsDto dto = new TestResultsDto(); - Set ids; + List targetInfos = null; - try { - ids = Arrays.stream(idList.split(",")) - .filter(s -> !s.trim().isEmpty()) - .map(Integer::parseInt) - .collect(Collectors.toSet()); - } catch (NumberFormatException e) { - String msg = "Invalid parameter 'ids': " + e.getMessage(); - SimpleLogger.warn(msg); - return Response.status(400).entity(WrappedResponseDto.withError(msg)).build(); - } + if(! allCovered) { + Set ids; - List targetInfos = noKillSwitch(() -> sutController.getTargetInfos(ids)); - if (targetInfos == null) { - String msg = "Failed to collect target information for " + ids.size() + " ids"; - SimpleLogger.error(msg); - return Response.status(500).entity(WrappedResponseDto.withError(msg)).build(); + try { + ids = Arrays.stream(idList.split(",")) + .filter(s -> !s.trim().isEmpty()) + .map(Integer::parseInt) + .collect(Collectors.toSet()); + } catch (NumberFormatException e) { + String msg = "Invalid parameter 'ids': " + e.getMessage(); + SimpleLogger.warn(msg); + return Response.status(400).entity(WrappedResponseDto.withError(msg)).build(); + } + + targetInfos = noKillSwitch(() -> sutController.getTargetInfos(ids)); + if (targetInfos == null) { + String msg = "Failed to collect target information for " + ids.size() + " ids"; + SimpleLogger.error(msg); + return Response.status(500).entity(WrappedResponseDto.withError(msg)).build(); + } + } else{ + + targetInfos = noKillSwitch(() -> sutController.getAllCoveredTargetInfos()); + if (targetInfos == null) { + String msg = "Failed to collect all covered target information"; + SimpleLogger.error(msg); + return Response.status(500).entity(WrappedResponseDto.withError(msg)).build(); + } } targetInfos.forEach(t -> { @@ -474,6 +494,8 @@ public Response getTestResults( dto.targets.add(info); }); + //if we want just info on covered targets, don't add extra info + if(!allCovered) { /* Note: it is important that extra is computed before AdditionalInfo, as heuristics on SQL might add new entries to String specializations @@ -482,54 +504,57 @@ public Response getTestResults( not on External :( But, as anyway we are going to refactor it in Core at a later point, no need to waste time for a tmp workaround + TODO: actually ended up fixing it for External. but still need to decide if + refactoring everything into core */ - dto.extraHeuristics = noKillSwitch(() -> sutController.getExtraHeuristics()); - - List additionalInfos = noKillSwitch(() -> sutController.getAdditionalInfoList()); - if (additionalInfos != null) { - additionalInfos.forEach(a -> { - AdditionalInfoDto info = new AdditionalInfoDto(); - info.queryParameters = new HashSet<>(a.getQueryParametersView()); - info.headers = new HashSet<>(a.getHeadersView()); - info.lastExecutedStatement = a.getLastExecutedStatement(); - info.rawAccessOfHttpBodyPayload = a.isRawAccessOfHttpBodyPayload(); - info.parsedDtoNames = new HashSet<>(a.getParsedDtoNamesView()); - info.externalServices = a.getExternalServices().stream() - .map(es -> new ExternalServiceInfoDto( - es.getProtocol(), - es.getHostname(), - es.getRemotePort() - )) - .collect(Collectors.toList()); - info.employedDefaultWM = a.getEmployedDefaultWM().stream().map( - des -> new ExternalServiceInfoDto( - des.getProtocol(), - des.getHostname(), - des.getRemotePort() - ) - ).collect(Collectors.toList()); - info.stringSpecializations = new LinkedHashMap<>(); - for (Map.Entry> entry : - a.getStringSpecializationsView().entrySet()) { - - assert !entry.getValue().isEmpty(); - - List list = entry.getValue().stream() - .map(it -> new StringSpecializationInfoDto( - it.getStringSpecialization().toString(), - it.getValue(), - it.getType().toString())) + dto.extraHeuristics = noKillSwitch(() -> sutController.getExtraHeuristics()); + + List additionalInfos = noKillSwitch(() -> sutController.getAdditionalInfoList()); + if (additionalInfos != null) { + additionalInfos.forEach(a -> { + AdditionalInfoDto info = new AdditionalInfoDto(); + info.queryParameters = new HashSet<>(a.getQueryParametersView()); + info.headers = new HashSet<>(a.getHeadersView()); + info.lastExecutedStatement = a.getLastExecutedStatement(); + info.rawAccessOfHttpBodyPayload = a.isRawAccessOfHttpBodyPayload(); + info.parsedDtoNames = new HashSet<>(a.getParsedDtoNamesView()); + info.externalServices = a.getExternalServices().stream() + .map(es -> new ExternalServiceInfoDto( + es.getProtocol(), + es.getHostname(), + es.getRemotePort() + )) .collect(Collectors.toList()); + info.employedDefaultWM = a.getEmployedDefaultWM().stream().map( + des -> new ExternalServiceInfoDto( + des.getProtocol(), + des.getHostname(), + des.getRemotePort() + ) + ).collect(Collectors.toList()); + info.stringSpecializations = new LinkedHashMap<>(); + for (Map.Entry> entry : + a.getStringSpecializationsView().entrySet()) { + + assert !entry.getValue().isEmpty(); + + List list = entry.getValue().stream() + .map(it -> new StringSpecializationInfoDto( + it.getStringSpecialization().toString(), + it.getValue(), + it.getType().toString())) + .collect(Collectors.toList()); + + info.stringSpecializations.put(entry.getKey(), list); + } - info.stringSpecializations.put(entry.getKey(), list); - } - - dto.additionalInfoList.add(info); - }); - } else { - String msg = "Failed to collect additional info"; - SimpleLogger.error(msg); - return Response.status(500).entity(WrappedResponseDto.withError(msg)).build(); + dto.additionalInfoList.add(info); + }); + } else { + String msg = "Failed to collect additional info"; + SimpleLogger.error(msg); + return Response.status(500).entity(WrappedResponseDto.withError(msg)).build(); + } } if (killSwitch) { diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index c77a6806cf..0f33c847d2 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -1011,6 +1011,9 @@ public final String getDatabaseDriverName(){ public abstract List getTargetInfos(Collection ids); + public abstract List getAllCoveredTargetInfos(); + + /** * @return additional info for each action in the test. * The list is ordered based on the action index. diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/InstrumentationController.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/InstrumentationController.java index b6179aeb27..719a1189b4 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/InstrumentationController.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/InstrumentationController.java @@ -9,6 +9,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; public class InstrumentationController { @@ -50,6 +51,17 @@ public static void setExecutingAction(boolean executingAction){ ExecutionTracer.setExecutingAction(executingAction); } + public static List getAllCoveredTargetInfos(){ + + Map objectives = ExecutionTracer.getInternalReferenceToObjectiveCoverage(); + + return objectives.entrySet().stream() + .filter(e -> e.getValue().value == 1d) // only covered + //try to save bandwidth by only sending mapped ids + .map(e -> e.getValue().enforceMappedId().withNoDescriptiveId()) + .collect(Collectors.toList()); + } + public static List getTargetInfos(Collection ids){ List list = new ArrayList<>(); diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/TargetInfo.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/TargetInfo.java index 0f28bdc4db..fd56c16fd2 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/TargetInfo.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/TargetInfo.java @@ -1,5 +1,7 @@ package org.evomaster.client.java.instrumentation; +import org.evomaster.client.java.instrumentation.staticstate.ObjectiveRecorder; + import java.io.Serializable; /** @@ -44,6 +46,18 @@ public TargetInfo withMappedId(int theID){ return new TargetInfo(theID, descriptiveId, value, actionIndex); } + public TargetInfo enforceMappedId(){ + if(descriptiveId == null){ + throw new IllegalStateException("Cannot enforce mapped id from records in which the descriptive id was removed"); + } + if(mappedId != null){ + return this; + } + int theID = ObjectiveRecorder.getMappedId(descriptiveId); + return new TargetInfo(theID, descriptiveId, value, actionIndex); + } + + public TargetInfo withNoDescriptiveId(){ return new TargetInfo(mappedId, null, value, actionIndex); } diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/AgentController.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/AgentController.java index cf4b895dc0..aeaaccbfe3 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/AgentController.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/AgentController.java @@ -72,6 +72,9 @@ public static void start(int port){ case TARGETS_INFO: handleTargetInfos(); break; + case ALL_COVERED_TARGETS_INFO: + handleAllCoveredTargetsInfo(); + break; case ACTION_INDEX: handleActionIndex(); sendCommand(Command.ACK); @@ -199,6 +202,14 @@ private static void handleExtractingSpecifiedDto(){ } } + private static void handleAllCoveredTargetsInfo(){ + try { + sendObject(InstrumentationController.getAllCoveredTargetInfos()); + }catch (Exception e) { + SimpleLogger.error("Failure in handling all covered info extraction: "+e.getMessage()); + } + } + private static void handleTargetInfos() { try { diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/Command.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/Command.java index 7f61fe8690..87e23b8cf2 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/Command.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/Command.java @@ -8,5 +8,5 @@ */ public enum Command implements Serializable { - NEW_SEARCH, NEW_TEST, TARGETS_INFO, ACK, ACTION_INDEX, ADDITIONAL_INFO, UNITS_INFO, KILL_SWITCH, EXECUTING_INIT_SQL, EXECUTING_ACTION, BOOT_TIME_INFO, EXTRACT_JVM_DTO + NEW_SEARCH, NEW_TEST, ALL_COVERED_TARGETS_INFO, TARGETS_INFO, ACK, ACTION_INDEX, ADDITIONAL_INFO, UNITS_INFO, KILL_SWITCH, EXECUTING_INIT_SQL, EXECUTING_ACTION, BOOT_TIME_INFO, EXTRACT_JVM_DTO } diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/ServerController.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/ServerController.java index 9416d4a8be..c2a24f472b 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/ServerController.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/ServerController.java @@ -208,6 +208,26 @@ public boolean setExecutingAction(boolean executingAction){ return sendWithDataAndExpectACK(Command.EXECUTING_ACTION, executingAction); } + public synchronized List getAllCoveredTargetsInfo() { + boolean sent = sendCommand(Command.ALL_COVERED_TARGETS_INFO); + if (!sent) { + SimpleLogger.error("Failed to send message"); + return null; + } + + Object response = waitAndGetResponse(); + if (response == null) { + SimpleLogger.error("Failed to read response about all covered targets"); + return null; + } + + if (!(response instanceof List)) { + throw new IllegalStateException(errorMsgExpectingResponse(response, "a List")); + } + + return (List) response; + } + public synchronized List getTargetsInfo(Collection ids) { boolean sent = sendCommand(Command.TARGETS_INFO); if (!sent) { From 815b67f659c554a2fef04675b69ca2aa42dc7f6d Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Wed, 26 Apr 2023 14:14:54 +0200 Subject: [PATCH 003/345] starting with Minimizer --- .../kotlin/org/evomaster/core/BaseModule.kt | 3 +++ .../main/kotlin/org/evomaster/core/EMConfig.kt | 6 ++++++ .../evomaster/core/search/service/Minimizer.kt | 17 +++++++++++++++++ .../core/search/service/SearchAlgorithm.kt | 13 +++++++++++-- docs/options.md | 1 + 5 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt diff --git a/core/src/main/kotlin/org/evomaster/core/BaseModule.kt b/core/src/main/kotlin/org/evomaster/core/BaseModule.kt index 0cefe45c5f..e55d16cbb4 100644 --- a/core/src/main/kotlin/org/evomaster/core/BaseModule.kt +++ b/core/src/main/kotlin/org/evomaster/core/BaseModule.kt @@ -81,6 +81,9 @@ class BaseModule(val args: Array, val noTests: Boolean = false) : Abstra bind(ExecutionInfoReporter::class.java) .asEagerSingleton() + bind(Minimizer::class.java) + .asEagerSingleton() + //no longer needed if TestSuiteWriter is moved out? // if(noTests){ // bind(TestCaseWriter::class.java) diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index ece85a9aeb..ea9595f72c 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -1724,6 +1724,12 @@ class EMConfig { var addPreDefinedTests : Boolean = true + @Cfg("Apply a minimization phase to make the generated tests more readable." + + " Achieved coverage would stay the same." + + " Generating shorter test cases might come at the cost of having more test cases.") + var minimize : Boolean = true + + @Experimental @FilePath(true) @Regex("(.*jacoco.*\\.jar)|(^$)") diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt new file mode 100644 index 0000000000..8add7f7048 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt @@ -0,0 +1,17 @@ +package org.evomaster.core.search.service + +/** + * Reduce + */ +class Minimizer { + + + + /** + * Based on the tests in the archive, update the archive by having, for each target T, + * the minimum number of actions needed to cover it + */ + fun minimizeActionsPerCoveredTargetInArchive(){ + //TODO + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/SearchAlgorithm.kt b/core/src/main/kotlin/org/evomaster/core/search/service/SearchAlgorithm.kt index 545f62ba97..ef14d8bc4f 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/SearchAlgorithm.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/SearchAlgorithm.kt @@ -34,6 +34,10 @@ abstract class SearchAlgorithm where T : Individual { @Inject(optional = true) private lateinit var mutator: Mutator + @Inject + private lateinit var minimizer: Minimizer + + private var lastSnapshot = 0 protected fun getMutatator() : Mutator { @@ -78,6 +82,11 @@ abstract class SearchAlgorithm where T : Individual { } private fun handleAfterSearch() { + + if(config.minimize){ + minimizer.minimizeActionsPerCoveredTargetInArchive() + } + if(config.addPreDefinedTests) { for (ind in sampler.getPreDefinedIndividuals()) { ff.calculateCoverage(ind)?.run { @@ -88,8 +97,8 @@ abstract class SearchAlgorithm where T : Individual { } private fun needsToSnapshot(): Boolean { - var isSnapshotEnabled = config.enableWriteSnapshotTests - var snapshotPeriod = config.writeSnapshotTestsIntervalInSeconds + val isSnapshotEnabled = config.enableWriteSnapshotTests + val snapshotPeriod = config.writeSnapshotTestsIntervalInSeconds return isSnapshotEnabled && time.getElapsedSeconds() - lastSnapshot > snapshotPeriod } diff --git a/docs/options.md b/docs/options.md index cd74cfa7c7..e8e1f88682 100644 --- a/docs/options.md +++ b/docs/options.md @@ -97,6 +97,7 @@ There are 3 types of options: |`maxTestSize`| __Int__. Max number of 'actions' (e.g., RESTful calls or SQL commands) that can be done in a single test. *Constraints*: `min=1.0`. *Default value*: `10`.| |`maxTimeInSeconds`| __Int__. Maximum number of seconds allowed for the search. The more time is allowed, the better results one can expect. But then of course the test generation will take longer. Only applicable depending on the stopping criterion. If this value is 0, the setting 'maxTime' will be used instead. *Constraints*: `min=0.0`. *Default value*: `0`.| |`maxlengthOfHistoryForAGM`| __Int__. Specify a maximum length of history when applying archive-based gene mutation. *Default value*: `10`.| +|`minimize`| __Boolean__. Apply a minimization phase to make the generated tests more readable. Achieved coverage would stay the same. Generating shorter test cases might come at the cost of having more test cases. *Default value*: `true`.| |`minimumSizeControl`| __Int__. Specify minimum size when bloatControlForSecondaryObjective. *Constraints*: `min=0.0`. *Default value*: `2`.| |`populationSize`| __Int__. Define the population size in the search algorithms that use populations (e.g., Genetic Algorithms, but not MIO). *Constraints*: `min=1.0`. *Default value*: `30`.| |`probOfApplySQLActionToCreateResources`| __Double__. Specify a probability to apply SQL actions for preparing resources for REST Action. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.5`.| From df1101771b6c0402f28982f962abb9517bec93f2 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Wed, 26 Apr 2023 14:55:42 +0200 Subject: [PATCH 004/345] more on minimizer --- .../evomaster/core/search/service/Archive.kt | 4 ++++ .../core/search/service/FitnessFunction.kt | 6 ++--- .../core/search/service/Minimizer.kt | 22 +++++++++++++++++-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt index 4f94ff4129..4d672b2ea1 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt @@ -90,6 +90,10 @@ class Archive where T : Individual { } + fun getCopyOfUniqueCoveringIndividuals() : List{ + return getUniquePopulation().map { it.individual } + } + private fun getUniquePopulation(): MutableSet> { /* diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/FitnessFunction.kt b/core/src/main/kotlin/org/evomaster/core/search/service/FitnessFunction.kt index 68ee502386..0261179f7d 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/FitnessFunction.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/FitnessFunction.kt @@ -54,7 +54,7 @@ abstract class FitnessFunction where T : Individual { executionInfoReporter.addLatestComputationOverhead(computation, time.evaluatedIndividuals) } - var ei = calculateIndividualWithPostHandling(individual, targets, a) + var ei = calculateIndividualCoverageWithStats(individual, targets, a) if(ei == null){ /* @@ -67,7 +67,7 @@ abstract class FitnessFunction where T : Individual { //let's wait a little, just in case... Thread.sleep(5_000) - ei = calculateIndividualWithPostHandling(individual, targets, a) + ei = calculateIndividualCoverageWithStats(individual, targets, a) if(ei == null){ @@ -95,7 +95,7 @@ abstract class FitnessFunction where T : Individual { */ protected abstract fun doCalculateCoverage(individual: T, targets: Set) : EvaluatedIndividual? - private fun calculateIndividualWithPostHandling(individual: T, targets: Set, actionsSize: Int) : EvaluatedIndividual?{ + private fun calculateIndividualCoverageWithStats(individual: T, targets: Set, actionsSize: Int) : EvaluatedIndividual?{ val ei = SearchTimeController.measureTimeMillis( { t, ind -> diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt index 8add7f7048..4abab0bb8a 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt @@ -1,17 +1,35 @@ package org.evomaster.core.search.service +import com.google.inject.Inject +import org.evomaster.core.logging.LoggingUtil +import org.evomaster.core.search.Individual + /** - * Reduce + * Reduce/simplify the final test outputs */ class Minimizer { - + @Inject + private lateinit var archive: Archive<*> /** * Based on the tests in the archive, update the archive by having, for each target T, * the minimum number of actions needed to cover it */ fun minimizeActionsPerCoveredTargetInArchive(){ + + val current = archive.getCopyOfUniqueCoveringIndividuals() + + LoggingUtil.getInfoLogger().info("Starting to apply minimization phase on ${current.size} tests") + + val beforeCovered = archive.numberOfCoveredTargets() + + + + //TODO + + + } } \ No newline at end of file From 519455e0252ed98589a92d693c490c661fa5d327 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Fri, 28 Apr 2023 10:33:27 +0200 Subject: [PATCH 005/345] making Minimizer parametric --- .../main/kotlin/org/evomaster/core/BaseModule.kt | 3 --- .../problem/enterprise/service/EnterpriseFitness.kt | 2 ++ .../graphql/service/GraphQLBlackBoxModule.kt | 9 +++++++++ .../core/problem/graphql/service/GraphQLModule.kt | 9 +++++++++ .../core/problem/rest/service/BlackBoxRestModule.kt | 8 ++++++++ .../core/problem/rest/service/RestModule.kt | 4 ++++ .../evomaster/core/problem/rpc/service/RPCModule.kt | 8 ++++++++ .../core/problem/webfrontend/service/WebModule.kt | 9 +++++++++ .../core/search/service/FitnessFunction.kt | 13 +++++++++++++ .../org/evomaster/core/search/service/Minimizer.kt | 12 ++++++++---- .../core/search/service/SearchAlgorithm.kt | 2 +- 11 files changed, 71 insertions(+), 8 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/BaseModule.kt b/core/src/main/kotlin/org/evomaster/core/BaseModule.kt index e55d16cbb4..0cefe45c5f 100644 --- a/core/src/main/kotlin/org/evomaster/core/BaseModule.kt +++ b/core/src/main/kotlin/org/evomaster/core/BaseModule.kt @@ -81,9 +81,6 @@ class BaseModule(val args: Array, val noTests: Boolean = false) : Abstra bind(ExecutionInfoReporter::class.java) .asEagerSingleton() - bind(Minimizer::class.java) - .asEagerSingleton() - //no longer needed if TestSuiteWriter is moved out? // if(noTests){ // bind(TestCaseWriter::class.java) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt index 2ecc1aba18..51b3b7c25b 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt @@ -144,6 +144,8 @@ abstract class EnterpriseFitness : FitnessFunction() where T : Individual protected fun updateFitnessAfterEvaluation(targets: Set, individual: T, fv: FitnessValue) : TestResultsDto?{ val ids = targetsToEvaluate(targets, individual) + FIXME + val dto = rc.getTestResults(ids) if (dto == null) { log.warn("Cannot retrieve coverage") diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLBlackBoxModule.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLBlackBoxModule.kt index 66ba22e3bb..c1311f56f6 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLBlackBoxModule.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLBlackBoxModule.kt @@ -5,10 +5,12 @@ import com.google.inject.TypeLiteral import org.evomaster.core.output.service.GraphQLTestCaseWriter import org.evomaster.core.output.service.TestCaseWriter import org.evomaster.core.problem.graphql.GraphQLIndividual +import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.remote.service.RemoteController import org.evomaster.core.remote.service.RemoteControllerImplementation import org.evomaster.core.search.service.Archive import org.evomaster.core.search.service.FitnessFunction +import org.evomaster.core.search.service.Minimizer import org.evomaster.core.search.service.Sampler class GraphQLBlackBoxModule( @@ -37,6 +39,13 @@ class GraphQLBlackBoxModule( bind(object : TypeLiteral>() {}) .to(object : TypeLiteral>() {}) + bind(object : TypeLiteral>(){}) + .asEagerSingleton() + + bind(object : TypeLiteral>(){}) + .asEagerSingleton() + + if(usingRemoteController) { bind(RemoteController::class.java) .to(RemoteControllerImplementation::class.java) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLModule.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLModule.kt index bce9fcd26f..9d6206c574 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLModule.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLModule.kt @@ -6,10 +6,12 @@ import org.evomaster.core.output.service.GraphQLTestCaseWriter import org.evomaster.core.output.service.TestCaseWriter import org.evomaster.core.output.service.TestSuiteWriter import org.evomaster.core.problem.graphql.GraphQLIndividual +import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.remote.service.RemoteController import org.evomaster.core.remote.service.RemoteControllerImplementation import org.evomaster.core.search.service.Archive import org.evomaster.core.search.service.FitnessFunction +import org.evomaster.core.search.service.Minimizer import org.evomaster.core.search.service.Sampler import org.evomaster.core.search.service.mutator.Mutator import org.evomaster.core.search.service.mutator.StandardMutator @@ -41,6 +43,13 @@ class GraphQLModule : AbstractModule() { bind(object : TypeLiteral>() {}) .to(object : TypeLiteral>() {}) + bind(object : TypeLiteral>(){}) + .asEagerSingleton() + + bind(object : TypeLiteral>(){}) + .asEagerSingleton() + + bind(RemoteController::class.java) .to(RemoteControllerImplementation::class.java) .asEagerSingleton() diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/BlackBoxRestModule.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/BlackBoxRestModule.kt index 6abd72c4ff..979842dbea 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/BlackBoxRestModule.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/BlackBoxRestModule.kt @@ -10,6 +10,7 @@ import org.evomaster.core.remote.service.RemoteController import org.evomaster.core.remote.service.RemoteControllerImplementation import org.evomaster.core.search.service.Archive import org.evomaster.core.search.service.FitnessFunction +import org.evomaster.core.search.service.Minimizer import org.evomaster.core.search.service.Sampler class BlackBoxRestModule( @@ -38,6 +39,13 @@ class BlackBoxRestModule( bind(object : TypeLiteral>() {}) .to(object : TypeLiteral>() {}) + bind(object : TypeLiteral>(){}) + .asEagerSingleton() + + bind(object : TypeLiteral>(){}) + .asEagerSingleton() + + if(usingRemoteController) { bind(RemoteController::class.java) .to(RemoteControllerImplementation::class.java) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestModule.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestModule.kt index 67e5b2cc8e..6abb6e0962 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestModule.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestModule.kt @@ -50,7 +50,11 @@ class RestModule(private val bindRemote : Boolean = true) : AbstractModule(){ bind(object : TypeLiteral>() {}) .to(object : TypeLiteral>() {}) + bind(object : TypeLiteral>(){}) + .asEagerSingleton() + bind(object : TypeLiteral>(){}) + .asEagerSingleton() bind(object : TypeLiteral>() {}) .to(object : TypeLiteral>(){}) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCModule.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCModule.kt index 5ed8fe8131..795e0d22ef 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCModule.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCModule.kt @@ -5,11 +5,13 @@ import com.google.inject.TypeLiteral import org.evomaster.core.output.service.RPCTestCaseWriter import org.evomaster.core.output.service.TestCaseWriter import org.evomaster.core.output.service.TestSuiteWriter +import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.rpc.RPCIndividual import org.evomaster.core.remote.service.RemoteController import org.evomaster.core.remote.service.RemoteControllerImplementation import org.evomaster.core.search.service.Archive import org.evomaster.core.search.service.FitnessFunction +import org.evomaster.core.search.service.Minimizer import org.evomaster.core.search.service.Sampler import org.evomaster.core.search.service.mutator.Mutator import org.evomaster.core.search.service.mutator.StandardMutator @@ -45,6 +47,12 @@ class RPCModule : AbstractModule(){ bind(object : TypeLiteral>() {}) .to(object : TypeLiteral>() {}) + bind(object : TypeLiteral>(){}) + .asEagerSingleton() + + bind(object : TypeLiteral>(){}) + .asEagerSingleton() + bind(RemoteController::class.java) .to(RemoteControllerImplementation::class.java) .asEagerSingleton() diff --git a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebModule.kt b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebModule.kt index bc889875f8..f558716bf1 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebModule.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebModule.kt @@ -5,11 +5,13 @@ import com.google.inject.TypeLiteral import org.evomaster.core.output.service.TestCaseWriter import org.evomaster.core.output.service.TestSuiteWriter import org.evomaster.core.output.service.WebTestCaseWriter +import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.webfrontend.WebIndividual import org.evomaster.core.remote.service.RemoteController import org.evomaster.core.remote.service.RemoteControllerImplementation import org.evomaster.core.search.service.Archive import org.evomaster.core.search.service.FitnessFunction +import org.evomaster.core.search.service.Minimizer import org.evomaster.core.search.service.Sampler import org.evomaster.core.search.service.mutator.Mutator import org.evomaster.core.search.service.mutator.StandardMutator @@ -46,6 +48,13 @@ class WebModule: AbstractModule() { bind(object : TypeLiteral>() {}) .to(object : TypeLiteral>() {}) + bind(object : TypeLiteral>(){}) + .asEagerSingleton() + + bind(object : TypeLiteral>(){}) + .asEagerSingleton() + + bind(RemoteController::class.java) .to(RemoteControllerImplementation::class.java) .asEagerSingleton() diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/FitnessFunction.kt b/core/src/main/kotlin/org/evomaster/core/search/service/FitnessFunction.kt index 0261179f7d..7cf1b7682b 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/FitnessFunction.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/FitnessFunction.kt @@ -95,6 +95,19 @@ abstract class FitnessFunction where T : Individual { */ protected abstract fun doCalculateCoverage(individual: T, targets: Set) : EvaluatedIndividual? + /** + * Compute the fitness function, but only for the covered targets (ie partial heuristics are ignored), + * and without collecting any general stats. + * + * This is an expensive operations, used for post-processing phases, eg test case minimization. + * Note that during the search we use heuristics to minimize the number of data to retrieve, + * so there the fitness value is just partial + */ + fun computeWholeAchievedCoverageForPostProcessing(individual: T) : EvaluatedIndividual?{ + + TODO + } + private fun calculateIndividualCoverageWithStats(individual: T, targets: Set, actionsSize: Int) : EvaluatedIndividual?{ val ei = SearchTimeController.measureTimeMillis( diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt index 4abab0bb8a..ba866e447c 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt @@ -7,14 +7,18 @@ import org.evomaster.core.search.Individual /** * Reduce/simplify the final test outputs */ -class Minimizer { +class Minimizer { @Inject - private lateinit var archive: Archive<*> + private lateinit var archive: Archive /** * Based on the tests in the archive, update the archive by having, for each target T, - * the minimum number of actions needed to cover it + * the minimum number of actions needed to cover it. + * + * Using the same/similar kind of algorithm as explained in: + * + * "EvoSuite: On The Challenges of Test Case Generation in the Real World" */ fun minimizeActionsPerCoveredTargetInArchive(){ @@ -24,7 +28,7 @@ class Minimizer { val beforeCovered = archive.numberOfCoveredTargets() - + //TODO diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/SearchAlgorithm.kt b/core/src/main/kotlin/org/evomaster/core/search/service/SearchAlgorithm.kt index ef14d8bc4f..7d36d96ba9 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/SearchAlgorithm.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/SearchAlgorithm.kt @@ -35,7 +35,7 @@ abstract class SearchAlgorithm where T : Individual { private lateinit var mutator: Mutator @Inject - private lateinit var minimizer: Minimizer + private lateinit var minimizer: Minimizer private var lastSnapshot = 0 From 492ff3ed9511004502ee96ac80943720ebc7eee6 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Wed, 3 May 2023 14:10:50 +0200 Subject: [PATCH 006/345] fixing compilation issues --- .../enterprise/service/EnterpriseFitness.kt | 11 +++++++---- .../graphql/service/GraphQLBlackBoxFitness.kt | 2 +- .../problem/graphql/service/GraphQLFitness.kt | 14 ++++++++------ .../rest/service/AbstractRestFitness.kt | 4 ++-- .../rest/service/BlackBoxRestFitness.kt | 2 +- .../core/problem/rest/service/RestFitness.kt | 4 ++-- .../rest/service/RestResourceFitness.kt | 5 +++-- .../core/problem/rpc/service/RPCFitness.kt | 4 ++-- .../problem/webfrontend/service/WebFitness.kt | 5 +++-- .../core/remote/service/RemoteController.kt | 2 +- .../service/RemoteControllerImplementation.kt | 7 ++++++- .../evomaster/core/search/service/Archive.kt | 2 +- .../core/search/service/FitnessFunction.kt | 18 ++++++++++-------- .../external/service/DummyController.kt | 2 +- .../rest/individual/RestIndividualTestBase.kt | 2 +- .../algorithms/constant/ConstantFitness.kt | 2 +- .../search/algorithms/onemax/OneMaxFitness.kt | 2 +- .../matchproblem/PrimitiveTypeMatchFitness.kt | 2 +- 18 files changed, 52 insertions(+), 38 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt index 51b3b7c25b..2d4128a0d4 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt @@ -141,12 +141,15 @@ abstract class EnterpriseFitness : FitnessFunction() where T : Individual } } - protected fun updateFitnessAfterEvaluation(targets: Set, individual: T, fv: FitnessValue) : TestResultsDto?{ - val ids = targetsToEvaluate(targets, individual) + protected fun updateFitnessAfterEvaluation(targets: Set, allCovered: Boolean, individual: T, fv: FitnessValue) : TestResultsDto?{ - FIXME + val dto = if(allCovered){ + rc.getTestResults(allCovered = true) + } else { + val ids = targetsToEvaluate(targets, individual) + rc.getTestResults(ids) + } - val dto = rc.getTestResults(ids) if (dto == null) { log.warn("Cannot retrieve coverage") return null diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLBlackBoxFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLBlackBoxFitness.kt index e028545709..af4adbc9fe 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLBlackBoxFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLBlackBoxFitness.kt @@ -18,7 +18,7 @@ class GraphQLBlackBoxFitness : GraphQLFitness() { private val log: Logger = LoggerFactory.getLogger(GraphQLBlackBoxFitness::class.java) } - override fun doCalculateCoverage(individual: GraphQLIndividual, targets: Set): EvaluatedIndividual? { + override fun doCalculateCoverage(individual: GraphQLIndividual, targets: Set, allCovered: Boolean): EvaluatedIndividual? { if(config.bbExperiments){ /* diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLFitness.kt index 9207e7deea..2a72c4a35e 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLFitness.kt @@ -34,7 +34,8 @@ open class GraphQLFitness : HttpWsFitness() { override fun doCalculateCoverage( individual: GraphQLIndividual, - targets: Set + targets: Set, + allCovered: Boolean ): EvaluatedIndividual? { rc.resetSUT() @@ -78,7 +79,7 @@ open class GraphQLFitness : HttpWsFitness() { // return null // } - val dto = updateFitnessAfterEvaluation(targets, individual, fv) + val dto = updateFitnessAfterEvaluation(targets, allCovered, individual, fv) ?: return null handleExtra(dto, fv) @@ -86,10 +87,11 @@ open class GraphQLFitness : HttpWsFitness() { val graphQLActionResults = actionResults.filterIsInstance() handleResponseTargets(fv, actions, graphQLActionResults, dto.additionalInfoList) - - if (config.isEnabledTaintAnalysis()) { - Lazy.assert { graphQLActionResults.size == dto.additionalInfoList.size } - TaintAnalysis.doTaintAnalysis(individual, dto.additionalInfoList, randomness, config.enableSchemaConstraintHandling) + if(!allCovered) { + if (config.isEnabledTaintAnalysis()) { + Lazy.assert { graphQLActionResults.size == dto.additionalInfoList.size } + TaintAnalysis.doTaintAnalysis(individual, dto.additionalInfoList, randomness, config.enableSchemaConstraintHandling) + } } return EvaluatedIndividual( diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestFitness.kt index 81f986075a..b978292519 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestFitness.kt @@ -738,7 +738,7 @@ abstract class AbstractRestFitness : HttpWsFitness() where T : Individual } protected fun restActionResultHandling( - individual: RestIndividual, targets: Set, actionResults: List, fv: FitnessValue + individual: RestIndividual, targets: Set, allCovered: Boolean, actionResults: List, fv: FitnessValue ): TestResultsDto? { if (actionResults.any { it is RestCallResult && it.getTcpProblem() }) { @@ -754,7 +754,7 @@ abstract class AbstractRestFitness : HttpWsFitness() where T : Individual return null } - val dto = updateFitnessAfterEvaluation(targets, individual as T, fv) + val dto = updateFitnessAfterEvaluation(targets, allCovered, individual as T, fv) ?: return null handleExtra(dto, fv) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/BlackBoxRestFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/BlackBoxRestFitness.kt index e46f1f1fff..22730b3d99 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/BlackBoxRestFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/BlackBoxRestFitness.kt @@ -18,7 +18,7 @@ class BlackBoxRestFitness : RestFitness() { private val log: Logger = LoggerFactory.getLogger(BlackBoxRestFitness::class.java) } - override fun doCalculateCoverage(individual: RestIndividual, targets: Set): EvaluatedIndividual? { + override fun doCalculateCoverage(individual: RestIndividual, targets: Set, allCovered: Boolean): EvaluatedIndividual? { val cookies = mutableMapOf>() val tokens = mutableMapOf() diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt index 8b1f44bda9..b532438042 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt @@ -18,7 +18,7 @@ open class RestFitness : AbstractRestFitness() { } - override fun doCalculateCoverage(individual: RestIndividual, targets: Set): EvaluatedIndividual? { + override fun doCalculateCoverage(individual: RestIndividual, targets: Set, allCovered: Boolean): EvaluatedIndividual? { rc.resetSUT() @@ -89,7 +89,7 @@ open class RestFitness : AbstractRestFitness() { } val restActionResults = actionResults.filterIsInstance() - restActionResultHandling(individual, targets, restActionResults, fv)?:return null + restActionResultHandling(individual, targets, allCovered,restActionResults, fv)?:return null if (log.isTraceEnabled){ log.trace("restActionResult are handled") diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt index f9a32bb2a4..0f5c39dd56 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt @@ -37,7 +37,8 @@ class RestResourceFitness : AbstractRestFitness() { */ override fun doCalculateCoverage( individual: RestIndividual, - targets: Set + targets: Set, + allCovered: Boolean ): EvaluatedIndividual? { rc.resetSUT() @@ -181,7 +182,7 @@ class RestResourceFitness : AbstractRestFitness() { } val allRestResults = actionResults.filterIsInstance() - val dto = restActionResultHandling(individual, targets, allRestResults, fv) ?: return null + val dto = restActionResultHandling(individual, targets, allCovered, allRestResults, fv) ?: return null /* harvest actual requests once all actions are executed diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt index 40a003b444..0ef031aae2 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt @@ -38,7 +38,7 @@ class RPCFitness : ApiWsFitness() { @Inject lateinit var rpcHandler: RPCEndpointsHandler - override fun doCalculateCoverage(individual: RPCIndividual, targets: Set): EvaluatedIndividual? { + override fun doCalculateCoverage(individual: RPCIndividual, targets: Set, allCovered: Boolean): EvaluatedIndividual? { rc.resetSUT() @@ -56,7 +56,7 @@ class RPCFitness : ApiWsFitness() { } } - val dto = updateFitnessAfterEvaluation(targets, individual, fv) + val dto = updateFitnessAfterEvaluation(targets, allCovered, individual, fv) ?: return null handleExtra(dto, fv) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebFitness.kt index 3232a09814..d2c559e1f5 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebFitness.kt @@ -36,7 +36,8 @@ class WebFitness : EnterpriseFitness() { override fun doCalculateCoverage( individual: WebIndividual, - targets: Set + targets: Set, + allCovered: Boolean ): EvaluatedIndividual? { rc.resetSUT() @@ -70,7 +71,7 @@ class WebFitness : EnterpriseFitness() { } } - val dto = updateFitnessAfterEvaluation(targets, individual, fv) + val dto = updateFitnessAfterEvaluation(targets, allCovered, individual, fv) ?: return null handleExtra(dto, fv) diff --git a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteController.kt b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteController.kt index 027b7f5756..75c4ef07dd 100644 --- a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteController.kt +++ b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteController.kt @@ -24,7 +24,7 @@ interface RemoteController : DatabaseExecutor { fun startANewSearch(): Boolean - fun getTestResults(ids: Set = setOf(), ignoreKillSwitch: Boolean = false): TestResultsDto? + fun getTestResults(ids: Set = setOf(), ignoreKillSwitch: Boolean = false, allCovered: Boolean = false): TestResultsDto? fun executeNewRPCActionAndGetResponse(actionDto: ActionDto) : ActionResponseDto? diff --git a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt index 901a47c286..723da9019e 100644 --- a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt +++ b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt @@ -285,7 +285,11 @@ class RemoteControllerImplementation() : RemoteController{ return readAndCheckResponse(response, "Failed to inform SUT of new search") } - override fun getTestResults(ids: Set, ignoreKillSwitch: Boolean): TestResultsDto? { + override fun getTestResults(ids: Set, ignoreKillSwitch: Boolean, allCovered: Boolean): TestResultsDto? { + + if(allCovered && ids.isNotEmpty()){ + throw IllegalArgumentException("Cannot specify allCovered and specific ids at same time") + } val queryParam = ids.joinToString(",") @@ -294,6 +298,7 @@ class RemoteControllerImplementation() : RemoteController{ .path(ControllerConstants.TEST_RESULTS) .queryParam("ids", queryParam) .queryParam("killSwitch", !ignoreKillSwitch && config.killSwitch) + .queryParam("allCovered", allCovered) .request(MediaType.APPLICATION_JSON_TYPE) .get() } diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt index 4d672b2ea1..b8071dde43 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt @@ -102,7 +102,7 @@ class Archive where T : Individual { This is not an issue, as each individual is copied when sampled. Here, as an individual can go to many populations, - we want to avoiding it counting it several times. + we want to avoid counting it several times. */ val uniques = mutableSetOf>() diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/FitnessFunction.kt b/core/src/main/kotlin/org/evomaster/core/search/service/FitnessFunction.kt index 7cf1b7682b..fcaff4e2ca 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/FitnessFunction.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/FitnessFunction.kt @@ -43,9 +43,9 @@ abstract class FitnessFunction where T : Individual { } /** - * @return [null] if there were problems in calculating the coverage + * @return null if there were problems in calculating the coverage */ - fun calculateCoverage(individual: T, targets: Set = setOf()) : EvaluatedIndividual?{ + fun calculateCoverage(individual: T, targets: Set = setOf(), allCovered: Boolean = false) : EvaluatedIndividual?{ val a = individual.seeMainExecutableActions().count() @@ -89,11 +89,14 @@ abstract class FitnessFunction where T : Individual { /** - * calculated coverage with specified targets + * calculated coverage with specified targets. * - * @return [null] if there were problems in calculating the coverage + * if [allCovered] is true, then ids are ignored, and info on all fully-covered targets + * are returned. Also, in such case, we do not compute any extra info needed for the search + * + * @return null if there were problems in calculating the coverage */ - protected abstract fun doCalculateCoverage(individual: T, targets: Set) : EvaluatedIndividual? + protected abstract fun doCalculateCoverage(individual: T, targets: Set, allCovered: Boolean) : EvaluatedIndividual? /** * Compute the fitness function, but only for the covered targets (ie partial heuristics are ignored), @@ -104,8 +107,7 @@ abstract class FitnessFunction where T : Individual { * so there the fitness value is just partial */ fun computeWholeAchievedCoverageForPostProcessing(individual: T) : EvaluatedIndividual?{ - - TODO + return doCalculateCoverage(individual, setOf(), true) } private fun calculateIndividualCoverageWithStats(individual: T, targets: Set, actionsSize: Int) : EvaluatedIndividual?{ @@ -115,7 +117,7 @@ abstract class FitnessFunction where T : Individual { time.reportExecutedIndividualTime(t, actionsSize) ind?.executionTimeMs = t }, - {doCalculateCoverage(individual, targets)} + {doCalculateCoverage(individual, targets, false)} ) // plugin execution info reporter here, to avoid the time spent by execution reporter handleExecutionInfo(ei) diff --git a/core/src/test/kotlin/org/evomaster/core/problem/external/service/DummyController.kt b/core/src/test/kotlin/org/evomaster/core/problem/external/service/DummyController.kt index 4978955577..8976f39279 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/external/service/DummyController.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/external/service/DummyController.kt @@ -35,7 +35,7 @@ class DummyController: RemoteController { TODO("Not yet implemented") } - override fun getTestResults(ids: Set, ignoreKillSwitch: Boolean): TestResultsDto? { + override fun getTestResults(ids: Set, ignoreKillSwitch: Boolean, allCovered: Boolean): TestResultsDto? { TODO("Not yet implemented") } diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualTestBase.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualTestBase.kt index c44bb7d033..2999c02de4 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualTestBase.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualTestBase.kt @@ -591,7 +591,7 @@ abstract class RestIndividualTestBase { return true } - override fun getTestResults(ids: Set, ignoreKillSwitch: Boolean): TestResultsDto? { + override fun getTestResults(ids: Set, ignoreKillSwitch: Boolean, allCovered: Boolean): TestResultsDto? { assertNotNull(sqlInsertBuilder) newEvaluation() val result = TestResultsDto().apply { diff --git a/core/src/test/kotlin/org/evomaster/core/search/algorithms/constant/ConstantFitness.kt b/core/src/test/kotlin/org/evomaster/core/search/algorithms/constant/ConstantFitness.kt index 4a6834a165..0ee5649db8 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/algorithms/constant/ConstantFitness.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/algorithms/constant/ConstantFitness.kt @@ -10,7 +10,7 @@ import org.evomaster.core.search.service.FitnessFunction class ConstantFitness : FitnessFunction() { - override fun doCalculateCoverage(individual: ConstantIndividual, targets: Set): EvaluatedIndividual? { + override fun doCalculateCoverage(individual: ConstantIndividual, targets: Set, allCovered: Boolean): EvaluatedIndividual? { val target = 123 val res = individual.getGene().value diff --git a/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxFitness.kt b/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxFitness.kt index e322ab2966..1069ebcccd 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxFitness.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxFitness.kt @@ -9,7 +9,7 @@ import org.evomaster.core.search.tracer.Traceable class OneMaxFitness : FitnessFunction() { - override fun doCalculateCoverage(individual: OneMaxIndividual, targets: Set) + override fun doCalculateCoverage(individual: OneMaxIndividual, targets: Set, allCovered: Boolean) : EvaluatedIndividual? { val fv = FitnessValue(individual.size().toDouble()) diff --git a/core/src/test/kotlin/org/evomaster/core/search/matchproblem/PrimitiveTypeMatchFitness.kt b/core/src/test/kotlin/org/evomaster/core/search/matchproblem/PrimitiveTypeMatchFitness.kt index e923f23ca7..3ee1feeb8e 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/matchproblem/PrimitiveTypeMatchFitness.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/matchproblem/PrimitiveTypeMatchFitness.kt @@ -17,7 +17,7 @@ class PrimitiveTypeMatchFitness : FitnessFunction( var type : ONE2M = ONE2M.ONE_EQUAL_WITH_ONE - override fun doCalculateCoverage(individual: PrimitiveTypeMatchIndividual, targets: Set): EvaluatedIndividual? { + override fun doCalculateCoverage(individual: PrimitiveTypeMatchIndividual, targets: Set, allCovered: Boolean): EvaluatedIndividual? { val fv = FitnessValue(individual.size().toDouble()) From 7e0a280080d4bfd50b75438ec7586a23f0a18122 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Wed, 3 May 2023 14:37:40 +0200 Subject: [PATCH 007/345] working on minimizer --- .../evomaster/core/search/service/Archive.kt | 10 ++++++++ .../core/search/service/FitnessFunction.kt | 2 +- .../core/search/service/Minimizer.kt | 24 ++++++++++++++++++- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt index b8071dde43..9afe4d2296 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt @@ -83,6 +83,16 @@ class Archive where T : Individual { private var lastChosen: Int? = null + /** + * Kill all populations. + * This is meanly needed for minimization phase, in which archive needs to be cleared and + * tests re-added to it + */ + fun clearPopulations(){ + populations.clear() + } + + fun extractSolution(): Solution { val uniques = getUniquePopulation() diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/FitnessFunction.kt b/core/src/main/kotlin/org/evomaster/core/search/service/FitnessFunction.kt index fcaff4e2ca..002d6c3b2f 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/FitnessFunction.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/FitnessFunction.kt @@ -45,7 +45,7 @@ abstract class FitnessFunction where T : Individual { /** * @return null if there were problems in calculating the coverage */ - fun calculateCoverage(individual: T, targets: Set = setOf(), allCovered: Boolean = false) : EvaluatedIndividual?{ + fun calculateCoverage(individual: T, targets: Set = setOf()) : EvaluatedIndividual?{ val a = individual.seeMainExecutableActions().count() diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt index ba866e447c..4bc96caa4c 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt @@ -3,15 +3,24 @@ package org.evomaster.core.search.service import com.google.inject.Inject import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.search.Individual +import org.slf4j.Logger +import org.slf4j.LoggerFactory /** * Reduce/simplify the final test outputs */ class Minimizer { + companion object{ + private val log : Logger = LoggerFactory.getLogger(Minimizer::class.java) + } + @Inject private lateinit var archive: Archive + @Inject + private lateinit var fitness : FitnessFunction + /** * Based on the tests in the archive, update the archive by having, for each target T, * the minimum number of actions needed to cover it. @@ -28,10 +37,23 @@ class Minimizer { val beforeCovered = archive.numberOfCoveredTargets() + /* + Previously evaluated individual only had partial info, due to performance issues. + Need to make sure to fetch all coverage info. + */ + val population = current.mapNotNull { + val ei = fitness.computeWholeAchievedCoverageForPostProcessing(it) + if(ei == null){ + log.warn("Failed to re-evaluate individual during minimization") + } + ei + } + archive.clearPopulations() + population.forEach{archive.addIfNeeded(it)} - //TODO + TODO From 15c7f05ac4f1028a0b574b7ba367d394c5fea936 Mon Sep 17 00:00:00 2001 From: Juan Pablo Galeotti Date: Thu, 4 May 2023 09:17:14 -0300 Subject: [PATCH 008/345] @NotBlank implemented --- .../org/evomaster/core/database/DbActionGeneBuilder.kt | 6 +++++- .../kotlin/org/evomaster/core/database/SqlInsertBuilder.kt | 3 ++- .../kotlin/org/evomaster/core/database/schema/Column.kt | 4 +++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt b/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt index 860c960414..45fe8fd7a8 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt @@ -672,7 +672,11 @@ class DbActionGeneBuilder { val columnMinLength = if (isFixedLength) { column.size } else { - 0 + if (column.isNotBlank==true) { + 1 + } else { + 0 + } } StringGene(name = column.name, minLength = columnMinLength, maxLength = column.size) } diff --git a/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt b/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt index 82ab5d905c..cc134a2371 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt @@ -340,7 +340,8 @@ class SqlInsertBuilder( enumValuesAsStrings = mergedEnumValuesAsStrings, lowerBound = mergedLowerBound, upperBound = mergedUpperBound, - likePatterns = mergedLikePatterns + likePatterns = mergedLikePatterns, + isNotBlank = extra.constraints.isNotBlank ) } diff --git a/core/src/main/kotlin/org/evomaster/core/database/schema/Column.kt b/core/src/main/kotlin/org/evomaster/core/database/schema/Column.kt index 48d2f966d1..707204081e 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/schema/Column.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/schema/Column.kt @@ -50,10 +50,12 @@ data class Column( val compositeType: List? = null, - val compositeTypeName: String? = null + val compositeTypeName: String? = null, // public boolean identity; + val isNotBlank: Boolean? = null + //TODO something for other constraints ) \ No newline at end of file From fbd1f9d01f54812703063a4fef11f45c8ae447f7 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Wed, 10 May 2023 21:23:28 +0200 Subject: [PATCH 009/345] working on minimizer --- .../org/evomaster/core/search/service/Archive.kt | 9 +++++++++ .../org/evomaster/core/search/service/Minimizer.kt | 12 +++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt index 9afe4d2296..5a9c98d6a3 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt @@ -327,6 +327,15 @@ class Archive where T : Individual { return populations.keys.filter { !isCovered(it) }.toSet() } + /** + * Get all known targets that are fully covered + * + * @return a list of ids + */ + fun coveredTargets(): Set { + return populations.keys.filter { isCovered(it) }.toSet() + } + fun wouldReachNewTarget(ei: EvaluatedIndividual): Boolean { diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt index 4bc96caa4c..2cc8f89a6d 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt @@ -53,9 +53,19 @@ class Minimizer { population.forEach{archive.addIfNeeded(it)} - TODO + val afterCovered = archive.numberOfCoveredTargets() + + if(afterCovered < beforeCovered){ + //could happen if background threads, for example + LoggingUtil.getInfoLogger().warn("Recomputing coverage did lose some targets: from $beforeCovered to $afterCovered" + + ", i.e., lost ${beforeCovered-afterCovered}") + assert(false)//shouldn't really happen in the E2E... + } + val covered = archive.coveredTargets() + LoggingUtil.getInfoLogger().info("Analyzing ${covered.size} targets") + TODO } } \ No newline at end of file From 4396050634ddd008be24a84f22e602d7471cfaa3 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Fri, 12 May 2023 14:48:37 +0200 Subject: [PATCH 010/345] basic minimization --- .../enterprise/EnterpriseIndividual.kt | 4 + .../org/evomaster/core/search/Individual.kt | 10 ++ .../core/search/service/Minimizer.kt | 108 ++++++++++++++++-- .../core/search/service/SearchAlgorithm.kt | 4 +- .../search/service/SearchStatusUpdater.kt | 26 +++-- .../examples/branches/BranchesManualTest.java | 4 +- .../db/base/DbBaseTTIssueManualTest.java | 4 +- 7 files changed, 136 insertions(+), 24 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt index e6f7a09af8..f83ab274b7 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt @@ -136,6 +136,10 @@ abstract class EnterpriseIndividual( killChildByIndex(position) } + override fun removeMainExecutableAction(relativeIndex: Int){ + removeMainActionGroupAt(relativeIndex) + } + /** * return a list of all db actions in [this] individual * that include all initializing actions plus db actions among main actions. diff --git a/core/src/main/kotlin/org/evomaster/core/search/Individual.kt b/core/src/main/kotlin/org/evomaster/core/search/Individual.kt index e1dbd721ce..ae6f12e21d 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/Individual.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/Individual.kt @@ -215,6 +215,16 @@ abstract class Individual(override var trackOperator: TrackOperator? = null, return list } + /** + * Remove a main action, using relative index between 0 and this.size() + */ + open fun removeMainExecutableAction(relativeIndex: Int){ + if(seeInitializingActions().isNotEmpty()){ + throw IllegalStateException("For cases in which there are initializing actions, this method must be overridden") + } + killChildByIndex(relativeIndex) + } + /** * Return a view of all initializing actions done before the main * ones. Example: these could set up database before doing HTTP diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt index 2cc8f89a6d..a8f25edb99 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt @@ -1,8 +1,10 @@ package org.evomaster.core.search.service import com.google.inject.Inject +import org.evomaster.core.EMConfig import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.search.Individual +import org.evomaster.core.search.service.mutator.StructureMutator import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -21,6 +23,23 @@ class Minimizer { @Inject private lateinit var fitness : FitnessFunction + @Inject + private lateinit var config: EMConfig + + @Inject + private lateinit var mutator: StructureMutator + + fun pruneNonNeededDatabaseActions(){ + //TODO + } + + /** + * eg, removed un-needed optional parameters + */ + fun simplifyActions(){ + //TODO + } + /** * Based on the tests in the archive, update the archive by having, for each target T, * the minimum number of actions needed to cover it. @@ -29,11 +48,90 @@ class Minimizer { * * "EvoSuite: On The Challenges of Test Case Generation in the Real World" */ - fun minimizeActionsPerCoveredTargetInArchive(){ + fun minimizeMainActionsPerCoveredTargetInArchive() { + + LoggingUtil.getInfoLogger().info("Starting to apply minimization phase") + + recomputeArchiveWithFullCoverageInfo() val current = archive.getCopyOfUniqueCoveringIndividuals() + .filter { it.size() > 1 } //can't minimize below 1 - LoggingUtil.getInfoLogger().info("Starting to apply minimization phase on ${current.size} tests") + LoggingUtil.getInfoLogger().info("Analyzing ${current.size} tests with size greater than 1") + + val n = current.size + var k = 0; + + if(config.showProgress){ + println() + printProgress(k,n) + } + + current.forEach{ + //TODO could have a maximum timeout for the minimization phase, and stop minimization when timeout is exceed + + k++ + + val singles = splitIntoSingleCalls(it) + singles.forEach {s -> + fitness.computeWholeAchievedCoverageForPostProcessing(s)?.run { archive.addIfNeeded(this) } + } + + /* + Above works well if each action is independent. + To handle cases in which there is need of more than 1 action together, we need to remove each + action one at a time, and see if there is any improvement. For example, having size N, + remove from 0 to N, one at a time. If any of those is successful (ie addIfNeeded returns true), then + re-apply recursively to all the successful copies with length N-1. + + TODO implement such algorithm + */ + + printProgress(k,n) + } + } + + private fun splitIntoSingleCalls(ind: T) : List{ + if(ind.size() <= 1){ + throw IllegalArgumentException("Need at least 2 actions to apply split") + } + + val copy = ind.copy() + + val n = copy.size() + return (0 until n) + .map {index -> (copy.copy() as T) + .apply { removeAllMainActionsButIndex(this,index) } + } + } + + private fun removeAllMainActionsButIndex(ind: T, index: Int){ + + val n = ind.size() + + for(i in n-1 downTo index+1){ + ind.removeMainExecutableAction(i) + } + for(i in 0 until index){ + ind.removeMainExecutableAction(0) + } + } + + private fun printProgress(k: Int, n: Int){ + if(! config.showProgress){ + return + } + + //TODO check if need a time-delta for updates, as in SearchStatusUpdater + + SearchStatusUpdater.upLineAndErase() + println("Minimization progress: $k/$n") + } + + private fun recomputeArchiveWithFullCoverageInfo(){ + val current = archive.getCopyOfUniqueCoveringIndividuals() + + LoggingUtil.getInfoLogger().info("Recomputing full coverage for ${current.size} tests") val beforeCovered = archive.numberOfCoveredTargets() @@ -61,11 +159,5 @@ class Minimizer { ", i.e., lost ${beforeCovered-afterCovered}") assert(false)//shouldn't really happen in the E2E... } - - val covered = archive.coveredTargets() - LoggingUtil.getInfoLogger().info("Analyzing ${covered.size} targets") - - TODO - } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/SearchAlgorithm.kt b/core/src/main/kotlin/org/evomaster/core/search/service/SearchAlgorithm.kt index 7d36d96ba9..f22a86c07d 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/SearchAlgorithm.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/SearchAlgorithm.kt @@ -84,7 +84,9 @@ abstract class SearchAlgorithm where T : Individual { private fun handleAfterSearch() { if(config.minimize){ - minimizer.minimizeActionsPerCoveredTargetInArchive() + minimizer.minimizeMainActionsPerCoveredTargetInArchive() + minimizer.pruneNonNeededDatabaseActions() + minimizer.simplifyActions() } if(config.addPreDefinedTests) { diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/SearchStatusUpdater.kt b/core/src/main/kotlin/org/evomaster/core/search/service/SearchStatusUpdater.kt index bea9b16124..2389711be5 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/SearchStatusUpdater.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/SearchStatusUpdater.kt @@ -21,6 +21,21 @@ class SearchStatusUpdater : SearchListener{ private lateinit var archive: Archive<*> + companion object{ + fun eraseLine(){ + print("\u001b[2K") // erase line + } + + fun moveUp(){ + print("\u001b[1A") // move up by 1 line + } + + fun upLineAndErase(){ + moveUp() + eraseLine() + } + } + private var passed = "-1" private var lastUpdateMS = 0L @@ -118,16 +133,5 @@ class SearchStatusUpdater : SearchListener{ print("\u001b[s") */ - private fun eraseLine(){ - print("\u001b[2K") // erase line - } - private fun moveUp(){ - print("\u001b[1A") // move up by 1 line - } - - private fun upLineAndErase(){ - moveUp() - eraseLine() - } } \ No newline at end of file diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/branches/BranchesManualTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/branches/BranchesManualTest.java index 78bddccd16..aafa9e8f59 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/branches/BranchesManualTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/branches/BranchesManualTest.java @@ -37,7 +37,7 @@ public void reset(){ @Test public void test(){ - TestResultsDto dto = remoteController.getTestResults(Collections.emptySet(), true); + TestResultsDto dto = remoteController.getTestResults(Collections.emptySet(), true, false); assertEquals(0, dto.targets.size()); given().contentType(ContentType.JSON) @@ -48,7 +48,7 @@ public void test(){ .statusCode(200) .body("value", is(0)); - dto = remoteController.getTestResults(Collections.emptySet(), true); + dto = remoteController.getTestResults(Collections.emptySet(), true, false); assertTrue(dto.targets.size() > 0); List targetDescriptions = dto.targets.stream() diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/db/base/DbBaseTTIssueManualTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/db/base/DbBaseTTIssueManualTest.java index 68893a8680..0460359ca9 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/db/base/DbBaseTTIssueManualTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/db/base/DbBaseTTIssueManualTest.java @@ -46,7 +46,7 @@ public void testExtraHeuristics(){ .statusCode(404); //make sure that the SQL Extra Heuristics is computed - TestResultsDto result = remoteController.getTestResults(new HashSet<>(), true); + TestResultsDto result = remoteController.getTestResults(new HashSet<>(), true, false); assertFalse(result.extraHeuristics.isEmpty()); assertFalse(result.extraHeuristics.get(second.index).heuristics.isEmpty()); } @@ -79,7 +79,7 @@ public void testTaintEqualInSql(){ .statusCode(404); //make sure that the SQL Extra Heuristics is computed - TestResultsDto result = remoteController.getTestResults(new HashSet<>(), true); + TestResultsDto result = remoteController.getTestResults(new HashSet<>(), true, false); assertFalse(result.extraHeuristics.isEmpty()); assertFalse(result.extraHeuristics.get(second.index).heuristics.isEmpty()); From a82f35e89bfe6ec36e36f3906b20eeecff7e192c Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Fri, 12 May 2023 14:59:23 +0200 Subject: [PATCH 011/345] working on failing tests --- .../rest/service/AbstractRestFitness.kt | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestFitness.kt index b978292519..e975911d0c 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestFitness.kt @@ -768,14 +768,21 @@ abstract class AbstractRestFitness : HttpWsFitness() where T : Individual handleExternalServiceInfo(fv, dto.additionalInfoList) - if (config.expandRestIndividuals) { - expandIndividual(individual, dto.additionalInfoList, actionResults) - } + if(! allCovered) { + if (config.expandRestIndividuals) { + expandIndividual(individual, dto.additionalInfoList, actionResults) + } - if (config.isEnabledTaintAnalysis()) { - Lazy.assert { actionResults.size == dto.additionalInfoList.size } - //TODO add taint analysis for resource-based solution - TaintAnalysis.doTaintAnalysis(individual, dto.additionalInfoList, randomness, config.enableSchemaConstraintHandling) + if (config.isEnabledTaintAnalysis()) { + Lazy.assert { actionResults.size == dto.additionalInfoList.size } + //TODO add taint analysis for resource-based solution + TaintAnalysis.doTaintAnalysis( + individual, + dto.additionalInfoList, + randomness, + config.enableSchemaConstraintHandling + ) + } } return dto From b0c90da98e5e32074f6aca72aaff5c4bd2309512 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 15 May 2023 13:00:16 +0200 Subject: [PATCH 012/345] enabling LIKE for H2 --- .../org/evomaster/core/database/DbActionGeneBuilder.kt | 8 +++++++- .../kotlin/org/evomaster/core/database/schema/Column.kt | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt b/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt index b9c899d08c..7abb861159 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt @@ -682,10 +682,16 @@ class DbActionGeneBuilder { /** * Builds a RegexGene using a name and a list of LIKE patterns. * The resulting gene is a disjunction of the given patterns + * + * TODO need to handle NOT and ILIKE */ fun buildLikeRegexGene(geneName: String, likePatterns: List, databaseType: DatabaseType): RegexGene { return when (databaseType) { - DatabaseType.POSTGRES, DatabaseType.MYSQL -> buildPostgresMySQLLikeRegexGene(geneName, likePatterns) + DatabaseType.POSTGRES, //https://www.postgresqltutorial.com/postgresql-tutorial/postgresql-like/ + DatabaseType.H2, // http://www.h2database.com/html/grammar.html#like_predicate_right_hand_side + DatabaseType.MYSQL -> { + buildPostgresMySQLLikeRegexGene(geneName, likePatterns) + } //TODO: support other database SIMILAR_TO check expressions else -> throw UnsupportedOperationException( "Must implement LIKE expressions for database %s".format( diff --git a/core/src/main/kotlin/org/evomaster/core/database/schema/Column.kt b/core/src/main/kotlin/org/evomaster/core/database/schema/Column.kt index 48d2f966d1..3d9170f41e 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/schema/Column.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/schema/Column.kt @@ -32,6 +32,9 @@ data class Column( val similarToPatterns: List? = null, + /** + * FIXME: could have NOT (ie negation) as well as ignore case (ILIKE) + */ val likePatterns: List? = null, val databaseType: DatabaseType, From 9c803899985c05015cbd036f8d2c0db8b80776ef Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 15 May 2023 15:01:14 +0200 Subject: [PATCH 013/345] fixed issue in minimizer --- .../org/evomaster/core/search/Individual.kt | 2 ++ .../core/search/service/Minimizer.kt | 35 +++++++++++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/search/Individual.kt b/core/src/main/kotlin/org/evomaster/core/search/Individual.kt index ae6f12e21d..2b5d341a8f 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/Individual.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/Individual.kt @@ -221,7 +221,9 @@ abstract class Individual(override var trackOperator: TrackOperator? = null, open fun removeMainExecutableAction(relativeIndex: Int){ if(seeInitializingActions().isNotEmpty()){ throw IllegalStateException("For cases in which there are initializing actions, this method must be overridden") + //also MUST be overwritten if direct children might have subtrees with more than one main action, like in case of RestResource } + //if there is no init action, then the relativeIndex is an actual index killChildByIndex(relativeIndex) } diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt index a8f25edb99..ae904e0205 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt @@ -3,6 +3,9 @@ package org.evomaster.core.search.service import com.google.inject.Inject import org.evomaster.core.EMConfig import org.evomaster.core.logging.LoggingUtil +import org.evomaster.core.problem.gui.GuiIndividual +import org.evomaster.core.problem.rest.RestIndividual +import org.evomaster.core.search.GroupsOfChildren import org.evomaster.core.search.Individual import org.evomaster.core.search.service.mutator.StructureMutator import org.slf4j.Logger @@ -55,7 +58,10 @@ class Minimizer { recomputeArchiveWithFullCoverageInfo() val current = archive.getCopyOfUniqueCoveringIndividuals() - .filter { it.size() > 1 } //can't minimize below 1 + .filter { + //it.size() > 1 // FIXME, see issue described below + it.groupsView()!!.sizeOfGroup(GroupsOfChildren.MAIN) > 1 + } //can't minimize below 1 LoggingUtil.getInfoLogger().info("Analyzing ${current.size} tests with size greater than 1") @@ -72,9 +78,11 @@ class Minimizer { k++ - val singles = splitIntoSingleCalls(it) - singles.forEach {s -> - fitness.computeWholeAchievedCoverageForPostProcessing(s)?.run { archive.addIfNeeded(this) } + if(it !is GuiIndividual) { //doesn't make sense for GUI sequences, as strongly dependent + val singles = splitIntoSingleCalls(it) + singles.forEach { s -> + fitness.computeWholeAchievedCoverageForPostProcessing(s)?.run { archive.addIfNeeded(this) } + } } /* @@ -92,13 +100,28 @@ class Minimizer { } private fun splitIntoSingleCalls(ind: T) : List{ - if(ind.size() <= 1){ + + val n = ind.groupsView()!!.sizeOfGroup(GroupsOfChildren.MAIN) + //val n = ind.size() + /* + FIXME: we currently have a rather major limitation, has in REST we have group of calls + related to same resources, and cannot currently delete single calls with messing up lot of things... + We need to do some major refactoring. + + Man comments: + there might be two options: + 1) re-construct RestResourceCalls , e.g., three actions, A-B-C, remove B, then construct the resources with A and C, + 2) remove the resource only if all actions are reductant + there might be a problematic regarding value binding, then you can remove all binding before the minimization phase, since it is last one + see replaceResourceCall in RestIndividual , it can use for option 1. removeResourceCall can be used to remove resource, eg, option 2 + replaceResourceCall and removeResourceCall have handled the binding, should be fine. + */ + if(n <= 1){ throw IllegalArgumentException("Need at least 2 actions to apply split") } val copy = ind.copy() - val n = copy.size() return (0 until n) .map {index -> (copy.copy() as T) .apply { removeAllMainActionsButIndex(this,index) } From 709ce049fb844658b1b10d3986c60e93405f4137 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 15 May 2023 15:52:29 +0200 Subject: [PATCH 014/345] setting out timeout for minimization --- .../core/problem/rest/SamplerVerifierTest.kt | 2 +- .../kotlin/org/evomaster/core/EMConfig.kt | 5 +++ .../core/search/service/Minimizer.kt | 42 ++++++++++++++++++- .../core/search/service/SearchAlgorithm.kt | 4 ++ .../db/base/DbBaseTTIssueManualTest.java | 4 +- 5 files changed, 52 insertions(+), 5 deletions(-) diff --git a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/SamplerVerifierTest.kt b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/SamplerVerifierTest.kt index 62e47b79c8..e8eda45aa7 100644 --- a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/SamplerVerifierTest.kt +++ b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/SamplerVerifierTest.kt @@ -397,7 +397,7 @@ class SamplerVerifierTest { return true } - override fun getTestResults(ids: Set, ignoreKillSwitch: Boolean): TestResultsDto? { + override fun getTestResults(ids: Set, ignoreKillSwitch: Boolean, allCovered: Boolean): TestResultsDto? { return null } diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index 8a83e6986d..3bf73f69b7 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -1733,6 +1733,11 @@ class EMConfig { var minimize : Boolean = true + @Cfg("Maximum number of minutes that will be dedicated to the minimization phase." + + " A negative number mean no timeout is considered." + + " A value of 0 means minimization will be skipped, even if minimize=true.") + var minimizeTimeout = 5 + @Experimental @FilePath(true) @Regex("(.*jacoco.*\\.jar)|(^$)") diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt index ae904e0205..f5a9d51b58 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt @@ -4,7 +4,6 @@ import com.google.inject.Inject import org.evomaster.core.EMConfig import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.problem.gui.GuiIndividual -import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.search.GroupsOfChildren import org.evomaster.core.search.Individual import org.evomaster.core.search.service.mutator.StructureMutator @@ -32,6 +31,36 @@ class Minimizer { @Inject private lateinit var mutator: StructureMutator + + private var startTimer : Long = -1 + + + fun doStartTheTimer(){ + startTimer = System.currentTimeMillis() + } + + fun passedTimeInSecond() : Int { + if(startTimer < 0){ + throw IllegalStateException("Timer was not started") + } + return ((System.currentTimeMillis() - startTimer) / 1000).toInt() + } + + fun checkHasTimedout() : Boolean{ + if(startTimer < 0){ + throw IllegalStateException("Timer was not started") + } + if(config.minimizeTimeout < 0){ + return false + } + if(config.minimizeTimeout == 0){ + return true + } + val current = System.currentTimeMillis() + val passed = (current - startTimer) / (1000 * 60.0) + return passed >- config.minimizeTimeout + } + fun pruneNonNeededDatabaseActions(){ //TODO } @@ -53,6 +82,11 @@ class Minimizer { */ fun minimizeMainActionsPerCoveredTargetInArchive() { + if(checkHasTimedout()){ + LoggingUtil.getInfoLogger().warn("Minimization phase has timed-out. You can use --minimizeTimeout to increase it.") + return + } + LoggingUtil.getInfoLogger().info("Starting to apply minimization phase") recomputeArchiveWithFullCoverageInfo() @@ -74,7 +108,11 @@ class Minimizer { } current.forEach{ - //TODO could have a maximum timeout for the minimization phase, and stop minimization when timeout is exceed + + if(checkHasTimedout()){ + LoggingUtil.getInfoLogger().warn("Minimization phase has timed-out. You can use --minimizeTimeout to increase it.") + return + } k++ diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/SearchAlgorithm.kt b/core/src/main/kotlin/org/evomaster/core/search/service/SearchAlgorithm.kt index f22a86c07d..d5656e0995 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/SearchAlgorithm.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/SearchAlgorithm.kt @@ -2,6 +2,7 @@ package org.evomaster.core.search.service import com.google.inject.Inject import org.evomaster.core.EMConfig +import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.search.Individual import org.evomaster.core.search.Solution import org.evomaster.core.search.service.mutator.Mutator @@ -84,9 +85,12 @@ abstract class SearchAlgorithm where T : Individual { private fun handleAfterSearch() { if(config.minimize){ + minimizer.doStartTheTimer() minimizer.minimizeMainActionsPerCoveredTargetInArchive() minimizer.pruneNonNeededDatabaseActions() minimizer.simplifyActions() + val seconds = minimizer.passedTimeInSecond() + LoggingUtil.getInfoLogger().info("Minimization phase took $seconds seconds") } if(config.addPreDefinedTests) { diff --git a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/db/base/DbBaseTTIssueManualTest.java b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/db/base/DbBaseTTIssueManualTest.java index 718d2b0fef..b7d373d957 100644 --- a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/db/base/DbBaseTTIssueManualTest.java +++ b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/db/base/DbBaseTTIssueManualTest.java @@ -46,7 +46,7 @@ public void testExtraHeuristics(){ .statusCode(404); //make sure that the SQL Extra Heuristics is computed - TestResultsDto result = remoteController.getTestResults(new HashSet<>(), true); + TestResultsDto result = remoteController.getTestResults(new HashSet<>(), true, false); assertNotNull(result); assertFalse(result.extraHeuristics.isEmpty()); assertFalse(result.extraHeuristics.get(second.index).heuristics.isEmpty()); @@ -80,7 +80,7 @@ public void testTaintEqualInSql(){ .statusCode(404); //make sure that the SQL Extra Heuristics is computed - TestResultsDto result = remoteController.getTestResults(new HashSet<>(), true); + TestResultsDto result = remoteController.getTestResults(new HashSet<>(), true, false); assertNotNull(result); assertFalse(result.extraHeuristics.isEmpty()); assertFalse(result.extraHeuristics.get(second.index).heuristics.isEmpty()); From 07d740708a5c44aafedc431077bf5d3673306b49 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 15 May 2023 16:04:19 +0200 Subject: [PATCH 015/345] one bug down, more to go --- .../main/kotlin/org/evomaster/core/search/service/Minimizer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt index f5a9d51b58..3b99a50f1d 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt @@ -58,7 +58,7 @@ class Minimizer { } val current = System.currentTimeMillis() val passed = (current - startTimer) / (1000 * 60.0) - return passed >- config.minimizeTimeout + return passed > config.minimizeTimeout } fun pruneNonNeededDatabaseActions(){ From b7e57054f1391eb955bd82a61fb5992cd96497c9 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 15 May 2023 21:58:02 +0200 Subject: [PATCH 016/345] fixing issue with missing data --- .../client/java/controller/internal/EMController.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java index 15f10969be..b2e924ae69 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java @@ -555,6 +555,16 @@ public Response getTestResults( SimpleLogger.error(msg); return Response.status(500).entity(WrappedResponseDto.withError(msg)).build(); } + } else { + // there is still some data that we need during minimization + List additionalInfos = noKillSwitch(() -> sutController.getAdditionalInfoList()); + if (additionalInfos != null) { + additionalInfos.forEach(a -> { + AdditionalInfoDto info = new AdditionalInfoDto(); + info.lastExecutedStatement = a.getLastExecutedStatement(); + dto.additionalInfoList.add(info); + }); + } } if (killSwitch) { From ab30e1078acaf9be21bcc21ec8dac6d6649d7aa5 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Tue, 16 May 2023 09:29:34 +0200 Subject: [PATCH 017/345] fix for failing tests --- .../core/search/service/Minimizer.kt | 42 +++++++++++-------- .../search/algorithms/onemax/OneMaxFitness.kt | 2 +- .../algorithms/onemax/OneMaxIndividual.kt | 7 ++-- .../service/mutator/MutatorWithOneMaxTest.kt | 2 +- docs/options.md | 1 + 5 files changed, 30 insertions(+), 24 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt index 3b99a50f1d..ae855f466b 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt @@ -46,7 +46,7 @@ class Minimizer { return ((System.currentTimeMillis() - startTimer) / 1000).toInt() } - fun checkHasTimedout() : Boolean{ + private fun checkHasTimedout() : Boolean{ if(startTimer < 0){ throw IllegalStateException("Timer was not started") } @@ -93,8 +93,7 @@ class Minimizer { val current = archive.getCopyOfUniqueCoveringIndividuals() .filter { - //it.size() > 1 // FIXME, see issue described below - it.groupsView()!!.sizeOfGroup(GroupsOfChildren.MAIN) > 1 + getSize(it) > 1 } //can't minimize below 1 LoggingUtil.getInfoLogger().info("Analyzing ${current.size} tests with size greater than 1") @@ -137,23 +136,30 @@ class Minimizer { } } + private fun getSize(ind: T) : Int{ + /* + FIXME: we currently have a rather major limitation, has in REST we have group of calls + related to same resources, and cannot currently delete single calls with messing up lot of things... + We need to do some major refactoring. + + Man comments: + there might be two options: + 1) re-construct RestResourceCalls , e.g., three actions, A-B-C, remove B, then construct the resources with A and C, + 2) remove the resource only if all actions are reductant + there might be a problematic regarding value binding, then you can remove all binding before the minimization phase, since it is last one + see replaceResourceCall in RestIndividual , it can use for option 1. removeResourceCall can be used to remove resource, eg, option 2 + replaceResourceCall and removeResourceCall have handled the binding, should be fine. + + TODO a further problem is that, for some custom tests, we have no group definitions + */ + return ind.groupsView()?.sizeOfGroup(GroupsOfChildren.MAIN) + ?: ind.size() + } + private fun splitIntoSingleCalls(ind: T) : List{ - val n = ind.groupsView()!!.sizeOfGroup(GroupsOfChildren.MAIN) - //val n = ind.size() - /* - FIXME: we currently have a rather major limitation, has in REST we have group of calls - related to same resources, and cannot currently delete single calls with messing up lot of things... - We need to do some major refactoring. - - Man comments: - there might be two options: - 1) re-construct RestResourceCalls , e.g., three actions, A-B-C, remove B, then construct the resources with A and C, - 2) remove the resource only if all actions are reductant - there might be a problematic regarding value binding, then you can remove all binding before the minimization phase, since it is last one - see replaceResourceCall in RestIndividual , it can use for option 1. removeResourceCall can be used to remove resource, eg, option 2 - replaceResourceCall and removeResourceCall have handled the binding, should be fine. - */ + val n = getSize(ind) + if(n <= 1){ throw IllegalArgumentException("Need at least 2 actions to apply split") } diff --git a/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxFitness.kt b/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxFitness.kt index 1069ebcccd..4b325c74d4 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxFitness.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxFitness.kt @@ -14,7 +14,7 @@ class OneMaxFitness : FitnessFunction() { val fv = FitnessValue(individual.size().toDouble()) - targetsToEvaluate(targets, individual) + (if(allCovered) (0 until individual.n).toSet() else targetsToEvaluate(targets, individual)) .forEach { fv.updateTarget(it, individual.getValue(it)) } return EvaluatedIndividual( diff --git a/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxIndividual.kt b/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxIndividual.kt index 6bac5a368d..6bbccaaf76 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxIndividual.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxIndividual.kt @@ -1,6 +1,5 @@ package org.evomaster.core.search.algorithms.onemax -import org.evomaster.core.search.Action import org.evomaster.core.search.Individual import org.evomaster.core.search.gene.collection.EnumGene import org.evomaster.core.search.gene.Gene @@ -12,13 +11,13 @@ class OneMaxIndividual( val n : Int, trackOperator: TrackOperator? = null, index : Int = -1, - action: OneMaxAction = createAction(n) + action: OneMaxAction = createGenes(n) ): Individual (trackOperator, index, mutableListOf(action),{ k -> OneMaxAction::class.java.isAssignableFrom(k)}) { companion object { - fun createAction(n: Int): OneMaxAction { + fun createGenes(n: Int): OneMaxAction { val list = mutableListOf>() (0 until n).forEach { val gene = EnumGene("$it", listOf(0.0, 0.25, 0.5, 0.75, 1.0), 0) @@ -68,7 +67,7 @@ class OneMaxIndividual( } override fun size() : Int { - return n + return 1 } diff --git a/core/src/test/kotlin/org/evomaster/core/search/service/mutator/MutatorWithOneMaxTest.kt b/core/src/test/kotlin/org/evomaster/core/search/service/mutator/MutatorWithOneMaxTest.kt index ee6cd25bcd..81eb35e0cd 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/service/mutator/MutatorWithOneMaxTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/service/mutator/MutatorWithOneMaxTest.kt @@ -27,7 +27,7 @@ class MutatorWithOneMaxTest { fun init(){ injector = LifecycleInjector.builder() - .withModules(ManipulatedOneMaxModule(), BaseModule()) + .withModules(ManipulatedOneMaxModule(), BaseModule(arrayOf("--seed=42"))) .build().createInjector() manager = injector.getInstance(LifecycleManager::class.java) diff --git a/docs/options.md b/docs/options.md index f6f798b199..95707b7c62 100644 --- a/docs/options.md +++ b/docs/options.md @@ -99,6 +99,7 @@ There are 3 types of options: |`maxTimeInSeconds`| __Int__. Maximum number of seconds allowed for the search. The more time is allowed, the better results one can expect. But then of course the test generation will take longer. Only applicable depending on the stopping criterion. If this value is 0, the setting 'maxTime' will be used instead. *Constraints*: `min=0.0`. *Default value*: `0`.| |`maxlengthOfHistoryForAGM`| __Int__. Specify a maximum length of history when applying archive-based gene mutation. *Default value*: `10`.| |`minimize`| __Boolean__. Apply a minimization phase to make the generated tests more readable. Achieved coverage would stay the same. Generating shorter test cases might come at the cost of having more test cases. *Default value*: `true`.| +|`minimizeTimeout`| __Int__. Maximum number of minutes that will be dedicated to the minimization phase. A negative number mean no timeout is considered. A value of 0 means minimization will be skipped, even if minimize=true. *Default value*: `5`.| |`minimumSizeControl`| __Int__. Specify minimum size when bloatControlForSecondaryObjective. *Constraints*: `min=0.0`. *Default value*: `2`.| |`populationSize`| __Int__. Define the population size in the search algorithms that use populations (e.g., Genetic Algorithms, but not MIO). *Constraints*: `min=1.0`. *Default value*: `30`.| |`probOfApplySQLActionToCreateResources`| __Double__. Specify a probability to apply SQL actions for preparing resources for REST Action. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.5`.| From 44e52be91410ea8a9d4e26fbc23c11da6e1b3374 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Tue, 16 May 2023 12:25:45 +0200 Subject: [PATCH 018/345] more fixes --- .../problem/rest/service/ResourceRestModule.kt | 11 +++++++++++ .../core/problem/rest/service/RestModule.kt | 4 ++++ .../evomaster/core/search/service/Minimizer.kt | 7 ++++++- .../search/algorithms/onemax/OneMaxIndividual.kt | 7 +++++++ .../evomaster/core/search/service/ArchiveTest.kt | 16 ++++++++-------- .../core/search/service/ProcessMonitorTest.kt | 3 +-- .../track/MioAlgorithmOnTrackOneMaxTest.kt | 1 + 7 files changed, 38 insertions(+), 11 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceRestModule.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceRestModule.kt index 9f587cfd7d..857cd49bf4 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceRestModule.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceRestModule.kt @@ -12,6 +12,7 @@ import org.evomaster.core.remote.service.RemoteController import org.evomaster.core.remote.service.RemoteControllerImplementation import org.evomaster.core.search.service.Archive import org.evomaster.core.search.service.FitnessFunction +import org.evomaster.core.search.service.Minimizer import org.evomaster.core.search.service.Sampler import org.evomaster.core.search.service.mutator.Mutator import org.evomaster.core.search.service.mutator.StandardMutator @@ -50,10 +51,20 @@ class ResourceRestModule(private val bindRemote : Boolean = true) : AbstractModu .to(RestResourceFitness::class.java) .asEagerSingleton() + bind(object : TypeLiteral>() {}) + .to(RestResourceFitness::class.java) + .asEagerSingleton() + bind(object : TypeLiteral>() {}) .to(RestResourceFitness::class.java) .asEagerSingleton() + bind(object : TypeLiteral>(){}) + .asEagerSingleton() + + bind(object : TypeLiteral>(){}) + .asEagerSingleton() + bind(object : TypeLiteral>() {}) .asEagerSingleton() diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestModule.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestModule.kt index 6abb6e0962..36a9e4c071 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestModule.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestModule.kt @@ -44,6 +44,10 @@ class RestModule(private val bindRemote : Boolean = true) : AbstractModule(){ .to(RestFitness::class.java) .asEagerSingleton() + bind(object : TypeLiteral>() {}) + .to(RestFitness::class.java) + .asEagerSingleton() + bind(object : TypeLiteral>() {}) .asEagerSingleton() diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt index ae855f466b..5c16b8a835 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt @@ -96,6 +96,11 @@ class Minimizer { getSize(it) > 1 } //can't minimize below 1 + if(current.isEmpty()){ + LoggingUtil.getInfoLogger().info("No test to minimize") + return + } + LoggingUtil.getInfoLogger().info("Analyzing ${current.size} tests with size greater than 1") val n = current.size @@ -192,7 +197,7 @@ class Minimizer { //TODO check if need a time-delta for updates, as in SearchStatusUpdater SearchStatusUpdater.upLineAndErase() - println("Minimization progress: $k/$n") + println("* Minimization progress: $k/$n") } private fun recomputeArchiveWithFullCoverageInfo(){ diff --git a/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxIndividual.kt b/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxIndividual.kt index 6bbccaaf76..6c61155842 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxIndividual.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxIndividual.kt @@ -67,6 +67,13 @@ class OneMaxIndividual( } override fun size() : Int { + /* + there is only 1 single action in the individual. + in the past, we used N as size, but that screw up a lot of assumptions in the current + behavior of the search. + As OneMax is only used in the tests, we fix it, although many unit tests might still refer + to the old behaviour + */ return 1 } diff --git a/core/src/test/kotlin/org/evomaster/core/search/service/ArchiveTest.kt b/core/src/test/kotlin/org/evomaster/core/search/service/ArchiveTest.kt index 240fac2f33..74cbe8775d 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/service/ArchiveTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/service/ArchiveTest.kt @@ -248,7 +248,7 @@ class ArchiveTest{ .distinct() .count() - assertEquals(2, n) + assertEquals(1, n) //reduce buffer size, so "a" should disappear when sampling, as longer config.archiveTargetLimit = 1 @@ -297,9 +297,9 @@ class ArchiveTest{ /* Man: need to check with Andrea */ - assertEquals(2, sizes.size) - assertTrue(sizes.contains(1)) - assertTrue(sizes.contains(2)) + assertEquals(1, sizes.size) +// assertTrue(sizes.contains(1)) +// assertTrue(sizes.contains(2)) } @Test @@ -353,7 +353,7 @@ class ArchiveTest{ .distinct() assertEquals(1, sizes.size) - assertEquals(2, sizes.first()) + assertEquals(1, sizes.first()) //shorter, so should replace "a" val c = OneMaxIndividual(1) @@ -388,7 +388,7 @@ class ArchiveTest{ .distinct() assertEquals(1, sizes.size) - assertEquals(2, sizes.first()) + assertEquals(1, sizes.first()) //better fitness, but still longer on any of those targets val c = OneMaxIndividual(3) @@ -402,7 +402,7 @@ class ArchiveTest{ .distinct() assertEquals(1, sizes.size) - assertEquals(2, sizes.first()) + assertEquals(1, sizes.first()) //same length, but highest overall fitness @@ -456,7 +456,7 @@ class ArchiveTest{ .distinct() assertEquals(1, sizes.size) - assertEquals(2, sizes.first()) + assertEquals(1, sizes.first()) //now size is always 1, not depending on n (0 until 50).forEach { _ -> val b = OneMaxIndividual(2) diff --git a/core/src/test/kotlin/org/evomaster/core/search/service/ProcessMonitorTest.kt b/core/src/test/kotlin/org/evomaster/core/search/service/ProcessMonitorTest.kt index c9af098e11..71a85ff1e8 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/service/ProcessMonitorTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/service/ProcessMonitorTest.kt @@ -50,7 +50,7 @@ class ProcessMonitorTest{ config.stoppingCriterion = EMConfig.StoppingCriterion.FITNESS_EVALUATIONS config.processFormat = EMConfig.ProcessDataFormat.JSON_ALL config.useTimeInFeedbackSampling = false - + config.minimize = false } @@ -173,7 +173,6 @@ class ProcessMonitorTest{ val addedA = archive.addIfNeeded(evalA) assert(addedA) - assertEquals(1, archive.getSnapshotOfBestIndividuals().size) val b = OneMaxIndividual(2) TestUtils.doInitializeIndividualForTesting(b,randomness) diff --git a/core/src/test/kotlin/org/evomaster/core/search/service/track/MioAlgorithmOnTrackOneMaxTest.kt b/core/src/test/kotlin/org/evomaster/core/search/service/track/MioAlgorithmOnTrackOneMaxTest.kt index e8eecbfdfd..ef5e4a2a68 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/service/track/MioAlgorithmOnTrackOneMaxTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/service/track/MioAlgorithmOnTrackOneMaxTest.kt @@ -37,6 +37,7 @@ class MioAlgorithmOnTrackOneMaxTest { object : TypeLiteral>() {})) config = injector.getInstance(EMConfig::class.java) + config.minimize = false tracker = injector.getInstance(ArchiveMutationTrackService::class.java) From 639600c8a3d17b449bd2a1f4781c774fab829446 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Tue, 16 May 2023 13:00:10 +0200 Subject: [PATCH 019/345] fixed bug --- .../main/kotlin/org/evomaster/core/search/service/Minimizer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt index 5c16b8a835..536ad99cf9 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt @@ -179,7 +179,7 @@ class Minimizer { private fun removeAllMainActionsButIndex(ind: T, index: Int){ - val n = ind.size() + val n = getSize(ind) for(i in n-1 downTo index+1){ ind.removeMainExecutableAction(i) From 3fd66d6277ae6ffb485c570e2946b5cc894dd399 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Tue, 16 May 2023 14:14:40 +0200 Subject: [PATCH 020/345] calling Winston Wolfe when killing children --- .../evomaster/core/search/StructuralElement.kt | 17 +++++++++++++++++ .../org/evomaster/core/search/gene/Gene.kt | 6 ++++++ 2 files changed, 23 insertions(+) diff --git a/core/src/main/kotlin/org/evomaster/core/search/StructuralElement.kt b/core/src/main/kotlin/org/evomaster/core/search/StructuralElement.kt index f1e06a6778..2eedab6279 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/StructuralElement.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/StructuralElement.kt @@ -220,8 +220,22 @@ abstract class StructuralElement ( (children as MutableList).addAll(position, list) } + + /** + * Subclasses overriding this will need to call super method. + * + * Make sure that after we kill children, we do not leave a mess (eg dangling cross-tree dependencies) + */ + open fun callWinstonWolfe(){ + children.forEach{it.callWinstonWolfe()} + } + //https://preview.redd.it/hg27vjl7x0241.jpg?auto=webp&s=d3c8b5d2cfbf12a05715271e0cf7f1c26e962827 open fun killAllChildren(){ + + //we are prepared... we call Winston Wolfe before the "mess"... + callWinstonWolfe() + children.forEach { it.parent = null; //let's avoid memory leaks } @@ -250,6 +264,9 @@ abstract class StructuralElement ( open fun killChildByIndex(index: Int) : StructuralElement{ val groupId = groups?.groupForChild(index)?.id val child = children.removeAt(index) + + child.callWinstonWolfe() + child.parent = null groups?.removedFromGroup(groupId!!) return child diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/Gene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/Gene.kt index 34697a6057..f8865f14f1 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/Gene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/Gene.kt @@ -351,6 +351,12 @@ abstract class Gene( return checkForGloballyValid() && getViewOfChildren().all { it.isGloballyValid() } } + override fun callWinstonWolfe() { + super.callWinstonWolfe() + + removeThisFromItsBindingGenes() + //TODO in future will deal with FK as well here + } protected open fun checkForGloballyValid() = true From 45d2b229d3272fd626dc199caaaf66edfca2b41f Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Tue, 16 May 2023 15:36:32 +0200 Subject: [PATCH 021/345] E2E for minimization --- .../kotlin/org/evomaster/core/EMConfig.kt | 3 ++ .../service/ResourceSampleMethodController.kt | 2 +- .../core/search/service/SearchAlgorithm.kt | 4 ++ .../search/service/SearchStatusUpdater.kt | 5 ++ docs/options.md | 1 + .../v3/minimize/MinimizeApplication.kt | 20 ++++++++ .../openapi/v3/minimize/MinimizeRest.kt | 25 ++++++++++ .../openapi/v3/minimize/MinimizeController.kt | 6 +++ .../openapi/v3/minimize/MinimizeEMTest.kt | 49 +++++++++++++++++++ 9 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/minimize/MinimizeApplication.kt create mode 100644 e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/minimize/MinimizeRest.kt create mode 100644 e2e-tests/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/minimize/MinimizeController.kt create mode 100644 e2e-tests/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/minimize/MinimizeEMTest.kt diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index fdbaa05cb5..1d5e0f9da3 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -1036,6 +1036,9 @@ class EMConfig { @Min(1.0) var maxTestSize = 10 + @Cfg("Based on some heuristics, there are cases in which 'maxTestSize' can be overridden at runtime") + var enableOptimizedTestSize = true + @Cfg("Tracking of SQL commands to improve test generation") var heuristicsForSQL = true diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampleMethodController.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampleMethodController.kt index ae15fe965a..cbc00eb631 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampleMethodController.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampleMethodController.kt @@ -53,7 +53,7 @@ class ResourceSampleMethodController { /* if only S1iR is applicable, we recommend that maxTestSize is 1. */ - if(methods.values.filter { it.applicable }.size == 1 && methods.getValue(S1iR).applicable) + if(config.enableOptimizedTestSize && methods.values.filter { it.applicable }.size == 1 && methods.getValue(S1iR).applicable) config.maxTestSize = 1 } diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/SearchAlgorithm.kt b/core/src/main/kotlin/org/evomaster/core/search/service/SearchAlgorithm.kt index d5656e0995..0b50d03f2f 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/SearchAlgorithm.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/SearchAlgorithm.kt @@ -38,6 +38,8 @@ abstract class SearchAlgorithm where T : Individual { @Inject private lateinit var minimizer: Minimizer + @Inject + private lateinit var ssu: SearchStatusUpdater private var lastSnapshot = 0 @@ -84,6 +86,8 @@ abstract class SearchAlgorithm where T : Individual { private fun handleAfterSearch() { + ssu.enabled = false + if(config.minimize){ minimizer.doStartTheTimer() minimizer.minimizeMainActionsPerCoveredTargetInArchive() diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/SearchStatusUpdater.kt b/core/src/main/kotlin/org/evomaster/core/search/service/SearchStatusUpdater.kt index 2389711be5..236c909aaf 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/SearchStatusUpdater.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/SearchStatusUpdater.kt @@ -20,6 +20,7 @@ class SearchStatusUpdater : SearchListener{ @Inject private lateinit var archive: Archive<*> + var enabled = true companion object{ fun eraseLine(){ @@ -70,6 +71,10 @@ class SearchStatusUpdater : SearchListener{ override fun newActionEvaluated() { + if(!enabled){ + return + } + val percentageInt = (time.percentageUsedBudget() * 100).toInt() val current = String.format("%.3f", time.percentageUsedBudget() * 100) diff --git a/docs/options.md b/docs/options.md index 010b43654a..7c8199d44b 100644 --- a/docs/options.md +++ b/docs/options.md @@ -67,6 +67,7 @@ There are 3 types of options: |`doesApplyNameMatching`| __Boolean__. Whether to apply text/name analysis to derive relationships between name entities, e.g., a resource identifier with a name of table. *Default value*: `true`.| |`e_u1f984`| __Boolean__. QWN0aXZhdGUgdGhlIFVuaWNvcm4gTW9kZQ==. *Default value*: `false`.| |`enableBasicAssertions`| __Boolean__. Generate basic assertions. Basic assertions (comparing the returned object to itself) are added to the code. NOTE: this should not cause any tests to fail. *Default value*: `true`.| +|`enableOptimizedTestSize`| __Boolean__. Based on some heuristics, there are cases in which 'maxTestSize' can be overridden at runtime. *Default value*: `true`.| |`enableTrackEvaluatedIndividual`| __Boolean__. Whether to enable tracking the history of modifications of the individuals with its fitness values (i.e., evaluated individual) during the search. Note that we enforced that set enableTrackIndividual false when enableTrackEvaluatedIndividual is true since information of individual is part of evaluated individual. *Default value*: `true`.| |`enableWeightBasedMutationRateSelectionForGene`| __Boolean__. Specify whether to enable weight-based mutation selection for selecting genes to mutate for a gene. *Default value*: `true`.| |`endNumberOfMutations`| __Int__. Number of applied mutations on sampled individuals, by the end of the search. *Constraints*: `min=0.0`. *Default value*: `10`.| diff --git a/e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/minimize/MinimizeApplication.kt b/e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/minimize/MinimizeApplication.kt new file mode 100644 index 0000000000..fa580bac18 --- /dev/null +++ b/e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/minimize/MinimizeApplication.kt @@ -0,0 +1,20 @@ +package com.foo.rest.examples.spring.openapi.v3.minimize + +import org.springframework.boot.SpringApplication +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration +import org.springframework.context.annotation.ComponentScan +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestMapping + +@SpringBootApplication(exclude = [SecurityAutoConfiguration::class]) +open class MinimizeApplication { + companion object { + @JvmStatic + fun main(args: Array) { + SpringApplication.run(MinimizeApplication::class.java, *args) + } + } +} \ No newline at end of file diff --git a/e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/minimize/MinimizeRest.kt b/e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/minimize/MinimizeRest.kt new file mode 100644 index 0000000000..e8a5784b93 --- /dev/null +++ b/e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/minimize/MinimizeRest.kt @@ -0,0 +1,25 @@ +package com.foo.rest.examples.spring.openapi.v3.minimize + +import org.springframework.http.ResponseEntity +import org.springframework.stereotype.Controller +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping(path = ["/api/minimize"]) +class MinimizeRest { + + + @GetMapping(path = ["/{x}"]) + open fun get( @PathVariable("x") x: Int) : ResponseEntity { + + if(x < 0) return ResponseEntity.ok("a") + if(x < 100) return ResponseEntity.ok("b") + if(x < 1_000) return ResponseEntity.ok("c") + if(x < 10_000) return ResponseEntity.ok("d") + + return return ResponseEntity.ok("e") + } +} \ No newline at end of file diff --git a/e2e-tests/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/minimize/MinimizeController.kt b/e2e-tests/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/minimize/MinimizeController.kt new file mode 100644 index 0000000000..cdb1b3f072 --- /dev/null +++ b/e2e-tests/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/minimize/MinimizeController.kt @@ -0,0 +1,6 @@ +package com.foo.rest.examples.spring.openapi.v3.minimize + +import com.foo.rest.examples.spring.openapi.v3.SpringController + + +class MinimizeController: SpringController(MinimizeApplication::class.java) \ No newline at end of file diff --git a/e2e-tests/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/minimize/MinimizeEMTest.kt b/e2e-tests/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/minimize/MinimizeEMTest.kt new file mode 100644 index 0000000000..30a1852b6c --- /dev/null +++ b/e2e-tests/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/minimize/MinimizeEMTest.kt @@ -0,0 +1,49 @@ +package org.evomaster.e2etests.spring.openapi.v3.minimize + +import com.foo.rest.examples.spring.openapi.v3.minimize.MinimizeController +import org.evomaster.core.problem.rest.HttpVerb +import org.evomaster.e2etests.spring.openapi.v3.SpringTestBase +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import java.nio.file.Files +import java.nio.file.Paths + +/** + * Created by arcuri82 on 03-Mar-20. + */ +class MinimizeEMTest : SpringTestBase() { + + companion object { + @BeforeAll + @JvmStatic + fun init() { + initClass(MinimizeController()) + } + } + + + @Test + fun testRunEM() { + runTestHandlingFlakyAndCompilation( + "MinimizeEM", + "org.foo.MinimizeEM", + 5 + ) { args: MutableList -> + + args.add("--minimize") + args.add("true") + args.add("--maxTestSize") + args.add("1000") + args.add("--probOfSmartSampling") + args.add("0") + args.add("--enableOptimizedTestSize") + args.add("false") + + val solution = initAndRun(args) + + assertTrue(solution.individuals.size >= 5) //at least 5 distinct, mutually exclusive branches + assertTrue(solution.individuals.all{it.individual.size() == 1}) + } + } +} \ No newline at end of file From dcef0916f4a5720adf4383701c11714fa249cfc2 Mon Sep 17 00:00:00 2001 From: hghianni Date: Wed, 17 May 2023 16:53:00 -0300 Subject: [PATCH 022/345] Early approach to supporting Mongo data generation --- .../controller/api/ControllerConstants.java | 2 + .../api/dto/ExtraHeuristicsDto.java | 4 +- .../dto/database/execution/FailedQuery.java | 32 +++++ .../database/execution/MongoExecutionDto.java | 9 ++ .../operations/MongoDatabaseCommandDto.java | 8 ++ .../operations/MongoInsertionDto.java | 11 ++ .../operations/MongoInsertionEntryDto.java | 6 + .../operations/MongoInsertionResultsDto.java | 8 ++ .../controller/EmbeddedSutController.java | 5 + .../controller/ExternalSutController.java | 8 ++ .../client/java/controller/SutHandler.java | 5 + .../controller/internal/EMController.java | 61 ++++++++++ .../controller/internal/SutController.java | 22 ++++ .../controller/internal/db/MongoHandler.java | 113 +++++++++++++++++- .../java/controller/mongo/MongoOperation.java | 18 +++ .../controller/mongo/MongoScriptRunner.java | 50 ++++++++ .../java/controller/mongo/dsl/MongoDsl.java | 100 ++++++++++++++++ .../mongo/dsl/MongoSequenceDsl.java | 12 ++ .../mongo/dsl/MongoStatementDsl.java | 33 +++++ .../methodreplacement/ReplacementList.java | 1 + .../MongoRepositoryClassReplacement.java | 48 ++++++++ .../instrumentation/external/Command.java | 2 +- .../external/ServerController.java | 4 + .../staticstate/ExecutionTracer.java | 4 + .../core/problem/rest/SamplerVerifierTest.kt | 8 +- .../rest/service/resource/ResourceTestBase.kt | 8 +- .../kotlin/org/evomaster/core/EMConfig.kt | 8 ++ .../core/database/DatabaseExecutor.kt | 10 +- .../core/mongo/MongoActionGeneBuilder.kt | 26 ++++ .../org/evomaster/core/mongo/MongoDbAction.kt | 52 ++++++++ .../core/mongo/MongoDbActionResult.kt | 36 ++++++ .../core/mongo/MongoDbActionTransformer.kt | 36 ++++++ .../evomaster/core/mongo/MongoExecution.kt | 13 ++ .../core/mongo/MongoInsertBuilder.kt | 9 ++ .../org/evomaster/core/output/MongoWriter.kt | 86 +++++++++++++ .../core/output/service/ApiTestCaseWriter.kt | 35 ++++-- .../core/output/service/TestSuiteWriter.kt | 7 ++ .../api/service/ApiWsStructureMutator.kt | 52 ++++++++ .../enterprise/EnterpriseIndividual.kt | 25 +++- .../enterprise/service/EnterpriseFitness.kt | 40 ++++++- .../enterprise/service/EnterpriseSampler.kt | 24 ++++ .../core/problem/graphql/GraphQLIndividual.kt | 2 + .../core/problem/rest/RestIndividual.kt | 7 +- .../rest/resource/RestResourceCalls.kt | 11 +- .../core/problem/rest/service/RestFitness.kt | 2 + .../rest/service/RestResourceFitness.kt | 3 + .../core/problem/rpc/RPCIndividual.kt | 2 + .../service/RemoteControllerImplementation.kt | 26 +++- .../org/evomaster/core/search/ActionFilter.kt | 5 + .../evomaster/core/search/EvaluatedAction.kt | 6 +- .../core/search/EvaluatedIndividual.kt | 9 +- .../org/evomaster/core/search/FitnessValue.kt | 23 ++++ .../evomaster/core/search/GroupsOfChildren.kt | 2 + .../org/evomaster/core/search/Individual.kt | 2 +- .../org/evomaster/core/search/Solution.kt | 5 + .../ImpactsOfIndividual.kt | 3 +- .../InitializationActionImpacts.kt | 3 +- .../core/database/SqlInsertBuilderTest.kt | 8 +- .../core/output/service/FakeController.kt | 6 + .../external/service/DummyController.kt | 8 +- .../rest/individual/RestIndividualTestBase.kt | 9 +- .../rest/resource/ResourceNodeWithDbTest.kt | 8 +- .../spring/rest/mongo/MongoController.java | 8 ++ 63 files changed, 1147 insertions(+), 52 deletions(-) create mode 100644 client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/FailedQuery.java create mode 100644 client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/MongoExecutionDto.java create mode 100644 client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoDatabaseCommandDto.java create mode 100644 client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionDto.java create mode 100644 client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionEntryDto.java create mode 100644 client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionResultsDto.java create mode 100644 client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoOperation.java create mode 100644 client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoScriptRunner.java create mode 100644 client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoDsl.java create mode 100644 client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoSequenceDsl.java create mode 100644 client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoStatementDsl.java create mode 100644 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MongoRepositoryClassReplacement.java create mode 100644 core/src/main/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilder.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionResult.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionTransformer.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/mongo/MongoExecution.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/mongo/MongoInsertBuilder.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/output/MongoWriter.kt diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/ControllerConstants.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/ControllerConstants.java index d4403e4fe4..51be312251 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/ControllerConstants.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/ControllerConstants.java @@ -26,5 +26,7 @@ public class ControllerConstants { public static final String DATABASE_COMMAND = "/databaseCommand"; + public static final String MONGO_INSERTION = "/mongoInsertion"; + public static final String POST_SEARCH_ACTION = "/postSearchAction"; } diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/ExtraHeuristicsDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/ExtraHeuristicsDto.java index bcf1ac1b73..acc5f8d908 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/ExtraHeuristicsDto.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/ExtraHeuristicsDto.java @@ -1,7 +1,7 @@ package org.evomaster.client.java.controller.api.dto; import org.evomaster.client.java.controller.api.dto.database.execution.ExecutionDto; - +import org.evomaster.client.java.controller.api.dto.database.execution.MongoExecutionDto; import java.util.ArrayList; import java.util.List; @@ -19,4 +19,6 @@ public class ExtraHeuristicsDto { public List heuristics = new ArrayList<>(); public ExecutionDto databaseExecutionDto; + + public MongoExecutionDto mongoExecutionDto; } diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/FailedQuery.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/FailedQuery.java new file mode 100644 index 0000000000..06127ecaff --- /dev/null +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/FailedQuery.java @@ -0,0 +1,32 @@ +package org.evomaster.client.java.controller.api.dto.database.execution; + +import java.util.Map; + +public class FailedQuery { + // Add database + public FailedQuery(String collection, Class documentsType, Map accessedFields) { + this.collection = collection; + this.documentsType = documentsType; + this.accessedFields = accessedFields; + } + + public FailedQuery(){ + this.collection = ""; + } + + private final String collection; + private Class documentsType; + private Map accessedFields; + + public String getCollection() { + return collection; + } + + public Map getAccessedFields() { + return accessedFields; + } + + public Class getDocumentsType() { + return documentsType; + } +} diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/MongoExecutionDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/MongoExecutionDto.java new file mode 100644 index 0000000000..fa4341dec3 --- /dev/null +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/MongoExecutionDto.java @@ -0,0 +1,9 @@ +package org.evomaster.client.java.controller.api.dto.database.execution; + + +import java.util.ArrayList; +import java.util.List; + +public class MongoExecutionDto { + public List failedQueries = new ArrayList<>(); +} diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoDatabaseCommandDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoDatabaseCommandDto.java new file mode 100644 index 0000000000..f76beea4b0 --- /dev/null +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoDatabaseCommandDto.java @@ -0,0 +1,8 @@ +package org.evomaster.client.java.controller.api.dto.database.operations; + +import java.util.ArrayList; +import java.util.List; + +public class MongoDatabaseCommandDto { + public List insertions = new ArrayList<>(); +} diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionDto.java new file mode 100644 index 0000000000..59b6b9f4c6 --- /dev/null +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionDto.java @@ -0,0 +1,11 @@ +package org.evomaster.client.java.controller.api.dto.database.operations; + +import java.util.ArrayList; +import java.util.List; + +public class MongoInsertionDto { + + public String collectionName; + + public List data = new ArrayList<>(); +} diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionEntryDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionEntryDto.java new file mode 100644 index 0000000000..a23e0c3706 --- /dev/null +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionEntryDto.java @@ -0,0 +1,6 @@ +package org.evomaster.client.java.controller.api.dto.database.operations; + +public class MongoInsertionEntryDto { + public String fieldName; + public String value; +} diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionResultsDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionResultsDto.java new file mode 100644 index 0000000000..899442d640 --- /dev/null +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionResultsDto.java @@ -0,0 +1,8 @@ +package org.evomaster.client.java.controller.api.dto.database.operations; + +import java.util.ArrayList; +import java.util.List; + +public class MongoInsertionResultsDto { + public List executionResults = new ArrayList<>(); +} diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/EmbeddedSutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/EmbeddedSutController.java index 297127c65c..e4897e0006 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/EmbeddedSutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/EmbeddedSutController.java @@ -87,6 +87,11 @@ public final void setExecutingInitSql(boolean executingInitSql) { ExecutionTracer.setExecutingInitSql(executingInitSql); } + @Override + public final void setExecutingInitMongo(boolean executingInitMongo) { + ExecutionTracer.setExecutingInitMongo(executingInitMongo); + } + @Override public final void setExecutingAction(boolean executingAction){ ExecutionTracer.setExecutingAction(executingAction); diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java index 0331d61a31..a1c921ce0f 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java @@ -460,6 +460,14 @@ public final void setExecutingInitSql(boolean executingInitSql) { ExecutionTracer.setExecutingInitSql(executingInitSql); } + @Override + public final void setExecutingInitMongo(boolean executingInitMongo) { + checkInstrumentation(); + serverController.setExecutingInitMongo(executingInitMongo); + // sync executingInitMongo on the local ExecutionTracer + ExecutionTracer.setExecutingInitMongo(executingInitMongo); + } + @Override public final void setExecutingAction(boolean executingAction){ checkInstrumentation(); diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/SutHandler.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/SutHandler.java index 5b55a9cb70..a512d7b2fc 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/SutHandler.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/SutHandler.java @@ -2,6 +2,8 @@ import org.evomaster.client.java.controller.api.dto.database.operations.InsertionDto; import org.evomaster.client.java.controller.api.dto.database.operations.InsertionResultsDto; +import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionDto; +import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionResultsDto; import org.evomaster.client.java.controller.db.DbCleaner; import org.evomaster.client.java.controller.db.SqlScriptRunner; import org.evomaster.client.java.controller.db.SqlScriptRunnerCached; @@ -89,6 +91,7 @@ default void setupForGeneratedTest(){} */ InsertionResultsDto execInsertionsIntoDatabase(List insertions, InsertionResultsDto... previous); + MongoInsertionResultsDto execInsertionsIntoMongoDatabase(List insertions); /** *

@@ -177,6 +180,8 @@ default void extractRPCSchema(){} List getDbSpecifications(); + default Object getMongoConnection() {return null;} + /** *

diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java index f126e33a71..ce0c4fc6f4 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java @@ -5,7 +5,10 @@ import org.evomaster.client.java.controller.api.dto.*; import org.evomaster.client.java.controller.api.dto.database.operations.DatabaseCommandDto; import org.evomaster.client.java.controller.api.dto.database.operations.InsertionResultsDto; +import org.evomaster.client.java.controller.api.dto.database.operations.MongoDatabaseCommandDto; +import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionResultsDto; import org.evomaster.client.java.controller.api.dto.problem.*; +import org.evomaster.client.java.controller.mongo.MongoScriptRunner; import org.evomaster.client.java.controller.problem.*; import org.evomaster.client.java.controller.db.QueryResult; import org.evomaster.client.java.controller.db.SqlScriptRunner; @@ -720,4 +723,62 @@ going to be instrumented (as not in org.evomaster) sutController.setExecutingInitSql(false); } } + + @Path(ControllerConstants.MONGO_INSERTION) + @Consumes(Formats.JSON_V1) + @POST + public Response executeMongoInsertion(MongoDatabaseCommandDto dto, @Context HttpServletRequest httpServletRequest) { + + assert trackRequestSource(httpServletRequest); + + try { + + sutController.setExecutingInitMongo(true); + + SimpleLogger.debug("Received mongo database command"); + + Object connection = noKillSwitch(sutController::getMongoConnection); + if (connection == null) { + String msg = "No active database connection"; + SimpleLogger.warn(msg); + return Response.status(400).entity(WrappedResponseDto.withError(msg)).build(); + } + + if (dto.insertions == null || dto.insertions.isEmpty()) { + String msg = "No input command"; + SimpleLogger.warn(msg); + return Response.status(400).entity(WrappedResponseDto.withError(msg)).build(); + } + + if (dto.insertions.stream().anyMatch(i -> i.collectionName == null || i.collectionName.isEmpty())) { + String msg = "Insertion with no target collection"; + SimpleLogger.warn(msg); + return Response.status(400).entity(WrappedResponseDto.withError(msg)).build(); + } + + MongoInsertionResultsDto mongoInsertionResultsDto = null; + + + try { + mongoInsertionResultsDto = MongoScriptRunner.execInsert(connection, dto.insertions); + } catch (Exception e) { + String msg = "Failed to execute database command: " + e.getMessage(); + SimpleLogger.warn(msg); + return Response.status(400).entity(WrappedResponseDto.withError(msg)).build(); + } + + if (mongoInsertionResultsDto != null) { + return Response.status(200).entity(WrappedResponseDto.withData(mongoInsertionResultsDto)).build(); + } else { + return Response.status(204).entity(WrappedResponseDto.withNoData()).build(); + } + + } catch (RuntimeException e) { + String msg = "Thrown exception: " + e.getMessage(); + SimpleLogger.error(msg, e); + return Response.status(500).entity(WrappedResponseDto.withError(msg)).build(); + } finally { + sutController.setExecutingInitMongo(false); + } + } } diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index c8a26e195b..05a9b3f241 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -16,6 +16,8 @@ import org.evomaster.client.java.controller.api.dto.database.execution.ExecutionDto; import org.evomaster.client.java.controller.api.dto.database.operations.InsertionDto; import org.evomaster.client.java.controller.api.dto.database.operations.InsertionResultsDto; +import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionDto; +import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionResultsDto; import org.evomaster.client.java.controller.api.dto.database.schema.DbSchemaDto; import org.evomaster.client.java.controller.api.dto.database.schema.ExtraConstraintsDto; import org.evomaster.client.java.controller.api.dto.problem.RPCProblemDto; @@ -27,6 +29,7 @@ import org.evomaster.client.java.controller.internal.db.MongoHandler; import org.evomaster.client.java.controller.internal.db.SchemaExtractor; import org.evomaster.client.java.controller.internal.db.SqlHandler; +import org.evomaster.client.java.controller.mongo.MongoScriptRunner; import org.evomaster.client.java.controller.problem.ProblemInfo; import org.evomaster.client.java.controller.problem.RPCProblem; import org.evomaster.client.java.controller.problem.rpc.CustomizedNotNullAnnotationForRPCDto; @@ -225,6 +228,21 @@ public InsertionResultsDto execInsertionsIntoDatabase(List inserti } } + @Override + public MongoInsertionResultsDto execInsertionsIntoMongoDatabase(List insertions) { + + Object connection = getMongoConnection(); + if (connection == null) { + throw new IllegalStateException("No connection to mongo database"); + } + + try { + return MongoScriptRunner.execInsert(connection, insertions); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + public int getActionIndex(){ return actionIndex; } @@ -371,6 +389,8 @@ public final void computeMongoHeuristics(ExtraHeuristicsDto dto){ )) .forEach(h -> dto.heuristics.add(h)); } + + if(mongoHandler.isExtractMongoExecution()){dto.mongoExecutionDto = mongoHandler.getExecutionDto();} } /** @@ -1089,6 +1109,8 @@ public final String getDatabaseDriverName(){ public abstract void setExecutingInitSql(boolean executingInitSql); + public abstract void setExecutingInitMongo(boolean executingInitMongo); + public abstract void setExecutingAction(boolean executingAction); public abstract BootTimeInfoDto getBootTimeInfoDto(); diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/MongoHandler.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/MongoHandler.java index 92c32c0f32..b87698702e 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/MongoHandler.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/MongoHandler.java @@ -1,11 +1,19 @@ package org.evomaster.client.java.controller.internal.db; +import org.evomaster.client.java.controller.api.dto.database.execution.FailedQuery; +import org.evomaster.client.java.controller.api.dto.database.execution.MongoExecutionDto; import org.evomaster.client.java.controller.mongo.MongoHeuristicsCalculator; +import org.evomaster.client.java.controller.mongo.MongoOperation; +import org.evomaster.client.java.controller.mongo.QueryParser; +import org.evomaster.client.java.controller.mongo.operations.*; import org.evomaster.client.java.instrumentation.MongoInfo; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * Class used to act upon Mongo commands executed by the SUT @@ -32,9 +40,12 @@ public class MongoHandler { */ private volatile boolean calculateHeuristics; + private final List failedQueries; + public MongoHandler() { distances = new ArrayList<>(); operations = new ArrayList<>(); + failedQueries = new ArrayList<>(); extractMongoExecution = true; calculateHeuristics = true; } @@ -42,6 +53,7 @@ public MongoHandler() { public void reset() { operations.clear(); distances.clear(); + failedQueries.clear(); } public void handle(MongoInfo info) { @@ -62,8 +74,12 @@ public List getDistances() { dist = Double.MAX_VALUE; } distances.add(new MongoOperationDistance(mongoInfo.getQuery(), dist)); - }); + if (dist > 0 ) { + Object collection = mongoInfo.getCollection(); + failedQueries.add(new MongoOperation(collection, mongoInfo.getQuery())); + } + }); operations.clear(); return distances; @@ -95,6 +111,101 @@ private static Iterable getDocuments(Object collection) { } } + public MongoExecutionDto getExecutionDto(){ + MongoExecutionDto dto = new MongoExecutionDto(); + dto.failedQueries = failedQueries.stream().map(this::extractRelevantInfo).collect(Collectors.toList()); + return dto; + } + + private FailedQuery extractRelevantInfo(MongoOperation operation) { + QueryOperation query = new QueryParser().parse(operation.getQuery()); + Object collection = operation.getCollection(); + + Map accessedFields = extractFieldsInQuery(query); + Class documentsType = extractDocumentsType(collection); + + return new FailedQuery("operation.getCollection()", documentsType, accessedFields); + } + + private static Class extractDocumentsType(Object collection) { + try { + Class collectionClass = collection.getClass().getClassLoader().loadClass("com.mongodb.client.MongoCollection"); + return (Class) collectionClass.getMethod("getDocumentClass").invoke(collection); + + } catch (NoSuchMethodException | ClassNotFoundException | InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private static Map extractFieldsInQuery(QueryOperation operation) { + Map accessedFields = new HashMap<>(); + + if(operation instanceof ComparisonOperation) { + ComparisonOperation op = (ComparisonOperation) operation; + accessedFields.put(op.getFieldName(), op.getValue()); + } + + if(operation instanceof AndOperation) { + AndOperation op = (AndOperation) operation; + op.getConditions().forEach(cond -> accessedFields.putAll(extractFieldsInQuery(cond))); + } + + if(operation instanceof OrOperation) { + OrOperation op = (OrOperation) operation; + op.getConditions().forEach(cond -> accessedFields.putAll(extractFieldsInQuery(cond))); + } + + if(operation instanceof NorOperation) { + NorOperation op = (NorOperation) operation; + op.getConditions().forEach(cond -> accessedFields.putAll(extractFieldsInQuery(cond))); + } + + if(operation instanceof InOperation) { + InOperation op = (InOperation) operation; + accessedFields.put(op.getFieldName(), op.getValues().get(0)); + } + + if(operation instanceof NotInOperation) { + NotInOperation op = (NotInOperation) operation; + accessedFields.put(op.getFieldName(), op.getValues().get(0)); + } + + if(operation instanceof AllOperation) { + AllOperation op = (AllOperation) operation; + accessedFields.put(op.getFieldName(), op.getValues()); + } + + if(operation instanceof SizeOperation) { + SizeOperation op = (SizeOperation) operation; + accessedFields.put(op.getFieldName(), op.getValue()); + } + + if(operation instanceof ExistsOperation) { + ExistsOperation op = (ExistsOperation) operation; + accessedFields.put(op.getFieldName(), null); + } + + if(operation instanceof ModOperation) { + ModOperation op = (ModOperation) operation; + accessedFields.put(op.getFieldName(), op.getDivisor()); + } + + if(operation instanceof TypeOperation) { + TypeOperation op = (TypeOperation) operation; + accessedFields.put(op.getFieldName(), op.getType()); + } + + /* + if(operation instanceof ElemMatchOperation) { + ElemMatchOperation op = (ElemMatchOperation) operation; + accessedFields.put(op.getFieldName(), op.getValue()); + } + + */ + + return accessedFields; + } + public boolean isCalculateHeuristics() { return calculateHeuristics; } diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoOperation.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoOperation.java new file mode 100644 index 0000000000..aff9ec9367 --- /dev/null +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoOperation.java @@ -0,0 +1,18 @@ +package org.evomaster.client.java.controller.mongo; +public class MongoOperation { + private final Object query; + private final Object collection; + + public MongoOperation(Object collection, Object query) { + this.collection = collection; + this.query = query; + } + + public Object getCollection() { + return collection; + } + + public Object getQuery() { + return query; + } +} diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoScriptRunner.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoScriptRunner.java new file mode 100644 index 0000000000..255146e735 --- /dev/null +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoScriptRunner.java @@ -0,0 +1,50 @@ +package org.evomaster.client.java.controller.mongo; + +import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionDto; +import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionResultsDto; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Class used to execute Mongo commands + */ +public class MongoScriptRunner { + + /** + * Default constructor + */ + public MongoScriptRunner() { + } + + public static MongoInsertionResultsDto execInsert(Object conn, List insertions){ + + if (insertions == null || insertions.isEmpty()) { + throw new IllegalArgumentException("No data to insert"); + } + + List mongoResults = new ArrayList<>(Collections.nCopies(insertions.size(), false)); + + for (int i = 0; i < insertions.size(); i++) { + + MongoInsertionDto insDto = insertions.get(i); + + /* + try { + insDto.data.forEach(field -> conn.getDatabase("persons").getCollection(insDto.collectionName).insertOne(Document.parse(field.value))); + mongoResults.set(i, true); + } catch (Exception e) { + String msg = "Failed to execute insertion with index " + i + " with Mongo. Error: " + e.getMessage(); + throw new RuntimeException(msg, e); + } + + */ + } + + MongoInsertionResultsDto insertionResultsDto = new MongoInsertionResultsDto(); + insertionResultsDto.executionResults = mongoResults; + return insertionResultsDto; + } + +} diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoDsl.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoDsl.java new file mode 100644 index 0000000000..889236447b --- /dev/null +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoDsl.java @@ -0,0 +1,100 @@ +package org.evomaster.client.java.controller.mongo.dsl; + +import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionDto; +import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionEntryDto; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * DSL (Domain Specific Language) for operations on + * the Mongo Database + */ +public class MongoDsl implements MongoSequenceDsl, MongoStatementDsl{ + + private List list = new ArrayList<>(); + + private final List previousInsertionDtos = new ArrayList<>(); + + private MongoDsl() { + } + + private MongoDsl(List... previous) { + if (previous != null && previous.length > 0){ + Arrays.stream(previous).forEach(previousInsertionDtos::addAll); + } + } + + /** + * @return a DSL object to create SQL operations + */ + public static MongoSequenceDsl mongo() { + + return new MongoDsl(); + } + + /** + * @param previous a DSL object which is executed in the front of this + * @return a DSL object to create SQL operations + */ + public static MongoSequenceDsl mongo(List... previous) { + + return new MongoDsl(previous); + } + + @Override + public MongoStatementDsl insertInto(String collectionName) { + + checkDsl(); + + if (collectionName == null || collectionName.isEmpty()) { + throw new IllegalArgumentException("Unspecified collection"); + } + + MongoInsertionDto dto = new MongoInsertionDto(); + dto.collectionName = collectionName; + list.add(dto); + + return this; + } + + @Override + public MongoStatementDsl d(String printableValue) { + + checkDsl(); + + MongoInsertionEntryDto entry = new MongoInsertionEntryDto(); + entry.value = printableValue; + + current().data.add(entry); + + return this; + } + + @Override + public MongoSequenceDsl and() { + return this; + } + + @Override + public List dtos() { + + List tmp = list; + list = null; + + return tmp; + } + + + private MongoInsertionDto current() { + return list.get(list.size() - 1); + } + + private void checkDsl() { + if (list == null) { + throw new IllegalStateException("DTO was already built for this object"); + } + } + +} diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoSequenceDsl.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoSequenceDsl.java new file mode 100644 index 0000000000..806b915eba --- /dev/null +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoSequenceDsl.java @@ -0,0 +1,12 @@ +package org.evomaster.client.java.controller.mongo.dsl; + +public interface MongoSequenceDsl { + + /** + * An insertion operation on the Mongo Database (MongoDB) + * + * @param collectionName the target table in the DB + * @return a statement in which it can be specified the values to add + */ + MongoStatementDsl insertInto(String collectionName); +} diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoStatementDsl.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoStatementDsl.java new file mode 100644 index 0000000000..8f9e24437e --- /dev/null +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoStatementDsl.java @@ -0,0 +1,33 @@ +package org.evomaster.client.java.controller.mongo.dsl; + +import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionDto; + +import java.util.List; + +public interface MongoStatementDsl { + + /** + * Add a value to insert + * + * @param printableValue the value that is going to be inserted, as + * it would be printed as string. + * This means that 5 is represented with "5", + * whereas "5" with "'5'" + * @return the continuation of this statement, in which more values can be added + */ + MongoStatementDsl d(String printableValue); + + /** + * Close the current statement + * @return the sequence object on which new Mongo commands can be added + */ + MongoSequenceDsl and(); + + /** + * Build the DTOs (Data Transfer Object) from this DSL, + * closing it (ie, not usable any longer). + * @return a list of DTOs representing all the insertion SQL commands defined in this DSL. + */ + List dtos(); + +} diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/ReplacementList.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/ReplacementList.java index f109975fe0..ddaf10f758 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/ReplacementList.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/ReplacementList.java @@ -49,6 +49,7 @@ public static List getList() { new MatcherClassReplacement(), new MethodClassReplacement(), new MongoClassReplacement(), + new MongoRepositoryClassReplacement(), new OkHttpClient3BuilderClassReplacement(), new OkHttpClient3ClassReplacement(), new OkHttpClientClassReplacement(), diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MongoRepositoryClassReplacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MongoRepositoryClassReplacement.java new file mode 100644 index 0000000000..3f5b6220ec --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MongoRepositoryClassReplacement.java @@ -0,0 +1,48 @@ +package org.evomaster.client.java.instrumentation.coverage.methodreplacement.classes; + +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.ThirdPartyMethodReplacementClass; +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.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + + +public class MongoRepositoryClassReplacement extends ThirdPartyMethodReplacementClass { + private static final MongoRepositoryClassReplacement singleton = new MongoRepositoryClassReplacement(); + + @Override + protected String getNameOfThirdPartyTargetClass() { + return "org.springframework.data.mongodb.repository.support.SimpleMongoRepository"; + } + + // This is not working. It may be related to the fact that this constructor seems to be called using reflection. + // When a MongoRepository is created in Spring it uses a MongoCollection under the hood. + // The type of the collection is the default (Document) despite probably the repository was created of some custom type like Person. + // The fact that elements of the collection should be Person is stored in Spring (SimpleMongoRepository). + // The idea is to instrument the constructor of SimpleMongoRepository to obtain the actual type of the collection. + + // Change category to MONGO + @Replacement(replacingStatic = false, + replacingConstructor = true, + type = ReplacementType.TRACKER, id = "SimpleMongoRepository", + usageFilter = UsageFilter.ANY, + category = ReplacementCategory.SQL) + public static void SimpleMongoRepository( + Object mongoRepository, + @ThirdPartyCast(actualType = "org.springframework.data.mongodb.repository.query.MongoEntityInformation") Object metadata, + @ThirdPartyCast(actualType = "org.springframework.data.mongodb.repository.core.MongoOperations") Object mongoOperations) { + try { + Method method = getOriginal(singleton, "SimpleMongoRepository", mongoRepository); + method.invoke(mongoRepository, metadata, mongoOperations); + // Store info + } catch (IllegalAccessException e){ + throw new RuntimeException(e); + } catch (InvocationTargetException e){ + throw (RuntimeException) e.getCause(); + } + } +} diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/Command.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/Command.java index 7f61fe8690..1c378218e1 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/Command.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/Command.java @@ -8,5 +8,5 @@ */ public enum Command implements Serializable { - NEW_SEARCH, NEW_TEST, TARGETS_INFO, ACK, ACTION_INDEX, ADDITIONAL_INFO, UNITS_INFO, KILL_SWITCH, EXECUTING_INIT_SQL, EXECUTING_ACTION, BOOT_TIME_INFO, EXTRACT_JVM_DTO + NEW_SEARCH, NEW_TEST, TARGETS_INFO, ACK, ACTION_INDEX, ADDITIONAL_INFO, UNITS_INFO, KILL_SWITCH, EXECUTING_INIT_SQL, EXECUTING_INIT_MONGO, EXECUTING_ACTION, BOOT_TIME_INFO, EXTRACT_JVM_DTO } diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/ServerController.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/ServerController.java index 9416d4a8be..2fb308227f 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/ServerController.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/ServerController.java @@ -204,6 +204,10 @@ public boolean setExecutingInitSql(boolean executingInitSql) { return sendWithDataAndExpectACK(Command.EXECUTING_INIT_SQL, executingInitSql); } + public boolean setExecutingInitMongo(boolean executingInitMongo) { + return sendWithDataAndExpectACK(Command.EXECUTING_INIT_MONGO, executingInitMongo); + } + public boolean setExecutingAction(boolean executingAction){ return sendWithDataAndExpectACK(Command.EXECUTING_ACTION, executingAction); } diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/staticstate/ExecutionTracer.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/staticstate/ExecutionTracer.java index 7dca36f19a..4e73901a6d 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/staticstate/ExecutionTracer.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/staticstate/ExecutionTracer.java @@ -189,6 +189,10 @@ public static void setExecutingInitSql(boolean executingInitSql) { ExecutionTracer.executingInitSql = executingInitSql; } + public static void setExecutingInitMongo(boolean executingInitMongo) { + ExecutionTracer.executingInitMongo = executingInitMongo; + } + public static boolean isExecutingAction() { return executingAction; } diff --git a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/SamplerVerifierTest.kt b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/SamplerVerifierTest.kt index 62e47b79c8..5b92b27325 100644 --- a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/SamplerVerifierTest.kt +++ b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/SamplerVerifierTest.kt @@ -6,9 +6,7 @@ import com.google.inject.Provides import com.google.inject.Singleton import com.netflix.governator.guice.LifecycleInjector import org.evomaster.client.java.controller.api.dto.* -import org.evomaster.client.java.controller.api.dto.database.operations.DatabaseCommandDto -import org.evomaster.client.java.controller.api.dto.database.operations.InsertionResultsDto -import org.evomaster.client.java.controller.api.dto.database.operations.QueryResultDto +import org.evomaster.client.java.controller.api.dto.database.operations.* import org.evomaster.client.java.controller.api.dto.problem.RestProblemDto import org.evomaster.core.BaseModule import org.evomaster.core.problem.rest.service.ResourceRestModule @@ -370,6 +368,10 @@ class SamplerVerifierTest { return null } + override fun executeMongoDatabaseInsertions(dto: MongoDatabaseCommandDto): MongoInsertionResultsDto? { + return null + } + override fun getSutInfo(): SutInfoDto? { return sutInfoDto } diff --git a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/ResourceTestBase.kt b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/ResourceTestBase.kt index 1f4d914e77..f93fde25de 100644 --- a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/ResourceTestBase.kt +++ b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/ResourceTestBase.kt @@ -3,9 +3,7 @@ package org.evomaster.core.problem.rest.service.resource import com.google.inject.Module import com.netflix.governator.lifecycle.LifecycleManager import com.netflix.governator.guice.LifecycleInjector -import org.evomaster.client.java.controller.api.dto.database.operations.DatabaseCommandDto -import org.evomaster.client.java.controller.api.dto.database.operations.InsertionResultsDto -import org.evomaster.client.java.controller.api.dto.database.operations.QueryResultDto +import org.evomaster.client.java.controller.api.dto.database.operations.* import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor import org.evomaster.core.BaseModule @@ -84,6 +82,10 @@ abstract class ResourceTestBase : ExtractTestBaseH2(), ResourceBasedTestInterfac return null } + override fun executeMongoDatabaseInsertions(dto: MongoDatabaseCommandDto): MongoInsertionResultsDto? { + return null + } + override fun executeDatabaseCommandAndGetQueryResults(dto: DatabaseCommandDto): QueryResultDto? { return SqlScriptRunner.execCommand(connection, dto.command).toDto() } diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index 04b090cd98..81323e91b5 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -525,6 +525,8 @@ class EMConfig { fun shouldGenerateSqlData() = isMIO() && (generateSqlDataWithDSE || generateSqlDataWithSearch) + fun shouldGenerateMongoData() = generateMongoData + fun experimentalFeatures(): List { val properties = getConfigurationProperties() @@ -1046,6 +1048,9 @@ class EMConfig { @Cfg("Enable extracting SQL execution info") var extractSqlExecutionInfo = true + @Cfg("Enable extracting Mongo execution info") + var extractMongoExecutionInfo = true + @Experimental @Cfg("Enable EvoMaster to generate SQL data with direct accesses to the database. Use Dynamic Symbolic Execution") var generateSqlDataWithDSE = false @@ -1053,6 +1058,9 @@ class EMConfig { @Cfg("Enable EvoMaster to generate SQL data with direct accesses to the database. Use a search algorithm") var generateSqlDataWithSearch = true + @Cfg("Enable EvoMaster to generate Mongo data with direct accesses to the database") + var generateMongoData = true + @Cfg("When generating SQL data, how many new rows (max) to generate for each specific SQL Select") @Min(1.0) var maxSqlInitActionsPerMissingData = 5 diff --git a/core/src/main/kotlin/org/evomaster/core/database/DatabaseExecutor.kt b/core/src/main/kotlin/org/evomaster/core/database/DatabaseExecutor.kt index 1163c854dc..8ec33cb36d 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/DatabaseExecutor.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/DatabaseExecutor.kt @@ -1,8 +1,6 @@ package org.evomaster.core.database -import org.evomaster.client.java.controller.api.dto.database.operations.DatabaseCommandDto -import org.evomaster.client.java.controller.api.dto.database.operations.InsertionResultsDto -import org.evomaster.client.java.controller.api.dto.database.operations.QueryResultDto +import org.evomaster.client.java.controller.api.dto.database.operations.* interface DatabaseExecutor { @@ -24,4 +22,10 @@ interface DatabaseExecutor { * Return the result of whether it success (first) and new pks in such insertions (second), if any */ fun executeDatabaseInsertionsAndGetIdMapping(dto: DatabaseCommandDto): InsertionResultsDto? + + /** + * Execute a the given INSERT MONGO command (in DTO format). + * Return the result of whether it success + */ + fun executeMongoDatabaseInsertions(dto: MongoDatabaseCommandDto): MongoInsertionResultsDto? } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilder.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilder.kt new file mode 100644 index 0000000000..601c5336e9 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilder.kt @@ -0,0 +1,26 @@ +package org.evomaster.core.mongo + +import org.evomaster.core.search.gene.* +import org.evomaster.core.search.gene.collection.ArrayGene +import org.evomaster.core.search.gene.datetime.DateGene +import org.evomaster.core.search.gene.numeric.* +import org.evomaster.core.search.gene.string.StringGene +import java.util.Date + +class MongoActionGeneBuilder { + fun buildGene(fieldName: String, value: T): Gene { + return when (value) { + is Int -> IntegerGene(fieldName, min = Int.MIN_VALUE, max = Int.MAX_VALUE) + is Long -> LongGene(fieldName, min = Long.MIN_VALUE, max = Long.MAX_VALUE) + is Double -> DoubleGene(fieldName, min = Double.MIN_VALUE, max = Double.MAX_VALUE) + is String -> StringGene(name = fieldName, minLength = Int.MIN_VALUE) + is Boolean -> BooleanGene(name = fieldName) + //is Array<*> -> ArrayGene<*>(name = fieldName) + //is Object -> ObjectGene + //is Date -> DateGene + //is null -> NullGene + //Unhandled Types: Binary Data, Object Id, Regular Expression, Javascript, Timestamp, Decimal128, Min/max key + else -> throw IllegalArgumentException("Cannot handle: $fieldName.") + } + } +} diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt new file mode 100644 index 0000000000..c542e9e3b4 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt @@ -0,0 +1,52 @@ +package org.evomaster.core.mongo + +import org.evomaster.core.search.Action +import org.evomaster.core.search.gene.Gene +import org.evomaster.core.search.gene.ObjectGene +import java.util.* + +class MongoDbAction(val collection: String, val documentsType: Class<*>, val accessedFields: Map, computedGenes: List? = null) : Action(listOf()) { + + val genes: List = (computedGenes ?: computeGenes()) .also { addChildren(it) } + + private fun computeGenes(): List { + // I should be as specific as I can with the type the collection's documents should have. + // There are a few ways to get that info: + + // 1) Using , which is the result of collection.getDocumentsClass() + // Spring for example "ignore" this and store type info inside SampleMongoRepository + + // 2) Instrument the constructor of SampleMongoRepository and retrieve the info + // Probably can reuse something from GsonClassReplacement. + + // 3) Extract from the query the fields () used and type of each of them. This probably won't + // work fine as usually a subset of fields is used in a query. But is better than creating a + // Document. + + val genes = + if(documentsType.typeName == "org.bson.Document"){ + // 3) + accessedFields.map { MongoActionGeneBuilder().buildGene(it.key, it.value) } + }else{ + // 1) + documentsType.declaredFields.map { MongoActionGeneBuilder().buildGene(it.name, it.type) } + } + return Collections.singletonList(ObjectGene("BSON", genes)) + } + + override fun getName(): String { + return "MONGO_Find_${collection}_${accessedFields.map { it.key }.sorted().joinToString("_")}" + } + + override fun seeTopGenes(): List { + return genes + } + + override fun shouldCountForFitnessEvaluations(): Boolean { + return false + } + + override fun copyContent(): Action { + return MongoDbAction(collection, documentsType, accessedFields, genes.map(Gene::copy)) + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionResult.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionResult.kt new file mode 100644 index 0000000000..09f68ce633 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionResult.kt @@ -0,0 +1,36 @@ +package org.evomaster.core.mongo +import org.evomaster.core.search.Action +import org.evomaster.core.search.ActionResult + +/** + * sql insert action execution result + */ +class MongoDbActionResult : ActionResult { + + constructor(stopping: Boolean = false) : super(stopping) + constructor(other: MongoDbActionResult): super(other) + + companion object{ + const val INSERT_MONGO_EXECUTE_SUCCESSFULLY = "INSERT_MONGO_EXECUTE_SUCCESSFULLY" + } + + override fun copy(): MongoDbActionResult { + return MongoDbActionResult(this) + } + + /** + * @param success specifies whether the INSERT SQL executed successfully + * + * NOTE THAT here for SELECT, the execution result is false by default. + */ + fun setInsertExecutionResult(success: Boolean) = addResultValue(INSERT_MONGO_EXECUTE_SUCCESSFULLY, success.toString()) + + /** + * @return whether the db action executed successfully + */ + fun getInsertExecutionResult() = getResultValue(INSERT_MONGO_EXECUTE_SUCCESSFULLY)?.toBoolean()?:false + + override fun matchedType(action: Action): Boolean { + return action is MongoDbAction + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionTransformer.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionTransformer.kt new file mode 100644 index 0000000000..2137357f2d --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionTransformer.kt @@ -0,0 +1,36 @@ +package org.evomaster.core.mongo + +import org.evomaster.client.java.controller.api.dto.database.operations.* + +object MongoDbActionTransformer { + + fun transform(insertions: List) : MongoDatabaseCommandDto { + + val list = mutableListOf() + + for (i in 0 until insertions.size) { + + val action = insertions[i] + + val insertion = MongoInsertionDto().apply { collectionName = action.collection } + + val g = action.seeTopGenes().first() + val entry = MongoInsertionEntryDto() + + // If is printed as JSON there might a problem + // A Document(from Mongo) can be created from a JSON but some info might be lost + // Preferably is created from a EJSON (Extended JSON) as JSON can only directly represent a + // subset of the types supported by BSON. + // Maybe we can create a new OutputFormat + + entry.value = g.getValueAsPrintableString() + entry.fieldName = g.getVariableName() + + insertion.data.add(entry) + + list.add(insertion) + } + + return MongoDatabaseCommandDto().apply { this.insertions = list } + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoExecution.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoExecution.kt new file mode 100644 index 0000000000..f4dbff3051 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoExecution.kt @@ -0,0 +1,13 @@ +package org.evomaster.core.mongo + +import org.evomaster.client.java.controller.api.dto.database.execution.FailedQuery +import org.evomaster.client.java.controller.api.dto.database.execution.MongoExecutionDto +class MongoExecution(val failedQueries: List) { + + companion object { + + fun fromDto(dto: MongoExecutionDto?): MongoExecution { + return MongoExecution(dto!!.failedQueries) + } + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoInsertBuilder.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoInsertBuilder.kt new file mode 100644 index 0000000000..8df7126243 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoInsertBuilder.kt @@ -0,0 +1,9 @@ +package org.evomaster.core.mongo + +class MongoInsertBuilder { + + fun createMongoInsertionAction(collection: String, documentsType: Class<*>, accessedFields: Map): List { + // FIX + return mutableListOf(MongoDbAction(collection, documentsType, accessedFields)) + } +} diff --git a/core/src/main/kotlin/org/evomaster/core/output/MongoWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/MongoWriter.kt new file mode 100644 index 0000000000..b6e78002d1 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/output/MongoWriter.kt @@ -0,0 +1,86 @@ +package org.evomaster.core.output + +import org.apache.commons.lang3.StringEscapeUtils +import org.evomaster.core.search.EvaluatedMongoDbAction +import org.evomaster.core.search.gene.ObjectGene + +/** + * Class used to generate the code in the test dealing with insertion of + * data into MONGO databases. + */ +object MongoWriter { + + /** + * generate mongo insert actions into test case based on [mongoDbInitialization] + * @param format is the format of tests to be generated + * @param mongoDbInitialization contains the db actions to be generated + * @param lines is used to save generated textual lines with respects to [mongoDbInitialization] + * @param groupIndex specifies an index of a group of this [mongoDbInitialization] + * @param insertionVars is a list of previous variable names of the db actions (Pair.first) and corresponding results (Pair.second) + * @param skipFailure specifies whether to skip failure tests + */ + fun handleMongoDbInitialization( + format: OutputFormat, + mongoDbInitialization: List, + lines: Lines, + groupIndex: String ="", + insertionVars: MutableList>, + skipFailure: Boolean) { + + //if (dbInitialization.isEmpty() || dbInitialization.none { !it.action.representExistingData && (!skipFailure || it.result.getInsertExecutionResult())}) { + //return + //} + + val insertionVar = "insertions${groupIndex}" + val insertionVarResult = "${insertionVar}result" + val previousVar = insertionVars.joinToString(", ") { it.first } + val previousVarResults = insertionVars.joinToString(", ") { it.second } + mongoDbInitialization + .filter { !skipFailure || it.result.getInsertExecutionResult()} + .forEachIndexed { index, evaluatedMongoDbAction -> + + lines.add(when { + index == 0 && format.isJava() -> "List $insertionVar = mongo($previousVar)" + index == 0 && format.isKotlin() -> "val $insertionVar = mongo($previousVar)" + else -> ".and()" + } + ".insertInto(\"${evaluatedMongoDbAction.action.collection}\")") + + if (index == 0) { + lines.indent() + } + + lines.indented { + evaluatedMongoDbAction.action.seeTopGenes() + .filter { it.isPrintable() } + .forEach { g -> + when (g) { + is ObjectGene -> { + val printableValue = StringEscapeUtils.escapeJava(g.getValueAsPrintableString()) + lines.add(".d(\"$printableValue\")") + } + + else -> { + } + } + } + + } + } + + lines.add(".dtos()") + lines.appendSemicolon(format) + + lines.deindent() + + lines.add(when{ + format.isJava() -> "MongoInsertionResultsDto " + format.isKotlin() -> "val " + else -> throw IllegalStateException("Not support mongo insertions generation for $format") + } + "$insertionVarResult = controller.execInsertionsIntoMongoDatabase(${if (previousVarResults.isBlank()) insertionVar else "$insertionVar, $previousVarResults"})") + lines.appendSemicolon(format) + + insertionVars.add(insertionVar to insertionVarResult) + + } + +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/ApiTestCaseWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/ApiTestCaseWriter.kt index 937c5d8340..7e8537c51d 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/ApiTestCaseWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/ApiTestCaseWriter.kt @@ -4,12 +4,12 @@ import com.google.gson.Gson import com.google.gson.JsonSyntaxException import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionResult -import org.evomaster.core.output.CookieWriter -import org.evomaster.core.output.Lines -import org.evomaster.core.output.SqlWriter -import org.evomaster.core.output.TokenWriter +import org.evomaster.core.mongo.MongoDbAction +import org.evomaster.core.mongo.MongoDbActionResult +import org.evomaster.core.output.* import org.evomaster.core.search.EvaluatedDbAction import org.evomaster.core.search.EvaluatedIndividual +import org.evomaster.core.search.EvaluatedMongoDbAction import org.evomaster.core.search.gene.utils.GeneUtils abstract class ApiTestCaseWriter : TestCaseWriter() { @@ -33,20 +33,35 @@ abstract class ApiTestCaseWriter : TestCaseWriter() { TokenWriter.handleGettingTokens(format, ind, lines, baseUrlOfSut, this) //FIXME this doing initializations, not field declaration - val initializingActions = ind.individual.seeInitializingActions().filterIsInstance() - val initializingActionResults = (ind.seeResults(initializingActions)) - if (initializingActionResults.any { (it as? DbActionResult) == null }) + //REFACTOR TO HANDLE MULTIPLE DATABASES + val initializingSqlActions = ind.individual.seeInitializingActions().filterIsInstance() + val initializingSqlActionResults = (ind.seeResults(initializingSqlActions)) + if (initializingSqlActionResults.any { (it as? DbActionResult) == null }) throw IllegalStateException("the type of results are expected as DbActionResults") + val initializingMongoActions = ind.individual.seeInitializingActions().filterIsInstance() + val initializingMongoResults = (ind.seeResults(initializingMongoActions)) + if (initializingMongoResults.any { (it as? MongoDbActionResult) == null }) + throw IllegalStateException("the type of results are expected as MongoDbActionResults") - if (ind.individual.seeInitializingActions().isNotEmpty()) { + + if (initializingSqlActions.isNotEmpty()) { SqlWriter.handleDbInitialization( format, - initializingActions.indices.map { - EvaluatedDbAction(initializingActions[it], initializingActionResults[it] as DbActionResult) + initializingSqlActions.indices.map { + EvaluatedDbAction(initializingSqlActions[it], initializingSqlActions[it] as DbActionResult) }, lines, insertionVars = insertionVars, skipFailure = config.skipFailureSQLInTestFile) } + + if (initializingMongoActions.isNotEmpty()) { + MongoWriter.handleMongoDbInitialization( + format, + initializingMongoActions.indices.map { + EvaluatedMongoDbAction(initializingMongoActions[it], initializingMongoResults[it] as MongoDbActionResult) + }, + lines, insertionVars = insertionVars, skipFailure = config.skipFailureSQLInTestFile) + } } /** diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt index 21ae6f7b07..5bb3aaa57a 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt @@ -2,6 +2,7 @@ package org.evomaster.core.output.service import com.google.inject.Inject import org.evomaster.client.java.controller.api.dto.database.operations.InsertionDto +import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionDto import org.evomaster.core.EMConfig import org.evomaster.core.output.* import org.evomaster.core.output.service.TestWriterUtils.Companion.getWireMockVariableName @@ -367,6 +368,12 @@ class TestSuiteWriter { addImport(InsertionDto::class.qualifiedName!!, lines) } + if(solution.hasAnyMongoAction()) { + addImport("org.evomaster.client.java.controller.mongo.dsl.MongoDsl.mongo", lines, true) + addImport("org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionResultsDto", lines) + addImport(MongoInsertionDto::class.qualifiedName!!, lines) + } + // TODO: BMR - this is temporarily added as WiP. Should we have a more targeted import (i.e. not import everything?) if (config.enableBasicAssertions) { diff --git a/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt b/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt index 803f900dba..5d03a57350 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt @@ -1,11 +1,13 @@ package org.evomaster.core.problem.api.service import com.google.inject.Inject +import org.evomaster.client.java.controller.api.dto.database.execution.FailedQuery import org.evomaster.core.EMConfig import org.evomaster.core.Lazy import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionUtils import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.api.ApiWsIndividual import org.evomaster.core.problem.enterprise.EnterpriseActionGroup import org.evomaster.core.problem.externalservice.httpws.service.HarvestActualHttpWsResponseHandler @@ -143,6 +145,39 @@ abstract class ApiWsStructureMutator : StructureMutator() { sampler: ApiWsSampler ) { addInitializingDbActions(individual, mutatedGenes, sampler) + addInitializingMongoDbActions(individual, mutatedGenes, sampler) + } + + private fun addInitializingMongoDbActions( + individual: EvaluatedIndividual<*>, + mutatedGenes: MutatedGeneSpecification?, + sampler: ApiWsSampler + ) { + if (!config.shouldGenerateMongoData()) { + return + } + + val ind = individual.individual as? T + ?: throw IllegalArgumentException("Invalid individual type") + + val fw = individual.fitness.getViewOfAggregatedFailedFind() + + if (fw.isEmpty()) { + return + } + + val old = mutableListOf().plus(ind.seeInitializingActions().filterIsInstance()) + + val addedInsertions = handleFailedFind(ind, fw, mutatedGenes, sampler) + + // update impact based on added genes + if (mutatedGenes != null && config.isEnabledArchiveGeneSelection()) { + individual.updateImpactGeneDueToAddedInitializationGenes( + mutatedGenes, + old, + addedInsertions + ) + } } private fun addInitializingDbActions( @@ -279,6 +314,23 @@ abstract class ApiWsStructureMutator : StructureMutator() { return addedInsertions } + private fun handleFailedFind( + ind: T, + ff: List, + mutatedGenes: MutatedGeneSpecification?, sampler: ApiWsSampler + ): MutableList>? { + + val addedInsertions = if (mutatedGenes != null) mutableListOf>() else null + + ff.forEach { + val insertions = sampler.sampleMongoInsertion(it.collection, it.documentsType, it.accessedFields) + ind.addInitializingMongoDbActions(actions = insertions) + addedInsertions?.add(insertions) + } + + return addedInsertions + } + private fun findMissing(fw: Map>, dbactions: List): Map> { return fw.filter { e -> diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt index e6f7a09af8..cff492e57c 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt @@ -3,6 +3,7 @@ package org.evomaster.core.problem.enterprise import org.evomaster.core.Lazy import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionUtils +import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.api.ApiWsIndividual import org.evomaster.core.problem.externalservice.ApiExternalServiceAction import org.evomaster.core.problem.graphql.GraphQLAction @@ -12,6 +13,7 @@ import org.evomaster.core.search.service.Randomness import org.evomaster.core.search.tracer.TrackOperator import org.slf4j.Logger import org.slf4j.LoggerFactory +import java.util.* /** @@ -74,7 +76,8 @@ abstract class EnterpriseIndividual( } //TODO in future ll need to refactor to handle multiple databases and NoSQL ones - val db = ChildGroup(GroupsOfChildren.INITIALIZATION_SQL,{e -> e is ActionComponent && e.flatten().all { a -> a is DbAction }}, + //CHANGE: This is momentary for testing. Needs refactor to handle multiple databases + val db = ChildGroup(GroupsOfChildren.INITIALIZATION_MONGO,{e -> e is ActionComponent && e.flatten().all { a -> a is MongoDbAction }}, if(sizeDb==0) -1 else 0 , if(sizeDb==0) -1 else sizeDb-1 ) @@ -101,10 +104,12 @@ abstract class EnterpriseIndividual( ActionFilter.MAIN_EXECUTABLE -> groupsView()!!.getAllInGroup(GroupsOfChildren.MAIN) .flatMap { (it as ActionComponent).flatten() } .filter { it !is DbAction && it !is ApiExternalServiceAction } - ActionFilter.INIT -> groupsView()!!.getAllInGroup(GroupsOfChildren.INITIALIZATION_SQL).flatMap { (it as ActionComponent).flatten() } + //CHANGE: This is momentary for testing. Needs refactor to handle multiple databases + ActionFilter.INIT -> groupsView()!!.getAllInGroup(GroupsOfChildren.INITIALIZATION_MONGO).flatMap { (it as ActionComponent).flatten() } // WARNING: this can still return DbAction and External ones... ActionFilter.NO_INIT -> groupsView()!!.getAllInGroup(GroupsOfChildren.MAIN).flatMap { (it as ActionComponent).flatten() } ActionFilter.ONLY_SQL -> seeAllActions().filterIsInstance() + ActionFilter.ONLY_MONGO -> seeAllActions().filterIsInstance() ActionFilter.NO_SQL -> seeAllActions().filter { it !is DbAction } ActionFilter.ONLY_EXTERNAL_SERVICE -> seeAllActions().filterIsInstance() ActionFilter.NO_EXTERNAL_SERVICE -> seeAllActions().filter { it !is ApiExternalServiceAction } @@ -145,6 +150,8 @@ abstract class EnterpriseIndividual( */ fun seeDbActions() : List = seeActions(ActionFilter.ONLY_SQL) as List + fun seeMongoDbActions() : List = seeActions(ActionFilter.ONLY_MONGO) as List + /** * return a list of all external service actions in [this] individual * that include all the initializing actions among the main actions @@ -194,9 +201,15 @@ abstract class EnterpriseIndividual( private fun getLastIndexOfDbActionToAdd(): Int = groupsView()!!.endIndexForGroupInsertionInclusive(GroupsOfChildren.INITIALIZATION_SQL) + private fun getLastIndexOfMongoDbActionToAdd(): Int = + groupsView()!!.endIndexForGroupInsertionInclusive(GroupsOfChildren.INITIALIZATION_MONGO) + private fun getFirstIndexOfDbActionToAdd(): Int = groupsView()!!.startIndexForGroupInsertionInclusive(GroupsOfChildren.INITIALIZATION_SQL) + private fun getFirstIndexOfMongoDbActionToAdd(): Int = + groupsView()!!.startIndexForGroupInsertionInclusive(GroupsOfChildren.INITIALIZATION_MONGO) + /** * add [actions] at [relativePosition] * if [relativePosition] = -1, append the [actions] at the end @@ -209,6 +222,14 @@ abstract class EnterpriseIndividual( } } + fun addInitializingMongoDbActions(relativePosition: Int=-1, actions: List){ + if (relativePosition < 0) { + addChildrenToGroup(getLastIndexOfMongoDbActionToAdd(), actions, GroupsOfChildren.INITIALIZATION_MONGO) + } else{ + addChildrenToGroup(getFirstIndexOfMongoDbActionToAdd()+relativePosition, actions, GroupsOfChildren.INITIALIZATION_MONGO) + } + } + private fun resetInitializingActions(actions: List){ killChildren { it is DbAction } // TODO: Can be merged with DbAction later diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt index c0c25e5901..9465f35bcc 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt @@ -11,7 +11,10 @@ import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionResult import org.evomaster.core.database.DbActionTransformer import org.evomaster.core.logging.LoggingUtil -import org.evomaster.core.problem.api.service.ApiWsFitness +import org.evomaster.core.mongo.MongoDbAction +import org.evomaster.core.mongo.MongoDbActionResult +import org.evomaster.core.mongo.MongoDbActionTransformer +import org.evomaster.core.mongo.MongoExecution import org.evomaster.core.remote.service.RemoteController import org.evomaster.core.search.Action import org.evomaster.core.search.ActionResult @@ -125,6 +128,31 @@ abstract class EnterpriseFitness : FitnessFunction() where T : Individual return true } + fun doMongoDbCalls(allDbActions: List, actionResults: MutableList) : Boolean { + + if (allDbActions.isEmpty()) { + return true + } + + val mongoDbResults = (allDbActions.indices).map { MongoDbActionResult() } + actionResults.addAll(mongoDbResults) + + val dto = try { + MongoDbActionTransformer.transform(allDbActions) + }catch (e : IllegalArgumentException){ + throw e + } + + val mongoResults = rc.executeMongoDatabaseInsertions(dto) + val executedResults = mongoResults?.executionResults + + executedResults?.forEachIndexed { index, b -> + mongoDbResults[index].setInsertExecutionResult(b) + } + + return true + } + protected fun registerNewAction(action: Action, index: Int){ rc.registerNewAction(getActionDto(action, index)) } @@ -241,5 +269,15 @@ abstract class EnterpriseFitness : FitnessFunction() where T : Individual if (toMinimize.isNotEmpty()) fv.setExtraToMinimize(i, toMinimize) } } + + if (configuration.extractMongoExecutionInfo) { + + for (i in 0 until dto.extraHeuristics.size) { + val extra = dto.extraHeuristics[i] + fv.setMongoExecution(i, MongoExecution.fromDto(extra.mongoExecutionDto)) + } + + fv.aggregateMongoDatabaseData() + } } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt index 8c36f48150..7f74be10e1 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt @@ -4,6 +4,8 @@ import com.google.inject.Inject import org.evomaster.client.java.controller.api.dto.SutInfoDto import org.evomaster.core.database.DbAction import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.mongo.MongoDbAction +import org.evomaster.core.mongo.MongoInsertBuilder import org.evomaster.core.output.OutputFormat import org.evomaster.core.remote.SutProblemException import org.evomaster.core.remote.service.RemoteController @@ -76,6 +78,28 @@ abstract class EnterpriseSampler : Sampler() where T : Individual { return actions } + fun sampleMongoInsertion(collection: String, documentsType: Class<*>, accessedFields: Map): List { + + // Should I use something like this? + //val extraConstraints = randomness.nextBoolean(apc.getExtraSqlDbConstraintsProbability()) + + val actions = MongoInsertBuilder().createMongoInsertionAction(collection, documentsType, accessedFields) + ?: throw IllegalStateException("No MongoDB schema is available") + actions.flatMap{it.seeTopGenes()}.forEach{it.doInitialize(randomness)} + + /* + if (log.isTraceEnabled){ + log.trace("at sampleMongoInsertion, {} insertions are added, and they are {}", actions.size, + actions.joinToString(",") { + if (it is MongoDbAction) it.getResolvedName() else it.getName() + }) + } + + */ + + return actions + } + fun canInsertInto(tableName: String) : Boolean { //TODO might need to refactor/remove once we deal with VIEWs return sqlInsertBuilder?.isTable(tableName) ?: false diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLIndividual.kt index 1cd3bda269..56db3c0c10 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLIndividual.kt @@ -2,6 +2,7 @@ package org.evomaster.core.problem.graphql import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionUtils +import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.api.ApiWsIndividual import org.evomaster.core.problem.enterprise.EnterpriseActionGroup import org.evomaster.core.problem.externalservice.ApiExternalServiceAction @@ -41,6 +42,7 @@ class GraphQLIndividual( GeneFilter.ALL -> seeAllActions().flatMap(Action::seeTopGenes) GeneFilter.NO_SQL -> seeActions(ActionFilter.NO_SQL).flatMap(Action::seeTopGenes) GeneFilter.ONLY_SQL -> seeDbActions().flatMap(DbAction::seeTopGenes) + GeneFilter.ONLY_MONGO -> seeMongoDbActions().flatMap(MongoDbAction::seeTopGenes) GeneFilter.ONLY_EXTERNAL_SERVICE -> seeExternalServiceActions().flatMap(ApiExternalServiceAction::seeTopGenes) } } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt index 8624edee77..913c8a1ae6 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt @@ -2,6 +2,7 @@ package org.evomaster.core.problem.rest import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionUtils +import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.api.ApiWsIndividual import org.evomaster.core.problem.externalservice.ApiExternalServiceAction import org.evomaster.core.problem.rest.resource.RestResourceCalls @@ -33,7 +34,7 @@ class RestIndividual( ): ApiWsIndividual(trackOperator, index, allActions, childTypeVerifier = { RestResourceCalls::class.java.isAssignableFrom(it) - || DbAction::class.java.isAssignableFrom(it) + || DbAction::class.java.isAssignableFrom(it) || MongoDbAction::class.java.isAssignableFrom(it) }, groups) { companion object{ @@ -76,7 +77,8 @@ class RestIndividual( index, children.map { it.copy() }.toMutableList() as MutableList, mainSize = groupsView()!!.sizeOfGroup(GroupsOfChildren.MAIN), - dbSize = groupsView()!!.sizeOfGroup(GroupsOfChildren.INITIALIZATION_SQL) + //CHANGE: This is momentary for testing. Needs refactor to handle multiple databases + dbSize = groupsView()!!.sizeOfGroup(GroupsOfChildren.INITIALIZATION_MONGO) ) } @@ -104,6 +106,7 @@ class RestIndividual( GeneFilter.ALL -> seeAllActions().flatMap(Action::seeTopGenes) GeneFilter.NO_SQL -> seeActions(ActionFilter.NO_SQL).flatMap(Action::seeTopGenes) GeneFilter.ONLY_SQL -> seeDbActions().flatMap(DbAction::seeTopGenes) + GeneFilter.ONLY_MONGO -> seeMongoDbActions().flatMap(MongoDbAction::seeTopGenes) GeneFilter.ONLY_EXTERNAL_SERVICE -> seeExternalServiceActions().flatMap(ApiExternalServiceAction::seeTopGenes) } } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceCalls.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceCalls.kt index e86fbb6cb9..3f7945656a 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceCalls.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceCalls.kt @@ -3,6 +3,7 @@ package org.evomaster.core.problem.rest.resource import org.evomaster.core.Lazy import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionUtils +import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.api.param.Param @@ -37,7 +38,7 @@ class RestResourceCalls( randomness: Randomness? = null ) : ActionTree( children, - { k -> DbAction::class.java.isAssignableFrom(k) || EnterpriseActionGroup::class.java.isAssignableFrom(k) } + { k -> DbAction::class.java.isAssignableFrom(k) || MongoDbAction::class.java.isAssignableFrom(k) || EnterpriseActionGroup::class.java.isAssignableFrom(k) } ) { constructor( @@ -77,6 +78,10 @@ class RestResourceCalls( get() { return children.flatMap { it.flatten() }.filterIsInstance() } + private val mongoDbActions: List + get() { + return children.flatMap { it.flatten() }.filterIsInstance() + } private val externalServiceActions: List get() { @@ -170,11 +175,13 @@ class RestResourceCalls( fun seeActions(filter: ActionFilter): List { return when (filter) { ActionFilter.ALL -> dbActions.plus(externalServiceActions).plus(mainActions) - ActionFilter.INIT, ActionFilter.ONLY_SQL -> dbActions + ActionFilter.INIT -> dbActions.plus(mongoDbActions) + ActionFilter.ONLY_SQL -> dbActions ActionFilter.NO_INIT, ActionFilter.NO_SQL -> externalServiceActions.plus(mainActions) ActionFilter.MAIN_EXECUTABLE -> mainActions ActionFilter.ONLY_EXTERNAL_SERVICE -> externalServiceActions ActionFilter.NO_EXTERNAL_SERVICE -> dbActions.plus(mainActions) + ActionFilter.ONLY_MONGO -> mongoDbActions } } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt index 8b1f44bda9..126570443d 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt @@ -1,6 +1,7 @@ package org.evomaster.core.problem.rest.service import org.evomaster.core.database.DbAction +import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestCallResult import org.evomaster.core.problem.rest.RestIndividual @@ -34,6 +35,7 @@ open class RestFitness : AbstractRestFitness() { val actionResults: MutableList = mutableListOf() doDbCalls(individual.seeInitializingActions().filterIsInstance(), actionResults = actionResults) + doMongoDbCalls(individual.seeInitializingActions().filterIsInstance(), actionResults = actionResults) val fv = FitnessValue(individual.size().toDouble()) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt index f9a32bb2a4..0d9ca43cea 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt @@ -3,6 +3,7 @@ package org.evomaster.core.problem.rest.service import com.google.inject.Inject import org.evomaster.core.database.DbAction +import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.enterprise.EnterpriseActionGroup import org.evomaster.core.problem.externalservice.httpws.HttpExternalServiceAction import org.evomaster.core.problem.externalservice.httpws.HttpExternalServiceRequest @@ -61,6 +62,8 @@ class RestResourceFitness : AbstractRestFitness() { actionResults ) + doMongoDbCalls(individual.seeInitializingActions().filterIsInstance(), actionResults) + val cookies = getCookies(individual) val tokens = getTokens(individual) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt index 96ae923894..4bc03e1a36 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt @@ -3,6 +3,7 @@ package org.evomaster.core.problem.rpc import org.evomaster.core.Lazy import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionUtils +import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.api.ApiWsIndividual import org.evomaster.core.problem.enterprise.EnterpriseActionGroup import org.evomaster.core.problem.externalservice.ApiExternalServiceAction @@ -64,6 +65,7 @@ class RPCIndividual( return when (filter) { GeneFilter.ALL -> seeAllActions().flatMap(Action::seeTopGenes) GeneFilter.NO_SQL -> seeActions(ActionFilter.NO_SQL).flatMap(Action::seeTopGenes) + GeneFilter.ONLY_MONGO -> seeMongoDbActions().flatMap(MongoDbAction::seeTopGenes) GeneFilter.ONLY_SQL -> seeDbActions().flatMap(DbAction::seeTopGenes) GeneFilter.ONLY_EXTERNAL_SERVICE -> seeExternalServiceActions().flatMap(ApiExternalServiceAction::seeTopGenes) } diff --git a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt index 901a47c286..1b1f4f1fd0 100644 --- a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt +++ b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt @@ -4,9 +4,7 @@ import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException import com.google.inject.Inject import org.evomaster.client.java.controller.api.ControllerConstants import org.evomaster.client.java.controller.api.dto.* -import org.evomaster.client.java.controller.api.dto.database.operations.DatabaseCommandDto -import org.evomaster.client.java.controller.api.dto.database.operations.InsertionResultsDto -import org.evomaster.client.java.controller.api.dto.database.operations.QueryResultDto +import org.evomaster.client.java.controller.api.dto.database.operations.* import org.evomaster.core.EMConfig import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.remote.NoRemoteConnectionException @@ -420,6 +418,10 @@ class RemoteControllerImplementation() : RemoteController{ return executeDatabaseCommandAndGetResults(dto, object : GenericType>() {}) } + override fun executeMongoDatabaseInsertions(dto: MongoDatabaseCommandDto): MongoInsertionResultsDto? { + return executeMongoDatabaseCommandAndGetResults(dto, object : GenericType>() {}) + } + private fun executeDatabaseCommandAndGetResults(dto: DatabaseCommandDto, type: GenericType>): T?{ val response = makeHttpCall { @@ -438,6 +440,24 @@ class RemoteControllerImplementation() : RemoteController{ return dto?.data } + private fun executeMongoDatabaseCommandAndGetResults(dto: MongoDatabaseCommandDto, type: GenericType>): T? { + + val response = makeHttpCall { + getWebTarget() + .path(ControllerConstants.MONGO_INSERTION) + .request() + .post(Entity.entity(dto, MediaType.APPLICATION_JSON_TYPE)) + } + + val dto = getDtoFromResponse(response, type) + + if (!checkResponse(response, dto, "Failed to execute database command")) { + return null + } + + return dto?.data + } + private fun wasSuccess(response: Response?): Boolean { return response?.statusInfo?.family?.equals(Response.Status.Family.SUCCESSFUL) ?: false diff --git a/core/src/main/kotlin/org/evomaster/core/search/ActionFilter.kt b/core/src/main/kotlin/org/evomaster/core/search/ActionFilter.kt index 3beb8301b8..bf97763a7c 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/ActionFilter.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/ActionFilter.kt @@ -26,6 +26,11 @@ enum class ActionFilter { */ ONLY_SQL, + /** + * actions which are MONGO-related actions + */ + ONLY_MONGO, + /** * actions which are not SQL-related actions */ diff --git a/core/src/main/kotlin/org/evomaster/core/search/EvaluatedAction.kt b/core/src/main/kotlin/org/evomaster/core/search/EvaluatedAction.kt index 86dc4fa535..f3253c5b09 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/EvaluatedAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/EvaluatedAction.kt @@ -2,6 +2,8 @@ package org.evomaster.core.search import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionResult +import org.evomaster.core.mongo.MongoDbAction +import org.evomaster.core.mongo.MongoDbActionResult open class EvaluatedAction(open val action: Action, open val result: ActionResult) @@ -9,4 +11,6 @@ open class EvaluatedAction(open val action: Action, open val result: ActionResul /** * specialized evaluated db action */ -class EvaluatedDbAction(override val action: DbAction, override val result: DbActionResult) : EvaluatedAction(action, result) \ No newline at end of file +class EvaluatedDbAction(override val action: DbAction, override val result: DbActionResult) : EvaluatedAction(action, result) + +class EvaluatedMongoDbAction(override val action: MongoDbAction, override val result: MongoDbActionResult) : EvaluatedAction(action, result) \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/search/EvaluatedIndividual.kt b/core/src/main/kotlin/org/evomaster/core/search/EvaluatedIndividual.kt index bf382a1bf9..36960f9cfa 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/EvaluatedIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/EvaluatedIndividual.kt @@ -11,6 +11,8 @@ import org.evomaster.core.Lazy import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionResult import org.evomaster.core.logging.LoggingUtil +import org.evomaster.core.mongo.MongoDbAction +import org.evomaster.core.mongo.MongoDbActionResult import org.evomaster.core.problem.externalservice.ApiExternalServiceAction import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestCallResult @@ -712,7 +714,8 @@ class EvaluatedIndividual( /* if there exist other types of action (ie, not DbAction), this might need to be extended */ - action = individual.seeInitializingActions().filterIsInstance().find { it.seeTopGenes().contains(gene) } + //CHANGE: This is momentary for testing. Needs refactor to handle multiple databases + action = individual.seeInitializingActions().filterIsInstance().find { it.seeTopGenes().contains(gene) } if (action != null) { return impactInfo.getGene( @@ -764,7 +767,7 @@ class EvaluatedIndividual( val allExistingData = individual.seeInitializingActions().filter { it is DbAction && it.representExistingData } val diff = individual.seeInitializingActions() - .filter { !old.contains(it) && it is DbAction && !it.representExistingData } + .filter { !old.contains(it) && ((it is DbAction && !it.representExistingData) || it is MongoDbAction) } if (allExistingData.isNotEmpty()) impactInfo.updateExistingSQLData(allExistingData.size) @@ -802,7 +805,7 @@ class EvaluatedIndividual( Lazy.assert { individual.seeInitializingActions() - .filter { it is DbAction && !it.representExistingData }.size == impactInfo.getSizeOfActionImpacts(true) + .filter { (it is DbAction && !it.representExistingData) || it is MongoDbAction }.size == impactInfo.getSizeOfActionImpacts(true) } } diff --git a/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt b/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt index b0b4081e82..4a67ed9af1 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt @@ -1,10 +1,13 @@ package org.evomaster.core.search import org.evomaster.client.java.controller.api.dto.BootTimeInfoDto +import org.evomaster.client.java.controller.api.dto.database.execution.FailedQuery +import org.evomaster.client.java.controller.api.dto.database.execution.MongoExecutionDto import org.evomaster.core.EMConfig import org.evomaster.core.database.DatabaseExecution import org.evomaster.core.EMConfig.SecondaryObjectiveStrategy.* import org.evomaster.core.Lazy +import org.evomaster.core.mongo.MongoExecution import org.evomaster.core.problem.externalservice.httpws.HttpWsExternalService import org.evomaster.core.problem.externalservice.httpws.HttpExternalServiceRequest import org.evomaster.core.search.service.IdMapper @@ -73,12 +76,20 @@ class FitnessValue( */ val databaseExecutions: MutableMap = mutableMapOf() + val mongoExecutions: MutableMap = mutableMapOf() + /** * When SUT does SQL commands using WHERE, keep track of when those "fails" (ie evaluate * to false), in particular the tables and columns in them involved */ private val aggregatedFailedWhere: MutableMap> = mutableMapOf() + /** + * When SUT does MONGO commands using FIND, keep track of when those "fails" (ie evaluate + * to false), in particular the collection and fields in them involved + */ + private val aggregatedFailedFind: MutableList = mutableListOf() + /** * To keep track of accessed external services prevent from adding them again * TODO: This is not completed, not need to consider for review for now @@ -104,7 +115,9 @@ class FitnessValue( copy.targets.putAll(this.targets) copy.extraToMinimize.putAll(this.extraToMinimize) copy.databaseExecutions.putAll(this.databaseExecutions) //note: DatabaseExecution supposed to be immutable + copy.mongoExecutions.putAll(this.mongoExecutions) copy.aggregateDatabaseData() + copy.aggregateMongoDatabaseData() copy.executionTimeMs = executionTimeMs copy.accessedExternalServiceRequests.putAll(this.accessedExternalServiceRequests) copy.accessedDefaultWM.putAll(this.accessedDefaultWM.toMap()) @@ -124,6 +137,10 @@ class FitnessValue( {x -> x.failedWhere} )) } + fun aggregateMongoDatabaseData(){ + aggregatedFailedFind.clear() + mongoExecutions.values.map { aggregatedFailedFind.addAll(it.failedQueries) } + } fun setExtraToMinimize(actionIndex: Int, list: List) { extraToMinimize[actionIndex] = list.sorted() @@ -133,6 +150,10 @@ class FitnessValue( databaseExecutions[actionIndex] = databaseExecution } + fun setMongoExecution(actionIndex: Int, mongoExecution: MongoExecution){ + mongoExecutions[actionIndex] = mongoExecution + } + fun isAnyDatabaseExecutionInfo() = databaseExecutions.isNotEmpty() fun getViewOfData(): Map { @@ -141,6 +162,8 @@ class FitnessValue( fun getViewOfAggregatedFailedWhere() = aggregatedFailedWhere + fun getViewOfAggregatedFailedFind() = aggregatedFailedFind + fun doesCover(target: Int): Boolean { return targets[target]?.distance == MAX_VALUE } diff --git a/core/src/main/kotlin/org/evomaster/core/search/GroupsOfChildren.kt b/core/src/main/kotlin/org/evomaster/core/search/GroupsOfChildren.kt index 10628cf74c..af18fec076 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/GroupsOfChildren.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/GroupsOfChildren.kt @@ -25,6 +25,8 @@ class GroupsOfChildren( const val INITIALIZATION_SQL = "INITIALIZATION_SQL" + const val INITIALIZATION_MONGO = "INITIALIZATION_MONGO" + const val EXTERNAL_SERVICES = "EXTERNAL_SERVICES" const val RESOURCE_SQL = "RESOURCE_SQL" diff --git a/core/src/main/kotlin/org/evomaster/core/search/Individual.kt b/core/src/main/kotlin/org/evomaster/core/search/Individual.kt index e1dbd721ce..4667e81c92 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/Individual.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/Individual.kt @@ -159,7 +159,7 @@ abstract class Individual(override var trackOperator: TrackOperator? = null, throw IllegalStateException("${this::class.java.simpleName}: copyContent() IS NOT IMPLEMENTED") } - enum class GeneFilter { ALL, NO_SQL, ONLY_SQL, ONLY_EXTERNAL_SERVICE } + enum class GeneFilter { ALL, NO_SQL, ONLY_SQL, ONLY_MONGO, ONLY_EXTERNAL_SERVICE } /** * Return a view of all the Genes in this chromosome/individual diff --git a/core/src/main/kotlin/org/evomaster/core/search/Solution.kt b/core/src/main/kotlin/org/evomaster/core/search/Solution.kt index 70f6e2fef2..d3ce42c052 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/Solution.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/Solution.kt @@ -1,6 +1,7 @@ package org.evomaster.core.search import org.evomaster.core.database.DbAction +import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.output.Termination import org.evomaster.core.problem.externalservice.httpws.HttpExternalServiceAction @@ -51,4 +52,8 @@ where T : Individual { fun hasAnySqlAction() : Boolean{ return individuals.any { ind -> ind.individual.seeAllActions().any { a -> a is DbAction}} } + + fun hasAnyMongoAction() : Boolean{ + return individuals.any { ind -> ind.individual.seeAllActions().any { a -> a is MongoDbAction}} + } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactsOfIndividual.kt b/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactsOfIndividual.kt index 52ca8395ec..19b2dc8c94 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactsOfIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactsOfIndividual.kt @@ -1,6 +1,7 @@ package org.evomaster.core.search.impact.impactinfocollection import org.evomaster.core.database.DbAction +import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.search.Action import org.evomaster.core.search.FitnessValue import org.evomaster.core.search.Individual @@ -186,7 +187,7 @@ open class ImpactsOfIndividual( * thus, we need to synchronize the action impacts based on the [individual] */ fun syncBasedOnIndividual(individual: Individual) { - val initActions = individual.seeInitializingActions().filterIsInstance() + val initActions = individual.seeInitializingActions().filter { it is DbAction || it is MongoDbAction } //for initialization due to db action fixing val diff = initActions.size - initActionImpacts.getOriginalSize() if (diff < 0) { //truncation diff --git a/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/InitializationActionImpacts.kt b/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/InitializationActionImpacts.kt index 6e0a18cd32..78c3ddcf2a 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/InitializationActionImpacts.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/InitializationActionImpacts.kt @@ -2,6 +2,7 @@ package org.evomaster.core.search.impact.impactinfocollection import org.evomaster.core.Lazy import org.evomaster.core.database.DbAction +import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.search.Action import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -194,7 +195,7 @@ class InitializationActionImpacts(val abstract: Boolean, val enableImpactOnDupli } val original = completeSequence.size - val seq = list.filterIsInstance().filter{ !it.representExistingData } + val seq = list.filter{(it is DbAction && !it.representExistingData) || it is MongoDbAction } if (seq.size > original) { log.warn("there are more db actions after the truncation") return diff --git a/core/src/test/kotlin/org/evomaster/core/database/SqlInsertBuilderTest.kt b/core/src/test/kotlin/org/evomaster/core/database/SqlInsertBuilderTest.kt index 047680f516..6960c432e5 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/SqlInsertBuilderTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/SqlInsertBuilderTest.kt @@ -1,8 +1,6 @@ package org.evomaster.core.database -import org.evomaster.client.java.controller.api.dto.database.operations.DatabaseCommandDto -import org.evomaster.client.java.controller.api.dto.database.operations.InsertionResultsDto -import org.evomaster.client.java.controller.api.dto.database.operations.QueryResultDto +import org.evomaster.client.java.controller.api.dto.database.operations.* import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor import org.evomaster.core.search.gene.* @@ -414,6 +412,10 @@ class SqlInsertBuilderTest { return null } + override fun executeMongoDatabaseInsertions(dto: MongoDatabaseCommandDto): MongoInsertionResultsDto? { + return null + } + override fun executeDatabaseCommandAndGetQueryResults(dto: DatabaseCommandDto): QueryResultDto? { return SqlScriptRunner.execCommand(connection, dto.command).toDto() } diff --git a/core/src/test/kotlin/org/evomaster/core/output/service/FakeController.kt b/core/src/test/kotlin/org/evomaster/core/output/service/FakeController.kt index 9aad16f63f..db3fcebcbc 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/service/FakeController.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/service/FakeController.kt @@ -3,6 +3,8 @@ package org.evomaster.core.output.service import org.evomaster.client.java.controller.SutHandler import org.evomaster.client.java.controller.api.dto.database.operations.InsertionDto import org.evomaster.client.java.controller.api.dto.database.operations.InsertionResultsDto +import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionDto +import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionResultsDto import org.evomaster.client.java.controller.internal.db.DbSpecification @@ -21,6 +23,10 @@ class FakeController : SutHandler { return null } + override fun execInsertionsIntoMongoDatabase(insertions: MutableList?): MongoInsertionResultsDto? { + return null + } + override fun getDbSpecifications(): MutableList? { return null } diff --git a/core/src/test/kotlin/org/evomaster/core/problem/external/service/DummyController.kt b/core/src/test/kotlin/org/evomaster/core/problem/external/service/DummyController.kt index 4978955577..755ed8eb14 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/external/service/DummyController.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/external/service/DummyController.kt @@ -1,9 +1,7 @@ package org.evomaster.core.problem.external.service import org.evomaster.client.java.controller.api.dto.* -import org.evomaster.client.java.controller.api.dto.database.operations.DatabaseCommandDto -import org.evomaster.client.java.controller.api.dto.database.operations.InsertionResultsDto -import org.evomaster.client.java.controller.api.dto.database.operations.QueryResultDto +import org.evomaster.client.java.controller.api.dto.database.operations.* import org.evomaster.core.remote.service.RemoteController class DummyController: RemoteController { @@ -71,4 +69,8 @@ class DummyController: RemoteController { TODO("Not yet implemented") } + override fun executeMongoDatabaseInsertions(dto: MongoDatabaseCommandDto): MongoInsertionResultsDto? { + TODO("Not yet implemented") + } + } \ No newline at end of file diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualTestBase.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualTestBase.kt index c44bb7d033..9974a134e5 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualTestBase.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualTestBase.kt @@ -19,10 +19,7 @@ import io.swagger.v3.oas.models.responses.ApiResponse import io.swagger.v3.oas.models.responses.ApiResponses import org.evomaster.client.java.controller.api.dto.* import org.evomaster.client.java.controller.api.dto.database.execution.ExecutionDto -import org.evomaster.client.java.controller.api.dto.database.operations.DataRowDto -import org.evomaster.client.java.controller.api.dto.database.operations.DatabaseCommandDto -import org.evomaster.client.java.controller.api.dto.database.operations.InsertionResultsDto -import org.evomaster.client.java.controller.api.dto.database.operations.QueryResultDto +import org.evomaster.client.java.controller.api.dto.database.operations.* import org.evomaster.client.java.controller.api.dto.problem.RestProblemDto import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor @@ -663,6 +660,10 @@ abstract class RestIndividualTestBase { return null } + override fun executeMongoDatabaseInsertions(dto: MongoDatabaseCommandDto): MongoInsertionResultsDto? { + return null + } + } @AfterEach diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/resource/ResourceNodeWithDbTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/resource/ResourceNodeWithDbTest.kt index 8f2cba320e..57b0b64db0 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/resource/ResourceNodeWithDbTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/resource/ResourceNodeWithDbTest.kt @@ -1,9 +1,7 @@ package org.evomaster.core.problem.rest.resource import io.swagger.parser.OpenAPIParser -import org.evomaster.client.java.controller.api.dto.database.operations.DatabaseCommandDto -import org.evomaster.client.java.controller.api.dto.database.operations.InsertionResultsDto -import org.evomaster.client.java.controller.api.dto.database.operations.QueryResultDto +import org.evomaster.client.java.controller.api.dto.database.operations.* import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor import org.evomaster.core.EMConfig @@ -273,6 +271,10 @@ class ResourceNodeWithDbTest { return null } + override fun executeMongoDatabaseInsertions(dto: MongoDatabaseCommandDto): MongoInsertionResultsDto? { + return null + } + override fun executeDatabaseCommandAndGetQueryResults(dto: DatabaseCommandDto): QueryResultDto? { return SqlScriptRunner.execCommand(connection, dto.command).toDto() } diff --git a/e2e-tests/spring-rest-mongo/src/test/java/com/foo/spring/rest/mongo/MongoController.java b/e2e-tests/spring-rest-mongo/src/test/java/com/foo/spring/rest/mongo/MongoController.java index 912c220815..0f0405bde4 100644 --- a/e2e-tests/spring-rest-mongo/src/test/java/com/foo/spring/rest/mongo/MongoController.java +++ b/e2e-tests/spring-rest-mongo/src/test/java/com/foo/spring/rest/mongo/MongoController.java @@ -51,6 +51,9 @@ public String startSut() { "spring.data.mongodb.database=" + databaseName ); + // CHANGE: Is necessary to crete the collection first? + mongoClient.getDatabase("persons").createCollection("person"); + ctx = app.run(); return "http://localhost:" + getSutPort(); @@ -102,4 +105,9 @@ protected int getSutPort() { .getPropertySources().get("server.ports").getSource()) .get("local.server.port"); } + + @Override + public MongoClient getMongoConnection() { + return mongoClient; + } } From fcbd46f77ae8f77464fab67c6245a01ffcdeeaac Mon Sep 17 00:00:00 2001 From: Juan Pablo Galeotti Date: Thu, 18 May 2023 18:04:15 -0300 Subject: [PATCH 023/345] @EMail implemented --- .../PatternMatchingHelper.java | 17 ++++++- .../classes/MatcherClassReplacement.java | 9 ++-- .../ValidatorClassReplacement.java | 6 +-- .../core/database/DbActionGeneBuilder.kt | 5 +- .../core/database/SqlInsertBuilder.kt | 49 +++---------------- .../evomaster/core/parser/RegexHandlerTest.kt | 17 ++++++- .../examples/spring/db/jpa/EntityJPAData.java | 2 +- 7 files changed, 49 insertions(+), 56 deletions(-) diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/PatternMatchingHelper.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/PatternMatchingHelper.java index 6047ff4f85..394e83b2d3 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/PatternMatchingHelper.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/PatternMatchingHelper.java @@ -7,14 +7,24 @@ import org.evomaster.client.java.instrumentation.staticstate.ExecutionTracer; import java.util.Objects; +import java.util.regex.Matcher; import java.util.regex.Pattern; public class PatternMatchingHelper { + + private static final int DEFAULT_PATTERN_FLAGS = 0; + /** * Invocation to Pattern.matches() is free of side-effects. */ public static boolean matches(String regex, String input, String idTemplate) { + return matches(regex, DEFAULT_PATTERN_FLAGS, input, idTemplate); + } + /** + * Invocation to Pattern.matches() is free of side-effects. + */ + public static boolean matches(String regex, int flags, String input, String idTemplate) { Objects.requireNonNull(regex); Objects.requireNonNull(input); @@ -23,11 +33,14 @@ public static boolean matches(String regex, String input, String idTemplate) { new StringSpecializationInfo(StringSpecialization.REGEX_WHOLE, regex)); } + Pattern p = Pattern.compile(regex, flags); + Matcher m = p.matcher(input); + boolean matches = m.matches(); + if (idTemplate == null) { - return Pattern.matches(regex, input); + return matches; } - boolean matches = Pattern.matches(regex, input); if (matches) { ExecutionTracer.executedReplacedMethod(idTemplate, ReplacementType.BOOLEAN, diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MatcherClassReplacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MatcherClassReplacement.java index 20dc199ca3..c3a2e6f346 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MatcherClassReplacement.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MatcherClassReplacement.java @@ -17,7 +17,7 @@ */ public class MatcherClassReplacement implements MethodReplacementClass { - private static Field textField = null; + private static final Field textField; static { try { @@ -43,14 +43,13 @@ public Class getTargetClass() { */ @Replacement(type = ReplacementType.BOOLEAN, category = ReplacementCategory.BASE) public static boolean matches(Matcher caller, String idTemplate) { + Objects.requireNonNull(caller); - if (caller == null) { - caller.matches(); - } String text = getText(caller); String pattern = caller.pattern().toString(); + int flags = caller.pattern().flags(); - boolean patternMatchesResult = PatternMatchingHelper.matches(pattern, text, idTemplate); + boolean patternMatchesResult = PatternMatchingHelper.matches(pattern, flags, text, idTemplate); TaintType taintType = ExecutionTracer.getTaintType(text); diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/ValidatorClassReplacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/ValidatorClassReplacement.java index bd79656013..a034e7800a 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/ValidatorClassReplacement.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/ValidatorClassReplacement.java @@ -31,7 +31,7 @@ protected String getNameOfThirdPartyTargetClass() { castTo = "java.util.Set" ) // Set> validate(T object, Class... groups); - public static Object validate(Object caller, Object object, Class... groups ) throws Exception { + public static Object validate(Object caller, Object object, Class... groups ) throws Throwable { if(caller == null){ throw new NullPointerException(); @@ -39,7 +39,6 @@ public static Object validate(Object caller, Object object, Class... groups Method original = getOriginal(singleton, "validate", caller); - Object result = null; /* Looking at last line is too problematic. @@ -52,12 +51,13 @@ public static Object validate(Object caller, Object object, Class... groups */ String lastLine = "";//ExecutionTracer.getLastExecutedStatement(); //can be null + Object result; try { result = original.invoke(caller, object, groups); } catch (IllegalAccessException e){ throw new RuntimeException(e);// ah, the beauty of Java... } catch (InvocationTargetException e){ - throw (Exception) e.getCause(); + throw e.getCause(); // this could be a java.lang.AssertionError } if(object != null){ diff --git a/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt b/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt index f033750612..9b6a97caa3 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt @@ -725,8 +725,9 @@ class DbActionGeneBuilder { similarToPatterns: List, databaseType: DatabaseType ): RegexGene { - return when { - databaseType == DatabaseType.POSTGRES -> buildPostgresSimilarToRegexGene(geneName, similarToPatterns) + return when(databaseType) { + DatabaseType.POSTGRES, + DatabaseType.H2 -> buildPostgresSimilarToRegexGene(geneName, similarToPatterns) //TODO: support other database SIMILAR_TO check expressions else -> throw UnsupportedOperationException( "Must implement similarTo expressions for database %s".format( diff --git a/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt b/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt index cc134a2371..659a52eea8 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt @@ -51,43 +51,12 @@ class SqlInsertBuilder( private val name: String + companion object { private val log: Logger = LoggerFactory.getLogger(SqlInsertBuilder::class.java) - /** - * Converts a regular expression pattern to the equivalent SQL LIKE pattern. - * - * @param regexPattern the regular expression pattern to convert. - * @return the equivalent SQL LIKE pattern. - */ - private fun convertRegexToSqlLikePattern(regexPattern: String): String { - var likePattern = regexPattern - - // Replace ^ with % - likePattern = likePattern.replace("^", "%") - - // Replace $ with % - likePattern = likePattern.replace("$", "%") - - // Escape special characters with \ - likePattern = likePattern.replace("\\", "\\\\") - - // Replace . with _ - likePattern = likePattern.replace(".", "_") - - // Replace * with % - likePattern = likePattern.replace("*", "%") - - // Replace + with % - likePattern = likePattern.replace("+", "%") - - // Replace ? with _ - likePattern = likePattern.replace("?", "_") - - return likePattern - } - - } + private const val EMAIL_SIMILAR_TO_PATTERN = "[A-Za-z]{2,}@[A-Za-z]+.[A-Za-z]{2,}" + } init { @@ -320,17 +289,13 @@ class SqlInsertBuilder( extra.constraints.isNegativeOrZero?.let { if (it) 0 else null } ).minOrNull() - val likePatterns = (column.likePatterns ?: mutableListOf()).toMutableList() + val similarToPatterns = (column.similarToPatterns ?: mutableListOf()).toMutableList() extra.constraints.isEmail?.let { if (it) { - val emailRegexp = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" - likePatterns.add(convertRegexToSqlLikePattern(emailRegexp)) + similarToPatterns.add(EMAIL_SIMILAR_TO_PATTERN) } } - extra.constraints.patternRegExp?.let { - likePatterns.add(convertRegexToSqlLikePattern(it)) - } - val mergedLikePatterns: List? = likePatterns.takeIf { it.isNotEmpty() } + val mergedSimilarToPatterns: List? = similarToPatterns.takeIf { it.isNotEmpty() } //TODO all other constraints @@ -340,7 +305,7 @@ class SqlInsertBuilder( enumValuesAsStrings = mergedEnumValuesAsStrings, lowerBound = mergedLowerBound, upperBound = mergedUpperBound, - likePatterns = mergedLikePatterns, + similarToPatterns = mergedSimilarToPatterns, isNotBlank = extra.constraints.isNotBlank ) } diff --git a/core/src/test/kotlin/org/evomaster/core/parser/RegexHandlerTest.kt b/core/src/test/kotlin/org/evomaster/core/parser/RegexHandlerTest.kt index b3fcd8201b..caa6ce22fa 100644 --- a/core/src/test/kotlin/org/evomaster/core/parser/RegexHandlerTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/parser/RegexHandlerTest.kt @@ -9,6 +9,7 @@ import org.evomaster.core.search.service.mutator.MutationWeightControl import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows +import java.util.regex.Pattern internal class RegexHandlerTest{ @@ -18,7 +19,7 @@ internal class RegexHandlerTest{ //should not throw exception val s = "^[23456789ABCDEFGHJKMNPQRSTUVWXYZ]{10}$" RegexHandler.createGeneForJVM(s) - RegexHandler.createGeneForJVM(RegexSharedUtils.handlePartialMatch(s)); + RegexHandler.createGeneForJVM(RegexSharedUtils.handlePartialMatch(s)) val x = "^[XxA-Fa-f0-9]([A-Fa-f0-9]{63})$" RegexHandler.createGeneForJVM(x) @@ -53,5 +54,19 @@ internal class RegexHandlerTest{ } } + @Test + fun testPostgresSimilarToEmailRegex() { + val regex = "[A-Za-z0-9-.+]+@[A-Za-z0-9-.]+.[A-Za-z]{2,}" + val pattern = Pattern.compile(regex) + val regexGene = RegexHandler.createGeneForPostgresSimilarTo(regex) + val rand = Randomness() + + repeat(1000){ + regexGene.randomize(rand,true) + assertTrue(pattern.matcher(regexGene.getValueAsRawString()).find()) + } + + } + } \ No newline at end of file diff --git a/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java b/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java index 348499999b..34a5c762d8 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java +++ b/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java @@ -46,7 +46,7 @@ public void setNotBlank(String notBlank) { this.notblank = notBlank; } - //@Email + @Email @NotNull private String email; From 5b8cba5f193fb615574570f9ade3d209e6af0f40 Mon Sep 17 00:00:00 2001 From: Juan Pablo Galeotti Date: Fri, 19 May 2023 18:34:23 -0300 Subject: [PATCH 024/345] @Positive, @PositiveOrZero, @Negative and @NegativeOrZero are implemented --- .../controller/internal/SutController.java | 7 ++-- .../examples/spring/db/jpa/EntityJPAData.java | 32 +++++++++++++++++++ .../src/main/resources/sql/entityjpa.sql | 6 +++- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index c8a26e195b..c24ae2b8a7 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -181,7 +181,7 @@ public final boolean stopTheControllerServer() { controllerServer.stop(); return true; } catch (Exception e) { - SimpleLogger.error("Failed to stop the controller server: " + e.toString()); + SimpleLogger.error("Failed to stop the controller server: " + e); return false; } } @@ -892,7 +892,7 @@ public Object executeRPCEndpoint(String json) throws Exception{ * @param client is the client to execute the endpoint * @param endpoint is the endpoint to be executed */ - private final Object executeRPCEndpoint(Object client, EndpointSchema endpoint) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException { + private Object executeRPCEndpoint(Object client, EndpointSchema endpoint) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException { if (endpoint.getRequestParams().isEmpty()){ Method method = client.getClass().getDeclaredMethod(endpoint.getName()); return method.invoke(client); @@ -1150,7 +1150,7 @@ protected UnitsInfoDto getUnitsInfoDto(UnitsInfoRecorder recorder){ ec.decimalMaxValue = c.getDecimalMaxValue(); ec.isNegative = c.getIsNegative(); ec.isNegativeOrZero = c.getIsNegativeOrZero(); - ec.isPositive = c.getIsPositiveOrZero(); + ec.isPositive = c.getIsPositive(); ec.isPositiveOrZero = c.getIsPositiveOrZero(); ec.isFuture = c.getIsFuture(); ec.isFutureOrPresent = c.getIsFutureOrPresent(); @@ -1243,7 +1243,6 @@ public void resetDatabase(List tablesToClean) { * This is mainly used as workaround for cases in which EM's instrumentation crashes due * to some bugs in it. * (This is also the reason why it is not abstract) - * * Note: we currently cannot test this in a E2E, as agent is loaded _before_ te controller is defined */ public String packagesToSkipInstrumentation(){ diff --git a/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java b/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java index 34a5c762d8..32115776fe 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java +++ b/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java @@ -57,4 +57,36 @@ public String getEmail() { public void setEmail(String email) { this.email = email; } + + @Negative + private int negative; + + public int getNegative() { return negative; } + + public void setNegative(int negative) { this.negative = negative; } + + @NegativeOrZero + @Column(name="negative_or_zero") + private int negativeOrZero; + + public int getNegativeOrZero() { return negativeOrZero; } + + public void setNegativeOrZero(int negativeOrZero) { this.negativeOrZero = negativeOrZero; } + + @Positive + private int positive; + + public int getPositive() { return positive; } + + public void setPositive(int positive) { this.positive = positive; } + + @PositiveOrZero + @Column(name="positive_or_zero") + private int positiveOrZero; + + public int getPositiveOrZero() { return positiveOrZero; } + + public void setPositiveOrZero(int positiveOrZero) { this.positiveOrZero = positiveOrZero; } + + } diff --git a/e2e-tests/spring-rest-openapi-v2/src/main/resources/sql/entityjpa.sql b/e2e-tests/spring-rest-openapi-v2/src/main/resources/sql/entityjpa.sql index cca50d81f3..a578b4babe 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/main/resources/sql/entityjpa.sql +++ b/e2e-tests/spring-rest-openapi-v2/src/main/resources/sql/entityjpa.sql @@ -2,5 +2,9 @@ create table IF NOT EXISTS existing_table ( id integer primary key, x1 integer, notBlank varchar, - email varchar not null + email varchar not null, + negative integer not null, + negative_or_zero integer not null, + positive integer not null, + positive_or_zero integer not null ); \ No newline at end of file From f09ac29f109ad5be650a9e818703b95be3ae6134 Mon Sep 17 00:00:00 2001 From: Juan Pablo Galeotti Date: Sat, 20 May 2023 15:26:40 -0300 Subject: [PATCH 025/345] @Size(min,max) implemented --- .../core/database/DbActionGeneBuilder.kt | 11 ++- .../core/database/SqlInsertBuilder.kt | 11 ++- .../evomaster/core/database/schema/Column.kt | 5 +- .../examples/spring/db/jpa/EntityJPAData.java | 80 +++++++++++++++---- .../src/main/resources/sql/entityjpa.sql | 6 +- 5 files changed, 93 insertions(+), 20 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt b/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt index 9b6a97caa3..8444895620 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt @@ -672,13 +672,20 @@ class DbActionGeneBuilder { val columnMinLength = if (isFixedLength) { column.size } else { - if (column.isNotBlank==true) { + if (column.minSize !=null && column.minSize>0) { + column.minSize + } else if (column.isNotBlank==true) { 1 } else { 0 } } - StringGene(name = column.name, minLength = columnMinLength, maxLength = column.size) + val columnMaxLength = if (column.maxSize!=null) { + minOf(column.maxSize, column.size) + } else { + column.size + } + StringGene(name = column.name, minLength = columnMinLength, maxLength = columnMaxLength) } } } diff --git a/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt b/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt index 659a52eea8..e77a4446b1 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt @@ -289,6 +289,12 @@ class SqlInsertBuilder( extra.constraints.isNegativeOrZero?.let { if (it) 0 else null } ).minOrNull() + val minSize = extra.constraints.sizeMin + + val maxSize = extra.constraints.sizeMax + + val isNotBlank = extra.constraints.isNotBlank + val similarToPatterns = (column.similarToPatterns ?: mutableListOf()).toMutableList() extra.constraints.isEmail?.let { if (it) { @@ -298,6 +304,7 @@ class SqlInsertBuilder( val mergedSimilarToPatterns: List? = similarToPatterns.takeIf { it.isNotEmpty() } + //TODO all other constraints return column.copy( @@ -306,7 +313,9 @@ class SqlInsertBuilder( lowerBound = mergedLowerBound, upperBound = mergedUpperBound, similarToPatterns = mergedSimilarToPatterns, - isNotBlank = extra.constraints.isNotBlank + isNotBlank = isNotBlank, + minSize = minSize, + maxSize = maxSize ) } diff --git a/core/src/main/kotlin/org/evomaster/core/database/schema/Column.kt b/core/src/main/kotlin/org/evomaster/core/database/schema/Column.kt index a4c34bf5f4..ba77ff838e 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/schema/Column.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/schema/Column.kt @@ -57,8 +57,11 @@ data class Column( // public boolean identity; - val isNotBlank: Boolean? = null + val isNotBlank: Boolean? = null, + val minSize: Int? = null, + + val maxSize: Int? = null //TODO something for other constraints ) \ No newline at end of file diff --git a/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java b/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java index 32115776fe..52bc94f4dc 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java +++ b/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java @@ -3,6 +3,7 @@ import javax.persistence.*; import javax.validation.constraints.*; +import java.math.BigDecimal; @Entity @Table(name = "ExistingTable") @@ -16,12 +17,12 @@ public int getId() { } public void setId(int id) { - this.id= id; + this.id = id; } - @Min(value=42) - @Max(value=45) + @Min(value = 42) + @Max(value = 45) private int x1; public int getX1() { @@ -33,9 +34,8 @@ public void setX1(int x1) { } - @NotBlank - @Column(name="notblank") + @Column(name = "notblank") private String notblank; public String getNotBlank() { @@ -61,32 +61,82 @@ public void setEmail(String email) { @Negative private int negative; - public int getNegative() { return negative; } + public int getNegative() { + return negative; + } - public void setNegative(int negative) { this.negative = negative; } + public void setNegative(int negative) { + this.negative = negative; + } @NegativeOrZero - @Column(name="negative_or_zero") + @Column(name = "negative_or_zero") private int negativeOrZero; - public int getNegativeOrZero() { return negativeOrZero; } + public int getNegativeOrZero() { + return negativeOrZero; + } - public void setNegativeOrZero(int negativeOrZero) { this.negativeOrZero = negativeOrZero; } + public void setNegativeOrZero(int negativeOrZero) { + this.negativeOrZero = negativeOrZero; + } @Positive private int positive; - public int getPositive() { return positive; } + public int getPositive() { + return positive; + } - public void setPositive(int positive) { this.positive = positive; } + public void setPositive(int positive) { + this.positive = positive; + } @PositiveOrZero - @Column(name="positive_or_zero") + @Column(name = "positive_or_zero") private int positiveOrZero; - public int getPositiveOrZero() { return positiveOrZero; } + public int getPositiveOrZero() { + return positiveOrZero; + } + + public void setPositiveOrZero(int positiveOrZero) { + this.positiveOrZero = positiveOrZero; + } + + // @DecimalMin(value = "3.1416") + @Column(name = "decimal_min") + private int decimalMin; - public void setPositiveOrZero(int positiveOrZero) { this.positiveOrZero = positiveOrZero; } + public int getDecimalMin() { + return this.decimalMin; + } + public void setDecimalMin(int decimalMin) { + this.decimalMin = decimalMin; + } + + // @DecimalMax(value = "0.000001") + @Column(name = "decimal_max") + private int decimalMax; + + public int getDecimalMax() { + return this.decimalMax; + } + + public void setDecimalMax(int decimalMax) { + this.decimalMax = decimalMax; + } + + @Size(min = 1, max = 8) + private String size; + + public String getSize() { + return this.size; + } + + public void setSize(String size) { + this.size = size; + } } diff --git a/e2e-tests/spring-rest-openapi-v2/src/main/resources/sql/entityjpa.sql b/e2e-tests/spring-rest-openapi-v2/src/main/resources/sql/entityjpa.sql index a578b4babe..613211e443 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/main/resources/sql/entityjpa.sql +++ b/e2e-tests/spring-rest-openapi-v2/src/main/resources/sql/entityjpa.sql @@ -6,5 +6,9 @@ create table IF NOT EXISTS existing_table ( negative integer not null, negative_or_zero integer not null, positive integer not null, - positive_or_zero integer not null + positive_or_zero integer not null, + decimal_min integer not null, + decimal_max integer not null, + size varchar not null + ); \ No newline at end of file From b2331a0cc96daed48f363fe6f5095c533a6ce27e Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 22 May 2023 10:06:02 +0200 Subject: [PATCH 026/345] refactored update of bound genes --- .../org/evomaster/core/problem/rest/RestIndividual.kt | 2 -- .../core/problem/rest/resource/RestResourceCalls.kt | 9 +-------- .../org/evomaster/core/problem/rpc/RPCIndividual.kt | 3 +-- .../kotlin/org/evomaster/core/search/gene/ObjectGene.kt | 1 - .../evomaster/core/search/gene/collection/ArrayGene.kt | 5 +---- .../org/evomaster/core/search/gene/collection/MapGene.kt | 2 -- .../org/evomaster/core/search/gene/root/CompositeGene.kt | 7 ------- 7 files changed, 3 insertions(+), 26 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt index 8624edee77..eb50e7f84b 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt @@ -190,14 +190,12 @@ class RestIndividual( if(!getIndexedResourceCalls().keys.contains(position)) throw IllegalArgumentException("position is out of range of list") val removed = killChildByIndex(getFirstIndexOfRestResourceCalls() + position) as RestResourceCalls - removed.removeThisFromItsBindingGenes() } fun removeResourceCall(remove: List) { if(!getResourceCalls().containsAll(remove)) throw IllegalArgumentException("specified rest calls are not part of this individual") killChildren(remove) - remove.forEach { it.removeThisFromItsBindingGenes() } } /** diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceCalls.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceCalls.kt index e86fbb6cb9..2a48d91de3 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceCalls.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceCalls.kt @@ -444,14 +444,7 @@ class RestResourceCalls( } } - /** - * removing all binding which refers to [this] RestResourceCalls - */ - fun removeThisFromItsBindingGenes() { - (dbActions.plus(mainActions)).forEach { a -> - a.removeThisFromItsBindingGenes() - } - } + /** * employing the longest action to represent a group of calls on a resource diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt index 96ae923894..68eb94155d 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt @@ -103,8 +103,7 @@ class RPCIndividual( * remove an action from [actions] at [position] */ fun removeAction(position: Int) { - val removed = (killChildByIndex(getFirstIndexOfEnterpriseActionGroup() + position) as EnterpriseActionGroup).getMainAction() - removed.removeThisFromItsBindingGenes() + killChildByIndex(getFirstIndexOfEnterpriseActionGroup() + position) as EnterpriseActionGroup } private fun getFirstIndexOfEnterpriseActionGroup() = max(0, max(children.indexOfLast { it is DbAction }+1, children.indexOfFirst { it is EnterpriseActionGroup })) diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/ObjectGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/ObjectGene.kt index 7029f240f4..366632fde0 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/ObjectGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/ObjectGene.kt @@ -153,7 +153,6 @@ class ObjectGene( } val remove = randomness.choose(additionalFields!!) - remove.removeThisFromItsBindingGenes() killChild(remove) return true } diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/collection/ArrayGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/collection/ArrayGene.kt index e7079c48ad..cfc952fefe 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/collection/ArrayGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/collection/ArrayGene.kt @@ -235,9 +235,7 @@ class ArrayGene( addElement(gene) }else{ log.trace("Removing gene in mutation") - val removed = killChildByIndex(randomness.nextInt(elements.size)) as T - // remove binding if any other bound with - removed.removeThisFromItsBindingGenes() + killChildByIndex(randomness.nextInt(elements.size)) as T } return true } @@ -304,7 +302,6 @@ class ArrayGene( //this is a reference heap check, not based on `equalsTo` if (elements.contains(element)){ killChild(element) - element.removeThisFromItsBindingGenes() }else{ log.warn("the specified element (${if (element.isPrintable()) element.getValueAsPrintableString() else "not printable"})) does not exist in this array") } diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/collection/MapGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/collection/MapGene.kt index e2fa515974..f285bf7f4c 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/collection/MapGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/collection/MapGene.kt @@ -115,7 +115,6 @@ abstract class MapGene( } else { log.trace("Removing gene in mutation") val removed = killChildByIndex(randomness.nextInt(elements.size)) as Gene - removed.removeThisFromItsBindingGenes() } return true } @@ -184,7 +183,6 @@ abstract class MapGene( //this is a reference heap check, not based on `equalsTo` if (elements.contains(element)){ killChild(element) - element.removeThisFromItsBindingGenes() }else{ log.warn("the specified element (${if (element.isPrintable()) element.getValueAsPrintableString() else "not printable"})) does not exist in this map") } diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/root/CompositeGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/root/CompositeGene.kt index 918824a067..87fc7119d8 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/root/CompositeGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/root/CompositeGene.kt @@ -14,11 +14,4 @@ abstract class CompositeGene( constructor(name: String, child: Gene) : this(name, mutableListOf(child)) - - override fun killChild(child: StructuralElement){ - (child as Gene).removeThisFromItsBindingGenes() - super.killChild(child) - } - - } \ No newline at end of file From e0c8b2d2faa8e991fff5877f5caaa894766d5cb8 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 22 May 2023 10:25:33 +0200 Subject: [PATCH 027/345] fix for test --- .../core/search/algorithms/onemax/OneMaxIndividual.kt | 2 +- .../org/evomaster/core/search/service/ProcessMonitorTest.kt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxIndividual.kt b/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxIndividual.kt index 6c61155842..f036f0cc52 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxIndividual.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxIndividual.kt @@ -35,7 +35,7 @@ class OneMaxIndividual( // } fun resetAllToZero(){ - (0 until size()).forEach { + (0 until getAction().list.size).forEach { setValue(it, 0.0) } } diff --git a/core/src/test/kotlin/org/evomaster/core/search/service/ProcessMonitorTest.kt b/core/src/test/kotlin/org/evomaster/core/search/service/ProcessMonitorTest.kt index 71a85ff1e8..4cad403042 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/service/ProcessMonitorTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/service/ProcessMonitorTest.kt @@ -151,6 +151,9 @@ class ProcessMonitorTest{ @Test fun testSerializedTwoStepsAndOverall(){ + + assertTrue(archive.isEmpty()) + config.processFiles = "target/process_data_2s" config.enableProcessMonitor = true From f05a310c8941b88196df587361028942bbd4dd06 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 22 May 2023 11:29:05 +0200 Subject: [PATCH 028/345] support customized mock db --- .../api/dto/problem/rpc/MockDatabaseDto.java | 51 +++++++++++++++++++ .../java/controller/CustomizationHandler.java | 16 ++++-- .../controller/internal/SutController.java | 5 ++ 3 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/MockDatabaseDto.java diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/MockDatabaseDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/MockDatabaseDto.java new file mode 100644 index 0000000000..1ed3b74a91 --- /dev/null +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/MockDatabaseDto.java @@ -0,0 +1,51 @@ +package org.evomaster.client.java.controller.api.dto.problem.rpc; + +public class MockDatabaseDto { + + /** + * a key refers to the Web API + */ + public String appKey; + + /** + * it refers to a type of database, eg, mybatis + * nullable + */ + public String databaseType; + + /** + * it refers to a type of SQL, eg, select + * nullable + */ + public String commandType; + + /** + * sql command + * nullable + */ + public String sqlCommand; + + /** + * it refers to a command to be mocked + * not nullable + */ + public String commandName; + + /** + * it represents what requests are accepted for enabling + * this mock object + * null represents any request is acceptable + */ + public String requests; + + /** + * response to return + */ + public String response; + + /** + * it represents the datatype of response + */ + public String responseType; + +} diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/CustomizationHandler.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/CustomizationHandler.java index 5c6290f57c..f519c0aeaf 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/CustomizationHandler.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/CustomizationHandler.java @@ -4,11 +4,8 @@ import org.evomaster.client.java.controller.api.dto.ActionResponseDto; import org.evomaster.client.java.controller.api.dto.CustomizedCallResultCode; import org.evomaster.client.java.controller.api.dto.CustomizedRequestValueDto; -import org.evomaster.client.java.controller.api.dto.problem.rpc.EvaluatedRPCActionDto; -import org.evomaster.client.java.controller.api.dto.problem.rpc.MockRPCExternalServiceDto; -import org.evomaster.client.java.controller.api.dto.problem.rpc.RPCActionDto; +import org.evomaster.client.java.controller.api.dto.problem.rpc.*; import org.evomaster.client.java.controller.problem.rpc.CustomizedNotNullAnnotationForRPCDto; -import org.evomaster.client.java.controller.api.dto.problem.rpc.SeededRPCTestDto; import java.util.List; @@ -82,4 +79,15 @@ public interface CustomizationHandler { * @return whether the mocked instance starts successfully, */ boolean customizeMockingRPCExternalService(List externalServiceDtos, boolean enabled); + + /** + *

+ * implement how to enable/disable customized mock objects for database + *

+ * @param databaseDtos contains info about how to mock databases based on commandName + * @param enabled reflect to enable (set it true) or disable (set it false) mock objects for sql command based on commandName + * Note that null [databaseDtos] with false [enabled] means that all existing mock objects should be disabled. + * @return whether the mocked instance starts successfully, + */ + boolean customizeMockingDatabase(List databaseDtos, boolean enabled); } diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index c24ae2b8a7..6381c47e97 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -1215,6 +1215,11 @@ public boolean customizeMockingRPCExternalService(List databaseDtos, boolean enabled) { + return false; + } + @Override public void resetDatabase(List tablesToClean) { From 0554a74bea827611bdb7e3507308259bfacf616d Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 22 May 2023 11:34:59 +0200 Subject: [PATCH 029/345] update config --- core/src/main/kotlin/org/evomaster/core/EMConfig.kt | 6 +++++- docs/options.md | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index 04b090cd98..f7c522f91f 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -1810,6 +1810,10 @@ class EMConfig { @Cfg("Whether to apply customized method (i.e., implement 'customizeMockingRPCExternalService') to handle external services.") var enableCustomizedExternalServiceHandling = false + @Experimental + @Cfg("Whether to apply customized method (i.e., implement 'customizeMockingDatabase') to handle database.") + var enableCustomizedDatabaseHandling = false + @Experimental @Cfg("Whether to save mocked responses as separated files") var saveMockedResponseAsSeparatedFile = false @@ -2009,4 +2013,4 @@ class EMConfig { fun isEnabledInitializationStructureMutation() = isMIO() && initStructureMutationProbability > 0 && maxSizeOfMutatingInitAction > 0 fun isEnabledResourceSizeHandling() = isMIO() && probOfHandlingLength> 0 && maxSizeOfHandlingResource > 0 -} \ No newline at end of file +} diff --git a/docs/options.md b/docs/options.md index bea90322ad..3eef322527 100644 --- a/docs/options.md +++ b/docs/options.md @@ -151,6 +151,7 @@ There are 3 types of options: |`employResourceSizeHandlingStrategy`| __Enum__. Specify a strategy to determinate a number of resources to be manipulated throughout the search. *Valid values*: `NONE, RANDOM, DPC`. *Default value*: `NONE`.| |`employSmartDbClean`| __Boolean__. Specify whether to employ smart database clean to clear data in the database if the SUT has.`null` represents to employ the setting specified on the EM driver side. *Default value*: `null`.| |`enableAdaptiveResourceStructureMutation`| __Boolean__. Specify whether to decide the resource-based structure mutator and resource to be mutated adaptively based on impacts during focused search.Note that it only works when resource-based solution is enabled for solving REST problem. *Default value*: `false`.| +|`enableCustomizedDatabaseHandling`| __Boolean__. Whether to apply customized method (i.e., implement 'customizeMockingDatabase') to handle database. *Default value*: `false`.| |`enableCustomizedExternalServiceHandling`| __Boolean__. Whether to apply customized method (i.e., implement 'customizeMockingRPCExternalService') to handle external services. *Default value*: `false`.| |`enableNLPParser`| __Boolean__. Whether to employ NLP parser to process text. Note that to enable this parser, it is required to build the EvoMaster with the resource profile, i.e., mvn clean install -Presourceexp -DskipTests. *Default value*: `false`.| |`enableProcessMonitor`| __Boolean__. Whether or not enable a search process monitor for archiving evaluated individuals and Archive regarding an evaluation of search. This is only needed when running experiments with different parameter settings. *Default value*: `false`.| From b8f9c1ac644a1e101de722902220cc73a37e23cf Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 22 May 2023 11:52:13 +0200 Subject: [PATCH 030/345] dto for db mock objects --- .../java/controller/api/dto/problem/rpc/RPCActionDto.java | 5 +++++ .../controller/api/dto/problem/rpc/SeededRPCActionDto.java | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/RPCActionDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/RPCActionDto.java index 90082f6eca..5d3c88fcf4 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/RPCActionDto.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/RPCActionDto.java @@ -54,6 +54,11 @@ public class RPCActionDto { */ public List mockRPCExternalServiceDtos; + /** + * a list of mock objects for database sql commands + */ + public List mockDatabaseDtos; + /** * request params */ diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/SeededRPCActionDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/SeededRPCActionDto.java index 9cf8ef6598..15c3396655 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/SeededRPCActionDto.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/SeededRPCActionDto.java @@ -23,6 +23,11 @@ public class SeededRPCActionDto { */ public List mockRPCExternalServiceDtos; + /** + * a list of info to set up mock objects for databases + */ + public List mockDatabaseDtos; + /** * input parameters with json format * note the length of [inputParamTypes] must be same as [inputParams] From 042c7c67953718e98eaf5285e19cd0737388ca17 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 22 May 2023 12:48:10 +0200 Subject: [PATCH 031/345] setup db mock objects --- .../controller/internal/SutController.java | 33 ++++++++++++++----- .../problem/rpc/RPCEndpointsBuilder.java | 6 +++- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index 6381c47e97..363611d671 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -56,6 +56,7 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -794,20 +795,23 @@ public final void executeAction(RPCActionDto dto, ActionResponseDto responseDto) Object response; try { if (dto.mockRPCExternalServiceDtos != null && !dto.mockRPCExternalServiceDtos.isEmpty()){ - try { - boolean ok = customizeMockingRPCExternalService(dto.mockRPCExternalServiceDtos, true); - if (!ok) - SimpleLogger.warn("Warning: Fail to start mocked instances of RPC-based external services"); - }catch (Exception e){ - SimpleLogger.error("ERROR: Fail to process mocking of RPC-based external services:", e); - } + Boolean ok = handleCustomizedMethod(()->customizeMockingRPCExternalService(dto.mockRPCExternalServiceDtos, true)); + if (ok == null || !ok) + SimpleLogger.warn("Warning: Fail to start mocked instances of RPC-based external services with the customized method"); + } + if (dto.mockDatabaseDtos != null && !dto.mockDatabaseDtos.isEmpty()){ + Boolean ok = handleCustomizedMethod(()-> customizeMockingDatabase(dto.mockDatabaseDtos, true)); + if (ok == null || !ok) + SimpleLogger.warn("Warning: Fail to start mocked instances of databases with the customized method"); } response = executeRPCEndpoint(dto, false); } catch (Exception e) { throw new RuntimeException("ERROR: target exception should be caught, but "+ e.getMessage()); } finally { if (dto.mockRPCExternalServiceDtos != null && !dto.mockRPCExternalServiceDtos.isEmpty()) - customizeMockingRPCExternalService(dto.mockRPCExternalServiceDtos, false); // disable mocked responses + handleCustomizedMethod(()-> customizeMockingRPCExternalService(dto.mockRPCExternalServiceDtos, false)); // disable mocked responses + if (dto.mockDatabaseDtos != null && !dto.mockDatabaseDtos.isEmpty()) + handleCustomizedMethod(()-> customizeMockingDatabase(dto.mockDatabaseDtos, false)); // disable mock objects for database } //handle exception @@ -850,6 +854,19 @@ public final void executeAction(RPCActionDto dto, ActionResponseDto responseDto) } } + + /** + * avoid any exception introduced by customized method + */ + private T handleCustomizedMethod(Supplier call){ + try{ + return call.get(); + }catch (Throwable e){ + SimpleLogger.error("ERROR: Fail to process mocking with customized method:", e); + } + return null; + } + private Object executeRPCEndpoint(RPCActionDto dto, boolean throwTargetException) throws Exception { Object client = ((RPCProblem)getProblemInfo()).getClient(dto.interfaceId); EndpointSchema endpointSchema = getEndpointSchema(dto); diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java index 3201f3ccb5..e8804d7587 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java @@ -28,7 +28,7 @@ public class RPCEndpointsBuilder { private final static ObjectMapper objectMapper = new ObjectMapper(); - + private final static String OBJECT_FLAG = "OBJECT"; private final static String OBJECT_FLAG_SEPARATOR = ":"; @@ -1032,6 +1032,10 @@ public static Map> buildSeededTest(Map s.responseTypes.stream()).distinct().collect(Collectors.toList()), rpcType); + /* + for db mock objects, we currently just keep them as they are + */ + rpcActionDto.mockDatabaseDtos = actionDto.mockDatabaseDtos; test.add(rpcActionDto); }else { SimpleLogger.recordErrorMessage("Seeded Test Error: cannot find the action "+actionDto.functionName); From e22bfc4df0d5b74d223f3a1c2d67c91a3a56eee6 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 22 May 2023 13:07:55 +0200 Subject: [PATCH 032/345] start to handle it at core --- .../rpc/service/RPCEndpointsHandler.kt | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt index de580e3d46..e3cde8add4 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt @@ -112,16 +112,23 @@ class RPCEndpointsHandler { /** * a map of RPC action to external services for RPC-based SUT * - key is the id of action (which is consistent with key of [actionSchemaCluster]) - * - value is a list of seeded api external services for the RPC action which can be found with [externalServiceCluster] + * - value is a list of seeded api external services for the RPC action which can be found with [seededExternalServiceCluster] */ private val actionToExternalServiceMap = mutableMapOf>() /** - * a map of external service actions cluster + * a map of external service actions cluster based on seeded test cases * - key is action id * - value is the example of ApiExternalServiceAction that might be expanded */ - private val externalServiceCluster = mutableMapOf() + private val seededExternalServiceCluster = mutableMapOf() + + /** + * a map of mock objects for sql based on seeded test cases + * - key is action id + * - value is a list of mock object setup for database + */ + private val seededDbMockObjects = mutableMapOf>() /** * key is type in the schema @@ -176,7 +183,7 @@ class RPCEndpointsHandler { val ex = if (d.mockRPCExternalServiceDtos != null && d.mockRPCExternalServiceDtos.isNotEmpty()) d.mockRPCExternalServiceDtos.map { e-> e.responses.mapIndexed { index, r-> - val exAction = externalServiceCluster[ + val exAction = seededExternalServiceCluster[ RPCExternalServiceAction.getRPCExternalServiceActionName(e.interfaceFullName, e.functionName, e.requestRules[index], e.responseTypes[index]) ]!!.copy() as ApiExternalServiceAction try { @@ -231,7 +238,7 @@ class RPCEndpointsHandler { val exkey = RPCExternalServiceAction.getRPCExternalServiceActionName( dto.interfaceFullName, dto.functionName, dto.requestRules[index], s ) - if (!externalServiceCluster.containsKey(exkey)){ + if (!seededExternalServiceCluster.containsKey(exkey)){ val responseTypeClass = interfaceDto.identifiedResponseTypes?.find { it.type.fullTypeName == s } var fromClass = false val responseGene = ( @@ -261,7 +268,7 @@ class RPCEndpointsHandler { requestRuleIdentifier = dto.requestRules[index], responseParam = response) Lazy.assert { exkey == externalAction.getName() } - externalServiceCluster[exkey] = externalAction + seededExternalServiceCluster[exkey] = externalAction } actionToExternalServiceMap.getOrPut(actionKey){ mutableSetOf() }.add(exkey) @@ -1129,4 +1136,4 @@ class RPCEndpointsHandler { } return map } -} \ No newline at end of file +} From 70e130d0435b5aa37a9575da1da19de3675f64ff Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 22 May 2023 13:42:45 +0200 Subject: [PATCH 033/345] fix module issue --- .../problem/graphql/service/GraphQLBlackBoxModule.kt | 4 ++++ .../core/problem/graphql/service/GraphQLModule.kt | 4 ++++ .../core/problem/rest/service/BlackBoxRestModule.kt | 8 ++++++-- .../evomaster/core/problem/rpc/service/RPCModule.kt | 11 +++++++++++ .../core/problem/webfrontend/service/WebModule.kt | 11 +++++++++++ .../org/evomaster/core/search/service/Minimizer.kt | 3 --- 6 files changed, 36 insertions(+), 5 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLBlackBoxModule.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLBlackBoxModule.kt index c1311f56f6..de60c095b6 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLBlackBoxModule.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLBlackBoxModule.kt @@ -33,6 +33,10 @@ class GraphQLBlackBoxModule( .to(GraphQLBlackBoxFitness::class.java) .asEagerSingleton() + bind(object : TypeLiteral>() {}) + .to(GraphQLBlackBoxFitness::class.java) + .asEagerSingleton() + bind(object : TypeLiteral>() {}) .asEagerSingleton() diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLModule.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLModule.kt index 9d6206c574..c21844760f 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLModule.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLModule.kt @@ -37,6 +37,10 @@ class GraphQLModule : AbstractModule() { .to(GraphQLFitness::class.java) .asEagerSingleton() + bind(object : TypeLiteral>() {}) + .to(GraphQLFitness::class.java) + .asEagerSingleton() + bind(object : TypeLiteral>() {}) .asEagerSingleton() diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/BlackBoxRestModule.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/BlackBoxRestModule.kt index 979842dbea..ecb33f19e5 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/BlackBoxRestModule.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/BlackBoxRestModule.kt @@ -30,8 +30,12 @@ class BlackBoxRestModule( .asEagerSingleton() bind(object : TypeLiteral>() {}) - .to(BlackBoxRestFitness::class.java) - .asEagerSingleton() + .to(BlackBoxRestFitness::class.java) + .asEagerSingleton() + + bind(object : TypeLiteral>() {}) + .to(BlackBoxRestFitness::class.java) + .asEagerSingleton() bind(object : TypeLiteral>() {}) .asEagerSingleton() diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCModule.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCModule.kt index 795e0d22ef..071ddfd4df 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCModule.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCModule.kt @@ -7,6 +7,7 @@ import org.evomaster.core.output.service.TestCaseWriter import org.evomaster.core.output.service.TestSuiteWriter import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.rpc.RPCIndividual +import org.evomaster.core.problem.webfrontend.WebIndividual import org.evomaster.core.remote.service.RemoteController import org.evomaster.core.remote.service.RemoteControllerImplementation import org.evomaster.core.search.service.Archive @@ -37,10 +38,20 @@ class RPCModule : AbstractModule(){ bind(RPCEndpointsHandler::class.java) .asEagerSingleton() + bind(object : TypeLiteral>(){}) + .asEagerSingleton() + + bind(object : TypeLiteral>(){}) + .asEagerSingleton() + bind(object : TypeLiteral>() {}) .to(RPCFitness::class.java) .asEagerSingleton() + bind(object : TypeLiteral>() {}) + .to(RPCFitness::class.java) + .asEagerSingleton() + bind(object : TypeLiteral>() {}) .asEagerSingleton() diff --git a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebModule.kt b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebModule.kt index f558716bf1..85ddd01a8b 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebModule.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebModule.kt @@ -5,6 +5,7 @@ import com.google.inject.TypeLiteral import org.evomaster.core.output.service.TestCaseWriter import org.evomaster.core.output.service.TestSuiteWriter import org.evomaster.core.output.service.WebTestCaseWriter +import org.evomaster.core.problem.graphql.GraphQLIndividual import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.webfrontend.WebIndividual import org.evomaster.core.remote.service.RemoteController @@ -38,10 +39,20 @@ class WebModule: AbstractModule() { .asEagerSingleton() + bind(object : TypeLiteral>(){}) + .asEagerSingleton() + + bind(object : TypeLiteral>(){}) + .asEagerSingleton() + bind(object : TypeLiteral>() {}) .to(WebFitness::class.java) .asEagerSingleton() + bind(object : TypeLiteral>() {}) + .to(WebFitness::class.java) + .asEagerSingleton() + bind(object : TypeLiteral>() {}) .asEagerSingleton() diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt index 536ad99cf9..babd9491aa 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt @@ -28,9 +28,6 @@ class Minimizer { @Inject private lateinit var config: EMConfig - @Inject - private lateinit var mutator: StructureMutator - private var startTimer : Long = -1 From 4a9428af706cb1d3b9769cc0c1feda1ec3ff47fb Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 22 May 2023 14:02:57 +0200 Subject: [PATCH 034/345] more for debugging minimize issue --- .../main/kotlin/org/evomaster/core/EMConfig.kt | 5 +++++ .../evomaster/core/search/service/Minimizer.kt | 18 +++++++++++++++--- docs/options.md | 1 + 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index 1d5e0f9da3..e1fa364af0 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -1751,6 +1751,11 @@ class EMConfig { " A value of 0 means minimization will be skipped, even if minimize=true.") var minimizeTimeout = 5 + + @Cfg("When applying minimization phase, and some targets get lost when re-computing coverage," + + " then printout a detailed description.") + var minimizeShowLostTargets = true + @Experimental @FilePath(true) @Regex("(.*jacoco.*\\.jar)|(^$)") diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt index babd9491aa..78855bbfd1 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt @@ -28,6 +28,8 @@ class Minimizer { @Inject private lateinit var config: EMConfig + @Inject + private lateinit var idMapper: IdMapper private var startTimer : Long = -1 @@ -202,7 +204,7 @@ class Minimizer { LoggingUtil.getInfoLogger().info("Recomputing full coverage for ${current.size} tests") - val beforeCovered = archive.numberOfCoveredTargets() + val beforeCovered = archive.coveredTargets() /* Previously evaluated individual only had partial info, due to performance issues. @@ -220,12 +222,22 @@ class Minimizer { population.forEach{archive.addIfNeeded(it)} - val afterCovered = archive.numberOfCoveredTargets() + val afterCovered = archive.coveredTargets() - if(afterCovered < beforeCovered){ + if(afterCovered.size < beforeCovered.size){ //could happen if background threads, for example LoggingUtil.getInfoLogger().warn("Recomputing coverage did lose some targets: from $beforeCovered to $afterCovered" + ", i.e., lost ${beforeCovered-afterCovered}") + + if(config.minimizeShowLostTargets){ + LoggingUtil.getInfoLogger().warn("Missing targets:"); + for(target in beforeCovered){ + if(! afterCovered.contains(target)){ + LoggingUtil.getInfoLogger().warn(idMapper.getDescriptiveId(target)); + } + } + } + assert(false)//shouldn't really happen in the E2E... } } diff --git a/docs/options.md b/docs/options.md index 7c8199d44b..222782a7d9 100644 --- a/docs/options.md +++ b/docs/options.md @@ -100,6 +100,7 @@ There are 3 types of options: |`maxTimeInSeconds`| __Int__. Maximum number of seconds allowed for the search. The more time is allowed, the better results one can expect. But then of course the test generation will take longer. Only applicable depending on the stopping criterion. If this value is 0, the setting 'maxTime' will be used instead. *Constraints*: `min=0.0`. *Default value*: `0`.| |`maxlengthOfHistoryForAGM`| __Int__. Specify a maximum length of history when applying archive-based gene mutation. *Default value*: `10`.| |`minimize`| __Boolean__. Apply a minimization phase to make the generated tests more readable. Achieved coverage would stay the same. Generating shorter test cases might come at the cost of having more test cases. *Default value*: `true`.| +|`minimizeShowLostTargets`| __Boolean__. When applying minimization phase, and some targets get lost when re-computing coverage, then printout a detailed description. *Default value*: `true`.| |`minimizeTimeout`| __Int__. Maximum number of minutes that will be dedicated to the minimization phase. A negative number mean no timeout is considered. A value of 0 means minimization will be skipped, even if minimize=true. *Default value*: `5`.| |`minimumSizeControl`| __Int__. Specify minimum size when bloatControlForSecondaryObjective. *Constraints*: `min=0.0`. *Default value*: `2`.| |`populationSize`| __Int__. Define the population size in the search algorithms that use populations (e.g., Genetic Algorithms, but not MIO). *Constraints*: `min=1.0`. *Default value*: `30`.| From c3bfc92d36e9dfa6506794dcc1f2bb7188000f78 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 22 May 2023 14:17:40 +0200 Subject: [PATCH 035/345] added threshold for lost targets after minimization --- .../visitor/classv/CoverageClassVisitor.java | 1 + core/src/main/kotlin/org/evomaster/core/EMConfig.kt | 5 +++++ .../org/evomaster/core/search/service/Minimizer.kt | 13 ++++++++----- docs/options.md | 1 + 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/visitor/classv/CoverageClassVisitor.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/visitor/classv/CoverageClassVisitor.java index b14ca0f682..23a65eef89 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/visitor/classv/CoverageClassVisitor.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/visitor/classv/CoverageClassVisitor.java @@ -60,6 +60,7 @@ Methods added by the compiler (eg synthetics and bridges) are } if(bytecodeClassName.contains("$InterceptedDefinition") || + bytecodeClassName.contains("$Definition") || bytecodeClassName.contains("$Introspection")){ /* In general, we should avoid dealing with classes generated on the fly by frameworks like diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index e1fa364af0..36e2f805ac 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -1756,6 +1756,11 @@ class EMConfig { " then printout a detailed description.") var minimizeShowLostTargets = true + @PercentageAsProbability + @Cfg("Losing targets when recomputing coverage is expected (e.g., constructors of singletons)," + + " but problematic if too much") + var minimizeThresholdForLoss = 0.2 + @Experimental @FilePath(true) @Regex("(.*jacoco.*\\.jar)|(^$)") diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt index 78855bbfd1..e90dd13b65 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt @@ -223,11 +223,14 @@ class Minimizer { population.forEach{archive.addIfNeeded(it)} val afterCovered = archive.coveredTargets() - - if(afterCovered.size < beforeCovered.size){ - //could happen if background threads, for example - LoggingUtil.getInfoLogger().warn("Recomputing coverage did lose some targets: from $beforeCovered to $afterCovered" + - ", i.e., lost ${beforeCovered-afterCovered}") + val diff = beforeCovered.size-afterCovered.size + + if(diff > config.minimizeThresholdForLoss * beforeCovered.size){ + //could happen if background threads, for example, as well as constructors of singletons + LoggingUtil.getInfoLogger().warn("Recomputing coverage did lose many targets," + + " more than the threshold ${config.minimizeThresholdForLoss*100}%:" + + " from ${beforeCovered.size} to ${afterCovered.size}" + + ", i.e., lost $diff") if(config.minimizeShowLostTargets){ LoggingUtil.getInfoLogger().warn("Missing targets:"); diff --git a/docs/options.md b/docs/options.md index 222782a7d9..74889ff45e 100644 --- a/docs/options.md +++ b/docs/options.md @@ -101,6 +101,7 @@ There are 3 types of options: |`maxlengthOfHistoryForAGM`| __Int__. Specify a maximum length of history when applying archive-based gene mutation. *Default value*: `10`.| |`minimize`| __Boolean__. Apply a minimization phase to make the generated tests more readable. Achieved coverage would stay the same. Generating shorter test cases might come at the cost of having more test cases. *Default value*: `true`.| |`minimizeShowLostTargets`| __Boolean__. When applying minimization phase, and some targets get lost when re-computing coverage, then printout a detailed description. *Default value*: `true`.| +|`minimizeThresholdForLoss`| __Double__. Losing targets when recomputing coverage is expected (e.g., constructors of singletons), but problematic if too much. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.2`.| |`minimizeTimeout`| __Int__. Maximum number of minutes that will be dedicated to the minimization phase. A negative number mean no timeout is considered. A value of 0 means minimization will be skipped, even if minimize=true. *Default value*: `5`.| |`minimumSizeControl`| __Int__. Specify minimum size when bloatControlForSecondaryObjective. *Constraints*: `min=0.0`. *Default value*: `2`.| |`populationSize`| __Int__. Define the population size in the search algorithms that use populations (e.g., Genetic Algorithms, but not MIO). *Constraints*: `min=1.0`. *Default value*: `30`.| From f8495feed2881e79902aa06452b415de65434f45 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 22 May 2023 21:12:46 +0200 Subject: [PATCH 036/345] fixed issue in collection of info for minimization --- .../controller/internal/EMController.java | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java index b2e924ae69..519ceac971 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java @@ -494,8 +494,11 @@ public Response getTestResults( dto.targets.add(info); }); - //if we want just info on covered targets, don't add extra info - if(!allCovered) { + //if we want just info on covered targets, don't add extra info... + //however, it was problematic, as such info was used in different ways around the codebase... + //so put back only if major performance issue, an track down each usage of each piece of info +// if(!allCovered) { + /* Note: it is important that extra is computed before AdditionalInfo, as heuristics on SQL might add new entries to String specializations @@ -555,17 +558,18 @@ public Response getTestResults( SimpleLogger.error(msg); return Response.status(500).entity(WrappedResponseDto.withError(msg)).build(); } - } else { - // there is still some data that we need during minimization - List additionalInfos = noKillSwitch(() -> sutController.getAdditionalInfoList()); - if (additionalInfos != null) { - additionalInfos.forEach(a -> { - AdditionalInfoDto info = new AdditionalInfoDto(); - info.lastExecutedStatement = a.getLastExecutedStatement(); - dto.additionalInfoList.add(info); - }); - } - } +// } +// else { +// // there is still some data that we need during minimization +// List additionalInfos = noKillSwitch(() -> sutController.getAdditionalInfoList()); +// if (additionalInfos != null) { +// additionalInfos.forEach(a -> { +// AdditionalInfoDto info = new AdditionalInfoDto(); +// info.lastExecutedStatement = a.getLastExecutedStatement(); +// dto.additionalInfoList.add(info); +// }); +// } +// } if (killSwitch) { sutController.setKillSwitch(true); From d0fa36c335a140cd1aa573130334c9c30397826c Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 22 May 2023 23:00:39 +0200 Subject: [PATCH 037/345] action support --- .../api/dto/problem/rpc/MockDatabaseDto.java | 2 +- .../problem/rpc/RPCEndpointsBuilder.java | 9 ++- .../ApiExternalServiceAction.kt | 9 ++- .../rpc/DbAsExternalServiceAction.kt | 59 +++++++++++++++++++ .../rpc/RPCExternalServiceAction.kt | 16 +++-- ...ResponseParam.kt => ClassResponseParam.kt} | 16 +++-- .../rpc/parm/UpdateForRPCResponseParam.kt | 8 +-- .../rpc/service/RPCEndpointsHandler.kt | 7 +-- .../core/problem/rpc/service/RPCFitness.kt | 12 ++-- .../core/output/EvaluatedIndividualBuilder.kt | 6 +- 10 files changed, 106 insertions(+), 38 deletions(-) create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/DbAsExternalServiceAction.kt rename core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/parm/{RPCResponseParam.kt => ClassResponseParam.kt} (58%) diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/MockDatabaseDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/MockDatabaseDto.java index 1ed3b74a91..b716a06b5b 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/MockDatabaseDto.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/MockDatabaseDto.java @@ -46,6 +46,6 @@ public class MockDatabaseDto { /** * it represents the datatype of response */ - public String responseType; + public String responseFullType; } diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java index e8804d7587..cafd95fe34 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java @@ -1032,10 +1032,13 @@ public static Map> buildSeededTest(Map s.responseTypes.stream()).distinct().collect(Collectors.toList()), rpcType); - /* - for db mock objects, we currently just keep them as they are - */ + rpcActionDto.mockDatabaseDtos = actionDto.mockDatabaseDtos; + if (actionDto.mockDatabaseDtos != null && !actionDto.mockDatabaseDtos.isEmpty()){ + buildExternalServiceResponse(schema, + actionDto.mockDatabaseDtos.stream().map(s-> s.responseFullType).distinct().collect(Collectors.toList()), + rpcType); + } test.add(rpcActionDto); }else { SimpleLogger.recordErrorMessage("Seeded Test Error: cannot find the action "+actionDto.functionName); diff --git a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/ApiExternalServiceAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/ApiExternalServiceAction.kt index f7769c0e1b..9297629933 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/ApiExternalServiceAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/ApiExternalServiceAction.kt @@ -1,6 +1,5 @@ package org.evomaster.core.problem.externalservice -import org.evomaster.core.problem.api.param.Param import org.evomaster.core.problem.externalservice.param.ResponseParam import org.evomaster.core.search.Action @@ -19,6 +18,12 @@ abstract class ApiExternalServiceAction( used : Boolean = false ) : Action(listOf(response)){ + companion object{ + /** + * separator for generating names of external actions + */ + const val EXACTION_NAME_SEPARATOR =":::" + } val responses : List get() { return children.filterIsInstance()} @@ -76,4 +81,4 @@ abstract class ApiExternalServiceAction( fun confirmNotUsed() { this.used = false } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/DbAsExternalServiceAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/DbAsExternalServiceAction.kt new file mode 100644 index 0000000000..a09199a438 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/DbAsExternalServiceAction.kt @@ -0,0 +1,59 @@ +package org.evomaster.core.problem.externalservice.rpc + +import org.evomaster.core.problem.externalservice.ApiExternalServiceAction +import org.evomaster.core.problem.externalservice.rpc.parm.ClassResponseParam +import org.evomaster.core.search.StructuralElement +import org.evomaster.core.search.gene.Gene + +class DbAsExternalServiceAction ( + /** + * command name which will result in sql execution + */ + val commandName : String, + /** + * descriptive info for the external service if needed + * eg, app key for identifying the external service + */ + val descriptiveInfo : String? = null, + + /** + * accepted request + * such rule is not mutable, and it can be specified only with seeded tests + */ + val requestRuleIdentifier: String?, + + responseParam: ClassResponseParam, + active : Boolean = false, + used : Boolean = false +) : ApiExternalServiceAction(responseParam, active, used) { + + companion object{ + + fun getDbAsExternalServiceAction(exAction: DbAsExternalServiceAction) = "DbAsExternalServiceAction${EXACTION_NAME_SEPARATOR}${exAction.commandName}${EXACTION_NAME_SEPARATOR}${exAction.requestRuleIdentifier?:"ANY"}$EXACTION_NAME_SEPARATOR${(exAction.response as ClassResponseParam).className}" + } + + override fun getName(): String { + return getDbAsExternalServiceAction(this) + } + + override fun seeTopGenes(): List { + return response.genes + } + + override fun shouldCountForFitnessEvaluations(): Boolean { + return false + } + + override fun copyContent(): StructuralElement { + return DbAsExternalServiceAction(commandName, descriptiveInfo, requestRuleIdentifier, response.copy() as ClassResponseParam, active, used) + } + + /** + * @return a copy of DbAsExternalServiceAction and release its restricted request identifier + */ + fun getUnrestrictedDbAsExternalServiceAction(): DbAsExternalServiceAction { + return DbAsExternalServiceAction(commandName, descriptiveInfo, null, response.copy() as ClassResponseParam) + } + + +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/RPCExternalServiceAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/RPCExternalServiceAction.kt index 54fa03bd06..87a9844a86 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/RPCExternalServiceAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/RPCExternalServiceAction.kt @@ -2,7 +2,7 @@ package org.evomaster.core.problem.externalservice.rpc import org.evomaster.core.problem.api.param.Param import org.evomaster.core.problem.externalservice.ApiExternalServiceAction -import org.evomaster.core.problem.externalservice.rpc.parm.RPCResponseParam +import org.evomaster.core.problem.externalservice.rpc.parm.ClassResponseParam import org.evomaster.core.search.gene.Gene class RPCExternalServiceAction( @@ -36,19 +36,17 @@ class RPCExternalServiceAction( */ val requestRuleIdentifier: String?, - responseParam: RPCResponseParam, + responseParam: ClassResponseParam, active : Boolean = false, used : Boolean = false ) : ApiExternalServiceAction(responseParam, active, used) { companion object{ - private const val RPC_EX_NAME_SEPARATOR =":::" - - fun getRPCExternalServiceActionName(interfaceName: String, functionName: String, requestRuleIdentifier: String?, responseClassType: String) = "$interfaceName$RPC_EX_NAME_SEPARATOR$functionName$RPC_EX_NAME_SEPARATOR${requestRuleIdentifier?:"ANY"}$RPC_EX_NAME_SEPARATOR$responseClassType" + fun getRPCExternalServiceActionName(interfaceName: String, functionName: String, requestRuleIdentifier: String?, responseClassType: String) = "$interfaceName$EXACTION_NAME_SEPARATOR$functionName$EXACTION_NAME_SEPARATOR${requestRuleIdentifier?:"ANY"}$EXACTION_NAME_SEPARATOR$responseClassType" } override fun getName(): String { - return getRPCExternalServiceActionName(interfaceName, functionName, requestRuleIdentifier, (response as RPCResponseParam).className) + return getRPCExternalServiceActionName(interfaceName, functionName, requestRuleIdentifier, (response as ClassResponseParam).className) } override fun seeTopGenes(): List { @@ -60,17 +58,17 @@ class RPCExternalServiceAction( } override fun copyContent(): RPCExternalServiceAction { - return RPCExternalServiceAction(interfaceName, functionName, inputParamTypes?.toList(), descriptiveInfo, requestRuleIdentifier, response.copy() as RPCResponseParam, active, used) + return RPCExternalServiceAction(interfaceName, functionName, inputParamTypes?.toList(), descriptiveInfo, requestRuleIdentifier, response.copy() as ClassResponseParam, active, used) } /** * @return a copy of this and release its restricted request identifier */ fun getUnrestrictedRPCExternalServiceAction(): RPCExternalServiceAction { - return RPCExternalServiceAction(interfaceName, functionName, inputParamTypes?.toList(), descriptiveInfo, null, response.copy() as RPCResponseParam) + return RPCExternalServiceAction(interfaceName, functionName, inputParamTypes?.toList(), descriptiveInfo, null, response.copy() as ClassResponseParam) } fun addUpdateForParam(param: Param){ addChild(param) } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/parm/RPCResponseParam.kt b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/parm/ClassResponseParam.kt similarity index 58% rename from core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/parm/RPCResponseParam.kt rename to core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/parm/ClassResponseParam.kt index 5ca3042b1e..14426b4941 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/parm/RPCResponseParam.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/parm/ClassResponseParam.kt @@ -5,9 +5,13 @@ import org.evomaster.core.search.gene.collection.EnumGene import org.evomaster.core.search.gene.optional.OptionalGene /** + * + * response constrained with a class + * * need to consider the exception? + * */ -class RPCResponseParam( +class ClassResponseParam( val className: String, responseType: EnumGene, response: OptionalGene @@ -26,12 +30,12 @@ class RPCResponseParam( fromClass = true } - override fun copyContent(): RPCResponseParam { - return RPCResponseParam(className, responseType.copy() as EnumGene, responseBody.copy() as OptionalGene).also { it.fromClass = this.fromClass } + override fun copyContent(): ClassResponseParam { + return ClassResponseParam(className, responseType.copy() as EnumGene, responseBody.copy() as OptionalGene).also { it.fromClass = this.fromClass } } - fun copyWithSpecifiedResponseBody(response: OptionalGene): RPCResponseParam { - return RPCResponseParam(className, responseType.copy() as EnumGene, response.copy() as OptionalGene).also { it.fromClass = this.fromClass } + fun copyWithSpecifiedResponseBody(response: OptionalGene): ClassResponseParam { + return ClassResponseParam(className, responseType.copy() as EnumGene, response.copy() as OptionalGene).also { it.fromClass = this.fromClass } } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/parm/UpdateForRPCResponseParam.kt b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/parm/UpdateForRPCResponseParam.kt index 72f5ca8f38..d5853e0984 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/parm/UpdateForRPCResponseParam.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/parm/UpdateForRPCResponseParam.kt @@ -5,12 +5,12 @@ import org.evomaster.core.problem.api.param.UpdateForParam import org.evomaster.core.problem.externalservice.param.ResponseParam import org.evomaster.core.search.gene.Gene -class UpdateForRPCResponseParam(val responseParam: RPCResponseParam) +class UpdateForRPCResponseParam(val responseParam: ClassResponseParam) : ResponseParam("updateForBodyParam", responseParam.responseType, responseParam.responseBody, responseParam.extraProperties), UpdateForParam { override fun copyContent(): Param { - return UpdateForRPCResponseParam(responseParam.copy() as RPCResponseParam) + return UpdateForRPCResponseParam(responseParam.copy() as ClassResponseParam) } @@ -19,10 +19,10 @@ class UpdateForRPCResponseParam(val responseParam: RPCResponseParam) } override fun isSameTypeWithUpdatedParam(param: Param): Boolean { - return param is RPCResponseParam + return param is ClassResponseParam } override fun getUpdatedParam(): Param { return responseParam } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt index e3cde8add4..698b3982ed 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt @@ -15,7 +15,7 @@ import org.evomaster.core.problem.api.param.Param import org.evomaster.core.problem.enterprise.EnterpriseActionGroup import org.evomaster.core.problem.externalservice.ApiExternalServiceAction import org.evomaster.core.problem.externalservice.rpc.RPCExternalServiceAction -import org.evomaster.core.problem.externalservice.rpc.parm.RPCResponseParam +import org.evomaster.core.problem.externalservice.rpc.parm.ClassResponseParam import org.evomaster.core.problem.rest.RestActionBuilderV3 import org.evomaster.core.problem.rpc.RPCCallAction import org.evomaster.core.problem.rpc.RPCCallResult @@ -257,7 +257,7 @@ class RPCEndpointsHandler { } }.run { wrapWithOptionalGene(this, true) }) as OptionalGene - val response = RPCResponseParam(className = s, responseType = EnumGene("responseType", listOf("JSON")), response = responseGene) + val response = ClassResponseParam(className = s, responseType = EnumGene("responseType", listOf("JSON")), response = responseGene) if (fromClass) response.responseParsedWithClass() val externalAction = RPCExternalServiceAction( @@ -292,7 +292,7 @@ class RPCEndpointsHandler { inputParameterTypes = action.inputParamTypes requestRules = if (action.requestRuleIdentifier.isNullOrEmpty()) null else listOf(action.requestRuleIdentifier) responses = listOf(action.response.responseBody.getValueAsPrintableString(mode = mode)) - responseTypes = if ((action.response as? RPCResponseParam)?.className?.isNotBlank() == true) listOf((action.response as RPCResponseParam).className) else null + responseTypes = if ((action.response as? ClassResponseParam)?.className?.isNotBlank() == true) listOf((action.response as ClassResponseParam).className) else null } } @@ -550,7 +550,6 @@ class RPCEndpointsHandler { } } - val allDtoNames = infoDto.unitsInfoDto.parsedDtos.keys.toList() val allDtoSchemas = allDtoNames.map { infoDto.unitsInfoDto.parsedDtos[it]!! } RestActionBuilderV3.createObjectGeneForDTOs(allDtoNames, allDtoSchemas, allDtoNames, enableConstraintHandling = config.enableSchemaConstraintHandling) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt index 40a003b444..80768c5a9f 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt @@ -7,7 +7,7 @@ import org.evomaster.core.Lazy import org.evomaster.core.database.DbAction import org.evomaster.core.problem.api.service.ApiWsFitness import org.evomaster.core.problem.externalservice.rpc.RPCExternalServiceAction -import org.evomaster.core.problem.externalservice.rpc.parm.RPCResponseParam +import org.evomaster.core.problem.externalservice.rpc.parm.ClassResponseParam import org.evomaster.core.problem.externalservice.rpc.parm.UpdateForRPCResponseParam import org.evomaster.core.problem.rpc.RPCCallAction import org.evomaster.core.problem.rpc.RPCCallResult @@ -86,17 +86,17 @@ class RPCFitness : ApiWsFitness() { val exMissingDto = individual.seeExternalServiceActions() .filterIsInstance() .filterNot { - ((it.response as? RPCResponseParam)?:throw IllegalStateException("response of RPCExternalServiceAction should be RPCResponseParam, but it is ${it.response::class.java.simpleName}")).fromClass + ((it.response as? ClassResponseParam)?:throw IllegalStateException("response of RPCExternalServiceAction should be RPCResponseParam, but it is ${it.response::class.java.simpleName}")).fromClass } if (exMissingDto.isEmpty()) { return } - val missingDtoClass = exMissingDto.map { (it.response as RPCResponseParam).className } + val missingDtoClass = exMissingDto.map { (it.response as ClassResponseParam).className } rpcHandler.getJVMSchemaForDto(missingDtoClass.toSet()).forEach { expandResponse-> - exMissingDto.filter { (it.response as RPCResponseParam).run { !this.fromClass && expandResponse.key == this.className} }.forEach { a-> + exMissingDto.filter { (it.response as ClassResponseParam).run { !this.fromClass && expandResponse.key == this.className} }.forEach { a-> val gene = wrapWithOptionalGene(expandResponse.value, true) as OptionalGene - val updatedParam = (a.response as RPCResponseParam).copyWithSpecifiedResponseBody(gene) + val updatedParam = (a.response as ClassResponseParam).copyWithSpecifiedResponseBody(gene) updatedParam.responseParsedWithClass() val update = UpdateForRPCResponseParam(updatedParam) a.addUpdateForParam(update) @@ -328,4 +328,4 @@ class RPCFitness : ApiWsFitness() { } } } -} \ No newline at end of file +} diff --git a/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt b/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt index 0bd37207cd..6e2cb62ddf 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt @@ -5,7 +5,7 @@ import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionResult import org.evomaster.core.problem.externalservice.ApiExternalServiceAction import org.evomaster.core.problem.externalservice.rpc.RPCExternalServiceAction -import org.evomaster.core.problem.externalservice.rpc.parm.RPCResponseParam +import org.evomaster.core.problem.externalservice.rpc.parm.ClassResponseParam import org.evomaster.core.problem.rest.* import org.evomaster.core.problem.rest.resource.RestResourceCalls import org.evomaster.core.problem.rpc.RPCCallAction @@ -87,7 +87,7 @@ class EvaluatedIndividualBuilder { functionName = "foo", inputParamTypes = null, requestRuleIdentifier = null, - responseParam = RPCResponseParam( + responseParam = ClassResponseParam( className = "FakeRPCReturnDto", responseType = EnumGene("responseType", listOf("JSON")), response = OptionalGene("return", @@ -123,4 +123,4 @@ class EvaluatedIndividualBuilder { }) } } -} \ No newline at end of file +} From 7b3704713dc2fb0af3019816b81f36db6dbf461f Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 22 May 2023 23:24:08 +0200 Subject: [PATCH 038/345] parse mock db objects --- .../rpc/DbAsExternalServiceAction.kt | 8 +++- .../rpc/service/RPCEndpointsHandler.kt | 45 +++++++++++++++++-- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/DbAsExternalServiceAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/DbAsExternalServiceAction.kt index a09199a438..2d8044510f 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/DbAsExternalServiceAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/DbAsExternalServiceAction.kt @@ -29,11 +29,15 @@ class DbAsExternalServiceAction ( companion object{ - fun getDbAsExternalServiceAction(exAction: DbAsExternalServiceAction) = "DbAsExternalServiceAction${EXACTION_NAME_SEPARATOR}${exAction.commandName}${EXACTION_NAME_SEPARATOR}${exAction.requestRuleIdentifier?:"ANY"}$EXACTION_NAME_SEPARATOR${(exAction.response as ClassResponseParam).className}" + fun getDbAsExternalServiceAction( + commandName: String, + requestRuleIdentifier: String?, + responseClassName: String + ) = "DbAsExternalServiceAction${EXACTION_NAME_SEPARATOR}${commandName}${EXACTION_NAME_SEPARATOR}${requestRuleIdentifier?:"ANY"}$EXACTION_NAME_SEPARATOR${responseClassName}" } override fun getName(): String { - return getDbAsExternalServiceAction(this) + return getDbAsExternalServiceAction(commandName, requestRuleIdentifier, (response as ClassResponseParam).className) } override fun seeTopGenes(): List { diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt index 698b3982ed..75477a7c2c 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt @@ -14,6 +14,7 @@ import org.evomaster.core.parser.RegexHandler import org.evomaster.core.problem.api.param.Param import org.evomaster.core.problem.enterprise.EnterpriseActionGroup import org.evomaster.core.problem.externalservice.ApiExternalServiceAction +import org.evomaster.core.problem.externalservice.rpc.DbAsExternalServiceAction import org.evomaster.core.problem.externalservice.rpc.RPCExternalServiceAction import org.evomaster.core.problem.externalservice.rpc.parm.ClassResponseParam import org.evomaster.core.problem.rest.RestActionBuilderV3 @@ -125,10 +126,10 @@ class RPCEndpointsHandler { /** * a map of mock objects for sql based on seeded test cases - * - key is action id - * - value is a list of mock object setup for database + * - key is the id of sql action to mock + * - value is the example of MockDatabaseDto */ - private val seededDbMockObjects = mutableMapOf>() + private val seededDbMockObjects = mutableMapOf() /** * key is type in the schema @@ -274,6 +275,44 @@ class RPCEndpointsHandler { actionToExternalServiceMap.getOrPut(actionKey){ mutableSetOf() }.add(exkey) } } + + + rpcActionDto.mockDatabaseDtos?.forEach{ dbDto-> + if (dbDto.commandName != null && dbDto.appKey!=null && dbDto.responseFullType != null){ + val exKey = DbAsExternalServiceAction.getDbAsExternalServiceAction(dbDto.commandName, dbDto.requests, dbDto.responseFullType) + + if (!seededDbMockObjects.containsKey(exKey)){ + /* + currently we only handle the response with json + */ + val responseGene = (if (dbDto.response != null){ + val node = readJson(dbDto.response) + if (node != null){ + parseJsonNodeAsGene("return", node) + }else{ + StringGene("return") + } + }else{ + StringGene("return") + }.run { wrapWithOptionalGene(this, true) }) as OptionalGene + + + val response = ClassResponseParam(className = dbDto.responseFullType, responseType = EnumGene("responseType", listOf("JSON")), response = responseGene) + val dbExAction = DbAsExternalServiceAction( + dbDto.commandName, + dbDto.appKey, + dbDto.requests, + response + ) + seededDbMockObjects[exKey] = dbExAction + } + actionToExternalServiceMap.getOrPut(actionKey){ mutableSetOf() }.add(exKey) + + }else{ + LoggingUtil.uniqueWarn(log, "incorrect mockDatabaseDto with ${dbDto.commandName?:"null"} commandName, ${dbDto.appKey?:"null"} appKey, and ${dbDto.responseFullType?:"null"} responseFullType") + } + + } } /** From 75df08c3cbd390ac25f07f21fb55a4fe5a0499b5 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Tue, 23 May 2023 08:43:24 +0200 Subject: [PATCH 039/345] fixed tests --- .../kotlin/com/foo/graphql/db/exisitingdata/QueryResolver.kt | 4 +++- .../evomaster/e2etests/spring/openapi/v3/sleep/SleepEMTest.kt | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/e2e-tests/spring-graphql/src/main/kotlin/com/foo/graphql/db/exisitingdata/QueryResolver.kt b/e2e-tests/spring-graphql/src/main/kotlin/com/foo/graphql/db/exisitingdata/QueryResolver.kt index 6afd819476..cb34eb0f7f 100644 --- a/e2e-tests/spring-graphql/src/main/kotlin/com/foo/graphql/db/exisitingdata/QueryResolver.kt +++ b/e2e-tests/spring-graphql/src/main/kotlin/com/foo/graphql/db/exisitingdata/QueryResolver.kt @@ -10,7 +10,9 @@ class QueryResolver( @Autowired private val manager: EntityManager) : GraphQLQueryResolver { fun getY() : List{ - return manager.createQuery("select y from ExistingDataY y where y.x.id=42", ExistingDataY::class.java).resultList + return manager.createQuery("select y from ExistingDataY y where y.x.id=42", ExistingDataY::class.java) + .resultList + .let { if(it.isNotEmpty()) it else listOf() } } } diff --git a/e2e-tests/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/sleep/SleepEMTest.kt b/e2e-tests/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/sleep/SleepEMTest.kt index f1f9394c67..d7fc962375 100644 --- a/e2e-tests/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/sleep/SleepEMTest.kt +++ b/e2e-tests/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/sleep/SleepEMTest.kt @@ -40,7 +40,8 @@ class SleepEMTest : SpringTestBase() { assertTrue(solution.individuals.size >= 1) - assertEquals(iterations, SleepCounter.counter.get()) + //minimization re-execute the test + assertEquals(iterations+1, SleepCounter.counter.get()) } } From d343f8acf441370d10b7c74c4bad42f46c2fd404 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Tue, 23 May 2023 08:52:28 +0200 Subject: [PATCH 040/345] adding info on executed tests --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c3f25702c..2e3641dfa4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,6 +52,10 @@ jobs: path: core/target/evomaster.jar retention-days: ${{env.retention-days}} if-no-files-found: error + # Make test report accessible from GitHub Actions (as Maven logs are long) + - name: Publish Test Report + if: success() || failure() + uses: scacap/action-surefire-report@v1 # Upload coverage results - name: Upload coverage to CodeCov run: curl -s https://codecov.io/bash | bash From b9b36c956ac937a300eac7a663a377c12794c547 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Tue, 23 May 2023 14:36:04 +0200 Subject: [PATCH 041/345] fixing a test + trying different test reporter --- .github/workflows/ci.yml | 6 +++++- .../service/ExternalServiceMockingFlakyEMTest.java | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e3641dfa4..01a0fc9ccb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,9 +53,13 @@ jobs: retention-days: ${{env.retention-days}} if-no-files-found: error # Make test report accessible from GitHub Actions (as Maven logs are long) + # TODO: does not seem to work, see https://github.com/ScaCap/action-surefire-report/issues/161 +# - name: Publish Test Report +# if: success() || failure() +# uses: scacap/action-surefire-report@v1 - name: Publish Test Report if: success() || failure() - uses: scacap/action-surefire-report@v1 + uses: mikepenz/action-junit-report@v3 # Upload coverage results - name: Upload coverage to CodeCov run: curl -s https://codecov.io/bash | bash diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/wiremock/service/ExternalServiceMockingFlakyEMTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/wiremock/service/ExternalServiceMockingFlakyEMTest.java index 90bbb0690c..c66bd1bd8e 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/wiremock/service/ExternalServiceMockingFlakyEMTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/wiremock/service/ExternalServiceMockingFlakyEMTest.java @@ -18,6 +18,7 @@ import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class ExternalServiceMockingFlakyEMTest extends SpringTestBase { @@ -59,8 +60,10 @@ public void externalServiceMockingTest() throws Throwable { actions.addAll(call.seeActions(ActionFilter.ONLY_EXTERNAL_SERVICE)); } } - assertEquals(actions.size(), 13); - // End block + //assertEquals(actions.size(), 13); + //Andrea: there should be clear reason for hardcoded numbers like 13. otherwise, when we get a new + // value (like 12 in my case) how do we know it is a bug and not just a harmless change in the search process? + assertTrue(actions.size() > 0); assertHasAtLeastOne(solution, HttpVerb.GET, 200, "/api/wiremock/external", "true"); assertHasAtLeastOne(solution, HttpVerb.GET, 200, "/api/wiremock/external/complex", "true"); From 708e5962b5e2467e4b534fbefaa38309dfc0074f Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Tue, 23 May 2023 17:56:08 +0200 Subject: [PATCH 042/345] trying to configure test report --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 01a0fc9ccb..3cfa100b15 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,6 +60,8 @@ jobs: - name: Publish Test Report if: success() || failure() uses: mikepenz/action-junit-report@v3 + with: + report_paths: '**/target/surefire-reports/TEST-*.xml' # Upload coverage results - name: Upload coverage to CodeCov run: curl -s https://codecov.io/bash | bash From a3164e10d94151e131d29ad1cc23a56eed94897b Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Wed, 24 May 2023 13:25:36 +0200 Subject: [PATCH 043/345] clarification + fix for failing tests --- .github/workflows/ci.yml | 4 ---- .../kotlin/org/evomaster/core/search/service/Minimizer.kt | 4 +++- .../examples/hypermutation/AdaptiveHypermutationTest.java | 4 ++++ .../spring/examples/hypermutation/HypermutationTest.java | 4 ++++ 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3cfa100b15..28cc5b57a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,10 +53,6 @@ jobs: retention-days: ${{env.retention-days}} if-no-files-found: error # Make test report accessible from GitHub Actions (as Maven logs are long) - # TODO: does not seem to work, see https://github.com/ScaCap/action-surefire-report/issues/161 -# - name: Publish Test Report -# if: success() || failure() -# uses: scacap/action-surefire-report@v1 - name: Publish Test Report if: success() || failure() uses: mikepenz/action-junit-report@v3 diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt index e90dd13b65..20ddfa5019 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Minimizer.kt @@ -11,7 +11,9 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory /** - * Reduce/simplify the final test outputs + * Reduce/simplify the final test outputs. + * + * WARN: currently minimization loses all history info from EvaluatedIndividual, eg impact of genes */ class Minimizer { diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/hypermutation/AdaptiveHypermutationTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/hypermutation/AdaptiveHypermutationTest.java index a401b9cb29..cc97c27926 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/hypermutation/AdaptiveHypermutationTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/hypermutation/AdaptiveHypermutationTest.java @@ -45,6 +45,10 @@ public void testRunAdaptiveHypermutation() throws Throwable { args.add("--probOfRandomSampling"); args.add("0.0"); + //minimization loses impact info + args.add("--minimize"); + args.add("false"); + Solution solution = initAndRun(args); diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/hypermutation/HypermutationTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/hypermutation/HypermutationTest.java index 65077fb5bd..a02a9c539f 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/hypermutation/HypermutationTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/hypermutation/HypermutationTest.java @@ -50,6 +50,10 @@ public void testRunHypermutation() throws Throwable { args.add("--focusedSearchActivationTime"); args.add("0.1"); + //minimization loses impact info + args.add("--minimize"); + args.add("false"); + Solution solution = initAndRun(args); From 66668f2378b8b4ba81ed0e610314790ed65acf36 Mon Sep 17 00:00:00 2001 From: Juan Pablo Galeotti Date: Wed, 24 May 2023 19:43:46 -0300 Subject: [PATCH 044/345] @Pattern implemented --- .../core/database/DbActionGeneBuilder.kt | 70 +++++++----- .../core/database/SqlInsertBuilder.kt | 5 +- .../evomaster/core/database/schema/Column.kt | 5 +- .../core/database/DbActionGeneBuilderTest.kt | 26 ++--- .../postgres/ManySimilarToChecksTest.kt | 103 ++++++++++++++++++ .../postgres_many_similarto_checks_db.sql | 11 ++ e2e-tests/spring-rest-openapi-v2/pom.xml | 7 ++ .../examples/spring/db/jpa/EntityJPAData.java | 13 +++ .../examples/spring/db/jpa/EntityJPARest.java | 18 ++- .../src/main/resources/sql/entityjpa.sql | 4 +- .../examples/db/jpa/EntityJPAEMTest.java | 2 + 11 files changed, 212 insertions(+), 52 deletions(-) create mode 100644 core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ManySimilarToChecksTest.kt create mode 100644 core/src/test/resources/sql_schema/postgres_many_similarto_checks_db.sql diff --git a/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt b/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt index 8444895620..f346904d28 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt @@ -5,6 +5,7 @@ import org.evomaster.core.database.schema.Column import org.evomaster.core.database.schema.ColumnDataType import org.evomaster.core.database.schema.ForeignKey import org.evomaster.core.database.schema.Table +import org.evomaster.core.parser.RegexHandler import org.evomaster.core.parser.RegexHandler.createGeneForPostgresLike import org.evomaster.core.parser.RegexHandler.createGeneForPostgresSimilarTo import org.evomaster.core.search.gene.* @@ -20,9 +21,7 @@ import org.evomaster.core.search.gene.network.MacAddrGene import org.evomaster.core.search.gene.numeric.* import org.evomaster.core.search.gene.optional.ChoiceGene import org.evomaster.core.search.gene.optional.NullableGene -import org.evomaster.core.search.gene.regex.DisjunctionListRxGene import org.evomaster.core.search.gene.regex.RegexGene -import org.evomaster.core.search.gene.regex.RegexGene.Companion.DATABASE_REGEX_SEPARATOR import org.evomaster.core.search.gene.sql.* import org.evomaster.core.search.gene.sql.textsearch.SqlTextSearchQueryGene import org.evomaster.core.search.gene.sql.textsearch.SqlTextSearchVectorGene @@ -660,14 +659,31 @@ class DbActionGeneBuilder { EnumGene(name = column.name, data = column.enumValuesAsStrings) } } else { - if (column.similarToPatterns != null && column.similarToPatterns.isNotEmpty()) { + + val numberOfRegexPatterns = (column.similarToPatterns?.size ?: 0) + (column.likePatterns?.size ?:0) + if (column.javaRegExPattern == null ) 0 else 1 + + if (!column.similarToPatterns.isNullOrEmpty()) { val columnName = column.name - val similarToPatterns: List = column.similarToPatterns - buildSimilarToRegexGene(columnName, similarToPatterns, databaseType = column.databaseType) - } else if (column.likePatterns != null && column.likePatterns.isNotEmpty()) { + val similarToPattern: String = column.similarToPatterns[0] + if (numberOfRegexPatterns>1) { + /** + * TODO Handle a conjunction of Regex patterns + */ + log.warn("Handling only a regex pattern for (${column.name}). Using similar to pattern: ${similarToPattern}}") + } + buildSimilarToRegexGene(columnName, similarToPattern, databaseType = column.databaseType) + } else if (!column.likePatterns.isNullOrEmpty()) { val columnName = column.name - val likePatterns = column.likePatterns - buildLikeRegexGene(columnName, likePatterns, databaseType = column.databaseType) + val likePattern = column.likePatterns[0] + if (numberOfRegexPatterns>1) { + /** + * TODO Handle a conjunction of Regex patterns + */ + log.warn("Handling only a regex pattern for (${column.name}). Using like pattern: ${likePattern}}") + } + buildLikeRegexGene(columnName, likePattern, databaseType = column.databaseType) + } else if (column.javaRegExPattern != null) { + buildJavaRegexGene(column.name, column.javaRegExPattern) } else { val columnMinLength = if (isFixedLength) { column.size @@ -690,18 +706,24 @@ class DbActionGeneBuilder { } } + private fun buildJavaRegexGene(name: String, javaRegExPattern: String): RegexGene { + val disjunctionRxGenes = RegexHandler.createGeneForJVM(javaRegExPattern).disjunctions + return RegexGene(name, disjunctions = disjunctionRxGenes, "${RegexGene.JAVA_REGEX_PREFIX}${javaRegExPattern}") + + } + /** * Builds a RegexGene using a name and a list of LIKE patterns. * The resulting gene is a disjunction of the given patterns * * TODO need to handle NOT and ILIKE */ - fun buildLikeRegexGene(geneName: String, likePatterns: List, databaseType: DatabaseType): RegexGene { + fun buildLikeRegexGene(geneName: String, likePattern: String, databaseType: DatabaseType): RegexGene { return when (databaseType) { DatabaseType.POSTGRES, //https://www.postgresqltutorial.com/postgresql-tutorial/postgresql-like/ DatabaseType.H2, // http://www.h2database.com/html/grammar.html#like_predicate_right_hand_side DatabaseType.MYSQL -> { - buildPostgresMySQLLikeRegexGene(geneName, likePatterns) + buildPostgresMySQLLikeRegexGene(geneName, likePattern) } //TODO: support other database SIMILAR_TO check expressions else -> throw UnsupportedOperationException( @@ -712,29 +734,23 @@ class DbActionGeneBuilder { } } - private fun buildPostgresMySQLLikeRegexGene(geneName: String, likePatterns: List): RegexGene { - val disjunctionRxGenes = likePatterns - .map { createGeneForPostgresLike(it) } - .map { it.disjunctions } - .map { it.disjunctions } - .flatten() - return RegexGene(geneName, disjunctions = DisjunctionListRxGene(disjunctions = disjunctionRxGenes), "${RegexGene.DATABASE_REGEX_PREFIX}${likePatterns.joinToString(DATABASE_REGEX_SEPARATOR)}") + private fun buildPostgresMySQLLikeRegexGene(geneName: String, likePattern: String): RegexGene { + val disjunctionRxGenes = createGeneForPostgresLike(likePattern).disjunctions + return RegexGene(geneName, disjunctions = disjunctionRxGenes, "${RegexGene.DATABASE_REGEX_PREFIX}${likePattern}") } /** - * Builds a RegexGene using a name and a list of SIMILAR_TO patterns. - * The resulting gene is a disjunction of the given patterns - * according to the database we are using + * Builds a RegexGene using a name and a SIMILAR_TO pattern. */ fun buildSimilarToRegexGene( geneName: String, - similarToPatterns: List, + similarToPattern: String, databaseType: DatabaseType ): RegexGene { return when(databaseType) { DatabaseType.POSTGRES, - DatabaseType.H2 -> buildPostgresSimilarToRegexGene(geneName, similarToPatterns) + DatabaseType.H2 -> buildPostgresSimilarToRegexGene(geneName, similarToPattern) //TODO: support other database SIMILAR_TO check expressions else -> throw UnsupportedOperationException( "Must implement similarTo expressions for database %s".format( @@ -744,13 +760,9 @@ class DbActionGeneBuilder { } } - private fun buildPostgresSimilarToRegexGene(geneName: String, similarToPatterns: List): RegexGene { - val disjunctionRxGenes = similarToPatterns - .map { createGeneForPostgresSimilarTo(it) } - .map { it.disjunctions } - .map { it.disjunctions } - .flatten() - return RegexGene(geneName, disjunctions = DisjunctionListRxGene(disjunctions = disjunctionRxGenes), "${RegexGene.DATABASE_REGEX_PREFIX}${similarToPatterns.joinToString(DATABASE_REGEX_SEPARATOR)}") + private fun buildPostgresSimilarToRegexGene(geneName: String, similarToPattern: String): RegexGene { + val regexGene = createGeneForPostgresSimilarTo(similarToPattern) + return RegexGene(geneName, disjunctions = regexGene.disjunctions, "${RegexGene.DATABASE_REGEX_PREFIX}${similarToPattern}") } private fun buildSqlTimeWithTimeZoneGene(column: Column): TimeGene { diff --git a/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt b/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt index e77a4446b1..50fca3d9f6 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt @@ -303,7 +303,7 @@ class SqlInsertBuilder( } val mergedSimilarToPatterns: List? = similarToPatterns.takeIf { it.isNotEmpty() } - + val javaRegExPattern = extra.constraints.patternRegExp //TODO all other constraints @@ -315,7 +315,8 @@ class SqlInsertBuilder( similarToPatterns = mergedSimilarToPatterns, isNotBlank = isNotBlank, minSize = minSize, - maxSize = maxSize + maxSize = maxSize, + javaRegExPattern = javaRegExPattern ) } diff --git a/core/src/main/kotlin/org/evomaster/core/database/schema/Column.kt b/core/src/main/kotlin/org/evomaster/core/database/schema/Column.kt index ba77ff838e..0fac08cc30 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/schema/Column.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/schema/Column.kt @@ -61,7 +61,10 @@ data class Column( val minSize: Int? = null, - val maxSize: Int? = null + val maxSize: Int? = null, + + val javaRegExPattern: String? = null + //TODO something for other constraints ) \ No newline at end of file diff --git a/core/src/test/kotlin/org/evomaster/core/database/DbActionGeneBuilderTest.kt b/core/src/test/kotlin/org/evomaster/core/database/DbActionGeneBuilderTest.kt index a3fccaae83..ac6c85f104 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/DbActionGeneBuilderTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/DbActionGeneBuilderTest.kt @@ -2,9 +2,7 @@ package org.evomaster.core.database import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.core.search.gene.regex.RegexGene.Companion.DATABASE_REGEX_PREFIX -import org.evomaster.core.search.gene.regex.RegexGene.Companion.DATABASE_REGEX_SEPARATOR import org.evomaster.core.search.service.Randomness -import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test @@ -18,7 +16,7 @@ class DbActionGeneBuilderTest { val randomness = Randomness() val javaRegex = "/foo/../bar/(left|right)/[0-9]{4}-[0-9]{2}-[0-9]{2}(/[0-9]*)?" - val gene = DbActionGeneBuilder().buildSimilarToRegexGene("w_id", listOf(javaRegex), databaseType = DatabaseType.POSTGRES) + val gene = DbActionGeneBuilder().buildSimilarToRegexGene("w_id", javaRegex, databaseType = DatabaseType.POSTGRES) for (seed in 1..100L) { randomness.updateSeed(seed) @@ -29,7 +27,7 @@ class DbActionGeneBuilderTest { val pattern = Pattern.compile(javaRegex) val matcher = pattern.matcher(instance) - Assertions.assertTrue(matcher.find()) + assertTrue(matcher.find()) } } @@ -39,24 +37,24 @@ class DbActionGeneBuilderTest { val randomness = Randomness() - val likePatterns = listOf("hi", "%foo%", "%foo%x%", "%bar%", "%bar%y%", "%hello%") - val javaRegexPatterns = listOf("hi", ".*foo.*", ".*foo.*x.*", ".*bar.*", ".*bar.*y.*", ".*hello.*") - val gene = DbActionGeneBuilder().buildLikeRegexGene("f_id", likePatterns, databaseType = DatabaseType.POSTGRES) - - assertEquals("${DATABASE_REGEX_PREFIX}${likePatterns.joinToString(DATABASE_REGEX_SEPARATOR)}", gene.sourceRegex) - for (seed in 1..10000L) { randomness.updateSeed(seed) + val likePatterns = listOf("hi", "%foo%", "%foo%x%", "%bar%", "%bar%y%", "%hello%") + val javaRegexPatterns = listOf("hi", ".*foo.*", ".*foo.*x.*", ".*bar.*", ".*bar.*y.*", ".*hello.*") + + val chosenRegex = randomness.nextInt(0,likePatterns.size-1) + + val gene = DbActionGeneBuilder().buildLikeRegexGene("f_id", likePatterns[chosenRegex], databaseType = DatabaseType.POSTGRES) + + assertEquals("${DATABASE_REGEX_PREFIX}${likePatterns[chosenRegex]}", gene.sourceRegex) + gene.randomize(randomness, false) val instance = gene.getValueAsRawString() assertTrue( - javaRegexPatterns.stream().anyMatch { - Pattern.compile(it).matcher(instance).find() - }, - "invalid instance: " + instance + Pattern.compile(javaRegexPatterns[chosenRegex]).matcher(instance).find(), "invalid instance: " + instance ) } } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ManySimilarToChecksTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ManySimilarToChecksTest.kt new file mode 100644 index 0000000000..d78e1bbe8d --- /dev/null +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ManySimilarToChecksTest.kt @@ -0,0 +1,103 @@ +package org.evomaster.core.database.extract.postgres + +import org.evomaster.client.java.controller.db.SqlScriptRunner +import org.evomaster.client.java.controller.internal.db.SchemaExtractor +import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.search.gene.regex.RegexGene +import org.evomaster.core.search.service.Randomness +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import java.sql.SQLException + + +/** + * Created by jgaleotti on 24-May-23. + */ +class ManySimilarToChecksTest : ExtractTestBasePostgres() { + + override fun getSchemaLocation() = "/sql_schema/postgres_many_similarto_checks_db.sql" + + @Test + fun testManySimilarToPatternsSchemaExtraction() { + val schema = SchemaExtractor.extract(connection) + + assertEquals(2, schema.tables.first { it.name.equals("email_table", ignoreCase = true) }.tableCheckExpressions.size) + + val builder = SqlInsertBuilder(schema) + val emailTable = builder.getTable("email_table", useExtraConstraints = true) + + assertEquals(1, emailTable.columns.size) + val emailColumn = emailTable.columns.first() + + assertNotNull(emailColumn.similarToPatterns) + assertEquals(2, emailColumn.similarToPatterns!!.size) + } + + @Test + fun testCheckRegexGenesAreCreatedForMultiplePatterns() { + val schema = SchemaExtractor.extract(connection) + + val builder = SqlInsertBuilder(schema) + val actions = builder.createSqlInsertionAction("email_table", setOf("email_column")) + val genes = actions[0].seeTopGenes() + + assertEquals(1, genes.size) + assertTrue(genes[0] is RegexGene) + + } + + + @Disabled("pending implementing support for conjunction of patterns") + @Test + fun testCheckInsertionOfValuesUsingMultipleSimilarToPatterns() { + val randomness = Randomness() + val schema = SchemaExtractor.extract(connection) + + val builder = SqlInsertBuilder(schema) + val actions = builder.createSqlInsertionAction("email_table", setOf("email_column")) + + val genes = actions[0].seeTopGenes() + val regexGene = genes.firstIsInstance() + regexGene.randomize(randomness, false) + val expectedValue = regexGene.getValueAsRawString() + + val query = "Select * from email_table" + + val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) + assertTrue(queryResultBeforeInsertion.isEmpty) + + val dbCommandDto = DbActionTransformer.transform(actions) + + SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) + val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) + assertFalse(queryResultAfterInsertion.isEmpty) + + val row = queryResultAfterInsertion.seeRows()[0] + val textValue = row.getValueByName("email_column") + + assertEquals(expectedValue, textValue) + } + + + @Test + fun testInsertingAnInvalidValue() { + val query = "INSERT INTO email_table (email_column) VALUES ('mymail@mydomain.com');" + try { + SqlScriptRunner.execCommand(connection, query) + fail() + } catch (ex: SQLException) { + // pass + } + } + + @Test + fun testInsertingValidValue() { + val query = "INSERT INTO email_table (email_column) VALUES ('mymail@yahoo.com');" + SqlScriptRunner.execCommand(connection, query) + } + + +} \ No newline at end of file diff --git a/core/src/test/resources/sql_schema/postgres_many_similarto_checks_db.sql b/core/src/test/resources/sql_schema/postgres_many_similarto_checks_db.sql new file mode 100644 index 0000000000..276c1940d9 --- /dev/null +++ b/core/src/test/resources/sql_schema/postgres_many_similarto_checks_db.sql @@ -0,0 +1,11 @@ +CREATE TABLE email_table +( + email_column TEXT NOT NULL, + CONSTRAINT email_format CHECK (email_column SIMILAR TO '[A-Za-z]{2,}@[A-Za-z]+.[A-Za-z]{2,}'), + CONSTRAINT email_domain CHECK (email_column SIMILAR TO '%@(gmail|yahoo|hotmail)%') +); + + + + + diff --git a/e2e-tests/spring-rest-openapi-v2/pom.xml b/e2e-tests/spring-rest-openapi-v2/pom.xml index c138ae5f54..7f7cbc6d15 100644 --- a/e2e-tests/spring-rest-openapi-v2/pom.xml +++ b/e2e-tests/spring-rest-openapi-v2/pom.xml @@ -165,6 +165,13 @@ okhttp + + + org.locationtech.jts + jts-core + 1.19.0 + + diff --git a/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java b/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java index 52bc94f4dc..9495d69720 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java +++ b/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java @@ -129,6 +129,7 @@ public void setDecimalMax(int decimalMax) { } @Size(min = 1, max = 8) + @Column(name = "size") private String size; public String getSize() { @@ -139,4 +140,16 @@ public void setSize(String size) { this.size = size; } + @Pattern(regexp="\\Qfoo.com\\E") + @Column(name = "reg_exp") + private String regexp; + + public String getRegexp() { + return regexp; + } + + public void setRegexp(String regexp) { + this.regexp = regexp; + } + } diff --git a/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPARest.java b/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPARest.java index c570743f55..40eb426df9 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPARest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPARest.java @@ -1,11 +1,14 @@ package com.foo.rest.examples.spring.db.jpa; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; +import java.util.Iterator; + @RestController @RequestMapping(path = "/api/db/jpa") public class EntityJPARest { @@ -20,12 +23,19 @@ public class EntityJPARest { public ResponseEntity put() { repository.findById(0); // FIXME currently not handling empty SELECTs with no WHERE - EntityJPAData data = repository.findAll().iterator().next(); - data.setX1(42); + Iterator it = repository.findAll().iterator(); + if (it.hasNext()) { + EntityJPAData data = it.next(); + + data.setX1(42); + + repository.save(data); // this should fail if any value is invalid - repository.save(data); // this should fail if any value is invalid + return ResponseEntity.ok("OK"); + } else { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Resource not found"); - return ResponseEntity.ok("OK"); + } } } diff --git a/e2e-tests/spring-rest-openapi-v2/src/main/resources/sql/entityjpa.sql b/e2e-tests/spring-rest-openapi-v2/src/main/resources/sql/entityjpa.sql index 613211e443..022b2865ad 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/main/resources/sql/entityjpa.sql +++ b/e2e-tests/spring-rest-openapi-v2/src/main/resources/sql/entityjpa.sql @@ -9,6 +9,6 @@ create table IF NOT EXISTS existing_table ( positive_or_zero integer not null, decimal_min integer not null, decimal_max integer not null, - size varchar not null - + size varchar not null, + reg_exp varchar not null ); \ No newline at end of file diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/db/jpa/EntityJPAEMTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/db/jpa/EntityJPAEMTest.java index 61bebf6a0b..a3dabfa815 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/db/jpa/EntityJPAEMTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/db/jpa/EntityJPAEMTest.java @@ -58,6 +58,8 @@ public void testOkRunEM() throws Throwable { assertTrue(solution.getIndividuals().size() >= 1); assertHasAtLeastOne(solution, HttpVerb.PUT, 200, "/api/db/jpa", "OK"); + assertNone(solution,HttpVerb.PUT,500); + }); } From 2eafaf004a08925a69fb12c6db8787bc75d047db Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Thu, 25 May 2023 13:55:33 +0200 Subject: [PATCH 045/345] investigatin window DNS issue --- .../classes/SocketClassReplacement.java | 2 ++ .../v3/wiremock/inet/InetReplacementRest.kt | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/SocketClassReplacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/SocketClassReplacement.java index 3415454f8f..db85f1e618 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/SocketClassReplacement.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/SocketClassReplacement.java @@ -32,6 +32,8 @@ public static void connect(Socket caller, SocketAddress endpoint, int timeout) t if (endpoint instanceof InetSocketAddress) { InetSocketAddress socketAddress = (InetSocketAddress) endpoint; + FIXME cannot call getHostName() + if (ExternalServiceInfoUtils.skipHostnameOrIp(socketAddress.getHostName()) || ExecutionTracer.skipHostnameAndPort(socketAddress.getHostName(), socketAddress.getPort()) ) { diff --git a/e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/wiremock/inet/InetReplacementRest.kt b/e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/wiremock/inet/InetReplacementRest.kt index 66e82f7af2..18cfe18079 100644 --- a/e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/wiremock/inet/InetReplacementRest.kt +++ b/e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/wiremock/inet/InetReplacementRest.kt @@ -12,18 +12,34 @@ import java.net.Socket @RequestMapping(path = ["/api/inet"]) class InetReplacementRest { + var start = System.currentTimeMillis() + + fun log(msg: String){ + val now = System.currentTimeMillis() + val diff = (now - start) + println("$diff\t$msg") + start = now + } + @GetMapping(path = ["/exp"]) fun exp(): ResponseEntity { + val address = InetAddress.getByName("imaginary-host.local") return try { val socket = Socket() - socket.connect(InetSocketAddress(address, 10000), 1000) + log("Before address") + val a = InetSocketAddress(address, 10000) + log("Before connect") + socket.connect(a, 1000) + log("After connect") socket.close() + log("After close") ResponseEntity.ok("OK") } catch (e: Exception) { + log("Exception: ${e.message}") ResponseEntity.status(500).build() } } From 46c695b282489bd1ede6083a934051eb6be2a6a1 Mon Sep 17 00:00:00 2001 From: Juan Pablo Galeotti Date: Thu, 25 May 2023 17:43:02 -0300 Subject: [PATCH 046/345] @DecimalMax and @DecimalMin implemented (BigDecimal not supported yet) --- .../core/database/DbActionGeneBuilder.kt | 1 - .../core/database/SqlInsertBuilder.kt | 25 +++++++++++++++++-- .../examples/spring/db/jpa/EntityJPAData.java | 5 ++-- .../examples/db/jpa/EntityJPAEMTest.java | 2 +- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt b/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt index f346904d28..d8187884fc 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt @@ -709,7 +709,6 @@ class DbActionGeneBuilder { private fun buildJavaRegexGene(name: String, javaRegExPattern: String): RegexGene { val disjunctionRxGenes = RegexHandler.createGeneForJVM(javaRegExPattern).disjunctions return RegexGene(name, disjunctions = disjunctionRxGenes, "${RegexGene.JAVA_REGEX_PREFIX}${javaRegExPattern}") - } /** diff --git a/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt b/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt index 50fca3d9f6..d777340432 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt @@ -56,6 +56,25 @@ class SqlInsertBuilder( private val log: Logger = LoggerFactory.getLogger(SqlInsertBuilder::class.java) private const val EMAIL_SIMILAR_TO_PATTERN = "[A-Za-z]{2,}@[A-Za-z]+.[A-Za-z]{2,}" + + /** + * input = "12345" returns 12345 + * input = "3.14" returns 3 + * input = "42.0" returns 42 + * input = null returns null + */ + fun getLowerLongBound(input: String?): Long? = input?.toDoubleOrNull()?.toLong() ?: input?.toLongOrNull() + + /** + * input = "123" returns 123 + * input = "3.14" returns 4 + * input = "42.0" returns 42 + * input = null returns null + */ + fun getUpperLongBound(input: String?): Long? { + val decimalValue = input?.toDoubleOrNull() + return decimalValue?.toLong()?.let { if (decimalValue % 1 != 0.0) it + 1 else it } ?: input?.toLongOrNull() + } } @@ -279,14 +298,16 @@ class SqlInsertBuilder( column.lowerBound, extra.constraints.minValue, extra.constraints.isPositive?.let { if (it) 1 else null }, - extra.constraints.isPositiveOrZero?.let { if (it) 0 else null } + extra.constraints.isPositiveOrZero?.let { if (it) 0 else null }, + getLowerLongBound(extra.constraints.decimalMinValue) ).maxOrNull() val mergedUpperBound = listOfNotNull( column.upperBound, extra.constraints.maxValue, extra.constraints.isNegative?.let { if (it) -1 else null }, - extra.constraints.isNegativeOrZero?.let { if (it) 0 else null } + extra.constraints.isNegativeOrZero?.let { if (it) 0 else null }, + getUpperLongBound(extra.constraints.decimalMaxValue) ).minOrNull() val minSize = extra.constraints.sizeMin diff --git a/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java b/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java index 9495d69720..86048d3def 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java +++ b/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java @@ -3,7 +3,6 @@ import javax.persistence.*; import javax.validation.constraints.*; -import java.math.BigDecimal; @Entity @Table(name = "ExistingTable") @@ -104,7 +103,7 @@ public void setPositiveOrZero(int positiveOrZero) { this.positiveOrZero = positiveOrZero; } - // @DecimalMin(value = "3.1416") + @DecimalMin(value = "3.1416") @Column(name = "decimal_min") private int decimalMin; @@ -116,7 +115,7 @@ public void setDecimalMin(int decimalMin) { this.decimalMin = decimalMin; } - // @DecimalMax(value = "0.000001") + @DecimalMax(value = "0.000001") @Column(name = "decimal_max") private int decimalMax; diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/db/jpa/EntityJPAEMTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/db/jpa/EntityJPAEMTest.java index a3dabfa815..dacfca7818 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/db/jpa/EntityJPAEMTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/db/jpa/EntityJPAEMTest.java @@ -58,7 +58,7 @@ public void testOkRunEM() throws Throwable { assertTrue(solution.getIndividuals().size() >= 1); assertHasAtLeastOne(solution, HttpVerb.PUT, 200, "/api/db/jpa", "OK"); - assertNone(solution,HttpVerb.PUT,500); + //assertNone(solution,HttpVerb.PUT,500); }); } From fe11576a4a9dc6737471ad67837885a24b476d8e Mon Sep 17 00:00:00 2001 From: Juan Pablo Galeotti Date: Thu, 25 May 2023 18:50:41 -0300 Subject: [PATCH 047/345] @Digits implemented --- .../org/evomaster/core/database/SqlInsertBuilder.kt | 8 +++++++- .../foo/rest/examples/spring/db/jpa/EntityJPAData.java | 5 +++++ .../src/main/resources/sql/entityjpa.sql | 3 ++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt b/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt index d777340432..01793b240e 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt @@ -326,9 +326,14 @@ class SqlInsertBuilder( val javaRegExPattern = extra.constraints.patternRegExp + + val size = column.size.coerceAtMost(extra.constraints.digitsInteger?.plus(extra.constraints.digitsFraction ?: 0) ?: column.size) + val scale = extra.constraints.digitsFraction + //TODO all other constraints return column.copy( + size = size, nullable = mergedIsNullable, enumValuesAsStrings = mergedEnumValuesAsStrings, lowerBound = mergedLowerBound, @@ -337,7 +342,8 @@ class SqlInsertBuilder( isNotBlank = isNotBlank, minSize = minSize, maxSize = maxSize, - javaRegExPattern = javaRegExPattern + javaRegExPattern = javaRegExPattern, + scale = scale ) } diff --git a/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java b/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java index 86048d3def..e82b5cb590 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java +++ b/e2e-tests/spring-rest-openapi-v2/src/main/java/com/foo/rest/examples/spring/db/jpa/EntityJPAData.java @@ -3,6 +3,7 @@ import javax.persistence.*; import javax.validation.constraints.*; +import java.math.BigDecimal; @Entity @Table(name = "ExistingTable") @@ -151,4 +152,8 @@ public void setRegexp(String regexp) { this.regexp = regexp; } + + @Digits(integer = 2, fraction = 3) + @Column(name = "big_decimal") + private BigDecimal bigDecimal; } diff --git a/e2e-tests/spring-rest-openapi-v2/src/main/resources/sql/entityjpa.sql b/e2e-tests/spring-rest-openapi-v2/src/main/resources/sql/entityjpa.sql index 022b2865ad..bcb3ac3494 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/main/resources/sql/entityjpa.sql +++ b/e2e-tests/spring-rest-openapi-v2/src/main/resources/sql/entityjpa.sql @@ -10,5 +10,6 @@ create table IF NOT EXISTS existing_table ( decimal_min integer not null, decimal_max integer not null, size varchar not null, - reg_exp varchar not null + reg_exp varchar not null, + big_decimal decimal not null ); \ No newline at end of file From 0e3a09336c80fedab66cb521275d3d344869b107 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Fri, 26 May 2023 09:40:28 +0200 Subject: [PATCH 048/345] fix issue for networking on Windows --- .../classes/SocketClassReplacement.java | 17 ++++++++++++----- .../v3/wiremock/inet/InetReplacementRest.kt | 10 +++++----- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/SocketClassReplacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/SocketClassReplacement.java index db85f1e618..6162661626 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/SocketClassReplacement.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/SocketClassReplacement.java @@ -32,13 +32,20 @@ public static void connect(Socket caller, SocketAddress endpoint, int timeout) t if (endpoint instanceof InetSocketAddress) { InetSocketAddress socketAddress = (InetSocketAddress) endpoint; - FIXME cannot call getHostName() + /* + We MUST NOT call getHostName() anywhere in EM. + On Windows, it can take more than 4 seconds when dealing with a fake hostname. + This latter is common case when dealing with microservices where connections are done on localhost, + and we still want to mock them, so we give them a fake hostname. + A concrete example in EMB is CWA. + */ - if (ExternalServiceInfoUtils.skipHostnameOrIp(socketAddress.getHostName()) - || ExecutionTracer.skipHostnameAndPort(socketAddress.getHostName(), socketAddress.getPort()) + if (ExternalServiceInfoUtils.skipHostnameOrIp(socketAddress.getHostString()) + || ExecutionTracer.skipHostnameAndPort(socketAddress.getHostString(), socketAddress.getPort()) ) { caller.connect(endpoint, timeout); return; + } if (socketAddress.getAddress() instanceof Inet4Address) { @@ -51,8 +58,8 @@ FIXME cannot call getHostName() and if there is a mapping available then Socket will use that value to connect. Otherwise, nothing will happen. */ - if (ExecutionTracer.hasLocalAddressReplacement(socketAddress.getHostName())) { - String newHostname = ExecutionTracer.getRemoteHostname(socketAddress.getHostName()); + if (ExecutionTracer.hasLocalAddressReplacement(socketAddress.getHostString())) { + String newHostname = ExecutionTracer.getRemoteHostname(socketAddress.getHostString()); ExternalServiceInfo remoteHostInfo = new ExternalServiceInfo( ExternalServiceSharedUtils.DEFAULT_SOCKET_CONNECT_PROTOCOL, newHostname, diff --git a/e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/wiremock/inet/InetReplacementRest.kt b/e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/wiremock/inet/InetReplacementRest.kt index 18cfe18079..c40833cbcd 100644 --- a/e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/wiremock/inet/InetReplacementRest.kt +++ b/e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/wiremock/inet/InetReplacementRest.kt @@ -29,17 +29,17 @@ class InetReplacementRest { return try { val socket = Socket() - log("Before address") + // log("Before address") val a = InetSocketAddress(address, 10000) - log("Before connect") + // log("Before connect") socket.connect(a, 1000) - log("After connect") + // log("After connect") socket.close() - log("After close") + // log("After close") ResponseEntity.ok("OK") } catch (e: Exception) { - log("Exception: ${e.message}") + // log("Exception: ${e.message}") ResponseEntity.status(500).build() } } From 01fc34a3bd150419f85f17a44bacbbeded81357b Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Fri, 26 May 2023 13:28:29 +0200 Subject: [PATCH 049/345] disabled minimization for E2E due to possible bug in DNS caching --- .../openapi/v3/wiremock/inet/InetReplacementRest.kt | 3 ++- .../openapi/v3/wiremock/inet/InetReplacementEMTest.kt | 11 ++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/wiremock/inet/InetReplacementRest.kt b/e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/wiremock/inet/InetReplacementRest.kt index c40833cbcd..006662f266 100644 --- a/e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/wiremock/inet/InetReplacementRest.kt +++ b/e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/wiremock/inet/InetReplacementRest.kt @@ -24,6 +24,7 @@ class InetReplacementRest { @GetMapping(path = ["/exp"]) fun exp(): ResponseEntity { + //throw 500 by default on exception val address = InetAddress.getByName("imaginary-host.local") return try { @@ -40,7 +41,7 @@ class InetReplacementRest { ResponseEntity.ok("OK") } catch (e: Exception) { // log("Exception: ${e.message}") - ResponseEntity.status(500).build() + ResponseEntity.status(400).build() } } } \ No newline at end of file diff --git a/e2e-tests/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/wiremock/inet/InetReplacementEMTest.kt b/e2e-tests/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/wiremock/inet/InetReplacementEMTest.kt index e245e65f35..77ee7b34dd 100644 --- a/e2e-tests/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/wiremock/inet/InetReplacementEMTest.kt +++ b/e2e-tests/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/wiremock/inet/InetReplacementEMTest.kt @@ -36,12 +36,21 @@ class InetReplacementEMTest : SpringTestBase() { args.add("--externalServiceIP") args.add("127.0.0.2") + //FIXME should make sure it works with true. + //looks like a bug in resetting the DNS cache to default state + args.add("--minimize") + args.add("false") + val solution = initAndRun(args) Assertions.assertTrue(solution.individuals.size >= 1) if (!CIUtils.isRunningGA()) { - assertHasAtLeastOne(solution, HttpVerb.GET, 200, "/api/inet/exp", "OK") + assertHasAtLeastOne(solution, HttpVerb.GET, 200, "/api/inet/exp", "OK") + //FIXME should also have check on 500 and 400. + //actually should change code, as thrown exception leads to 500, need way to distinguish + //assertHasAtLeastOne(solution, HttpVerb.GET, 400, "/api/inet/exp",null) + //assertHasAtLeastOne(solution, HttpVerb.GET, 500, "/api/inet/exp",null) } }, 3 From baaa4b2bed9330bb7fc22e18d0e3cec6eb505f20 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Fri, 26 May 2023 23:52:34 +0200 Subject: [PATCH 050/345] handle db --- .../rpc/service/RPCEndpointsHandler.kt | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt index 75477a7c2c..e68f0f0649 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt @@ -177,15 +177,15 @@ class RPCEndpointsHandler { */ fun handledSeededTests(tests: Map>): List{ return tests.map {e-> - val td = e.value + val rpcActionDtos = e.value val exActions = mutableListOf>() - val rpcActions = td.map { d-> - val name = actionName(d.interfaceId, d.actionName) - val ex = if (d.mockRPCExternalServiceDtos != null && d.mockRPCExternalServiceDtos.isNotEmpty()) - d.mockRPCExternalServiceDtos.map { e-> + val rpcActions = rpcActionDtos.map { rpcActionDto-> + val name = actionName(rpcActionDto.interfaceId, rpcActionDto.actionName) + if (rpcActionDto.mockRPCExternalServiceDtos != null && rpcActionDto.mockRPCExternalServiceDtos.isNotEmpty()){ + val ex = rpcActionDto.mockRPCExternalServiceDtos.map { e-> e.responses.mapIndexed { index, r-> val exAction = seededExternalServiceCluster[ - RPCExternalServiceAction.getRPCExternalServiceActionName(e.interfaceFullName, e.functionName, e.requestRules[index], e.responseTypes[index]) + RPCExternalServiceAction.getRPCExternalServiceActionName(e.interfaceFullName, e.functionName, e.requestRules[index], e.responseTypes[index]) ]!!.copy() as ApiExternalServiceAction try { setGeneBasedOnString(exAction.response.responseBody, r) @@ -197,10 +197,30 @@ class RPCEndpointsHandler { } }.filterNotNull() }.flatten() - else mutableListOf() + exActions.add(ex) + } + + if (rpcActionDto.mockDatabaseDtos != null && rpcActionDto.mockDatabaseDtos.isNotEmpty()){ + val dbEx = rpcActionDto.mockDatabaseDtos.map { dbDto-> + val dbExAction = seededDbMockObjects[ + DbAsExternalServiceAction.getDbAsExternalServiceAction(dbDto.commandName, dbDto.requests, dbDto.responseFullType) + ]!!.copy() as DbAsExternalServiceAction + try { + if (dbDto.response != null) + setGeneBasedOnString(dbExAction.response.responseBody, dbDto.response) + else{ + dbExAction.response.responseBody.isActive = false + } + dbExAction + }catch (e: Exception){ + log.warn("Fail to handle mocked Database responses:${e.message}") + null + } + }.filterNotNull() + exActions.add(dbEx) + } - exActions.add(ex) - processEndpoint(name, d, true) + processEndpoint(name, rpcActionDto, true) }.toMutableList() if (rpcActions.any { it.seeTopGenes().any { g-> !g.isLocallyValid() } }){ From c0c37ea66f13f0cd908bd5db69153ad9e502a143 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 29 May 2023 13:43:07 +0200 Subject: [PATCH 051/345] disabled E2E for JS --- .github/workflows/ci.yml | 7 +++++-- buildAll.sh | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 28cc5b57a8..fd60a32f3c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -128,9 +128,12 @@ jobs: with: name: evomaster.jar path: core/target + ### NOTE: we no longer run this, as NodeJS for backend no longer supported. + ### This started to fail once introduced first version of "minimization", as JS driver would need + ### to be updated with new features to support it # E2E for JS. Must be run AFTER the JS and Core builds - - name: E2E for JavaScript - run: bash ./client-js/e2e-tests/e2e.sh +# - name: E2E for JavaScript +# run: bash ./client-js/e2e-tests/e2e.sh ### TODO currently disabled, as it takes forever on CI (1-2 hours...), compared to few minutes on local machine. ### Need to find out why diff --git a/buildAll.sh b/buildAll.sh index c78ce8b28d..74b6cf92ff 100644 --- a/buildAll.sh +++ b/buildAll.sh @@ -5,7 +5,7 @@ mvn clean install -DskipTests # Build .Net Driver -dotnet build +#dotnet build # Build JavaScript Driver cd client-js/evomaster-client-js From abaf1494f943d69b3bc40f1a2c16e60a12d20398 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 29 May 2023 13:52:52 +0200 Subject: [PATCH 052/345] updated params after TT experiments --- .../kotlin/org/evomaster/core/EMConfig.kt | 20 +++++-------------- docs/options.md | 10 +++++----- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index 04b090cd98..6618f668df 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -1058,14 +1058,8 @@ class EMConfig { var maxSqlInitActionsPerMissingData = 5 - /* - Likely this should always be on by default... it would increase search space, but that would be handled by - adaptive hypermutation - TODO need experiments - */ - @Experimental @Cfg("Force filling data of all columns when inserting new row, instead of only minimal required set.") - var forceSqlAllColumnInsertion = false + var forceSqlAllColumnInsertion = true @Cfg("Maximum size (in bytes) that EM handles response payloads in the HTTP responses. " + @@ -1186,16 +1180,14 @@ class EMConfig { var expandRestIndividuals = true - @Experimental @Cfg("Add an extra query param, to analyze how it is used/read by the SUT. Needed to discover new query params" + " that were not specified in the schema.") - var extraQueryParam = false + var extraQueryParam = true - @Experimental @Cfg("Add an extra HTTP header, to analyze how it is used/read by the SUT. Needed to discover new headers" + " that were not specified in the schema.") - var extraHeader = false + var extraHeader = true @Experimental @@ -1574,9 +1566,8 @@ class EMConfig { @Probability var baseTaintAnalysisProbability = 0.9 - @Experimental @Cfg("Whether input tracking is used on sampling time, besides mutation time") - var taintOnSampling = false + var taintOnSampling = true @Probability @Experimental @@ -1818,11 +1809,10 @@ class EMConfig { @Cfg("Specify test resource path where to save mocked responses as separated files") var testResourcePathToSaveMockedResponse = "" - @Experimental @Cfg("Whether to analyze how SQL databases are accessed to infer extra constraints from the business logic." + " An example is javax/jakarta annotation constraints defined on JPA entities.") @Probability(true) - var useExtraSqlDbConstraintsProbability = 0.0 + var useExtraSqlDbConstraintsProbability = 0.9 @Cfg("a probability of harvesting actual responses from external services as seeds.") diff --git a/docs/options.md b/docs/options.md index bea90322ad..8d1e3cdb45 100644 --- a/docs/options.md +++ b/docs/options.md @@ -75,10 +75,13 @@ There are 3 types of options: |`expandRestIndividuals`| __Boolean__. Enable to expand the genotype of REST individuals based on runtime information missing from Swagger. *Default value*: `true`.| |`expectationsActive`| __Boolean__. Enable Expectation Generation. If enabled, expectations will be generated. A variable called expectationsMasterSwitch is added to the test suite, with a default value of false. If set to true, an expectation that fails will cause the test case containing it to fail. *Default value*: `true`.| |`exportCoveredTarget`| __Boolean__. Specify whether to export covered targets info. *Default value*: `false`.| +|`extraHeader`| __Boolean__. Add an extra HTTP header, to analyze how it is used/read by the SUT. Needed to discover new headers that were not specified in the schema. *Default value*: `true`.| |`extraHeuristicsFile`| __String__. Where the extra heuristics file (if any) is going to be written (in CSV format). *Default value*: `extra_heuristics.csv`.| +|`extraQueryParam`| __Boolean__. Add an extra query param, to analyze how it is used/read by the SUT. Needed to discover new query params that were not specified in the schema. *Default value*: `true`.| |`extractSqlExecutionInfo`| __Boolean__. Enable extracting SQL execution info. *Default value*: `true`.| |`feedbackDirectedSampling`| __Enum__. Specify whether when we sample from archive we do look at the most promising targets for which we have had a recent improvement. *Valid values*: `NONE, LAST, FOCUSED_QUICKEST`. *Default value*: `LAST`.| |`focusedSearchActivationTime`| __Double__. The percentage of passed search before starting a more focused, less exploratory one. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.5`.| +|`forceSqlAllColumnInsertion`| __Boolean__. Force filling data of all columns when inserting new row, instead of only minimal required set. *Default value*: `true`.| |`geneMutationStrategy`| __Enum__. Strategy used to define the mutation probability. *Valid values*: `ONE_OVER_N, ONE_OVER_N_BIASED_SQL`. *Default value*: `ONE_OVER_N_BIASED_SQL`.| |`geneWeightBasedOnImpactsBy`| __Enum__. Specify a strategy to calculate a weight of a gene based on impacts. *Valid values*: `SORT_COUNTER, SORT_RATIO, COUNTER, RATIO`. *Default value*: `RATIO`.| |`generateSqlDataWithSearch`| __Boolean__. Enable EvoMaster to generate SQL data with direct accesses to the database. Use a search algorithm. *Default value*: `true`.| @@ -122,10 +125,12 @@ There are 3 types of options: |`structureMutationProbability`| __Double__. Probability of applying a mutation that can change the structure of a test. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.5`.| |`sutControllerHost`| __String__. Host name or IP address of where the SUT REST controller is listening on. *Default value*: `localhost`.| |`sutControllerPort`| __Int__. TCP port of where the SUT REST controller is listening on. *Constraints*: `min=0.0, max=65535.0`. *Default value*: `40100`.| +|`taintOnSampling`| __Boolean__. Whether input tracking is used on sampling time, besides mutation time. *Default value*: `true`.| |`tcpTimeoutMs`| __Int__. Number of milliseconds we are going to wait to get a response on a TCP connection, e.g., when making HTTP calls to a Web API. *Default value*: `30000`.| |`testSuiteFileName`| __String__. DEPRECATED. Rather use _outputFilePrefix_ and _outputFileSuffix_. *Default value*: `""`.| |`testSuiteSplitType`| __Enum__. Instead of generating a single test file, it could be split in several files, according to different strategies. *Valid values*: `NONE, CLUSTER, CODE`. *Default value*: `CLUSTER`.| |`tournamentSize`| __Int__. Number of elements to consider in a Tournament Selection (if any is used in the search algorithm). *Constraints*: `min=1.0`. *Default value*: `10`.| +|`useExtraSqlDbConstraintsProbability`| __Double__. Whether to analyze how SQL databases are accessed to infer extra constraints from the business logic. An example is javax/jakarta annotation constraints defined on JPA entities. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.9`.| |`useMethodReplacement`| __Boolean__. Apply method replacement heuristics to smooth the search landscape. Note that the method replacement instrumentations would still be applied, it is just that their testing targets will be ignored in the fitness function if this option is set to false. *Default value*: `true`.| |`useNonIntegerReplacement`| __Boolean__. Apply non-integer numeric comparison heuristics to smooth the search landscape. *Default value*: `true`.| |`useTimeInFeedbackSampling`| __Boolean__. Whether to use timestamp info on the execution time of the tests for sampling (e.g., to reward the quickest ones). *Default value*: `true`.| @@ -171,9 +176,6 @@ There are 3 types of options: |`externalRequestResponseSelectionStrategy`| __Enum__. Harvested external request response selection strategy. *Valid values*: `EXACT, CLOSEST_SAME_DOMAIN, CLOSEST_SAME_PATH, RANDOM`. *Default value*: `EXACT`.| |`externalServiceIP`| __String__. User provided external service IP. *Constraints*: `regex ^127\.((25[0-5]\|2[0-4][0-9]\|[01]?[0-9][0-9]?)\.){2}(25[0-5]\|2[0-4][0-9]\|[01]?[0-9][0-9]?)$`. *Default value*: `127.0.0.2`.| |`externalServiceIPSelectionStrategy`| __Enum__. Specify a method to select the first external service spoof IP address. *Valid values*: `NONE, DEFAULT, USER, RANDOM`. *Default value*: `NONE`.| -|`extraHeader`| __Boolean__. Add an extra HTTP header, to analyze how it is used/read by the SUT. Needed to discover new headers that were not specified in the schema. *Default value*: `false`.| -|`extraQueryParam`| __Boolean__. Add an extra query param, to analyze how it is used/read by the SUT. Needed to discover new query params that were not specified in the schema. *Default value*: `false`.| -|`forceSqlAllColumnInsertion`| __Boolean__. Force filling data of all columns when inserting new row, instead of only minimal required set. *Default value*: `false`.| |`generateSqlDataWithDSE`| __Boolean__. Enable EvoMaster to generate SQL data with direct accesses to the database. Use Dynamic Symbolic Execution. *Default value*: `false`.| |`heuristicsForMongo`| __Boolean__. Tracking of Mongo commands to improve test generation. *Default value*: `false`.| |`impactAfterMutationFile`| __String__. Specify a path to save collected impact info after each mutation during search, only useful for debugging. *Default value*: `impactSnapshot.csv`.| @@ -222,10 +224,8 @@ There are 3 types of options: |`startingPerOfGenesToMutate`| __Double__. Specify a starting percentage of genes of an individual to mutate. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.5`.| |`structureMutationProFS`| __Double__. Specify a probability of applying structure mutator during the focused search. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.0`.| |`structureMutationProbStrategy`| __Enum__. Specify a strategy to handle a probability of applying structure mutator during the focused search. *Valid values*: `SPECIFIED, SPECIFIED_FS, DPC_TO_SPECIFIED_BEFORE_FS, DPC_TO_SPECIFIED_AFTER_FS, ADAPTIVE_WITH_IMPACT`. *Default value*: `SPECIFIED`.| -|`taintOnSampling`| __Boolean__. Whether input tracking is used on sampling time, besides mutation time. *Default value*: `false`.| |`testResourcePathToSaveMockedResponse`| __String__. Specify test resource path where to save mocked responses as separated files. *Default value*: `""`.| |`treeDepth`| __Int__. Maximum tree depth in mutations/queries to be evaluated. This is to avoid issues when dealing with huge graphs in GraphQL. *Constraints*: `min=1.0`. *Default value*: `4`.| -|`useExtraSqlDbConstraintsProbability`| __Double__. Whether to analyze how SQL databases are accessed to infer extra constraints from the business logic. An example is javax/jakarta annotation constraints defined on JPA entities. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.0`.| |`useGlobalTaintInfoProbability`| __Double__. When sampling new individual, check whether to use already existing info on tainted values. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.0`.| |`useWeightedSampling`| __Boolean__. When sampling from archive based on targets, decide whether to use weights based on properties of the targets (e.g., a target likely leading to a flag will be sampled less often). *Default value*: `false`.| |`writeSnapshotTestsIntervalInSeconds`| __Int__. The size (in seconds) of the interval that the snapshots will be printed, if enabled. *Default value*: `3600`.| From 5adf79538515fb1a86f7a8a30565580e776019c4 Mon Sep 17 00:00:00 2001 From: hghianni Date: Tue, 30 May 2023 22:48:06 -0300 Subject: [PATCH 053/345] Take into account database name when inserting data --- .../dto/database/execution/FailedQuery.java | 12 ++--- .../operations/MongoInsertionDto.java | 2 + .../controller/internal/db/MongoHandler.java | 14 +++++- .../controller/mongo/MongoScriptRunner.java | 10 ++-- .../java/controller/mongo/dsl/MongoDsl.java | 10 ++-- .../mongo/dsl/MongoSequenceDsl.java | 2 +- .../org/evomaster/core/mongo/MongoDbAction.kt | 4 +- .../core/mongo/MongoDbActionTransformer.kt | 14 +++--- .../core/mongo/MongoInsertBuilder.kt | 5 +- .../org/evomaster/core/output/MongoWriter.kt | 2 +- .../api/service/ApiWsStructureMutator.kt | 2 +- .../enterprise/service/EnterpriseSampler.kt | 4 +- .../MongoPersonsWithoutPostApp.java | 20 ++++++++ .../com/mongo/personswithoutpost/Person.java | 13 ++++++ .../personswithoutpost/PersonRepository.java | 9 ++++ .../PersonWithoutPostController.java | 21 +++++++++ .../MongoPersonsWithoutPostAppController.java | 14 ++++++ .../rest/mongo/foo/MongoEMGenerationTest.java | 46 +++++++++++++++++++ 18 files changed, 174 insertions(+), 30 deletions(-) create mode 100644 e2e-tests/spring-rest-mongo/src/main/java/com/mongo/personswithoutpost/MongoPersonsWithoutPostApp.java create mode 100644 e2e-tests/spring-rest-mongo/src/main/java/com/mongo/personswithoutpost/Person.java create mode 100644 e2e-tests/spring-rest-mongo/src/main/java/com/mongo/personswithoutpost/PersonRepository.java create mode 100644 e2e-tests/spring-rest-mongo/src/main/java/com/mongo/personswithoutpost/PersonWithoutPostController.java create mode 100644 e2e-tests/spring-rest-mongo/src/test/java/com/foo/spring/rest/mongo/MongoPersonsWithoutPostAppController.java create mode 100644 e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/FailedQuery.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/FailedQuery.java index 06127ecaff..98be5552ba 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/FailedQuery.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/FailedQuery.java @@ -4,28 +4,28 @@ public class FailedQuery { // Add database - public FailedQuery(String collection, Class documentsType, Map accessedFields) { + public FailedQuery(String database, String collection, Class documentsType, Map accessedFields) { + this.database = database; this.collection = collection; this.documentsType = documentsType; this.accessedFields = accessedFields; } public FailedQuery(){ + this.database = ""; this.collection = ""; } + private final String database; private final String collection; private Class documentsType; private Map accessedFields; - public String getCollection() { - return collection; - } - + public String getDatabase() {return database;} + public String getCollection() {return collection;} public Map getAccessedFields() { return accessedFields; } - public Class getDocumentsType() { return documentsType; } diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionDto.java index 59b6b9f4c6..d0e7896a4a 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionDto.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionDto.java @@ -5,6 +5,8 @@ public class MongoInsertionDto { + public String databaseName; + public String collectionName; public List data = new ArrayList<>(); diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/MongoHandler.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/MongoHandler.java index b87698702e..6fbacf2ba9 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/MongoHandler.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/MongoHandler.java @@ -124,7 +124,19 @@ private FailedQuery extractRelevantInfo(MongoOperation operation) { Map accessedFields = extractFieldsInQuery(query); Class documentsType = extractDocumentsType(collection); - return new FailedQuery("operation.getCollection()", documentsType, accessedFields); + String collectionName; + String databaseName; + + try { + Class collectionClass = collection.getClass().getClassLoader().loadClass("com.mongodb.client.MongoCollection"); + Object namespace = collectionClass.getMethod("getNamespace").invoke(collection); + collectionName = (String) namespace.getClass().getMethod("getCollectionName").invoke(namespace); + databaseName = (String) namespace.getClass().getMethod("getDatabaseName").invoke(namespace); + } catch (ClassNotFoundException |IllegalAccessException | InvocationTargetException | NoSuchMethodException e){ + throw new RuntimeException(e); + } + + return new FailedQuery(databaseName, collectionName, documentsType, accessedFields); } private static Class extractDocumentsType(Object collection) { diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoScriptRunner.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoScriptRunner.java index 255146e735..d4855e61ae 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoScriptRunner.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoScriptRunner.java @@ -30,16 +30,18 @@ public static MongoInsertionResultsDto execInsert(Object conn, List conn.getDatabase("persons").getCollection(insDto.collectionName).insertOne(Document.parse(field.value))); + Class documentClass = Class.forName("org.bson.Document"); + Object document = documentClass.getDeclaredConstructor().newInstance(); + document = document.getClass().getMethod("parse", String.class).invoke(document, insDto.data.get(0).value); + Object database = conn.getClass().getMethod("getDatabase", String.class).invoke(conn,insDto.databaseName); + Object collection = database.getClass().getMethod("getCollection", String.class).invoke(database, insDto.collectionName); + Class.forName("com.mongodb.client.MongoCollection").getMethod("insertOne", Object.class).invoke(collection, document); mongoResults.set(i, true); } catch (Exception e) { String msg = "Failed to execute insertion with index " + i + " with Mongo. Error: " + e.getMessage(); throw new RuntimeException(msg, e); } - - */ } MongoInsertionResultsDto insertionResultsDto = new MongoInsertionResultsDto(); diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoDsl.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoDsl.java index 889236447b..83c8162678 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoDsl.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoDsl.java @@ -36,23 +36,27 @@ public static MongoSequenceDsl mongo() { /** * @param previous a DSL object which is executed in the front of this - * @return a DSL object to create SQL operations + * @return a DSL object to create MONGO operations */ public static MongoSequenceDsl mongo(List... previous) { - return new MongoDsl(previous); } @Override - public MongoStatementDsl insertInto(String collectionName) { + public MongoStatementDsl insertInto(String databaseName, String collectionName) { checkDsl(); + if (databaseName == null || databaseName.isEmpty()) { + throw new IllegalArgumentException("Unspecified database"); + } + if (collectionName == null || collectionName.isEmpty()) { throw new IllegalArgumentException("Unspecified collection"); } MongoInsertionDto dto = new MongoInsertionDto(); + dto.databaseName = databaseName; dto.collectionName = collectionName; list.add(dto); diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoSequenceDsl.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoSequenceDsl.java index 806b915eba..a69afb0cc6 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoSequenceDsl.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoSequenceDsl.java @@ -8,5 +8,5 @@ public interface MongoSequenceDsl { * @param collectionName the target table in the DB * @return a statement in which it can be specified the values to add */ - MongoStatementDsl insertInto(String collectionName); + MongoStatementDsl insertInto(String databaseName, String collectionName); } diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt index c542e9e3b4..e29dfb17f2 100644 --- a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt @@ -5,7 +5,7 @@ import org.evomaster.core.search.gene.Gene import org.evomaster.core.search.gene.ObjectGene import java.util.* -class MongoDbAction(val collection: String, val documentsType: Class<*>, val accessedFields: Map, computedGenes: List? = null) : Action(listOf()) { +class MongoDbAction(val database: String, val collection: String, val documentsType: Class<*>, val accessedFields: Map, computedGenes: List? = null) : Action(listOf()) { val genes: List = (computedGenes ?: computeGenes()) .also { addChildren(it) } @@ -47,6 +47,6 @@ class MongoDbAction(val collection: String, val documentsType: Class<*>, val acc } override fun copyContent(): Action { - return MongoDbAction(collection, documentsType, accessedFields, genes.map(Gene::copy)) + return MongoDbAction(database, collection, documentsType, accessedFields, genes.map(Gene::copy)) } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionTransformer.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionTransformer.kt index 2137357f2d..99b904f687 100644 --- a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionTransformer.kt +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionTransformer.kt @@ -8,22 +8,24 @@ object MongoDbActionTransformer { val list = mutableListOf() - for (i in 0 until insertions.size) { + for (element in insertions) { - val action = insertions[i] + val insertion = MongoInsertionDto().apply { + databaseName = element.database + collectionName = element.collection } - val insertion = MongoInsertionDto().apply { collectionName = action.collection } - - val g = action.seeTopGenes().first() + val g = element.seeTopGenes().first() val entry = MongoInsertionEntryDto() // If is printed as JSON there might a problem // A Document(from Mongo) can be created from a JSON but some info might be lost - // Preferably is created from a EJSON (Extended JSON) as JSON can only directly represent a + // Preferably is created from an EJSON (Extended JSON) as JSON can only directly represent a // subset of the types supported by BSON. // Maybe we can create a new OutputFormat entry.value = g.getValueAsPrintableString() + + // This is ignored for now entry.fieldName = g.getVariableName() insertion.data.add(entry) diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoInsertBuilder.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoInsertBuilder.kt index 8df7126243..32aa37835d 100644 --- a/core/src/main/kotlin/org/evomaster/core/mongo/MongoInsertBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoInsertBuilder.kt @@ -2,8 +2,7 @@ package org.evomaster.core.mongo class MongoInsertBuilder { - fun createMongoInsertionAction(collection: String, documentsType: Class<*>, accessedFields: Map): List { - // FIX - return mutableListOf(MongoDbAction(collection, documentsType, accessedFields)) + fun createMongoInsertionAction(database: String, collection: String, documentsType: Class<*>, accessedFields: Map): List { + return mutableListOf(MongoDbAction(database, collection, documentsType, accessedFields)) } } diff --git a/core/src/main/kotlin/org/evomaster/core/output/MongoWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/MongoWriter.kt index b6e78002d1..d7fc99f124 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/MongoWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/MongoWriter.kt @@ -43,7 +43,7 @@ object MongoWriter { index == 0 && format.isJava() -> "List $insertionVar = mongo($previousVar)" index == 0 && format.isKotlin() -> "val $insertionVar = mongo($previousVar)" else -> ".and()" - } + ".insertInto(\"${evaluatedMongoDbAction.action.collection}\")") + } + ".insertInto(\"${evaluatedMongoDbAction.action.database}\"" + ", " + "\"${evaluatedMongoDbAction.action.collection}\")") if (index == 0) { lines.indent() diff --git a/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt b/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt index 5d03a57350..ed2f1feba8 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt @@ -323,7 +323,7 @@ abstract class ApiWsStructureMutator : StructureMutator() { val addedInsertions = if (mutatedGenes != null) mutableListOf>() else null ff.forEach { - val insertions = sampler.sampleMongoInsertion(it.collection, it.documentsType, it.accessedFields) + val insertions = sampler.sampleMongoInsertion(it.database, it.collection, it.documentsType, it.accessedFields) ind.addInitializingMongoDbActions(actions = insertions) addedInsertions?.add(insertions) } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt index 7f74be10e1..8340d803a6 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt @@ -78,12 +78,12 @@ abstract class EnterpriseSampler : Sampler() where T : Individual { return actions } - fun sampleMongoInsertion(collection: String, documentsType: Class<*>, accessedFields: Map): List { + fun sampleMongoInsertion(database: String, collection: String, documentsType: Class<*>, accessedFields: Map): List { // Should I use something like this? //val extraConstraints = randomness.nextBoolean(apc.getExtraSqlDbConstraintsProbability()) - val actions = MongoInsertBuilder().createMongoInsertionAction(collection, documentsType, accessedFields) + val actions = MongoInsertBuilder().createMongoInsertionAction(database, collection, documentsType, accessedFields) ?: throw IllegalStateException("No MongoDB schema is available") actions.flatMap{it.seeTopGenes()}.forEach{it.doInitialize(randomness)} diff --git a/e2e-tests/spring-rest-mongo/src/main/java/com/mongo/personswithoutpost/MongoPersonsWithoutPostApp.java b/e2e-tests/spring-rest-mongo/src/main/java/com/mongo/personswithoutpost/MongoPersonsWithoutPostApp.java new file mode 100644 index 0000000000..3be81e80f6 --- /dev/null +++ b/e2e-tests/spring-rest-mongo/src/main/java/com/mongo/personswithoutpost/MongoPersonsWithoutPostApp.java @@ -0,0 +1,20 @@ +package com.mongo.personswithoutpost; + +import com.mongo.SwaggerConfiguration; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@EnableSwagger2 +@SpringBootApplication(exclude = SecurityAutoConfiguration.class) +public class MongoPersonsWithoutPostApp extends SwaggerConfiguration { + public MongoPersonsWithoutPostApp() { + super("persons"); + } + + public static void main(String[] args) { + SpringApplication.run(MongoPersonsWithoutPostApp.class, args); + } + +} \ No newline at end of file diff --git a/e2e-tests/spring-rest-mongo/src/main/java/com/mongo/personswithoutpost/Person.java b/e2e-tests/spring-rest-mongo/src/main/java/com/mongo/personswithoutpost/Person.java new file mode 100644 index 0000000000..dd6fd35209 --- /dev/null +++ b/e2e-tests/spring-rest-mongo/src/main/java/com/mongo/personswithoutpost/Person.java @@ -0,0 +1,13 @@ +package com.mongo.personswithoutpost; + +import org.springframework.data.annotation.Id; + +public class Person { + @Id + public String id; + public Integer age; + public Person(Integer age) { + this.age = age; + } +} + diff --git a/e2e-tests/spring-rest-mongo/src/main/java/com/mongo/personswithoutpost/PersonRepository.java b/e2e-tests/spring-rest-mongo/src/main/java/com/mongo/personswithoutpost/PersonRepository.java new file mode 100644 index 0000000000..817427b6f0 --- /dev/null +++ b/e2e-tests/spring-rest-mongo/src/main/java/com/mongo/personswithoutpost/PersonRepository.java @@ -0,0 +1,9 @@ +package com.mongo.personswithoutpost; + +import org.springframework.data.mongodb.repository.MongoRepository; + +import java.util.List; + +public interface PersonRepository extends MongoRepository { + List findByAge(Integer age); +} \ No newline at end of file diff --git a/e2e-tests/spring-rest-mongo/src/main/java/com/mongo/personswithoutpost/PersonWithoutPostController.java b/e2e-tests/spring-rest-mongo/src/main/java/com/mongo/personswithoutpost/PersonWithoutPostController.java new file mode 100644 index 0000000000..d1e1ed3ec7 --- /dev/null +++ b/e2e-tests/spring-rest-mongo/src/main/java/com/mongo/personswithoutpost/PersonWithoutPostController.java @@ -0,0 +1,21 @@ +package com.mongo.personswithoutpost; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping(path = "/persons") +public class PersonWithoutPostController { + + @Autowired + private PersonRepository persons; + + @GetMapping("list18") + public ResponseEntity find18s() { + int status = (persons.findByAge(18).isEmpty()) ? 400 : 200 ; + return ResponseEntity.status(status).build(); + } +} + + diff --git a/e2e-tests/spring-rest-mongo/src/test/java/com/foo/spring/rest/mongo/MongoPersonsWithoutPostAppController.java b/e2e-tests/spring-rest-mongo/src/test/java/com/foo/spring/rest/mongo/MongoPersonsWithoutPostAppController.java new file mode 100644 index 0000000000..f964fa0246 --- /dev/null +++ b/e2e-tests/spring-rest-mongo/src/test/java/com/foo/spring/rest/mongo/MongoPersonsWithoutPostAppController.java @@ -0,0 +1,14 @@ +package com.foo.spring.rest.mongo; + +import com.mongo.personswithoutpost.MongoPersonsWithoutPostApp; + +public class MongoPersonsWithoutPostAppController extends MongoController{ + public MongoPersonsWithoutPostAppController() { + super("persons", MongoPersonsWithoutPostApp.class); + } + + @Override + public String getPackagePrefixesToCover() { + return "com.mongo.persons"; + } +} diff --git a/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java b/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java new file mode 100644 index 0000000000..14218ebbb3 --- /dev/null +++ b/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java @@ -0,0 +1,46 @@ +package org.evomaster.e2etests.spring.rest.mongo.foo; + +import com.foo.spring.rest.mongo.MongoPersonsAppController; +import com.foo.spring.rest.mongo.MongoPersonsWithoutPostAppController; +import org.evomaster.core.problem.rest.HttpVerb; +import org.evomaster.core.problem.rest.RestIndividual; +import org.evomaster.core.search.Solution; +import org.evomaster.e2etests.utils.RestTestBase; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class + + +MongoEMGenerationTest extends RestTestBase { + + @BeforeAll + public static void initClass() throws Exception { + RestTestBase.initClass(new MongoPersonsWithoutPostAppController()); + } + + @Test + public void testRunEM() throws Throwable { + + runTestHandlingFlakyAndCompilation( + "MongoEMGeneration", + "org.foo.spring.rest.mongo.MongoEMGeneration", + 10000, + (args) -> { + args.add("--enableWeightBasedMutationRateSelectionForGene"); + args.add("false"); + args.add("--heuristicsForMongo"); + args.add("true"); + args.add("--instrumentMR_MONGO"); + args.add("true"); + + Solution solution = initAndRun(args); + + assertTrue(solution.getIndividuals().size() >= 1); + assertHasAtLeastOne(solution, HttpVerb.GET, 200, "/persons/list18", null); + assertHasAtLeastOne(solution, HttpVerb.GET, 400, "/persons/list18", null); + }); + } +} \ No newline at end of file From e2b35400216cac298a6d04a43a7c389e04ec0020 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Wed, 31 May 2023 15:08:30 +0200 Subject: [PATCH 054/345] fixing issue with JDK 17 --- .gitignore | 1 + .../client/java/controller/InstrumentedSutStarter.java | 6 ++++-- .../evomaster/client/java/instrumentation/Constants.java | 2 +- docs/library_dependencies.md | 1 + 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index a544164548..f5d77af5e2 100644 --- a/.gitignore +++ b/.gitignore @@ -125,3 +125,4 @@ Migrations/ /e2e-tests/spring-rest-h2-column-types/target/ /test-old-libraries/target/ /e2e-tests/spring-web/target/ +/e2e-tests/spring-rest-mongo/target/ diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/InstrumentedSutStarter.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/InstrumentedSutStarter.java index 8903efeb91..7230f4da32 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/InstrumentedSutStarter.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/InstrumentedSutStarter.java @@ -39,9 +39,11 @@ public static void loadAgent(){ } catch (Exception e){ throw new RuntimeException( "\nFailed to apply bytecode instrumentation with JavaAgent." + - "\nIf you are using JDK 11 or above, are you sure you added the JVM option" + + "\nIf you are using JDK 11 or above, are you sure you added the following JVM option?" + "\n -Djdk.attach.allowAttachSelf=true " + - "\n?" + + "\nAlso, if you are using JDK 17 or above, you also need the following:" + + // Update docs/jdks.md if this changes + "\n--add-opens java.base/java.util.regex=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED"+ "\nSee documentation at https://github.com/EMResearch/EvoMaster/blob/master/docs/jdks.md", e); } } diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/Constants.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/Constants.java index 713fd5cdca..7eb10dcde1 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/Constants.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/Constants.java @@ -6,7 +6,7 @@ public class Constants { public static final String PROP_SKIP_CLASSES = "em.skipClasses"; - public static final int ASM = Opcodes.ASM7; + public static final int ASM = Opcodes.ASM9; public static final String CLASS_INIT_METHOD = ""; diff --git a/docs/library_dependencies.md b/docs/library_dependencies.md index 1b4f9c8900..f0e0623924 100644 --- a/docs/library_dependencies.md +++ b/docs/library_dependencies.md @@ -33,6 +33,7 @@ Those can be imported with: org.evomaster evomaster-client-java-dependencies LATEST + pom test ``` From 907b98aebf286db65de9b41e22d8a29add77721f Mon Sep 17 00:00:00 2001 From: Juan Pablo Galeotti Date: Wed, 31 May 2023 16:34:05 -0300 Subject: [PATCH 055/345] @Pattern requires full matching --- .../org/evomaster/core/database/DbActionGeneBuilder.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt b/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt index d8187884fc..39ffc31f46 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt @@ -1,6 +1,7 @@ package org.evomaster.core.database import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType +import org.evomaster.client.java.instrumentation.shared.RegexSharedUtils import org.evomaster.core.database.schema.Column import org.evomaster.core.database.schema.ColumnDataType import org.evomaster.core.database.schema.ForeignKey @@ -707,8 +708,9 @@ class DbActionGeneBuilder { } private fun buildJavaRegexGene(name: String, javaRegExPattern: String): RegexGene { - val disjunctionRxGenes = RegexHandler.createGeneForJVM(javaRegExPattern).disjunctions - return RegexGene(name, disjunctions = disjunctionRxGenes, "${RegexGene.JAVA_REGEX_PREFIX}${javaRegExPattern}") + val fullMatchRegex = RegexSharedUtils.forceFullMatch(javaRegExPattern) + val disjunctionRxGenes = RegexHandler.createGeneForJVM(fullMatchRegex).disjunctions + return RegexGene(name, disjunctions = disjunctionRxGenes, "${RegexGene.JAVA_REGEX_PREFIX}${fullMatchRegex}") } /** From f57abb8fddaee4d8d3da11d3c7adcdc5093ec421 Mon Sep 17 00:00:00 2001 From: Juan Pablo Galeotti Date: Wed, 31 May 2023 19:38:42 -0300 Subject: [PATCH 056/345] org.locationtech.jts version goes in the main pom file --- e2e-tests/spring-rest-openapi-v2/pom.xml | 2 +- pom.xml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/e2e-tests/spring-rest-openapi-v2/pom.xml b/e2e-tests/spring-rest-openapi-v2/pom.xml index 7f7cbc6d15..6184bfe543 100644 --- a/e2e-tests/spring-rest-openapi-v2/pom.xml +++ b/e2e-tests/spring-rest-openapi-v2/pom.xml @@ -169,7 +169,7 @@ org.locationtech.jts jts-core - 1.19.0 + ${org.locationtech.jts.version} diff --git a/pom.xml b/pom.xml index 8e4487b8a3..adc73d12a3 100644 --- a/pom.xml +++ b/pom.xml @@ -158,6 +158,8 @@ 3.2.0 3.0.5 4.2.3 + 1.19.0 + From d8b639ee6fc24bec9504eabb4944fa44dc10dfb0 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Fri, 2 Jun 2023 12:00:11 +0200 Subject: [PATCH 057/345] fix the failing test --- .../core/problem/util/BindingBuilder.kt | 19 ++++++++++++++----- .../core/search/gene/string/StringGene.kt | 8 +++++++- .../RestResourceIndividualDisabledHMTest.kt | 11 +++++++---- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/util/BindingBuilder.kt b/core/src/main/kotlin/org/evomaster/core/problem/util/BindingBuilder.kt index 28ac6d29f3..ef8d37d001 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/util/BindingBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/util/BindingBuilder.kt @@ -172,6 +172,13 @@ object BindingBuilder { } } + /** + * @return whether the param name represents an extra param handled by taint analysis + */ + fun isExtraTaintParam(name : String) : Boolean{ + return name == TaintInputName.EXTRA_HEADER_TAINT || name == TaintInputName.EXTRA_PARAM_TAINT + } + private fun buildBindHeaderParam(p : HeaderParam, params: List): Pair?{ return params.find { it is HeaderParam && p.name == it.name}?.run { Pair(ParamUtil.getValueGene(p.gene), ParamUtil.getValueGene(this.gene)) @@ -202,14 +209,16 @@ object BindingBuilder { } private fun buildBindBodyParam(bp : BodyParam, targetPath: RestPath, sourcePath: RestPath, params: List, inner : Boolean, randomness: Randomness?) : List>{ - if(ParamUtil.numOfBodyParam(params) != params.size ){ - return params.filter { p -> p !is BodyParam } + val excludeExtraParams = params.filterNot { isExtraTaintParam(it.name) } + + if(ParamUtil.numOfBodyParam(excludeExtraParams) != excludeExtraParams.size ){ + return excludeExtraParams.filter { p -> p !is BodyParam } .flatMap {ip-> buildBindBodyAndOther(bp, targetPath, ip, sourcePath, true, inner) } - }else if(params.isNotEmpty()){ + }else if(excludeExtraParams.isNotEmpty()){ val valueGene = ParamUtil.getValueGene(bp.gene) - val pValueGene = ParamUtil.getValueGene(params[0].gene) + val pValueGene = ParamUtil.getValueGene(excludeExtraParams[0].gene) if(valueGene !is ObjectGene){ return listOf() } @@ -444,4 +453,4 @@ object BindingBuilder { return if(it.isEmpty()) -1 else it.keys.first() } } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/string/StringGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/string/StringGene.kt index f3f59cd203..71ca806656 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/string/StringGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/string/StringGene.kt @@ -204,6 +204,12 @@ class StringGene( */ //assert(!tainted) + /* + it binds with any value, skip to apply the global taint + */ + if (isBoundGene()) + return + //check if starting directly with a tainted value val state = getSearchGlobalState()!! //cannot be null when this method is called if(state.config.taintOnSampling){ @@ -922,4 +928,4 @@ class StringGene( return otherelements.none { it is Gene && it.flatView().any { g-> g is StringGene && g.getPossiblyTaintedValue().equals(getPossiblyTaintedValue(), ignoreCase = true) } } } -} \ No newline at end of file +} diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt index 98e4ebf2e1..7e2810f257 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt @@ -1,6 +1,7 @@ package org.evomaster.core.problem.rest.individual import com.google.inject.* +import org.evomaster.client.java.instrumentation.shared.TaintInputName import org.evomaster.core.database.DbAction import org.evomaster.core.database.schema.Table import org.evomaster.core.problem.rest.RestIndividual @@ -11,6 +12,7 @@ import org.evomaster.core.problem.rest.resource.RestResourceCalls import org.evomaster.core.problem.rest.resource.RestResourceNode import org.evomaster.core.problem.rest.service.* import org.evomaster.core.problem.util.BindingBuilder +import org.evomaster.core.problem.util.BindingBuilder.isExtraTaintParam import org.evomaster.core.problem.util.ParamUtil import org.evomaster.core.search.ActionFilter import org.evomaster.core.search.EvaluatedIndividual @@ -216,7 +218,9 @@ class RestResourceIndividualDisabledHMTest : RestIndividualTestBase(){ } } - val nosql = call.seeGenes(Individual.GeneFilter.NO_SQL).filter { it.isMutable() } + val nosql = call.seeGenes(Individual.GeneFilter.NO_SQL).filter { + !isExtraTaintParam(it.name) && it.isMutable() + } when(call.template!!.template){ @@ -304,7 +308,7 @@ class RestResourceIndividualDisabledHMTest : RestIndividualTestBase(){ // collect all mutable&bindingable leaf gene for all actions val allGene = call.seeActions(ActionFilter.ALL) .flatMap { it.seeTopGenes() } - .filter { it.isMutable() && it !is SqlPrimaryKeyGene && it !is SqlForeignKeyGene } + .filter { !isExtraTaintParam(it.name) && it.isMutable() && it !is SqlPrimaryKeyGene && it !is SqlForeignKeyGene } .flatMap { it.flatView{g: Gene -> g is DateGene || g is DateTimeGene || g is TimeGene} }.filter { it.getViewOfChildren().isEmpty() } allGene.groupBy { it.name }.forEach { (t, u) -> @@ -317,5 +321,4 @@ class RestResourceIndividualDisabledHMTest : RestIndividualTestBase(){ } } } - -} \ No newline at end of file +} From 3938e26500d9bfee8d52ff8a8f4d09190baf119f Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Fri, 2 Jun 2023 15:28:24 +0200 Subject: [PATCH 058/345] fix failing tests --- .../resource/db/ResourceDbMIOBasicTest.java | 5 +++-- ...ResourceDbMIOAndHypermutationBasicTest.java | 9 +++++---- .../resource/db/ResourceDbMIOBasicTest.java | 5 +++-- ...ResourceDbMIOAndHypermutationBasicTest.java | 18 +++++++++--------- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java index bc3a9ad13c..a7b83e12a2 100644 --- a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java +++ b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java @@ -5,6 +5,7 @@ import org.evomaster.core.problem.rest.resource.RestResourceCalls; import org.evomaster.core.problem.rest.resource.RestResourceNode; import org.evomaster.core.problem.rest.service.ResourceManageService; +import org.evomaster.core.problem.util.BindingBuilder; import org.evomaster.core.search.ActionFilter; import org.evomaster.core.search.Individual.GeneFilter; import org.evomaster.core.search.gene.Gene; @@ -45,7 +46,7 @@ public void testResourceSamplerAndParamBinding() { List calls = new ArrayList<>(); rmanger.sampleCall(raKey, true, calls, 10, false, Collections.emptyList(), raPostTemplate); assertEquals(2, calls.get(0).seeActions(ActionFilter.ALL).size()); - assertEquals(2, calls.get(0).seeGenes(GeneFilter.ALL).stream().filter(Gene::isMutable).count()); + assertEquals(2, calls.get(0).seeGenes(GeneFilter.ALL).stream().filter(s-> !BindingBuilder.INSTANCE.isExtraTaintParam(s.getName()) && s.isMutable()).count()); checkingBinding(calls.get(0), "POST-POST", raKey, false); String raIdKey = "/api/rA/{rAId}"; @@ -53,7 +54,7 @@ public void testResourceSamplerAndParamBinding() { calls.clear(); rmanger.sampleCall(raIdKey, true, calls, 10, false, Collections.emptyList(), raIdPostTemplate); assertEquals(2, calls.get(0).seeActions(ActionFilter.ALL).size()); - assertEquals(2, calls.get(0).seeGenes(GeneFilter.ALL).stream().filter(Gene::isMutable).count()); + assertEquals(2, calls.get(0).seeGenes(GeneFilter.ALL).stream().filter(s-> !BindingBuilder.INSTANCE.isExtraTaintParam(s.getName()) && s.isMutable()).count()); checkingBinding(calls.get(0), raIdPostTemplate, raIdKey, false); // SQL-GET diff --git a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java index 099e0cd417..9a7eb884f6 100644 --- a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java +++ b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java @@ -9,6 +9,7 @@ import org.evomaster.core.problem.rest.service.ResourceRestMutator; import org.evomaster.core.problem.rest.service.RestResourceFitness; import org.evomaster.core.problem.rest.service.RestResourceStructureMutator; +import org.evomaster.core.problem.util.BindingBuilder; import org.evomaster.core.search.ActionFilter; import org.evomaster.core.search.EvaluatedIndividual; import org.evomaster.core.search.Individual.GeneFilter; @@ -56,7 +57,7 @@ public void testResourceHypermutation(){ RestIndividual twoCalls = new RestIndividual(calls, SampleType.SMART_RESOURCE, null, Collections.emptyList(), null, 1); twoCalls.doInitializeLocalId(); EvaluatedIndividual twoCallsEval = ff.calculateCoverage(twoCalls, Collections.emptySet()); - assertEquals(4, mutator.genesToMutation(twoCalls, twoCallsEval, Collections.emptySet()).size()); + assertEquals(4, mutator.genesToMutation(twoCalls, twoCallsEval, Collections.emptySet()).stream().filter(s-> !BindingBuilder.INSTANCE.isExtraTaintParam(s.getName())).count()); MutatedGeneSpecification spec = new MutatedGeneSpecification(); RestIndividual mutatedTwoCalls = mutator.mutate(twoCallsEval, Collections.emptySet(), spec); @@ -123,7 +124,7 @@ public void testResourceDBHypermutation() { // all SQL genes can be bound with POST, so the mutable SQL genes should be empty. assertEquals(0, rAcall.seeGenes(GeneFilter.ONLY_SQL).size()); - assertEquals(1, rAcall.seeGenes(GeneFilter.ALL).stream().filter(Gene::isMutable).count()); + assertEquals(1, rAcall.seeGenes(GeneFilter.ALL).stream().filter(s-> !BindingBuilder.INSTANCE.isExtraTaintParam(s.getName()) && s.isMutable()).count()); String raIdKey = "/api/rA/{rAId}"; String raIdPostTemplate = "GET"; @@ -140,8 +141,8 @@ public void testResourceDBHypermutation() { checkingBinding(rAIdcall, "GET", raIdKey,true); //exclude 'id' gene as it can be bound with GET - assertEquals(2, rAIdcall.seeGenes(GeneFilter.ONLY_SQL).size()); - assertEquals(1, rAIdcall.seeGenes(GeneFilter.NO_SQL).size()); + assertEquals(2, rAIdcall.seeGenes(GeneFilter.ONLY_SQL).stream().filter(s-> !BindingBuilder.INSTANCE.isExtraTaintParam(s.getName())).count()); + assertEquals(1, rAIdcall.seeGenes(GeneFilter.NO_SQL).stream().filter(s-> !BindingBuilder.INSTANCE.isExtraTaintParam(s.getName())).count()); //test binding after value mutator RestIndividual raIdInd = new RestIndividual(calls, SampleType.SMART_RESOURCE, null, Collections.emptyList(), null, 1); diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java index 41cc0405a3..a2d90cb094 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java @@ -5,6 +5,7 @@ import org.evomaster.core.problem.rest.resource.RestResourceCalls; import org.evomaster.core.problem.rest.resource.RestResourceNode; import org.evomaster.core.problem.rest.service.ResourceManageService; +import org.evomaster.core.problem.util.BindingBuilder; import org.evomaster.core.search.ActionFilter; import org.evomaster.core.search.Individual.GeneFilter; import org.evomaster.e2etests.spring.examples.resource.ResourceMIOHWTestBase; @@ -44,7 +45,7 @@ public void testResourceSamplerAndParamBinding() { List calls = new ArrayList<>(); rmanger.sampleCall(raKey, true, calls, 10, false, Collections.emptyList(), raPostTemplate); assertEquals(2, calls.get(0).seeActions(ActionFilter.ALL).size()); - assertEquals(2, calls.get(0).seeGenes(GeneFilter.ALL).stream().filter(s-> s.isMutable()).count()); + assertEquals(2, calls.get(0).seeGenes(GeneFilter.ALL).stream().filter(s-> !BindingBuilder.INSTANCE.isExtraTaintParam(s.getName()) && s.isMutable()).count()); checkingBinding(calls.get(0), "POST-POST", raKey, false); String raIdKey = "/api/rA/{rAId}"; @@ -52,7 +53,7 @@ public void testResourceSamplerAndParamBinding() { calls.clear(); rmanger.sampleCall(raIdKey, true, calls, 10, false, Collections.emptyList(), raIdPostTemplate); assertEquals(2, calls.get(0).seeActions(ActionFilter.ALL).size()); - assertEquals(2, calls.get(0).seeGenes(GeneFilter.ALL).stream().filter(s-> s.isMutable()).count()); + assertEquals(2, calls.get(0).seeGenes(GeneFilter.ALL).stream().filter(s-> !BindingBuilder.INSTANCE.isExtraTaintParam(s.getName()) && s.isMutable()).count()); checkingBinding(calls.get(0), raIdPostTemplate, raIdKey, false); // SQL-GET diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java index f998b6b617..4571bcf2f4 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java @@ -9,6 +9,7 @@ import org.evomaster.core.problem.rest.service.ResourceRestMutator; import org.evomaster.core.problem.rest.service.RestResourceFitness; import org.evomaster.core.problem.rest.service.RestResourceStructureMutator; +import org.evomaster.core.problem.util.BindingBuilder; import org.evomaster.core.search.ActionFilter; import org.evomaster.core.search.EvaluatedIndividual; import org.evomaster.core.search.Individual.GeneFilter; @@ -20,8 +21,7 @@ import java.util.Collections; import java.util.List; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.*; public class ResourceDbMIOAndHypermutationBasicTest extends ResourceMIOHWTestBase { @@ -55,13 +55,13 @@ public void testResourceHypermutation(){ RestIndividual twoCalls = new RestIndividual(calls, SampleType.SMART_RESOURCE, null, Collections.emptyList(), null, 1); twoCalls.doInitializeLocalId(); EvaluatedIndividual twoCallsEval = ff.calculateCoverage(twoCalls, Collections.emptySet()); - assertEquals(4, mutator.genesToMutation(twoCalls, twoCallsEval, Collections.emptySet()).size()); + assertEquals(4, mutator.genesToMutation(twoCalls, twoCallsEval, Collections.emptySet()).stream().filter(s-> !BindingBuilder.INSTANCE.isExtraTaintParam(s.getName()) && s.isMutable()).count()); MutatedGeneSpecification spec = new MutatedGeneSpecification(); RestIndividual mutatedTwoCalls = mutator.mutate(twoCallsEval, Collections.emptySet(), spec); assertEquals(0, spec.mutatedDbGeneInfo().size()); // it might be flaky. but with specified seed, this should be determinate - assertEquals(2, spec.mutatedGeneInfo().size()); + assertFalse(spec.mutatedGeneInfo().isEmpty()); // Gene rdObj = calls.get(0).seeGenes(GeneFilter.NO_SQL).stream().findFirst().orElse(null); // Gene mrdObj = mutatedTwoCalls.getResourceCalls().get(0).seeGenes(GeneFilter.NO_SQL).stream().findFirst().orElse(null); @@ -121,8 +121,8 @@ public void testResourceDBHypermutation() { checkingBinding(rAcall, "POST", raKey, true); // all SQL genes can be bound with POST, so the mutable SQL genes should be empty. - assertEquals(0, rAcall.seeGenes(GeneFilter.ONLY_SQL).size()); - assertEquals(1, rAcall.seeGenes(GeneFilter.ALL).stream().filter(s-> s.isMutable()).count()); + assertEquals(0, rAcall.seeGenes(GeneFilter.ONLY_SQL).stream().filter(s-> !BindingBuilder.INSTANCE.isExtraTaintParam(s.getName())).count()); + assertEquals(1, rAcall.seeGenes(GeneFilter.ALL).stream().filter(s-> !BindingBuilder.INSTANCE.isExtraTaintParam(s.getName()) && s.isMutable()).count()); String raIdKey = "/api/rA/{rAId}"; String raIdPostTemplate = "GET"; @@ -139,15 +139,15 @@ public void testResourceDBHypermutation() { checkingBinding(rAIdcall, "GET", raIdKey,true); //exclude 'id' gene as it can be bound with GET - assertEquals(2, rAIdcall.seeGenes(GeneFilter.ONLY_SQL).size()); - assertEquals(1, rAIdcall.seeGenes(GeneFilter.NO_SQL).size()); + assertEquals(2, rAIdcall.seeGenes(GeneFilter.ONLY_SQL).stream().filter(s-> !BindingBuilder.INSTANCE.isExtraTaintParam(s.getName())).count()); + assertEquals(1, rAIdcall.seeGenes(GeneFilter.NO_SQL).stream().filter(s-> !BindingBuilder.INSTANCE.isExtraTaintParam(s.getName()) ).count()); //test binding after value mutator RestIndividual raIdInd = new RestIndividual(calls, SampleType.SMART_RESOURCE, null, Collections.emptyList(), null, 1); raIdInd.doInitializeLocalId(); EvaluatedIndividual rdIdEval = ff.calculateCoverage(raIdInd, Collections.emptySet()); // mutable genes should be 0+1+2+1=4 - assertEquals(4, mutator.genesToMutation(raIdInd, rdIdEval, Collections.emptySet()).size()); + assertEquals(4, mutator.genesToMutation(raIdInd, rdIdEval, Collections.emptySet()).stream().filter(s-> !BindingBuilder.INSTANCE.isExtraTaintParam(s.getName())).count()); MutatedGeneSpecification mutatedSpec = new MutatedGeneSpecification(); RestIndividual mutatedInd = mutator.mutate(rdIdEval, Collections.emptySet(), mutatedSpec); From 07e17b5d5c3b4df95022bc546af16b7e404fc320 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Fri, 2 Jun 2023 18:58:20 +0200 Subject: [PATCH 059/345] modify budget --- .../examples/hypermutation/AdaptiveHypermutationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/hypermutation/AdaptiveHypermutationTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/hypermutation/AdaptiveHypermutationTest.java index cc97c27926..cfffb27699 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/hypermutation/AdaptiveHypermutationTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/hypermutation/AdaptiveHypermutationTest.java @@ -26,7 +26,7 @@ public void testRunAdaptiveHypermutation() throws Throwable { runTestHandlingFlakyAndCompilation( "hypermtation/TestLowWeightHighImpact", "org.adaptivehypermuation.LowWeightHighImpactTest", - 3000, + 500, true, (args) -> { From 8e3871a4f22bda58a7bfc12fa5a3d97c01f53d76 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Fri, 2 Jun 2023 18:58:40 +0200 Subject: [PATCH 060/345] filter extra params --- .../hypermutation/ResourceDbMIOAndHypermutationBasicTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java index 9a7eb884f6..9da6fb496c 100644 --- a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java +++ b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java @@ -63,7 +63,7 @@ public void testResourceHypermutation(){ RestIndividual mutatedTwoCalls = mutator.mutate(twoCallsEval, Collections.emptySet(), spec); assertEquals(0, spec.mutatedDbGeneInfo().size()); // it might be flaky. but with specified seed, this should be determinate - assertEquals(2, spec.mutatedGeneInfo().size()); + assertFalse(spec.mutatedGeneInfo().isEmpty()); // Gene rdObj = calls.get(0).seeGenes(GeneFilter.NO_SQL).stream().findFirst().orElse(null); // Gene mrdObj = mutatedTwoCalls.getResourceCalls().get(0).seeGenes(GeneFilter.NO_SQL).stream().findFirst().orElse(null); @@ -149,7 +149,7 @@ public void testResourceDBHypermutation() { raIdInd.doInitializeLocalId(); EvaluatedIndividual rdIdEval = ff.calculateCoverage(raIdInd, Collections.emptySet()); // mutable genes should be 0+1+2+1=4 - assertEquals(4, mutator.genesToMutation(raIdInd, rdIdEval, Collections.emptySet()).size()); + assertEquals(4, mutator.genesToMutation(raIdInd, rdIdEval, Collections.emptySet()).stream().filter(s-> !BindingBuilder.INSTANCE.isExtraTaintParam(s.getName())).count()); MutatedGeneSpecification mutatedSpec = new MutatedGeneSpecification(); RestIndividual mutatedInd = mutator.mutate(rdIdEval, Collections.emptySet(), mutatedSpec); From 8f8b36b55691b1c14a4449990b5350ea7da2362e Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Sat, 3 Jun 2023 13:30:03 +0200 Subject: [PATCH 061/345] updated hiring info --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b3a8088c47..85f5138547 100644 --- a/README.md +++ b/README.md @@ -116,8 +116,8 @@ case studies in [EMB](https://github.com/EMResearch/EMB). Depending on the year, we might have funding for _postdoc_ and _PhD student_ positions to work on this project (in Oslo, Norway). Current positions: -* 2023: PhD student positions. [New calls](https://www.kristiania.no/om-kristiania/ledige-stillinger/?rmpage=job&rmjob=619&rmlang=NO). -* 2023: Postdoc positions. New calls not announced yet. +* 2023: PhD student positions. No new calls scheduled for the moment. +* 2023: Postdoc positions. No new calls scheduled for the moment. For questions on these positions, please contact Prof. Andrea Arcuri. From 3b08e06a6523b89dd9e62e7e26fe37d6243576b4 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 5 Jun 2023 10:05:09 +0200 Subject: [PATCH 062/345] fix a bug --- .../org/evomaster/core/search/gene/string/StringGene.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/string/StringGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/string/StringGene.kt index 71ca806656..7cc74c2c36 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/string/StringGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/string/StringGene.kt @@ -204,6 +204,12 @@ class StringGene( */ //assert(!tainted) + /* + the gene might be initialized without global constraint + */ + if (!checkForGloballyValid()) + repair() + /* it binds with any value, skip to apply the global taint */ From 64fb05d7e3737a23b79ec81dfa8fbe9af4f712a1 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 5 Jun 2023 10:05:59 +0200 Subject: [PATCH 063/345] reduce the budget --- .../spring/examples/hypermutation/HypermutationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/hypermutation/HypermutationTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/hypermutation/HypermutationTest.java index a02a9c539f..15af5e48f4 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/hypermutation/HypermutationTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/hypermutation/HypermutationTest.java @@ -28,7 +28,7 @@ public void testRunHypermutation() throws Throwable { runTestHandlingFlakyAndCompilation( "hypermtation/TestHyperweight", "org.adaptivehypermuation.HyperWeightTest", - 1000, + 500, true, (args) -> { From 22f6c03e1184d461d91e3e81d89b1dd23728c13b Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 5 Jun 2023 10:49:26 +0200 Subject: [PATCH 064/345] improving handling of homepage URL --- .../java/controller/api/SeleniumEMUtils.java | 44 ++++++++++++++++++- .../core/output/service/TestSuiteWriter.kt | 3 +- .../problem/webfrontend/service/WebSampler.kt | 16 ++++++- 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/SeleniumEMUtils.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/SeleniumEMUtils.java index 8af01e9fe4..105084c530 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/SeleniumEMUtils.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/SeleniumEMUtils.java @@ -10,6 +10,7 @@ import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.Locale; +import java.util.Objects; /** * Utility functions used in the generated tests to handle browser operations with Selenium @@ -18,18 +19,59 @@ public class SeleniumEMUtils { public static final String TESTCONTAINERS_HOST = "host.testcontainers.internal"; + public static String combineBaseUrlAndUrlPath(String base, String path){ + + Objects.requireNonNull(base); + Objects.requireNonNull(path); + + if(base.endsWith("/") && path.startsWith("/")){ + return base + path.substring(1,path.length()); + } else if(!base.endsWith("/") && !path.startsWith("/")){ + return base + "/" + path; + } else { + return base + path; + } + } + + public static String validateAndGetUrlOfStartingPageForDocker(String base, String path, boolean modifyLocalHost){ + return validateAndGetUrlOfStartingPageForDocker(combineBaseUrlAndUrlPath(base,path),modifyLocalHost); + } + public static String validateAndGetUrlOfStartingPageForDocker(String url, boolean modifyLocalHost){ if(url.isEmpty()){ throw new IllegalArgumentException("Starting page is not defined"); } + /* + hmmm... why did I use a URI instead of URL here??? + was it to avoid hostname resolving? + */ URI uri = null; try { uri = new URI(url); } catch (URISyntaxException e){ throw new IllegalArgumentException("Provided Home Page link is not a valid URL: " + e.getMessage()); } + + int port = uri.getPort(); + if(port < 0){ + //infer from protocol + String protocol = uri.getScheme(); + if("http".equals(protocol)){ + port = 80; + } else if("https".equals(protocol)){ + port = 443; + } else { + throw new IllegalArgumentException("Cannot infer port number from url: " + url); + } + } + //see https://www.testcontainers.org/modules/webdriver_containers/ - Testcontainers.exposeHostPorts(uri.getPort()); + Testcontainers.exposeHostPorts(port); + + String host = uri.getHost(); + if(host == null){ + throw new IllegalArgumentException("Cannot infer host from url: " + url); + } if(modifyLocalHost && uri.getHost().equalsIgnoreCase( "localhost")) { try { diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt index 21ae6f7b07..a14c39516a 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt @@ -604,8 +604,7 @@ class TestSuiteWriter { addStatement("$baseUrlOfSut = $controller.startSut()", lines) if(config.problemType == EMConfig.ProblemType.WEBFRONTEND){ val infoDto = remoteController.getSutInfo()!! //TODO refactor. save it in a service - val url = "$baseUrlOfSut+\"${infoDto.webProblem.urlPathOfStartingPage}\"" - addStatement("$baseUrlOfSut = validateAndGetUrlOfStartingPageForDocker($url, true)", lines) + addStatement("$baseUrlOfSut = validateAndGetUrlOfStartingPageForDocker($baseUrlOfSut,\"${infoDto.webProblem.urlPathOfStartingPage}\", true)", lines) } /* now only support white-box diff --git a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebSampler.kt index 2e59d22498..70c67c4a5e 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebSampler.kt @@ -1,5 +1,7 @@ package org.evomaster.core.problem.webfrontend.service +import org.evomaster.client.java.controller.api.SeleniumEMUtils +import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.problem.enterprise.EnterpriseActionGroup import org.evomaster.core.problem.enterprise.service.EnterpriseSampler import org.evomaster.core.problem.webfrontend.WebAction @@ -39,9 +41,21 @@ class WebSampler : EnterpriseSampler() { val startingPage = infoDto.webProblem.urlPathOfStartingPage ?: throw SutProblemException("Not specified urlPathOfStartingPage") - browserController.initUrlOfStartingPage(infoDto.baseUrlOfSUT + startingPage,true) + if(startingPage.startsWith("http")){ + LoggingUtil.uniqueUserWarn("The urlPathOfStartingPage you provided starts with 'http'." + + " You sure you provided a path instead of a whole URL?") + } + + val url = SeleniumEMUtils.combineBaseUrlAndUrlPath(infoDto.baseUrlOfSUT, startingPage) + try { + browserController.initUrlOfStartingPage(url, true) + } catch (e: IllegalArgumentException){ + throw SutProblemException("Issue with inferred URL for home page: $url\n${e.message}") + } browserController.startChromeInDocker() + LoggingUtil.getInfoLogger().info("Home page of tested application -> $url") + // setupAuthentication(infoDto) //TODO this will require refactoring From 8f3caee285868b07df3b7660c6bec8a42222ce12 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 5 Jun 2023 11:04:53 +0200 Subject: [PATCH 065/345] push up sample type info --- .../rest/service/resource/ResourceTestBase.kt | 2 +- .../core/problem/api/ApiWsIndividual.kt | 6 ++- .../enterprise/EnterpriseIndividual.kt | 3 +- .../{rest => enterprise}/SampleType.kt | 7 +-- .../core/problem/graphql/GraphQLIndividual.kt | 16 +++---- .../problem/graphql/service/GraphQLSampler.kt | 9 +--- .../service/GraphQLStructureMutator.kt | 4 +- .../core/problem/rest/RestIndividual.kt | 43 ++++++++++--------- .../rest/service/AbstractRestSampler.kt | 3 +- .../rest/service/ResourceManageService.kt | 3 +- .../problem/rest/service/ResourceSampler.kt | 3 +- .../core/problem/rest/service/RestSampler.kt | 5 +-- .../rest/service/RestStructureMutator.kt | 3 +- .../core/problem/rpc/RPCIndividual.kt | 8 +++- .../core/database/DbActionUtilsTest.kt | 2 +- .../extract/h2/CatwatchSqlExtractTest.kt | 6 +-- .../database/extract/h2/DoctorsExtractTest.kt | 4 +- .../extract/h2/ProxyPrintSqlExtractTest.kt | 6 +-- .../core/output/EvaluatedIndividualBuilder.kt | 3 +- .../core/output/TestCaseWriterTest.kt | 1 + .../RestResourceIndividualDisabledHMTest.kt | 3 +- .../search/impact/IndividualImpactTest.kt | 3 +- .../mutationweight/GeneWeightTestSchema.kt | 4 +- .../individual/RestIndividualStructureTest.kt | 4 +- .../ResourceDbMIOAndAdaptiveHMBasicTest.java | 2 +- ...esourceDbMIOAndHypermutationBasicTest.java | 3 +- .../ResourceDbMIOAndAdaptiveHMBasicTest.java | 2 +- ...esourceDbMIOAndHypermutationBasicTest.java | 2 +- 28 files changed, 83 insertions(+), 77 deletions(-) rename core/src/main/kotlin/org/evomaster/core/problem/{rest => enterprise}/SampleType.kt (61%) diff --git a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/ResourceTestBase.kt b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/ResourceTestBase.kt index 1f4d914e77..16b846fd44 100644 --- a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/ResourceTestBase.kt +++ b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/ResourceTestBase.kt @@ -18,7 +18,7 @@ import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.database.extract.h2.ExtractTestBaseH2 import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestIndividual -import org.evomaster.core.problem.rest.SampleType +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.rest.resource.RestResourceCalls import org.evomaster.core.problem.rest.service.* import org.evomaster.core.problem.rest.service.resource.model.ResourceBasedTestInterface diff --git a/core/src/main/kotlin/org/evomaster/core/problem/api/ApiWsIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/api/ApiWsIndividual.kt index bd4e2e2558..d2b3fa86d7 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/api/ApiWsIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/api/ApiWsIndividual.kt @@ -1,6 +1,7 @@ package org.evomaster.core.problem.api import org.evomaster.core.problem.enterprise.EnterpriseIndividual +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.search.ActionComponent import org.evomaster.core.search.GroupsOfChildren import org.evomaster.core.search.StructuralElement @@ -10,6 +11,7 @@ import org.evomaster.core.search.tracer.TrackOperator * the abstract individual for API based SUT, such as REST, GraphQL, RPC */ abstract class ApiWsIndividual ( + sampleType: SampleType, /** * a tracked operator to manipulate the individual (nullable) @@ -26,7 +28,7 @@ abstract class ApiWsIndividual ( children: MutableList, childTypeVerifier: (Class<*>) -> Boolean, groups : GroupsOfChildren = getEnterpriseTopGroups(children, children.size, 0) -): EnterpriseIndividual(trackOperator, index, children, childTypeVerifier, groups){ +): EnterpriseIndividual(sampleType, trackOperator, index, children, childTypeVerifier, groups){ -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt index f83ab274b7..a521e1ee6d 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt @@ -27,6 +27,7 @@ import org.slf4j.LoggerFactory * per action, and not here in the initialization phase. */ abstract class EnterpriseIndividual( + val sampleType: SampleType, /** * a tracked operator to manipulate the individual (nullable) */ @@ -232,4 +233,4 @@ abstract class EnterpriseIndividual( open fun getInsertTableNames(): List{ return dbInitialization.filterNot { it.representExistingData }.map { it.table.name } } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/SampleType.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/SampleType.kt similarity index 61% rename from core/src/main/kotlin/org/evomaster/core/problem/rest/SampleType.kt rename to core/src/main/kotlin/org/evomaster/core/problem/enterprise/SampleType.kt index 70d62972c8..ba825d512d 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/SampleType.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/SampleType.kt @@ -1,13 +1,14 @@ -package org.evomaster.core.problem.rest +package org.evomaster.core.problem.enterprise /** - * Specify how a REST individual was sampled. + * Specify how an enterprise individual was sampled. * This info is needed to have custom mutations of the * chromosome structures */ enum class SampleType { RANDOM, + SEEDED, SMART, SMART_GET_COLLECTION, SMART_RESOURCE -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLIndividual.kt index 1cd3bda269..c2d3ba5b64 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLIndividual.kt @@ -1,21 +1,21 @@ package org.evomaster.core.problem.graphql import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionUtils import org.evomaster.core.problem.api.ApiWsIndividual import org.evomaster.core.problem.enterprise.EnterpriseActionGroup import org.evomaster.core.problem.externalservice.ApiExternalServiceAction -import org.evomaster.core.problem.rest.SampleType +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.search.* import org.evomaster.core.search.gene.Gene class GraphQLIndividual( - val sampleType: SampleType, - allActions : MutableList, - mainSize : Int = allActions.size, - dbSize: Int = 0, - groups : GroupsOfChildren = getEnterpriseTopGroups(allActions,mainSize,dbSize) + sampleType: SampleType, + allActions : MutableList, + mainSize : Int = allActions.size, + dbSize: Int = 0, + groups : GroupsOfChildren = getEnterpriseTopGroups(allActions,mainSize,dbSize) ) : ApiWsIndividual( + sampleType = sampleType, children = allActions, childTypeVerifier = { EnterpriseActionGroup::class.java.isAssignableFrom(it) @@ -59,4 +59,4 @@ class GraphQLIndividual( override fun seeMainExecutableActions() : List{ return super.seeMainExecutableActions() as List } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLSampler.kt index ec5892217f..77fc97f485 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLSampler.kt @@ -1,16 +1,11 @@ package org.evomaster.core.problem.graphql.service -import com.google.inject.Inject -import org.evomaster.client.java.controller.api.dto.SutInfoDto -import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.problem.enterprise.EnterpriseActionGroup import org.evomaster.core.problem.graphql.* import org.evomaster.core.problem.graphql.builder.GraphQLActionBuilder import org.evomaster.core.problem.httpws.service.HttpWsSampler -import org.evomaster.core.problem.rest.SampleType -import org.evomaster.core.problem.rest.service.AbstractRestSampler +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.remote.SutProblemException -import org.evomaster.core.remote.service.RemoteController import org.slf4j.Logger import org.slf4j.LoggerFactory import javax.annotation.PostConstruct @@ -124,4 +119,4 @@ class GraphQLSampler : HttpWsSampler() { */ -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLStructureMutator.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLStructureMutator.kt index 56fda6b0dd..6820fc4f19 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLStructureMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLStructureMutator.kt @@ -7,7 +7,7 @@ import org.evomaster.core.problem.enterprise.EnterpriseActionGroup import org.evomaster.core.problem.graphql.GraphQLAction import org.evomaster.core.problem.graphql.GraphQLIndividual import org.evomaster.core.problem.api.service.ApiWsStructureMutator -import org.evomaster.core.problem.rest.SampleType +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.Individual import org.evomaster.core.search.service.mutator.MutatedGeneSpecification @@ -112,4 +112,4 @@ class GraphQLStructureMutator : ApiWsStructureMutator() { override fun getSqlInsertBuilder(): SqlInsertBuilder? { return sampler.sqlInsertBuilder } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt index eb50e7f84b..4f07ba536c 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt @@ -3,6 +3,7 @@ package org.evomaster.core.problem.rest import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionUtils import org.evomaster.core.problem.api.ApiWsIndividual +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.externalservice.ApiExternalServiceAction import org.evomaster.core.problem.rest.resource.RestResourceCalls import org.evomaster.core.problem.rest.resource.SamplerSpecification @@ -22,15 +23,15 @@ import kotlin.math.max * @property index indicates when the individual is created, ie, using num of evaluated individual. */ class RestIndividual( - val sampleType: SampleType, - val sampleSpec: SamplerSpecification? = null, - trackOperator: TrackOperator? = null, - index : Int = -1, - allActions : MutableList, - mainSize : Int = allActions.size, - dbSize: Int = 0, - groups : GroupsOfChildren = getEnterpriseTopGroups(allActions,mainSize,dbSize) -): ApiWsIndividual(trackOperator, index, allActions, + sampleType: SampleType, + val sampleSpec: SamplerSpecification? = null, + trackOperator: TrackOperator? = null, + index : Int = -1, + allActions : MutableList, + mainSize : Int = allActions.size, + dbSize: Int = 0, + groups : GroupsOfChildren = getEnterpriseTopGroups(allActions,mainSize,dbSize) +): ApiWsIndividual(sampleType, trackOperator, index, allActions, childTypeVerifier = { RestResourceCalls::class.java.isAssignableFrom(it) || DbAction::class.java.isAssignableFrom(it) @@ -41,22 +42,22 @@ class RestIndividual( } constructor( - resourceCalls: MutableList, - sampleType: SampleType, - sampleSpec: SamplerSpecification? = null, - dbInitialization: MutableList = mutableListOf(), - trackOperator: TrackOperator? = null, - index : Int = -1 + resourceCalls: MutableList, + sampleType: SampleType, + sampleSpec: SamplerSpecification? = null, + dbInitialization: MutableList = mutableListOf(), + trackOperator: TrackOperator? = null, + index : Int = -1 ) : this(sampleType, sampleSpec, trackOperator, index, mutableListOf().apply { addAll(dbInitialization); addAll(resourceCalls) }, resourceCalls.size, dbInitialization.size) constructor( - actions: MutableList, - sampleType: SampleType, - dbInitialization: MutableList = mutableListOf(), - trackOperator: TrackOperator? = null, - index : Int = Traceable.DEFAULT_INDEX + actions: MutableList, + sampleType: SampleType, + dbInitialization: MutableList = mutableListOf(), + trackOperator: TrackOperator? = null, + index : Int = Traceable.DEFAULT_INDEX ) : this( actions.map {RestResourceCalls(actions= listOf(it as RestCallAction), dbActions = listOf())}.toMutableList(), sampleType, @@ -325,4 +326,4 @@ class RestIndividual( override fun seeMainExecutableActions(): List { return super.seeMainExecutableActions() as List } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt index 9906a2601f..9607d86b8f 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt @@ -7,6 +7,7 @@ import org.evomaster.client.java.controller.api.dto.problem.ExternalServiceDto import org.evomaster.client.java.instrumentation.shared.TaintInputName import org.evomaster.core.EMConfig import org.evomaster.core.output.service.PartialOracles +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.externalservice.ExternalService import org.evomaster.core.problem.externalservice.httpws.HttpExternalServiceInfo import org.evomaster.core.problem.externalservice.httpws.service.HttpWsExternalServiceHandler @@ -355,4 +356,4 @@ abstract class AbstractRestSampler : HttpWsSampler() { } } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceManageService.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceManageService.kt index 7d8fad2856..0166441467 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceManageService.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceManageService.kt @@ -6,6 +6,7 @@ import org.evomaster.core.EMConfig.SqlInitResourceStrategy import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionUtils import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.rest.* import org.evomaster.core.problem.httpws.auth.HttpWsAuthenticationInfo import org.evomaster.core.problem.rest.resource.* @@ -359,4 +360,4 @@ class ResourceManageService { SqlInitResourceStrategy.DPC -> apc.getExploratoryValue(config.maxSizeOfHandlingResource, 1) } } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampler.kt index 28cda33d54..02c7530392 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampler.kt @@ -3,6 +3,7 @@ package org.evomaster.core.problem.rest.service import com.google.inject.Inject import org.evomaster.client.java.controller.api.dto.SutInfoDto import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.rest.* import org.evomaster.core.problem.httpws.auth.NoAuth import org.evomaster.core.problem.rest.resource.RestResourceCalls @@ -240,4 +241,4 @@ open class ResourceSampler : AbstractRestSampler() { ind.doGlobalInitialize(searchGlobalState) return ind } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestSampler.kt index e0b2dd54b0..27b8438f0e 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestSampler.kt @@ -1,8 +1,7 @@ package org.evomaster.core.problem.rest.service -import org.evomaster.client.java.controller.api.dto.SutInfoDto import org.evomaster.core.Lazy -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.rest.* import org.evomaster.core.problem.httpws.auth.HttpWsAuthenticationInfo import org.evomaster.core.problem.httpws.auth.NoAuth @@ -414,4 +413,4 @@ class RestSampler : AbstractRestSampler(){ -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestStructureMutator.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestStructureMutator.kt index c0d902dfd6..f3006d3738 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestStructureMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestStructureMutator.kt @@ -4,6 +4,7 @@ import com.google.inject.Inject import org.evomaster.core.Lazy import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.problem.api.service.ApiWsStructureMutator +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.rest.* import org.evomaster.core.problem.rest.resource.RestResourceCalls import org.evomaster.core.search.ActionFilter @@ -224,4 +225,4 @@ class RestStructureMutator : ApiWsStructureMutator() { return sampler.sqlInsertBuilder } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt index 68eb94155d..8a9f620117 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt @@ -5,6 +5,7 @@ import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionUtils import org.evomaster.core.problem.api.ApiWsIndividual import org.evomaster.core.problem.enterprise.EnterpriseActionGroup +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.externalservice.ApiExternalServiceAction import org.evomaster.core.search.* @@ -16,6 +17,7 @@ import kotlin.math.max * individual for RPC service */ class RPCIndividual( + sampleType: SampleType, trackOperator: TrackOperator? = null, index: Int = -1, allActions: MutableList, @@ -23,6 +25,7 @@ class RPCIndividual( dbSize: Int = 0, groups: GroupsOfChildren = getEnterpriseTopGroups(allActions, mainSize, dbSize) ) : ApiWsIndividual( + sampleType, trackOperator, index, allActions, childTypeVerifier = { EnterpriseActionGroup::class.java.isAssignableFrom(it) @@ -32,6 +35,7 @@ class RPCIndividual( ) { constructor( + sampleType: SampleType, actions: MutableList, externalServicesActions: MutableList> = mutableListOf(), /* @@ -41,6 +45,7 @@ class RPCIndividual( trackOperator: TrackOperator? = null, index: Int = -1 ) : this( + sampleType = sampleType, trackOperator = trackOperator, index = index, allActions = mutableListOf().apply { @@ -110,6 +115,7 @@ class RPCIndividual( override fun copyContent(): Individual { return RPCIndividual( + sampleType, trackOperator, index, children.map { it.copy() }.toMutableList() as MutableList, @@ -121,4 +127,4 @@ class RPCIndividual( override fun seeMainExecutableActions(): List { return super.seeMainExecutableActions() as List } -} \ No newline at end of file +} diff --git a/core/src/test/kotlin/org/evomaster/core/database/DbActionUtilsTest.kt b/core/src/test/kotlin/org/evomaster/core/database/DbActionUtilsTest.kt index 1c9588a229..cd1012cb31 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/DbActionUtilsTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/DbActionUtilsTest.kt @@ -6,7 +6,7 @@ import org.evomaster.core.database.schema.ColumnDataType import org.evomaster.core.database.schema.ForeignKey import org.evomaster.core.database.schema.Table import org.evomaster.core.problem.rest.RestIndividual -import org.evomaster.core.problem.rest.SampleType +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.sql.SqlAutoIncrementGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/CatwatchSqlExtractTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/h2/CatwatchSqlExtractTest.kt index a6fe464dff..4ce98aead0 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/CatwatchSqlExtractTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/h2/CatwatchSqlExtractTest.kt @@ -7,7 +7,7 @@ import org.evomaster.core.database.DbActionTransformer import org.evomaster.core.database.DbActionUtils import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.problem.rest.RestIndividual -import org.evomaster.core.problem.rest.SampleType +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.search.gene.sql.SqlPrimaryKeyGene import org.evomaster.core.search.service.Randomness import org.junit.jupiter.api.Assertions.* @@ -54,7 +54,7 @@ class CatwatchSqlExtractTest : ExtractTestBaseH2(){ val insertions = builder.createSqlInsertionAction("lANGUAGE_LIST", setOf("PROJECT_ID")) - val ind = RestIndividual(mutableListOf(),SampleType.RANDOM,null,insertions.toMutableList(),null,-1) + val ind = RestIndividual(mutableListOf(), SampleType.RANDOM,null,insertions.toMutableList(),null,-1) DbActionUtils.randomizeDbActionGenes(insertions.toMutableList(), Randomness()) @@ -79,4 +79,4 @@ class CatwatchSqlExtractTest : ExtractTestBaseH2(){ assertEquals(1, selections.seeRows().size); } -} \ No newline at end of file +} diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/DoctorsExtractTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/h2/DoctorsExtractTest.kt index 6b8a261ba7..fd6c1bbb64 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/DoctorsExtractTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/h2/DoctorsExtractTest.kt @@ -5,7 +5,7 @@ import org.evomaster.core.database.DbActionTransformer import org.evomaster.core.database.DbActionUtils import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.problem.rest.RestIndividual -import org.evomaster.core.problem.rest.SampleType +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.search.service.Randomness import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test @@ -36,4 +36,4 @@ class DoctorsExtractTest : ExtractTestBaseH2() { } -} \ No newline at end of file +} diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/ProxyPrintSqlExtractTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/h2/ProxyPrintSqlExtractTest.kt index 4156552ae2..78c0129e5e 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/ProxyPrintSqlExtractTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/h2/ProxyPrintSqlExtractTest.kt @@ -2,17 +2,15 @@ package org.evomaster.core.database.extract.h2 import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.EMConfig import org.evomaster.core.database.DbActionTransformer import org.evomaster.core.database.DbActionUtils import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.problem.rest.RestIndividual -import org.evomaster.core.problem.rest.SampleType +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.search.gene.sql.SqlAutoIncrementGene import org.evomaster.core.search.gene.sql.SqlForeignKeyGene import org.evomaster.core.search.gene.sql.SqlPrimaryKeyGene import org.evomaster.core.search.service.Randomness -import org.evomaster.core.search.service.SearchGlobalState import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test import org.junit.jupiter.api.function.Executable @@ -147,4 +145,4 @@ class ProxyPrintSqlExtractTest : ExtractTestBaseH2() { } -} \ No newline at end of file +} diff --git a/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt b/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt index 0bd37207cd..b18f8fa137 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt @@ -3,6 +3,7 @@ package org.evomaster.core.output import org.evomaster.core.TestUtils import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionResult +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.externalservice.ApiExternalServiceAction import org.evomaster.core.problem.externalservice.rpc.RPCExternalServiceAction import org.evomaster.core.problem.externalservice.rpc.parm.RPCResponseParam @@ -123,4 +124,4 @@ class EvaluatedIndividualBuilder { }) } } -} \ No newline at end of file +} diff --git a/core/src/test/kotlin/org/evomaster/core/output/TestCaseWriterTest.kt b/core/src/test/kotlin/org/evomaster/core/output/TestCaseWriterTest.kt index b222297193..d458033664 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/TestCaseWriterTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/TestCaseWriterTest.kt @@ -13,6 +13,7 @@ import org.evomaster.core.database.schema.Table import org.evomaster.core.output.EvaluatedIndividualBuilder.Companion.buildResourceEvaluatedIndividual import org.evomaster.core.output.service.PartialOracles import org.evomaster.core.output.service.RestTestCaseWriter +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.rest.* import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.FitnessValue diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt index 7e2810f257..073e5188eb 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt @@ -1,11 +1,10 @@ package org.evomaster.core.problem.rest.individual import com.google.inject.* -import org.evomaster.client.java.instrumentation.shared.TaintInputName import org.evomaster.core.database.DbAction import org.evomaster.core.database.schema.Table import org.evomaster.core.problem.rest.RestIndividual -import org.evomaster.core.problem.rest.SampleType +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.rest.resource.ResourceCluster import org.evomaster.core.problem.rest.resource.ResourceStatus import org.evomaster.core.problem.rest.resource.RestResourceCalls diff --git a/core/src/test/kotlin/org/evomaster/core/search/impact/IndividualImpactTest.kt b/core/src/test/kotlin/org/evomaster/core/search/impact/IndividualImpactTest.kt index 78fbd1cde8..2066e6cb0f 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/impact/IndividualImpactTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/impact/IndividualImpactTest.kt @@ -5,6 +5,7 @@ import org.evomaster.core.database.DbAction import org.evomaster.core.database.schema.Column import org.evomaster.core.database.schema.ColumnDataType import org.evomaster.core.database.schema.Table +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.rest.* import org.evomaster.core.problem.rest.param.QueryParam import org.evomaster.core.search.gene.numeric.IntegerGene @@ -70,4 +71,4 @@ class IndividualImpactTest { assertEquals(1, this[1].geneImpacts.size) } } -} \ No newline at end of file +} diff --git a/core/src/test/kotlin/org/evomaster/core/search/mutationweight/GeneWeightTestSchema.kt b/core/src/test/kotlin/org/evomaster/core/search/mutationweight/GeneWeightTestSchema.kt index 0bd9cfcec2..fc7b2243ef 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/mutationweight/GeneWeightTestSchema.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/mutationweight/GeneWeightTestSchema.kt @@ -10,7 +10,7 @@ import org.evomaster.core.database.schema.Table import org.evomaster.core.problem.rest.RestActionBuilderV3 import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestIndividual -import org.evomaster.core.problem.rest.SampleType +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.search.Action import org.evomaster.core.search.gene.datetime.DateGene import org.evomaster.core.search.gene.numeric.IntegerGene @@ -58,4 +58,4 @@ object GeneWeightTestSchema { return RestIndividual(actions = (0 until numRestAction).map { action1.copy() as RestCallAction }.toMutableList(), dbInitialization = dbActions, sampleType = SampleType.RANDOM) } -} \ No newline at end of file +} diff --git a/core/src/test/kotlin/org/evomaster/core/search/structuralelement/individual/RestIndividualStructureTest.kt b/core/src/test/kotlin/org/evomaster/core/search/structuralelement/individual/RestIndividualStructureTest.kt index 37af82a8b1..169ec44cce 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/structuralelement/individual/RestIndividualStructureTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/structuralelement/individual/RestIndividualStructureTest.kt @@ -6,9 +6,8 @@ import org.evomaster.core.database.schema.Column import org.evomaster.core.database.schema.ColumnDataType import org.evomaster.core.database.schema.ForeignKey import org.evomaster.core.database.schema.Table -import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestIndividual -import org.evomaster.core.problem.rest.SampleType +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.rest.param.BodyParam import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.ObjectGene @@ -18,7 +17,6 @@ import org.evomaster.core.search.structuralelement.StructuralElementBaseTest import org.evomaster.core.search.structuralelement.resourcecall.ResourceNodeCluster import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNotNull -import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test class RestIndividualStructureTest : StructuralElementBaseTest(){ diff --git a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/adaptivehm/ResourceDbMIOAndAdaptiveHMBasicTest.java b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/adaptivehm/ResourceDbMIOAndAdaptiveHMBasicTest.java index 43d1e08379..b4c07d2634 100644 --- a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/adaptivehm/ResourceDbMIOAndAdaptiveHMBasicTest.java +++ b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/adaptivehm/ResourceDbMIOAndAdaptiveHMBasicTest.java @@ -4,7 +4,7 @@ import com.google.inject.Key; import com.google.inject.TypeLiteral; import org.evomaster.core.problem.rest.RestIndividual; -import org.evomaster.core.problem.rest.SampleType; +import org.evomaster.core.problem.enterprise.SampleType; import org.evomaster.core.problem.rest.resource.RestResourceCalls; import org.evomaster.core.problem.rest.service.ResourceManageService; import org.evomaster.core.problem.rest.service.ResourceRestMutator; diff --git a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java index 9da6fb496c..088bf4d84a 100644 --- a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java +++ b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java @@ -2,7 +2,7 @@ import com.google.inject.Injector; import org.evomaster.core.problem.rest.RestIndividual; -import org.evomaster.core.problem.rest.SampleType; +import org.evomaster.core.problem.enterprise.SampleType; import org.evomaster.core.problem.rest.resource.RestResourceCalls; import org.evomaster.core.problem.rest.resource.RestResourceNode; import org.evomaster.core.problem.rest.service.ResourceManageService; @@ -13,7 +13,6 @@ import org.evomaster.core.search.ActionFilter; import org.evomaster.core.search.EvaluatedIndividual; import org.evomaster.core.search.Individual.GeneFilter; -import org.evomaster.core.search.gene.Gene; import org.evomaster.core.search.service.mutator.MutatedGeneSpecification; import org.evomaster.e2etests.spring.examples.resource.ResourceMIOHWTestBase; import org.junit.jupiter.api.Test; diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/adaptivehm/ResourceDbMIOAndAdaptiveHMBasicTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/adaptivehm/ResourceDbMIOAndAdaptiveHMBasicTest.java index 0bfb27aac5..62eb000d5f 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/adaptivehm/ResourceDbMIOAndAdaptiveHMBasicTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/adaptivehm/ResourceDbMIOAndAdaptiveHMBasicTest.java @@ -4,7 +4,7 @@ import com.google.inject.Key; import com.google.inject.TypeLiteral; import org.evomaster.core.problem.rest.RestIndividual; -import org.evomaster.core.problem.rest.SampleType; +import org.evomaster.core.problem.enterprise.SampleType; import org.evomaster.core.problem.rest.resource.RestResourceCalls; import org.evomaster.core.problem.rest.service.ResourceManageService; import org.evomaster.core.problem.rest.service.ResourceRestMutator; diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java index 4571bcf2f4..e13c6bc906 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java @@ -2,7 +2,7 @@ import com.google.inject.Injector; import org.evomaster.core.problem.rest.RestIndividual; -import org.evomaster.core.problem.rest.SampleType; +import org.evomaster.core.problem.enterprise.SampleType; import org.evomaster.core.problem.rest.resource.RestResourceCalls; import org.evomaster.core.problem.rest.resource.RestResourceNode; import org.evomaster.core.problem.rest.service.ResourceManageService; From 6cf56f949dde54c35ccb3ff38c031b9723807bf1 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 5 Jun 2023 11:10:44 +0200 Subject: [PATCH 066/345] refactor constructor and new instance --- .../org/evomaster/core/problem/gui/GuiIndividual.kt | 4 +++- .../core/problem/rpc/service/RPCEndpointsHandler.kt | 5 +++-- .../evomaster/core/problem/rpc/service/RPCSampler.kt | 10 ++++++---- .../core/problem/webfrontend/WebIndividual.kt | 6 +++++- .../core/problem/webfrontend/service/WebSampler.kt | 5 +++-- .../core/output/EvaluatedIndividualBuilder.kt | 2 +- 6 files changed, 21 insertions(+), 11 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/gui/GuiIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/gui/GuiIndividual.kt index e61615893f..06ec8f0e06 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/gui/GuiIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/gui/GuiIndividual.kt @@ -1,6 +1,7 @@ package org.evomaster.core.problem.gui import org.evomaster.core.problem.enterprise.EnterpriseIndividual +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.search.ActionComponent import org.evomaster.core.search.GroupsOfChildren import org.evomaster.core.search.StructuralElement @@ -16,6 +17,7 @@ import org.evomaster.core.search.tracer.TrackOperator * a link in a previous action */ abstract class GuiIndividual ( + sampleType: SampleType, /** * a tracked operator to manipulate the individual (nullable) @@ -32,4 +34,4 @@ abstract class GuiIndividual ( children: MutableList, childTypeVerifier: (Class<*>) -> Boolean, groups : GroupsOfChildren = getEnterpriseTopGroups(children, children.size, 0) -): EnterpriseIndividual(trackOperator, index, children, childTypeVerifier, groups) \ No newline at end of file +): EnterpriseIndividual(sampleType, trackOperator, index, children, childTypeVerifier, groups) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt index de580e3d46..b62e69a0a8 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt @@ -13,6 +13,7 @@ import org.evomaster.core.output.service.TestSuiteWriter import org.evomaster.core.parser.RegexHandler import org.evomaster.core.problem.api.param.Param import org.evomaster.core.problem.enterprise.EnterpriseActionGroup +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.externalservice.ApiExternalServiceAction import org.evomaster.core.problem.externalservice.rpc.RPCExternalServiceAction import org.evomaster.core.problem.externalservice.rpc.parm.RPCResponseParam @@ -199,7 +200,7 @@ class RPCEndpointsHandler { log.warn("The given test (${e.key}) is invalid (e.g., violate constraints) that will not be involved in the test generation") null }else - RPCIndividual(actions = rpcActions, externalServicesActions = exActions) + RPCIndividual(sampleType = SampleType.SEEDED, actions = rpcActions, externalServicesActions = exActions) }.filterNotNull() } @@ -1129,4 +1130,4 @@ class RPCEndpointsHandler { } return map } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCSampler.kt index aaf4218d9d..cfd72d543d 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCSampler.kt @@ -6,6 +6,7 @@ import org.evomaster.core.EMConfig import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.problem.api.service.ApiWsSampler import org.evomaster.core.problem.enterprise.EnterpriseActionGroup +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.graphql.GraphQLAction import org.evomaster.core.problem.rpc.RPCCallAction import org.evomaster.core.problem.rpc.RPCIndividual @@ -98,7 +99,7 @@ class RPCSampler: ApiWsSampler() { val a = sampleRandomAction(0.05) EnterpriseActionGroup(mutableListOf(a), RPCCallAction::class.java) } - val ind = createRPCIndividual(actions.toMutableList()) + val ind = createRPCIndividual(sampleType = SampleType.RANDOM, actions.toMutableList()) ind.doGlobalInitialize(searchGlobalState) return ind } @@ -138,19 +139,20 @@ class RPCSampler: ApiWsSampler() { rpcHandler.actionWithAllCandidates(actionWithAuth) .forEach { actionWithSeeded-> val a = EnterpriseActionGroup(mutableListOf(actionWithSeeded), RPCCallAction::class.java) - val ind = createRPCIndividual(mutableListOf(a)) + val ind = createRPCIndividual(SampleType.RANDOM, mutableListOf(a)) adHocInitialIndividuals.add(ind) } } } } - private fun createRPCIndividual(actions : MutableList) : RPCIndividual{ + private fun createRPCIndividual(sampleType: SampleType, actions : MutableList) : RPCIndividual{ // enable tracking in rpc return RPCIndividual( + sampleType = sampleType, trackOperator = if(config.trackingEnabled()) this else null, index = if (config.trackingEnabled()) time.evaluatedIndividuals else -1, allActions=actions as MutableList ) } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/WebIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/WebIndividual.kt index e321032d54..19bd0c2312 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/WebIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/WebIndividual.kt @@ -2,6 +2,7 @@ package org.evomaster.core.problem.webfrontend import org.evomaster.core.database.DbAction import org.evomaster.core.problem.enterprise.EnterpriseActionGroup +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.graphql.GraphQLIndividual import org.evomaster.core.problem.gui.GuiIndividual import org.evomaster.core.problem.rest.RestCallAction @@ -12,11 +13,13 @@ import org.evomaster.core.search.StructuralElement import org.evomaster.core.search.gene.Gene class WebIndividual( + sampleType: SampleType, children: MutableList, mainSize : Int = children.size, dbSize: Int = 0, groups : GroupsOfChildren = getEnterpriseTopGroups(children,mainSize,dbSize) ) : GuiIndividual( + sampleType = sampleType, children = children, childTypeVerifier = { EnterpriseActionGroup::class.java.isAssignableFrom(it) @@ -27,6 +30,7 @@ class WebIndividual( override fun copyContent(): Individual { return WebIndividual( + sampleType, children.map { it.copy() }.toMutableList() as MutableList, mainSize = groupsView()!!.sizeOfGroup(GroupsOfChildren.MAIN), dbSize = groupsView()!!.sizeOfGroup(GroupsOfChildren.INITIALIZATION_SQL) @@ -42,4 +46,4 @@ class WebIndividual( override fun seeMainExecutableActions(): List { return super.seeMainExecutableActions() as List } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebSampler.kt index 2e59d22498..9833092b66 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebSampler.kt @@ -1,6 +1,7 @@ package org.evomaster.core.problem.webfrontend.service import org.evomaster.core.problem.enterprise.EnterpriseActionGroup +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.enterprise.service.EnterpriseSampler import org.evomaster.core.problem.webfrontend.WebAction import org.evomaster.core.problem.webfrontend.WebIndividual @@ -63,7 +64,7 @@ class WebSampler : EnterpriseSampler() { val a = sampleUndefinedAction() actions.add(EnterpriseActionGroup(mutableListOf(a), WebAction::class.java)) } - val ind = WebIndividual(actions) + val ind = WebIndividual(SampleType.RANDOM, actions) ind.doGlobalInitialize(searchGlobalState) return ind @@ -75,4 +76,4 @@ class WebSampler : EnterpriseSampler() { fun sampleUndefinedAction() : WebAction{ return WebAction() } -} \ No newline at end of file +} diff --git a/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt b/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt index b18f8fa137..55c3f892cb 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt @@ -107,7 +107,7 @@ class EvaluatedIndividualBuilder { ): EvaluatedIndividual{ if (!format.isJavaOrKotlin()) throw IllegalArgumentException("do not support to generate faked evaluated RPC individual for testing test writer") - val individual = RPCIndividual(actions = actions, externalServicesActions = externalServicesActions) + val individual = RPCIndividual(SampleType.RANDOM, actions = actions, externalServicesActions = externalServicesActions) individual.doInitialize() From f98daf5aa0f04608492f0bca2edaccb4d805f7c3 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 5 Jun 2023 11:25:15 +0200 Subject: [PATCH 067/345] set sample type --- .../core/problem/enterprise/SampleType.kt | 27 ++++++++++++++++++- .../core/problem/rest/RestIndividual.kt | 2 +- .../rest/service/AbstractRestSampler.kt | 8 +++--- .../problem/rest/service/ResourceSampler.kt | 4 +-- .../core/problem/rest/service/RestSampler.kt | 4 +-- .../rest/service/RestStructureMutator.kt | 2 +- 6 files changed, 36 insertions(+), 11 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/SampleType.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/SampleType.kt index ba825d512d..0a8e790430 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/SampleType.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/SampleType.kt @@ -6,9 +6,34 @@ package org.evomaster.core.problem.enterprise * chromosome structures */ enum class SampleType { + /** + * pre-defined individual for a problem + * eg, a HTTP request to OpenAPI schema + */ + PREDEFINED, + + /** + * generated at random + */ RANDOM, + + /** + * generated based on seeded tests + */ SEEDED, + + /** + * generated by a smart strategy + */ SMART, - SMART_GET_COLLECTION, + + /** + * generated by a smart get collection strategy which is only for REST problem + */ + REST_SMART_GET_COLLECTION, + + /** + * generated by a resource-based strategy + */ SMART_RESOURCE } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt index 4f07ba536c..0fe73afd25 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt @@ -84,7 +84,7 @@ class RestIndividual( override fun canMutateStructure(): Boolean { return sampleType == SampleType.RANDOM || - sampleType == SampleType.SMART_GET_COLLECTION || + sampleType == SampleType.REST_SMART_GET_COLLECTION || sampleType == SampleType.SMART_RESOURCE } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt index 9607d86b8f..d82cc3bdf1 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt @@ -176,7 +176,7 @@ abstract class AbstractRestSampler : HttpWsSampler() { val seededTestCases = parser.parseTestCases(config.seedTestCasesPath) adHocInitialIndividuals.addAll(seededTestCases.map { it.forEach { a -> a.doInitialize() } - createIndividual(it) + createIndividual(SampleType.SEEDED, it) }) } @@ -186,7 +186,7 @@ abstract class AbstractRestSampler : HttpWsSampler() { override fun getPreDefinedIndividuals() : List{ val addCallAction = addCallToSwagger() ?: return listOf() addCallAction.doInitialize() - return listOf(createIndividual(mutableListOf(addCallAction))) + return listOf(createIndividual(SampleType.PREDEFINED,mutableListOf(addCallAction))) } open fun getExcludedActions() : List{ @@ -311,11 +311,11 @@ abstract class AbstractRestSampler : HttpWsSampler() { * @return a created individual with specified actions, i.e., [restCalls]. * All actions must have been already initialized */ - open fun createIndividual(restCalls: MutableList): RestIndividual { + open fun createIndividual(sampleType: SampleType, restCalls: MutableList): RestIndividual { if(restCalls.any { !it.isInitialized() }){ throw IllegalArgumentException("Action is not initialized") } - val ind = RestIndividual(restCalls, SampleType.SMART, mutableListOf()//, usedObjects.copy() + val ind = RestIndividual(restCalls, sampleType, mutableListOf()//, usedObjects.copy() ,trackOperator = if (config.trackingEnabled()) this else null, index = if (config.trackingEnabled()) time.evaluatedIndividuals else Traceable.DEFAULT_INDEX) ind.doInitializeLocalId() org.evomaster.core.Lazy.assert { ind.isInitialized() } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampler.kt index 02c7530392..ffaf6b797f 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampler.kt @@ -222,7 +222,7 @@ open class ResourceSampler : AbstractRestSampler() { } } - override fun createIndividual(restCalls: MutableList): RestIndividual { + override fun createIndividual(sampleType: SampleType, restCalls: MutableList): RestIndividual { val resourceCalls = restCalls.map { val node = rm.getResourceNodeFromCluster(it.path.toString()) @@ -235,7 +235,7 @@ open class ResourceSampler : AbstractRestSampler() { }.toMutableList() val ind = RestIndividual( resourceCalls=resourceCalls, - sampleType = SampleType.SMART_RESOURCE, + sampleType = sampleType, trackOperator = if (config.trackingEnabled()) this else null, index = if (config.trackingEnabled()) time.evaluatedIndividuals else Traceable.DEFAULT_INDEX) ind.doGlobalInitialize(searchGlobalState) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestSampler.kt index 27b8438f0e..f0c88d2a62 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestSampler.kt @@ -252,7 +252,7 @@ class RestSampler : AbstractRestSampler(){ test.add(test.size - 1, create) } - return SampleType.SMART_GET_COLLECTION + return SampleType.REST_SMART_GET_COLLECTION } } @@ -405,7 +405,7 @@ class RestSampler : AbstractRestSampler(){ val copy = a.value.copy() as RestCallAction copy.auth = auth copy.doInitialize(randomness) - val ind = createIndividual(mutableListOf(copy)) + val ind = createIndividual(SampleType.SMART, mutableListOf(copy)) ind.doGlobalInitialize(searchGlobalState) adHocInitialIndividuals.add(ind) } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestStructureMutator.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestStructureMutator.kt index f3006d3738..ad52952429 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestStructureMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestStructureMutator.kt @@ -52,7 +52,7 @@ class RestStructureMutator : ApiWsStructureMutator() { when (individual.sampleType) { SampleType.RANDOM -> mutateForRandomType(individual, mutatedGenes) - SampleType.SMART_GET_COLLECTION -> mutateForSmartGetCollection(individual, mutatedGenes) + SampleType.REST_SMART_GET_COLLECTION -> mutateForSmartGetCollection(individual, mutatedGenes) SampleType.SMART -> throw IllegalStateException( "SMART sampled individuals shouldn't be marked for structure mutations") From 7621c8d19611b2937425d4d5579e88b1c2848c5d Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 5 Jun 2023 11:29:17 +0200 Subject: [PATCH 068/345] skip applyGlobalUpdate if it is from seeded tests --- core/src/main/kotlin/org/evomaster/core/search/gene/Gene.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/Gene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/Gene.kt index f8865f14f1..c0be9c4a32 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/Gene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/Gene.kt @@ -12,6 +12,8 @@ import org.evomaster.core.search.service.mutator.genemutation.SubsetGeneMutation import org.slf4j.Logger import org.slf4j.LoggerFactory import org.evomaster.core.Lazy +import org.evomaster.core.problem.enterprise.EnterpriseIndividual +import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.search.RootElement import org.evomaster.core.search.gene.utils.GeneUtils import org.evomaster.core.search.service.SearchGlobalState @@ -213,7 +215,9 @@ abstract class Gene( if(ind.searchGlobalState == null){ throw IllegalStateException("Search Global State was not setup for the individual") } - applyGlobalUpdates() + + if (ind !is EnterpriseIndividual || ind.sampleType != SampleType.SEEDED) + applyGlobalUpdates() children.forEach { it.doGlobalInitialize() } } From 129f58fec6fe971d92087be97b8b6f09bcb0a56b Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 5 Jun 2023 12:15:26 +0200 Subject: [PATCH 069/345] fixed issue in Map replacements --- .../classes/MapClassReplacement.java | 71 ++++++++++++++++--- 1 file changed, 61 insertions(+), 10 deletions(-) diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MapClassReplacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MapClassReplacement.java index 7ec6747ede..58d0c72d81 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MapClassReplacement.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MapClassReplacement.java @@ -26,6 +26,17 @@ public class MapClassReplacement implements MethodReplacementClass { } } + /* + Note, if for any reason in a method X we need to call another method Y (eg for isEmpty() we call size()) to + compute any heuristics, then we MUST do so in a try/catch... + There are abominations like + + org.thymeleaf.spring6.expression.SPELContextMapWrapper + + where implements some methods, and throw exception on all others... + */ + + private static boolean hasLinearCost(Class klass){ Boolean isLinear = cacheIsLinearCost.get(klass); if(isLinear == null){ @@ -60,7 +71,12 @@ public static boolean isEmpty(Map caller, String idTemplate) { return result; } - int len = caller.size(); + int len; + try { + len = caller.size(); + } catch (Exception e){ + return result; + } Truthness t = TruthnessUtils.getTruthnessToEmpty(len); ExecutionTracer.executedReplacedMethod(idTemplate, ReplacementType.BOOLEAN, t); @@ -93,7 +109,12 @@ to containsKey() when doing a contains(). anymore, due to how we handle subclasses. See comments in that class. */ //Collection keyCollection = new HashSet(c.keySet()); - Collection keyCollection = c.keySet(); + Collection keyCollection; + try { + keyCollection = c.keySet(); + } catch (Exception e){ + return c.containsKey(o); + } CollectionsDistanceUtils.evaluateTaint(keyCollection, o); @@ -112,7 +133,12 @@ which still leads to infinite recursion, due to map() returning return c.containsKey(o); } - boolean result = keyCollection.contains(o); + boolean result; + try { + result = keyCollection.contains(o); + } catch (Exception e){ + return c.containsKey(o); + } if (idTemplate == null) { return result; @@ -164,11 +190,21 @@ public static boolean containsValue(Map c, Object o, String idTemplate) { return c.containsValue(o); } - Collection data = c.values(); + Collection data; + try { + data = c.values(); + }catch (Exception e){ + return c.containsValue(o); + } CollectionsDistanceUtils.evaluateTaint(data, o); - boolean result = data.contains(o); + boolean result; + try { + result = data.contains(o); + } catch (Exception e){ + return c.containsValue(o); + } if (idTemplate == null) { return result; @@ -201,8 +237,19 @@ public static boolean remove(Map map, Object key, Object value, String idTemplat return true; */ + try{ + map.keySet(); + }catch (Exception e){ + return map.remove(key, value); + } + CollectionsDistanceUtils.evaluateTaint(map.keySet(), key); - Object curValue = map.get(key); + Object curValue; + try { + curValue =map.get(key); + } catch (Exception e){ + return map.remove(key, value); + } if(curValue != null) { CollectionsDistanceUtils.evaluateTaint(Arrays.asList(curValue), value); } @@ -243,11 +290,15 @@ public static boolean replace(Map map, Object key, Object oldValue, Object newVa return true; */ - boolean removed = remove(map, key, oldValue,idTemplate); - if(removed){ - map.put(key, newValue); + try { + boolean removed = remove(map, key, oldValue, idTemplate); + if (removed) { + map.put(key, newValue); + } + return removed; + } catch (Exception e){ + return map.replace(key,oldValue, newValue); } - return removed; } } From dddf02ffcdefd429b2ad921c2c6e089af88193f1 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 5 Jun 2023 14:02:08 +0200 Subject: [PATCH 070/345] added support for JDK 17 --- .../java/controller/ExternalSutController.java | 18 ++++++++++++++---- .../controller/InstrumentedSutStarter.java | 11 +++++++++-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java index e05f7e86c1..682505141f 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/ExternalSutController.java @@ -15,10 +15,7 @@ import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Scanner; +import java.util.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -73,6 +70,9 @@ public abstract class ExternalSutController extends SutController { */ private volatile int jaCoCoPort = 0; + private volatile boolean needsJdk17Options = false; + + public final ExternalSutController setJaCoCo(String jaCoCoAgentLocation, String jaCoCoCliLocation, String jaCoCoOutputFile, int port){ this.jaCoCoAgentLocation = jaCoCoAgentLocation; this.jaCoCoCliLocation = jaCoCoCliLocation; @@ -90,6 +90,10 @@ public final void setupForGeneratedTest(){ //In the past, we configured P6Spy here } + public void setNeedsJdk17Options(boolean needsJdk17Options) { + this.needsJdk17Options = needsJdk17Options; + } + public final void setInstrumentation(boolean instrumentation) { this.instrumentation = instrumentation; } @@ -237,6 +241,12 @@ public final String startSut() { } } + if(needsJdk17Options){ + Arrays.stream(InstrumentedSutStarter.JDK_17_JVM_OPTIONS.split(" ")).forEach(it -> + command.add(it) + ); + } + String toSkip = System.getProperty(Constants.PROP_SKIP_CLASSES); if(toSkip != null && !toSkip.isEmpty()){ command.add("-D"+Constants.PROP_SKIP_CLASSES+"="+toSkip); diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/InstrumentedSutStarter.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/InstrumentedSutStarter.java index 7230f4da32..d402c76167 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/InstrumentedSutStarter.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/InstrumentedSutStarter.java @@ -27,6 +27,14 @@ public class InstrumentedSutStarter { //AgentLoader.loadAgentClass(InstrumentingAgent.class.getName(), "foobar_packagenameshouldnotexist."); } + + /** + * Annoying settings needed for JDK 17 :( + * + * Update docs/jdks.md if this changes + */ + public static final String JDK_17_JVM_OPTIONS = "--add-opens java.base/java.util.regex=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED"; + private static boolean alreadyLoaded = false; private final SutController sutController; @@ -42,8 +50,7 @@ public static void loadAgent(){ "\nIf you are using JDK 11 or above, are you sure you added the following JVM option?" + "\n -Djdk.attach.allowAttachSelf=true " + "\nAlso, if you are using JDK 17 or above, you also need the following:" + - // Update docs/jdks.md if this changes - "\n--add-opens java.base/java.util.regex=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED"+ + "\n"+JDK_17_JVM_OPTIONS+ "\nSee documentation at https://github.com/EMResearch/EvoMaster/blob/master/docs/jdks.md", e); } } From 4675b2aa6df484eba95ee53a1c284baf29368d3c Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Thu, 8 Jun 2023 09:49:17 +0200 Subject: [PATCH 071/345] refactoring --- .../coverage/methodreplacement/ReplacementList.java | 2 +- .../MongoCollectionClassReplacement.java} | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/{classes/MongoClassReplacement.java => thirdpartyclasses/MongoCollectionClassReplacement.java} (95%) diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/ReplacementList.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/ReplacementList.java index f109975fe0..f5623622f5 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/ReplacementList.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/ReplacementList.java @@ -48,7 +48,7 @@ public static List getList() { new MapClassReplacement(), new MatcherClassReplacement(), new MethodClassReplacement(), - new MongoClassReplacement(), + new MongoCollectionClassReplacement(), new OkHttpClient3BuilderClassReplacement(), new OkHttpClient3ClassReplacement(), new OkHttpClientClassReplacement(), diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MongoClassReplacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacement.java similarity index 95% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MongoClassReplacement.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacement.java index c2022e557e..169880915e 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MongoClassReplacement.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacement.java @@ -1,4 +1,4 @@ -package org.evomaster.client.java.instrumentation.coverage.methodreplacement.classes; +package org.evomaster.client.java.instrumentation.coverage.methodreplacement.thirdpartyclasses; import org.evomaster.client.java.instrumentation.MongoInfo; import org.evomaster.client.java.instrumentation.coverage.methodreplacement.Replacement; @@ -16,8 +16,8 @@ import java.util.List; -public class MongoClassReplacement extends ThirdPartyMethodReplacementClass { - private static final MongoClassReplacement singleton = new MongoClassReplacement(); +public class MongoCollectionClassReplacement extends ThirdPartyMethodReplacementClass { + private static final MongoCollectionClassReplacement singleton = new MongoCollectionClassReplacement(); @Override protected String getNameOfThirdPartyTargetClass() { From 7d817ac909d9d43238e5bf525afebd2d8d2f48d5 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Thu, 8 Jun 2023 10:07:23 +0200 Subject: [PATCH 072/345] clarifications/documentation with dealing with replacements of constructors --- .../coverage/methodreplacement/Replacement.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/Replacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/Replacement.java index 95cd3c2a06..a74e672301 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/Replacement.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/Replacement.java @@ -43,7 +43,13 @@ boolean replacingStatic() default false; /** - * Specify if the target to replace is a constructor call, ie, using "new" + * Specify if the target to replace is a constructor call, ie, using "new". + * Note that constructors are handled very specially. + * Replacement must return void, and rather save the newly create instance. + * Such instance must then be returned in a static method with name same + * as what currently stored in MethodReplacementClass.CONSUME_INSTANCE_METHOD_NAME + * See further documentation there. + * Also, recall that all these constraints are checked in ReplacementListTest */ boolean replacingConstructor() default false; From 11788742ad5c3dd78e820cab01b6ba8f3d174c02 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Thu, 8 Jun 2023 10:11:49 +0200 Subject: [PATCH 073/345] further clarification --- .../coverage/methodreplacement/Replacement.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/Replacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/Replacement.java index a74e672301..b0ed18fb3d 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/Replacement.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/Replacement.java @@ -45,7 +45,8 @@ /** * Specify if the target to replace is a constructor call, ie, using "new". * Note that constructors are handled very specially. - * Replacement must return void, and rather save the newly create instance. + * Replacement must return void, and rather save the newly create instance locally. + * To avoid concurrency issue, should save it in a ThreadLocal. * Such instance must then be returned in a static method with name same * as what currently stored in MethodReplacementClass.CONSUME_INSTANCE_METHOD_NAME * See further documentation there. From 8232cc7254a63ab9eb946364abf60dca39e2f147 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Thu, 8 Jun 2023 10:17:56 +0200 Subject: [PATCH 074/345] further clarification --- .../instrumentation/coverage/methodreplacement/Replacement.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/Replacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/Replacement.java index b0ed18fb3d..6a70361887 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/Replacement.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/Replacement.java @@ -51,6 +51,8 @@ * as what currently stored in MethodReplacementClass.CONSUME_INSTANCE_METHOD_NAME * See further documentation there. * Also, recall that all these constraints are checked in ReplacementListTest + * + * For an explanation of why we do this, look at MethodReplacementMethodVisitor comments there */ boolean replacingConstructor() default false; From 681dd0983f677b747919dd2b05b6dcd3dc94ae9e Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Thu, 8 Jun 2023 10:19:20 +0200 Subject: [PATCH 075/345] yet further clarification --- .../coverage/methodreplacement/Replacement.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/Replacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/Replacement.java index 6a70361887..0fd3de3a94 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/Replacement.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/Replacement.java @@ -50,9 +50,9 @@ * Such instance must then be returned in a static method with name same * as what currently stored in MethodReplacementClass.CONSUME_INSTANCE_METHOD_NAME * See further documentation there. - * Also, recall that all these constraints are checked in ReplacementListTest + * Also, recall that most of these constraints are checked in ReplacementListTest * - * For an explanation of why we do this, look at MethodReplacementMethodVisitor comments there + * For an explanation of why we do this, look at the comments in MethodReplacementMethodVisitor */ boolean replacingConstructor() default false; From e175c2e185f146bdb379431ccf8292e014741507 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Thu, 15 Jun 2023 11:36:28 +0200 Subject: [PATCH 076/345] add config for executing seeded tests first --- core/src/main/kotlin/org/evomaster/core/EMConfig.kt | 4 ++++ docs/options.md | 1 + 2 files changed, 5 insertions(+) diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index 327d31c8eb..17dfa61061 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -1627,6 +1627,10 @@ class EMConfig { @Cfg("Whether to seed EvoMaster with some initial test cases. These test cases will be used and evolved throughout the search process") var seedTestCases = false + @Experimental + @Cfg("Whether to execute seeded test cases first and collect statistics covered by such tests.") + var executedSeedTestCasesFirst = false + enum class SeedTestCasesFormat { POSTMAN } diff --git a/docs/options.md b/docs/options.md index e028068598..355158b50b 100644 --- a/docs/options.md +++ b/docs/options.md @@ -176,6 +176,7 @@ There are 3 types of options: |`errorTextEpsilon`| __Double__. The Distance Metric Error Text may use several values for epsilon.During experimentation, it may be useful to adjust these values. Epsilon describes the size of the neighbourhood used for clustering, so may result in different clustering results.Epsilon should be between 0.0 and 1.0. If the value is outside of that range, epsilon will use the default of 0.8. *Constraints*: `min=0.0, max=1.0`. *Default value*: `0.8`.| |`exceedTargetsFile`| __String__. Specify a path to save all not covered targets when the number is more than 100. *Default value*: `exceedTargets.txt`.| |`excludeTargetsForImpactCollection`| __String__. Specify prefixes of targets (e.g., MethodReplacement, Success_Call, Local) which will exclude in impact collection. Multiple exclusions should be separated with semicolon (i.e., ;). *Constraints*: `regex ^(\b(None\|NONE\|none)\b\|(\b(Class\|CLASS\|class\|Line\|LINE\|line\|Branch\|BRANCH\|branch\|MethodReplacement\|METHODREPLACEMENT\|method[r\|R]eplacement\|Success_Call\|SUCCESS_CALL\|success_[c\|C]all\|Local\|LOCAL\|local\|PotentialFault\|POTENTIALFAULT\|potential[f\|F]ault)\b(;\b(Class\|CLASS\|class\|Line\|LINE\|line\|Branch\|BRANCH\|branch\|MethodReplacement\|METHODREPLACEMENT\|method[r\|R]eplacement\|Success_Call\|SUCCESS_CALL\|success_[c\|C]all\|Local\|LOCAL\|local\|PotentialFault\|POTENTIALFAULT\|potential[f\|F]ault)\b)*))$`. *Default value*: `Local;MethodReplacement`.| +|`executedSeedTestCasesFirst`| __Boolean__. Whether to execute seeded test cases first and collect statistics covered by such tests. *Default value*: `false`.| |`exportDependencies`| __Boolean__. Specify whether to export derived dependencies among resources. *Default value*: `false`.| |`exportImpacts`| __Boolean__. Specify whether to export derived impacts among genes. *Default value*: `false`.| |`externalRequestHarvesterNumberOfThreads`| __Int__. Number of threads for external request harvester. No more threads than numbers of processors will be used. *Constraints*: `min=1.0`. *Default value*: `2`.| From c018afff5e3c79e7cd95610e831e879570e22c85 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Thu, 15 Jun 2023 12:35:22 +0200 Subject: [PATCH 077/345] execute seeded tests first --- .../resource/model/SimpleResourceSampler.kt | 4 +- .../kotlin/org/evomaster/core/EMConfig.kt | 4 -- .../problem/graphql/service/GraphQLSampler.kt | 7 ++++ .../rest/service/AbstractRestSampler.kt | 12 +++--- .../core/problem/rpc/service/RPCFitness.kt | 4 ++ .../core/problem/rpc/service/RPCSampler.kt | 27 ++++++++------ .../problem/webfrontend/service/WebSampler.kt | 8 ++++ .../evomaster/core/search/service/Sampler.kt | 37 ++++++++++++++++--- .../algorithms/constant/ConstantSampler.kt | 7 +++- .../search/algorithms/onemax/OneMaxSampler.kt | 5 ++- .../matchproblem/PrimitiveTypeMatchSampler.kt | 7 +++- docs/options.md | 1 - .../RestResourceSamplerSeedTestTest.java | 12 +++--- 13 files changed, 98 insertions(+), 37 deletions(-) diff --git a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/model/SimpleResourceSampler.kt b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/model/SimpleResourceSampler.kt index e9a8fece2e..5ab8a87d0e 100644 --- a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/model/SimpleResourceSampler.kt +++ b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/model/SimpleResourceSampler.kt @@ -22,6 +22,8 @@ class SimpleResourceSampler : ResourceSampler() { } initAdHocInitialIndividuals() + initSeededTests() + postInits() } @@ -30,4 +32,4 @@ class SimpleResourceSampler : ResourceSampler() { } override fun getExcludedActions(): List = listOf() -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index 17dfa61061..327d31c8eb 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -1627,10 +1627,6 @@ class EMConfig { @Cfg("Whether to seed EvoMaster with some initial test cases. These test cases will be used and evolved throughout the search process") var seedTestCases = false - @Experimental - @Cfg("Whether to execute seeded test cases first and collect statistics covered by such tests.") - var executedSeedTestCasesFirst = false - enum class SeedTestCasesFormat { POSTMAN } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLSampler.kt index 77fc97f485..a8a5f9f70b 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLSampler.kt @@ -1,5 +1,6 @@ package org.evomaster.core.problem.graphql.service +import org.evomaster.client.java.controller.api.dto.SutInfoDto import org.evomaster.core.problem.enterprise.EnterpriseActionGroup import org.evomaster.core.problem.graphql.* import org.evomaster.core.problem.graphql.builder.GraphQLActionBuilder @@ -72,6 +73,9 @@ class GraphQLSampler : HttpWsSampler() { updateConfigBasedOnSutInfoDto(infoDto) + if (config.seedTestCases) + initSeededTests() + log.debug("Done initializing {}", GraphQLSampler::class.simpleName) } @@ -118,5 +122,8 @@ class GraphQLSampler : HttpWsSampler() { number of Mutation before */ + override fun initSeededTests(infoDto: SutInfoDto?) { + // not supported yet + } } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt index d82cc3bdf1..8da7df6a57 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt @@ -112,7 +112,7 @@ abstract class AbstractRestSampler : HttpWsSampler() { } initAdHocInitialIndividuals() - + initSeededTests() postInits() updateConfigBasedOnSutInfoDto(infoDto) @@ -169,17 +169,18 @@ abstract class AbstractRestSampler : HttpWsSampler() { */ fun initAdHocInitialIndividuals(){ customizeAdHocInitialIndividuals() + } + override fun initSeededTests(infoDto: SutInfoDto?) { // if test case seeding is enabled, add those test cases too if (config.seedTestCases) { val parser = getParser() val seededTestCases = parser.parseTestCases(config.seedTestCasesPath) - adHocInitialIndividuals.addAll(seededTestCases.map { + seededIndividuals.addAll(seededTestCases.map { it.forEach { a -> a.doInitialize() } createIndividual(SampleType.SEEDED, it) }) } - } @@ -276,6 +277,7 @@ abstract class AbstractRestSampler : HttpWsSampler() { RestActionBuilderV3.addActionsFromSwagger(swagger, actionCluster, listOf(), enableConstraintHandling = config.enableSchemaConstraintHandling) initAdHocInitialIndividuals() + initSeededTests() addAuthFromConfig() @@ -291,8 +293,8 @@ abstract class AbstractRestSampler : HttpWsSampler() { return swagger } - override fun hasSpecialInit(): Boolean { - return adHocInitialIndividuals.isNotEmpty() && config.isEnabledSmartSampling() + override fun hasSpecialInitForSmartSampler(): Boolean { + return (adHocInitialIndividuals.isNotEmpty() && config.isEnabledSmartSampling()) } /** diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt index 878b4a51fc..0049b4b250 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt @@ -83,6 +83,10 @@ class RPCFitness : ApiWsFitness() { private fun expandIndividual( individual: RPCIndividual ){ + + /* + might later need to handle missing class when mocking database + */ val exMissingDto = individual.seeExternalServiceActions() .filterIsInstance() .filterNot { diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCSampler.kt index cfd72d543d..635d49cd8c 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCSampler.kt @@ -3,15 +3,12 @@ package org.evomaster.core.problem.rpc.service import com.google.inject.Inject import org.evomaster.client.java.controller.api.dto.SutInfoDto import org.evomaster.core.EMConfig -import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.problem.api.service.ApiWsSampler import org.evomaster.core.problem.enterprise.EnterpriseActionGroup import org.evomaster.core.problem.enterprise.SampleType -import org.evomaster.core.problem.graphql.GraphQLAction import org.evomaster.core.problem.rpc.RPCCallAction import org.evomaster.core.problem.rpc.RPCIndividual import org.evomaster.core.remote.SutProblemException -import org.evomaster.core.remote.service.RemoteController import org.evomaster.core.search.ActionComponent import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -62,6 +59,7 @@ class RPCSampler: ApiWsSampler() { initSqlInfo(infoDto) initAdHocInitialIndividuals(infoDto) + initSeededTests(infoDto) updateConfigBasedOnSutInfoDto(infoDto) log.debug("Done initializing {}", RPCSampler::class.simpleName) @@ -114,17 +112,24 @@ class RPCSampler: ApiWsSampler() { adHocInitialIndividuals.clear() createSingleCallIndividualOnEachAction() - if (config.seedTestCases && infoDto.rpcProblem?.seededTestDtos?.isNotEmpty() == true){ - adHocInitialIndividuals.addAll( - rpcHandler.handledSeededTests(infoDto.rpcProblem.seededTestDtos) - .map{ - it.seeAllActions().forEach { a -> a.doInitialize() } - it - } + adHocInitialIndividuals.forEach { + it.doGlobalInitialize(searchGlobalState) + } + } + + override fun initSeededTests(infoDto: SutInfoDto?) { + + if (config.seedTestCases && infoDto?.rpcProblem?.seededTestDtos?.isNotEmpty() == true){ + seededIndividuals.addAll( + rpcHandler.handledSeededTests(infoDto.rpcProblem.seededTestDtos) + .map{ + it.seeAllActions().forEach { a -> a.doInitialize() } + it + } ) } - adHocInitialIndividuals.forEach { + seededIndividuals.forEach{ it.doGlobalInitialize(searchGlobalState) } } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebSampler.kt index cd46d510a9..809714f9fe 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebSampler.kt @@ -1,6 +1,7 @@ package org.evomaster.core.problem.webfrontend.service import org.evomaster.client.java.controller.api.SeleniumEMUtils +import org.evomaster.client.java.controller.api.dto.SutInfoDto import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.problem.enterprise.EnterpriseActionGroup import org.evomaster.core.problem.enterprise.SampleType @@ -66,6 +67,9 @@ class WebSampler : EnterpriseSampler() { updateConfigBasedOnSutInfoDto(infoDto) + if (config.seedTestCases) + initSeededTests() + log.debug("Done initializing {}", WebSampler::class.simpleName) } @@ -90,4 +94,8 @@ class WebSampler : EnterpriseSampler() { fun sampleUndefinedAction() : WebAction{ return WebAction() } + + override fun initSeededTests(infoDto: SutInfoDto?) { + // not supported yet + } } diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt index 9f4d7e6c8e..7f0600206e 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt @@ -1,6 +1,7 @@ package org.evomaster.core.search.service import com.google.inject.Inject +import org.evomaster.client.java.controller.api.dto.SutInfoDto import org.evomaster.core.EMConfig import org.evomaster.core.search.Action import org.evomaster.core.search.EvaluatedIndividual @@ -41,7 +42,10 @@ abstract class Sampler : TrackOperator where T : Individual { */ protected val actionCluster: MutableMap = mutableMapOf() - + /** + * keep a list of seeded individual + */ + protected val seededIndividuals : MutableList = mutableListOf() /** * Create a new individual. Usually each call to this method @@ -53,9 +57,13 @@ abstract class Sampler : TrackOperator where T : Individual { log.trace("sampler will be applied") } + if (config.seedTestCases && seededIndividuals.isNotEmpty()){ + return seededIndividuals.removeLast() + } + val ind = if (forceRandomSample) { sampleAtRandom() - } else if ( config.isEnabledSmartSampling() && (hasSpecialInit() || randomness.nextBoolean(config.probOfSmartSampling))) { + } else if ( config.isEnabledSmartSampling() && (hasSpecialInitForSmartSampler() || randomness.nextBoolean(config.probOfSmartSampling))) { // If there is still special init set, sample from that, otherwise depend on probability smartSample() } else { @@ -75,6 +83,8 @@ abstract class Sampler : TrackOperator where T : Individual { */ protected abstract fun sampleAtRandom(): T + protected abstract fun initSeededTests(infoDto: SutInfoDto? = null) + open fun samplePostProcessing(ind: T){ val state = ind.searchGlobalState ?: return @@ -102,15 +112,32 @@ abstract class Sampler : TrackOperator where T : Individual { fun numberOfDistinctActions() = actionCluster.size + /** + * @return number of seeded individual which are not executed + */ + fun numberOfNotExecutedSeededIndividuals() = seededIndividuals.size + + /** + * @return number of seeded individual which are not executed + */ + fun getNotExecutedSeededIndividuals() = seededIndividuals.toList() + /** * When the search starts, there might be some predefined individuals * that we can sample. But we just need to sample each of them just once. * The [smartSample] must first pick from this set. * - * @return false if there is not left predefined individual to sample + * @return false if there is not left predefined individual to sample with smart sampler */ - open fun hasSpecialInit() = false + open fun hasSpecialInitForSmartSampler() = false + /** + * When the search starts, there might be some predefined individuals + * that we can sample. + * + * @return false if there is not left predefined individual to sample + */ + fun hasSpecialInit() : Boolean = seededIndividuals.isNotEmpty() || hasSpecialInitForSmartSampler() open fun resetSpecialInit() {} @@ -154,4 +181,4 @@ abstract class Sampler : TrackOperator where T : Individual { * eg to create valid assertions for them. */ open fun getPreDefinedIndividuals() = listOf() -} \ No newline at end of file +} diff --git a/core/src/test/kotlin/org/evomaster/core/search/algorithms/constant/ConstantSampler.kt b/core/src/test/kotlin/org/evomaster/core/search/algorithms/constant/ConstantSampler.kt index 31cb7cef8b..e4774a035e 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/algorithms/constant/ConstantSampler.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/algorithms/constant/ConstantSampler.kt @@ -1,5 +1,6 @@ package org.evomaster.core.search.algorithms.constant +import org.evomaster.client.java.controller.api.dto.SutInfoDto import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.service.Sampler @@ -15,4 +16,8 @@ class ConstantSampler : Sampler() { ind.doGlobalInitialize(searchGlobalState) return ind } -} \ No newline at end of file + + override fun initSeededTests(infoDto: SutInfoDto?) { + + } +} diff --git a/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxSampler.kt b/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxSampler.kt index 2294434790..0e8c525b95 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxSampler.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxSampler.kt @@ -1,5 +1,6 @@ package org.evomaster.core.search.algorithms.onemax +import org.evomaster.client.java.controller.api.dto.SutInfoDto import org.evomaster.core.search.service.Sampler @@ -16,5 +17,7 @@ class OneMaxSampler : Sampler(){ return sampled } + override fun initSeededTests(infoDto: SutInfoDto?) { -} \ No newline at end of file + } +} diff --git a/core/src/test/kotlin/org/evomaster/core/search/matchproblem/PrimitiveTypeMatchSampler.kt b/core/src/test/kotlin/org/evomaster/core/search/matchproblem/PrimitiveTypeMatchSampler.kt index 163a45d0f7..ef14e2ab93 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/matchproblem/PrimitiveTypeMatchSampler.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/matchproblem/PrimitiveTypeMatchSampler.kt @@ -1,5 +1,6 @@ package org.evomaster.core.search.matchproblem +import org.evomaster.client.java.controller.api.dto.SutInfoDto import org.evomaster.core.search.service.Sampler /** @@ -16,4 +17,8 @@ class PrimitiveTypeMatchSampler: Sampler() { ind.doGlobalInitialize(searchGlobalState) return ind } -} \ No newline at end of file + + override fun initSeededTests(infoDto: SutInfoDto?) { + + } +} diff --git a/docs/options.md b/docs/options.md index 355158b50b..e028068598 100644 --- a/docs/options.md +++ b/docs/options.md @@ -176,7 +176,6 @@ There are 3 types of options: |`errorTextEpsilon`| __Double__. The Distance Metric Error Text may use several values for epsilon.During experimentation, it may be useful to adjust these values. Epsilon describes the size of the neighbourhood used for clustering, so may result in different clustering results.Epsilon should be between 0.0 and 1.0. If the value is outside of that range, epsilon will use the default of 0.8. *Constraints*: `min=0.0, max=1.0`. *Default value*: `0.8`.| |`exceedTargetsFile`| __String__. Specify a path to save all not covered targets when the number is more than 100. *Default value*: `exceedTargets.txt`.| |`excludeTargetsForImpactCollection`| __String__. Specify prefixes of targets (e.g., MethodReplacement, Success_Call, Local) which will exclude in impact collection. Multiple exclusions should be separated with semicolon (i.e., ;). *Constraints*: `regex ^(\b(None\|NONE\|none)\b\|(\b(Class\|CLASS\|class\|Line\|LINE\|line\|Branch\|BRANCH\|branch\|MethodReplacement\|METHODREPLACEMENT\|method[r\|R]eplacement\|Success_Call\|SUCCESS_CALL\|success_[c\|C]all\|Local\|LOCAL\|local\|PotentialFault\|POTENTIALFAULT\|potential[f\|F]ault)\b(;\b(Class\|CLASS\|class\|Line\|LINE\|line\|Branch\|BRANCH\|branch\|MethodReplacement\|METHODREPLACEMENT\|method[r\|R]eplacement\|Success_Call\|SUCCESS_CALL\|success_[c\|C]all\|Local\|LOCAL\|local\|PotentialFault\|POTENTIALFAULT\|potential[f\|F]ault)\b)*))$`. *Default value*: `Local;MethodReplacement`.| -|`executedSeedTestCasesFirst`| __Boolean__. Whether to execute seeded test cases first and collect statistics covered by such tests. *Default value*: `false`.| |`exportDependencies`| __Boolean__. Specify whether to export derived dependencies among resources. *Default value*: `false`.| |`exportImpacts`| __Boolean__. Specify whether to export derived impacts among genes. *Default value*: `false`.| |`externalRequestHarvesterNumberOfThreads`| __Int__. Number of threads for external request harvester. No more threads than numbers of processors will be used. *Constraints*: `min=1.0`. *Default value*: `2`.| diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/seedtest/RestResourceSamplerSeedTestTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/seedtest/RestResourceSamplerSeedTestTest.java index e0d5dfcbc1..684e94c798 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/seedtest/RestResourceSamplerSeedTestTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/seedtest/RestResourceSamplerSeedTestTest.java @@ -1,15 +1,10 @@ package org.evomaster.e2etests.spring.examples.resource.seedtest; import com.google.inject.Injector; -import org.evomaster.core.problem.rest.RestIndividual; -import org.evomaster.core.problem.rest.service.ResourceRestMutator; import org.evomaster.core.problem.rest.service.ResourceSampler; -import org.evomaster.core.problem.rest.service.RestResourceFitness; -import org.evomaster.core.search.EvaluatedIndividual; import org.evomaster.e2etests.spring.examples.resource.ResourceMIOHWTestBase; import org.junit.jupiter.api.Test; -import java.util.HashSet; import java.util.List; import static org.junit.jupiter.api.Assertions.*; @@ -25,10 +20,11 @@ public void testResourceSamplerWithSeed() { Injector injector = init(args); ResourceSampler sampler = injector.getInstance(ResourceSampler.class); - assertEquals(13, sampler.getSizeOfAdHocInitialIndividuals()); + assertEquals(12, sampler.getSizeOfAdHocInitialIndividuals()); + assertEquals(1, sampler.numberOfNotExecutedSeededIndividuals()); sampler.getNotExecutedAdHocInitialIndividuals().forEach(s-> s.getResourceCalls().forEach(r-> assertNotNull(r.getResourceNode()))); - + sampler.getNotExecutedSeededIndividuals().forEach(s-> s.getResourceCalls().forEach(r-> assertNotNull(r.getResourceNode()))); } @Test @@ -40,6 +36,8 @@ public void testResourceSamplerWithoutSeed() { ResourceSampler sampler = injector.getInstance(ResourceSampler.class); assertEquals(12, sampler.getSizeOfAdHocInitialIndividuals()); + assertEquals(0, sampler.numberOfNotExecutedSeededIndividuals()); + sampler.getNotExecutedAdHocInitialIndividuals().forEach(s-> s.getResourceCalls().forEach(r-> assertNotNull(r.getResourceNode()))); } From b9c47a3ebd5ce17f31a3f05da812082feaec1e28 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Thu, 15 Jun 2023 13:19:58 +0200 Subject: [PATCH 078/345] record targets covered by seeds --- .../core/search/algorithms/MioAlgorithm.kt | 5 +++-- .../evomaster/core/search/service/Archive.kt | 18 +++++++++++++++++- .../evomaster/core/search/service/Sampler.kt | 14 ++++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/search/algorithms/MioAlgorithm.kt b/core/src/main/kotlin/org/evomaster/core/search/algorithms/MioAlgorithm.kt index b487a0491d..20c104c34c 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/algorithms/MioAlgorithm.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/algorithms/MioAlgorithm.kt @@ -3,7 +3,6 @@ package org.evomaster.core.search.algorithms import org.evomaster.core.EMConfig import org.evomaster.core.Lazy import org.evomaster.core.search.Individual -import org.evomaster.core.search.Solution import org.evomaster.core.search.service.SearchAlgorithm /** @@ -35,6 +34,8 @@ class MioAlgorithm : SearchAlgorithm() where T : Individual { archive.addIfNeeded(this) sampler.feedback(this) + if (sampler.isLastSeededIndividual()) + archive.archiveCoveredStatisticsBySeededTests() } return @@ -48,4 +49,4 @@ class MioAlgorithm : SearchAlgorithm() where T : Individual { } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt index 0b85310462..353ed23488 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt @@ -82,6 +82,9 @@ class Archive where T : Individual { */ private var lastChosen: Int? = null + data class CoveredStatisticsBySeededTests(val coveredTargets: List) + + private var coveredStatisticsBySeededTests : CoveredStatisticsBySeededTests? = null /** * Kill all populations. @@ -100,6 +103,19 @@ class Archive where T : Individual { } + + fun archiveCoveredStatisticsBySeededTests(){ + if (coveredStatisticsBySeededTests != null){ + throw IllegalStateException("`archiveCoveredStatisticsBySeededTests` can only be performed once") + } + + val current = extractSolution() + coveredStatisticsBySeededTests = CoveredStatisticsBySeededTests( + coveredTargets = current.overall.getViewOfData().filter { it.value.distance == FitnessValue.MAX_VALUE }.keys.toList() + ) + } + + fun getCopyOfUniqueCoveringIndividuals() : List{ return getUniquePopulation().map { it.individual } } @@ -641,4 +657,4 @@ class Archive where T : Individual { } } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt index 7f0600206e..e395857cee 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt @@ -47,6 +47,17 @@ abstract class Sampler : TrackOperator where T : Individual { */ protected val seededIndividuals : MutableList = mutableListOf() + private var pickingUpLastSeed = false + + /** + * @return if the last seeded was picked up + */ + fun isLastSeededIndividual() = pickingUpLastSeed + + private fun resetPickingUpLastSeededIndividual() { + pickingUpLastSeed = false + } + /** * Create a new individual. Usually each call to this method * will create a new, different individual, but there is no @@ -57,7 +68,10 @@ abstract class Sampler : TrackOperator where T : Individual { log.trace("sampler will be applied") } + resetPickingUpLastSeededIndividual() + if (config.seedTestCases && seededIndividuals.isNotEmpty()){ + pickingUpLastSeed = seededIndividuals.size == 1 return seededIndividuals.removeLast() } From f9dd3bd926c4f2147ac4f346b9bf80f346c65ce4 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Thu, 15 Jun 2023 13:42:52 +0200 Subject: [PATCH 079/345] fix failing tests --- .../core/problem/rpc/service/RPCEndpointsHandler.kt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt index 3e3c666c90..d71caae401 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt @@ -181,6 +181,7 @@ class RPCEndpointsHandler { val rpcActionDtos = e.value val exActions = mutableListOf>() val rpcActions = rpcActionDtos.map { rpcActionDto-> + val external = mutableListOf() val name = actionName(rpcActionDto.interfaceId, rpcActionDto.actionName) if (rpcActionDto.mockRPCExternalServiceDtos != null && rpcActionDto.mockRPCExternalServiceDtos.isNotEmpty()){ val ex = rpcActionDto.mockRPCExternalServiceDtos.map { e-> @@ -198,11 +199,12 @@ class RPCEndpointsHandler { } }.filterNotNull() }.flatten() - exActions.add(ex) + external.addAll(ex) } - if (rpcActionDto.mockDatabaseDtos != null && rpcActionDto.mockDatabaseDtos.isNotEmpty()){ - val dbEx = rpcActionDto.mockDatabaseDtos.map { dbDto-> + + if (rpcActionDto.mockDatabaseDtos != null && rpcActionDto.mockDatabaseDtos.isNotEmpty()){ + val dbEx = rpcActionDto.mockDatabaseDtos.map { dbDto-> val dbExAction = seededDbMockObjects[ DbAsExternalServiceAction.getDbAsExternalServiceAction(dbDto.commandName, dbDto.requests, dbDto.responseFullType) ]!!.copy() as DbAsExternalServiceAction @@ -218,9 +220,10 @@ class RPCEndpointsHandler { null } }.filterNotNull() - exActions.add(dbEx) + external.addAll(dbEx) } + exActions.add(external) processEndpoint(name, rpcActionDto, true) }.toMutableList() From 87f603ad8f4a7a071775b3f1ac2e02dc4975d81c Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Thu, 15 Jun 2023 21:14:38 +0200 Subject: [PATCH 080/345] fix failing tests --- .../resource/seedtest/RestResourceSamplerSeedTestTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/seedtest/RestResourceSamplerSeedTestTest.java b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/seedtest/RestResourceSamplerSeedTestTest.java index e0d5dfcbc1..2db1851a4e 100644 --- a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/seedtest/RestResourceSamplerSeedTestTest.java +++ b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/seedtest/RestResourceSamplerSeedTestTest.java @@ -25,9 +25,11 @@ public void testResourceSamplerWithSeed() { Injector injector = init(args); ResourceSampler sampler = injector.getInstance(ResourceSampler.class); - assertEquals(13, sampler.getSizeOfAdHocInitialIndividuals()); + assertEquals(12, sampler.getSizeOfAdHocInitialIndividuals()); + assertEquals(1, sampler.numberOfNotExecutedSeededIndividuals()); sampler.getNotExecutedAdHocInitialIndividuals().forEach(s-> s.getResourceCalls().forEach(r-> assertNotNull(r.getResourceNode()))); + sampler.getNotExecutedSeededIndividuals().forEach(s-> s.getResourceCalls().forEach(r-> assertNotNull(r.getResourceNode()))); } From 745456810bb772be686b1ac49a3d37601e20d1ac Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Thu, 15 Jun 2023 23:50:01 +0200 Subject: [PATCH 081/345] record targets covered by seeded tests with solution --- .../core/output/TestSuiteSplitter.kt | 28 ++++++------- .../org/evomaster/core/search/FitnessValue.kt | 40 ++++++++++++++++--- .../org/evomaster/core/search/Solution.kt | 6 ++- .../evomaster/core/search/TargetStatistic.kt | 5 +++ .../evomaster/core/search/service/Archive.kt | 2 +- .../service/monitor/SearchProcessMonitor.kt | 2 +- .../output/service/TestSuiteWriterRPCTest.kt | 5 ++- .../output/service/TestSuiteWriterTest.kt | 5 ++- 8 files changed, 66 insertions(+), 27 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/output/TestSuiteSplitter.kt b/core/src/main/kotlin/org/evomaster/core/output/TestSuiteSplitter.kt index d2294c867b..cdcc69f920 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/TestSuiteSplitter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/TestSuiteSplitter.kt @@ -32,7 +32,7 @@ object TestSuiteSplitter { Solution(individuals = g.value.toMutableList(), testSuiteNamePrefix = solution.testSuiteNamePrefix, testSuiteNameSuffix = solution.testSuiteNameSuffix, - termination = if (g.key) Termination.EXCEPTION else Termination.OTHER) + termination = if (g.key) Termination.EXCEPTION else Termination.OTHER, listOf()) } } } @@ -115,10 +115,10 @@ object TestSuiteSplitter { when (clusterableActions.size) { 0 -> splitResult.splitOutcome = mutableListOf() - 1 -> splitResult.splitOutcome = mutableListOf(Solution(errs, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.SUMMARY)) + 1 -> splitResult.splitOutcome = mutableListOf(Solution(errs, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.SUMMARY, listOf())) } val clusters = mutableMapOf>>() - val clusteringSol = Solution(errs, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.SUMMARY) + val clusteringSol = Solution(errs, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.SUMMARY, listOf()) /** In order for clustering to make sense, we need a set of clusterable actions with at least 2 elements. @@ -174,7 +174,7 @@ object TestSuiteSplitter { } val execSolList = execSol.toMutableList() - return Solution(execSolList, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.SUMMARY) + return Solution(execSolList, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.SUMMARY, listOf()) } private fun splitByCluster(clusters: MutableMap>>, @@ -198,13 +198,13 @@ object TestSuiteSplitter { } }.toMutableList() - val solSuccesses = Solution(successses, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.SUCCESSES) + val solSuccesses = Solution(successses, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.SUCCESSES, listOf()) val remainder = solution.individuals.filter { !errs.contains(it) && !successses.contains(it) }.toMutableList() - val solRemainder = Solution(remainder, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.OTHER) + val solRemainder = Solution(remainder, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.OTHER, listOf()) // Failures by cluster val sumSol = mutableSetOf>() @@ -224,7 +224,7 @@ object TestSuiteSplitter { skipped.forEach { sumSol.add(it) } - val solErrors = Solution(sumSol.toMutableList(), solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.FAULTS) + val solErrors = Solution(sumSol.toMutableList(), solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.FAULTS, listOf()) splitResult.splitOutcome = mutableListOf(solErrors, solSuccesses, solRemainder) @@ -267,9 +267,9 @@ object TestSuiteSplitter { !successses.contains(it) }.toMutableList() - return listOf(Solution(s500, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.FAULTS), - Solution(successses, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.SUCCESSES), - Solution(remainder, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.OTHER) + return listOf(Solution(s500, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.FAULTS, listOf()), + Solution(successses, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.SUCCESSES, listOf()), + Solution(remainder, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.OTHER, listOf()) ) } @@ -369,9 +369,9 @@ object TestSuiteSplitter { !successses.contains(it) }.toMutableList() - return listOf(Solution(s500, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.FAULTS), - Solution(successses, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.SUCCESSES), - Solution(remainder, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.OTHER) + return listOf(Solution(s500, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.FAULTS, listOf()), + Solution(successses, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.SUCCESSES, listOf()), + Solution(remainder, solution.testSuiteNamePrefix, solution.testSuiteNameSuffix, Termination.OTHER, listOf()) ) } @@ -523,4 +523,4 @@ object TestSuiteSplitter { */ -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt b/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt index b0b4081e82..05dbde6b2c 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt @@ -98,6 +98,11 @@ class FitnessValue( */ var executionTimeMs : Long = Long.MAX_VALUE + /** + * a list of targets covered with seeded tests + */ + private val coveredTargetsDuringSeeding : MutableSet = mutableSetOf() + fun copy(): FitnessValue { val copy = FitnessValue(size) @@ -172,6 +177,18 @@ class FitnessValue( .count() } + fun setTargetsCoveredBySeeding(coveredTargets: List){ + coveredTargetsDuringSeeding.addAll(coveredTargets) + } + + fun coveredTargetsDuringSeeding() : Int{ + return coveredTargetsDuringSeeding.size + } + fun coveredTargetsDuringSeeding(prefix: String, idMapper: IdMapper) : Int{ + return coveredTargetsDuringSeeding + .count { idMapper.getDescriptiveId(it).startsWith(prefix) } + } + /** * this method is to report the union results with targets at boot-time * @param prefix specifies the target with specific prefix to return (eg Line), null means return all types of targets @@ -183,18 +200,31 @@ class FitnessValue( */ fun unionWithBootTimeCoveredTargets(prefix: String?, idMapper: IdMapper, bootTimeInfoDto: BootTimeInfoDto?): TargetStatistic{ if (bootTimeInfoDto?.targets == null){ - return (if (prefix == null) coveredTargets() else coveredTargets(prefix, idMapper)).run { TargetStatistic( - BOOT_TIME_INFO_UNAVAILABLE,this, max(BOOT_TIME_INFO_UNAVAILABLE,0)+this) } + return (if (prefix == null) coveredTargets() else coveredTargets(prefix, idMapper)).run { + TargetStatistic( + bootTime = BOOT_TIME_INFO_UNAVAILABLE, + searchTime = this - coveredTargetsDuringSeeding(), + seedingTime = coveredTargetsDuringSeeding(), + max(BOOT_TIME_INFO_UNAVAILABLE,0)+this) + } } val bootTime = bootTimeInfoDto.targets.filter { it.value == MAX_VALUE && (prefix == null || it.descriptiveId.startsWith(prefix)) } // counter for duplicated targets var duplicatedcounter = 0 + + val seedingTime = coveredTargetsDuringSeeding.count { s -> + ((prefix == null || idMapper.getDescriptiveId(s).startsWith(prefix))).apply { + if (this && bootTime.any { it.descriptiveId == idMapper.getDescriptiveId(s) }) + duplicatedcounter++ + } + } + val searchTime = targets.entries.count { e -> (e.value.distance == MAX_VALUE && (prefix == null || idMapper.getDescriptiveId(e.key).startsWith(prefix))).apply { if (this && bootTime.any { it.descriptiveId == idMapper.getDescriptiveId(e.key) }) duplicatedcounter++ } - } + } - seedingTime /* related to task https://trello.com/c/EoWcV6KX/810-issue-with-assertion-checks-in-e2e @@ -213,7 +243,7 @@ class FitnessValue( } */ - return TargetStatistic(bootTime.size, searchTime, bootTime.size + searchTime - duplicatedcounter) + return TargetStatistic(bootTime = bootTime.size, searchTime = searchTime, seedingTime = seedingTime,bootTime.size + searchTime + seedingTime - duplicatedcounter) } fun coverTarget(id: Int) { @@ -722,4 +752,4 @@ class FitnessValue( fun getViewExternalRequestToDefaultWMByAction(actionIndex: Int) = accessedDefaultWM[actionIndex] fun getViewEmployedDefaultWM() = accessedDefaultWM.values.flatMap { it.values } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/search/Solution.kt b/core/src/main/kotlin/org/evomaster/core/search/Solution.kt index 70f6e2fef2..26be857b48 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/Solution.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/Solution.kt @@ -9,7 +9,8 @@ class Solution( val individuals: MutableList>, val testSuiteNamePrefix: String, val testSuiteNameSuffix: String, - val termination: Termination = Termination.NONE + val termination: Termination = Termination.NONE, + val targetsDuringSeeding : List ) where T : Individual { @@ -22,6 +23,7 @@ where T : Individual { overall.merge(it.fitness) overall.size += it.individual.size() } + overall.setTargetsCoveredBySeeding(targetsDuringSeeding) } fun getFileName() : String{ @@ -51,4 +53,4 @@ where T : Individual { fun hasAnySqlAction() : Boolean{ return individuals.any { ind -> ind.individual.seeAllActions().any { a -> a is DbAction}} } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/search/TargetStatistic.kt b/core/src/main/kotlin/org/evomaster/core/search/TargetStatistic.kt index 57ad178ad5..7ba725cc46 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/TargetStatistic.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/TargetStatistic.kt @@ -15,6 +15,11 @@ data class TargetStatistic( */ val searchTime: Int, + /** + * achieved by seeded tests + */ + val seedingTime: Int, + /** * total unique targets covered at the end * this includes targets covered during boot-time, search-time and authentication diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt index 353ed23488..e25b07dc13 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt @@ -99,7 +99,7 @@ class Archive where T : Individual { fun extractSolution(): Solution { val uniques = getUniquePopulation() - return Solution(uniques.toMutableList(), config.outputFilePrefix, config.outputFileSuffix, Termination.NONE) + return Solution(uniques.toMutableList(), config.outputFilePrefix, config.outputFileSuffix, Termination.NONE, coveredStatisticsBySeededTests?.coveredTargets?: listOf()) } diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/monitor/SearchProcessMonitor.kt b/core/src/main/kotlin/org/evomaster/core/search/service/monitor/SearchProcessMonitor.kt index db1e84b29e..15e9f52abd 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/monitor/SearchProcessMonitor.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/monitor/SearchProcessMonitor.kt @@ -204,7 +204,7 @@ class SearchProcessMonitor: SearchListener { private fun saveStepAsTest(index: Int, evalInd: EvaluatedIndividual, doesIncludeTarget : Boolean){ val name = getStepName(index, false) val testFile = TestSuiteFileName(name) - val solution = Solution(individuals = mutableListOf(evalInd), testSuiteNamePrefix = name, testSuiteNameSuffix = "") + val solution = Solution(individuals = mutableListOf(evalInd), testSuiteNamePrefix = name, testSuiteNameSuffix = "", targetsDuringSeeding = listOf()) val content = writer.convertToCompilableTestCode( solution = solution, testSuiteFileName = testFile, controllerName = controllerName, controllerInput = null) diff --git a/core/src/test/kotlin/org/evomaster/core/output/service/TestSuiteWriterRPCTest.kt b/core/src/test/kotlin/org/evomaster/core/output/service/TestSuiteWriterRPCTest.kt index d79d10b4c3..581d457299 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/service/TestSuiteWriterRPCTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/service/TestSuiteWriterRPCTest.kt @@ -67,7 +67,8 @@ class TestSuiteWriterRPCTest{ ), config.outputFilePrefix, config.outputFileSuffix, - Termination.NONE + Termination.NONE, + listOf() ) @@ -100,4 +101,4 @@ class TestSuiteWriterRPCTest{ assertTrue(testContent.contains("controller.mockRPCExternalServicesWithCustomizedHandling(controller.readFileAsStringFromTestResource(\"test_0_MockedResponseInfo_${it}.json\"),true)")) } } -} \ No newline at end of file +} diff --git a/core/src/test/kotlin/org/evomaster/core/output/service/TestSuiteWriterTest.kt b/core/src/test/kotlin/org/evomaster/core/output/service/TestSuiteWriterTest.kt index f2f37c37c5..a1bcfcf108 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/service/TestSuiteWriterTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/service/TestSuiteWriterTest.kt @@ -55,7 +55,8 @@ class TestSuiteWriterTest{ mutableListOf(), config.outputFilePrefix, config.outputFileSuffix, - Termination.NONE + Termination.NONE, + listOf() ) @@ -101,4 +102,4 @@ class TestSuiteWriterTest{ assertTrue(methods.any { it.name == "tearDown" }) assertTrue(methods.any { it.name == "initTest" }) } -} \ No newline at end of file +} From b631e174945617b42f6728d2d538732d8def4d83 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Thu, 15 Jun 2023 23:51:29 +0200 Subject: [PATCH 082/345] save targets covered by seeded tests in Statistics --- .../org/evomaster/core/search/service/Statistics.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Statistics.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Statistics.kt index 444ffa358c..c243191818 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Statistics.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Statistics.kt @@ -247,6 +247,13 @@ class Statistics : SearchListener { add(Pair("searchTimeCoveredLines", "${linesInfo.searchTime}")) add(Pair("searchTimeCoveredBranches", "${branchesInfo.searchTime}")) + // statistic info with seeded tests + add(Pair("notExecutedSeededTests", "${sampler?.numberOfNotExecutedSeededIndividuals()?:0}")) + add(Pair("seedingTimeCoveredTargets", "${targetsInfo.seedingTime}")) + add(Pair("seedingTimeCoveredLines", "${linesInfo.seedingTime}")) + add(Pair("seedingTimeCoveredBranches", "${branchesInfo.seedingTime}")) + + // statistic info for extractedSpecifiedDtos add(Pair("numOfExtractedSpecifiedDtos", "${unitsInfo?.extractedSpecifiedDtos?.size?:0}")) @@ -378,4 +385,4 @@ class Statistics : SearchListener { Files.write(path, content) } -} \ No newline at end of file +} From 62bd8205509a7fbab522275ac56a5c63ea7958e6 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Fri, 16 Jun 2023 00:18:28 +0200 Subject: [PATCH 083/345] export targets by distinigiushing from seeded tests --- .../evomaster/core/search/service/Archive.kt | 10 ++++- .../core/search/service/Statistics.kt | 38 ++++++++++++------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt index e25b07dc13..5371091b4c 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Archive.kt @@ -115,6 +115,8 @@ class Archive where T : Individual { ) } + fun anyTargetsCoveredSeededTests () = coveredStatisticsBySeededTests != null && coveredStatisticsBySeededTests!!.coveredTargets.isNotEmpty() + fun getCopyOfUniqueCoveringIndividuals() : List{ return getUniquePopulation().map { it.individual } @@ -579,11 +581,15 @@ class Archive where T : Individual { /** * @return a list of pairs which is composed of target id (first) and corresponding tests (second) */ - fun exportCoveredTargetsAsPair(solution: Solution<*>) : List>>{ + fun exportCoveredTargetsAsPair(solution: Solution<*>, includeTargetsCoveredBySeededTests: Boolean? = null) : List>>{ return populations.keys .asSequence() - .filter { isCovered(it) } + .filter { + isCovered(it) && (includeTargetsCoveredBySeededTests == null + || (coveredStatisticsBySeededTests==null) + || (if (includeTargetsCoveredBySeededTests) coveredStatisticsBySeededTests!!.coveredTargets.contains(it) else !coveredStatisticsBySeededTests!!.coveredTargets.contains(it))) + } .map { t-> Pair(idMapper.getDescriptiveId(t), solution.individuals.mapIndexed { index, f-> if (f.fitness.doesCover(t)) index else -1 }.filter { it != -1 }) }.toList() diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Statistics.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Statistics.kt index c243191818..99e70e66cf 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Statistics.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Statistics.kt @@ -352,9 +352,32 @@ class Statistics : SearchListener { if (path.parent != null) Files.createDirectories(path.parent) if (Files.exists(path)) log.info("The existing file on ${config.coveredTargetFile} is going to be replaced") - val info = archive.exportCoveredTargetsAsPair(solution) val separator = "," // for csv format + val content = mutableListOf() + + if (archive.anyTargetsCoveredSeededTests()){ + content.addAll(getPrintContentForCoveredTargets(archive.exportCoveredTargetsAsPair(solution, true), separator, format)) + content.add(System.lineSeparator()) + content.add(System.lineSeparator()) + content.addAll(getPrintContentForCoveredTargets(archive.exportCoveredTargetsAsPair(solution, false), separator, format)) + }else{ + content.addAll(getPrintContentForCoveredTargets(archive.exportCoveredTargetsAsPair(solution), separator, format)) + } + + + // append boot-time targets + if(!config.blackBox || config.bbExperiments) { + remoteController?.getSutInfo()?.bootTimeInfoDto?.targets?.map { it.descriptiveId }?.sorted()?.apply { + if (isNotEmpty()){ + content.add(System.lineSeparator()) + content.addAll(this) + } + } + } + Files.write(path, content) + } + private fun getPrintContentForCoveredTargets(info: List>>, separator: String, format : EMConfig.SortCoveredTargetBy) : MutableList{ val content = mutableListOf() when(format){ EMConfig.SortCoveredTargetBy.NAME ->{ @@ -372,17 +395,6 @@ class Statistics : SearchListener { } } } - - // append boot-time targets - if(!config.blackBox || config.bbExperiments) { - remoteController?.getSutInfo()?.bootTimeInfoDto?.targets?.map { it.descriptiveId }?.sorted()?.apply { - if (isNotEmpty()){ - content.add(System.lineSeparator()) - content.addAll(this) - } - } - } - Files.write(path, content) + return content } - } From 093d5f999358caa09175ad80b0769af9465ef15b Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Fri, 16 Jun 2023 10:36:22 +0200 Subject: [PATCH 084/345] fix failing test --- .../spring/openapi/v3/statistics/StatisticsEMTest.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/e2e-tests/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/statistics/StatisticsEMTest.kt b/e2e-tests/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/statistics/StatisticsEMTest.kt index cebe064ba0..2d2becdf81 100644 --- a/e2e-tests/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/statistics/StatisticsEMTest.kt +++ b/e2e-tests/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/statistics/StatisticsEMTest.kt @@ -51,18 +51,20 @@ class StatisticsEMTest : SpringTestBase() { listOf("coveredTargets", "coveredLines", "coveredBranches").forEach { key-> data.filter { p-> p.header.endsWith(key, ignoreCase = true) }.apply { - assertEquals(3, size) + assertEquals(4, size) val bootTime = find { t-> t.header.startsWith("bootTime") }?.element?.toInt() val searchTime = find { t-> t.header.startsWith("searchTime") }?.element?.toInt() + val seedingTime = find { t-> t.header.startsWith("seedingTime") }?.element?.toInt() val total = find { t-> t.header.startsWith("covered") }?.element?.toInt() assertNotNull(bootTime) assertNotNull(searchTime) assertNotNull(total) + assertNotNull(seedingTime) assertTrue(bootTime!! > 0) assertTrue(searchTime!! > 0) - assertEquals(total!!, bootTime+searchTime) + assertEquals(total!!, bootTime+searchTime+seedingTime!!) } } } } -} \ No newline at end of file +} From fdadf3f061a348d9eced8d8c727465b8b8603284 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Fri, 16 Jun 2023 12:51:35 +0200 Subject: [PATCH 085/345] fix inconsistent info due to minimization phase --- .../org/evomaster/core/search/FitnessValue.kt | 33 ++++++++++++------- .../core/search/service/Statistics.kt | 11 +++++-- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt b/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt index 05dbde6b2c..f2363096a8 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt @@ -181,12 +181,22 @@ class FitnessValue( coveredTargetsDuringSeeding.addAll(coveredTargets) } - fun coveredTargetsDuringSeeding() : Int{ - return coveredTargetsDuringSeeding.size + private fun coveredTargetsDuringSeeding() : Int{ + return coveredTargetsDuringSeeding.filter { + /* + Due to minimize phase, then need to ensure that coveredTargetsDuringSeeding is part of targets + */ + targets.containsKey(it) && targets[it]!!.distance == MAX_VALUE + }.size } fun coveredTargetsDuringSeeding(prefix: String, idMapper: IdMapper) : Int{ return coveredTargetsDuringSeeding - .count { idMapper.getDescriptiveId(it).startsWith(prefix) } + .count { + /* + Due to minimize phase, then need to ensure that coveredTargetsDuringSeeding is part of targets + */ + targets.containsKey(it) && targets[it]!!.distance == MAX_VALUE + && idMapper.getDescriptiveId(it).startsWith(prefix) } } /** @@ -212,19 +222,20 @@ class FitnessValue( // counter for duplicated targets var duplicatedcounter = 0 - val seedingTime = coveredTargetsDuringSeeding.count { s -> - ((prefix == null || idMapper.getDescriptiveId(s).startsWith(prefix))).apply { - if (this && bootTime.any { it.descriptiveId == idMapper.getDescriptiveId(s) }) - duplicatedcounter++ - } - } + var seedingTime = 0 + var searchTime = 0 - val searchTime = targets.entries.count { e -> + targets.entries.forEach { e -> (e.value.distance == MAX_VALUE && (prefix == null || idMapper.getDescriptiveId(e.key).startsWith(prefix))).apply { + if (coveredTargetsDuringSeeding.contains(e.key)) + seedingTime++ + else + searchTime++ if (this && bootTime.any { it.descriptiveId == idMapper.getDescriptiveId(e.key) }) duplicatedcounter++ } - } - seedingTime + } + /* related to task https://trello.com/c/EoWcV6KX/810-issue-with-assertion-checks-in-e2e diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Statistics.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Statistics.kt index 99e70e66cf..a0a92e4817 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Statistics.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Statistics.kt @@ -355,6 +355,15 @@ class Statistics : SearchListener { val separator = "," // for csv format val content = mutableListOf() + when(format){ + EMConfig.SortCoveredTargetBy.NAME ->{ + content.add(DESCRIPTION_TARGET) + } + EMConfig.SortCoveredTargetBy.TEST ->{ + content.add(listOf(TEST_INDEX, DESCRIPTION_TARGET).joinToString(separator)) + } + } + if (archive.anyTargetsCoveredSeededTests()){ content.addAll(getPrintContentForCoveredTargets(archive.exportCoveredTargetsAsPair(solution, true), separator, format)) content.add(System.lineSeparator()) @@ -381,11 +390,9 @@ class Statistics : SearchListener { val content = mutableListOf() when(format){ EMConfig.SortCoveredTargetBy.NAME ->{ - content.add(DESCRIPTION_TARGET) content.addAll(info.map { it.first }.sorted()) } EMConfig.SortCoveredTargetBy.TEST ->{ - content.add(listOf(TEST_INDEX, DESCRIPTION_TARGET).joinToString(separator)) info.flatMap { it.second }.sorted().forEach {test-> /* currently, only index of tests are outputted. From c3e54be8dc397c85b9de8f43f1c91bbf14e8d0cf Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Fri, 16 Jun 2023 12:51:43 +0200 Subject: [PATCH 086/345] add tests --- .../TestabilityWithSeedTestEMTest.java | 64 +++++++++++++++++-- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/org/evomaster/e2etests/spring/rpc/examples/testability/TestabilityWithSeedTestEMTest.java b/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/org/evomaster/e2etests/spring/rpc/examples/testability/TestabilityWithSeedTestEMTest.java index 789a2e2e78..76ff17f8c2 100644 --- a/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/org/evomaster/e2etests/spring/rpc/examples/testability/TestabilityWithSeedTestEMTest.java +++ b/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/org/evomaster/e2etests/spring/rpc/examples/testability/TestabilityWithSeedTestEMTest.java @@ -1,23 +1,28 @@ package org.evomaster.e2etests.spring.rpc.examples.testability; -import com.foo.rpc.examples.spring.testability.TestabilityController; import com.foo.rpc.examples.spring.testability.TestabilityService; import com.foo.rpc.examples.spring.testability.TestabilityWithSeedTestController; import org.evomaster.core.problem.rpc.RPCCallResultCategory; import org.evomaster.core.problem.rpc.RPCIndividual; import org.evomaster.core.search.Solution; +import org.evomaster.core.search.service.Statistics; import org.evomaster.e2etests.spring.rpc.examples.SpringRPCTestBase; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; public class TestabilityWithSeedTestEMTest extends SpringRPCTestBase { - + private final String targetFile = "target/covered-targets/TestabilityWithSeedTestTargets.txt"; @BeforeAll public static void initClass() throws Exception { SpringRPCTestBase.initClass(new TestabilityWithSeedTestController()); @@ -38,13 +43,64 @@ public void testRunEM() throws Throwable { args.add("--seedTestCases"); args.add("true"); + args.add("--exportCoveredTarget"); + args.add("true"); + args.add("--coveredTargetFile"); + args.add(targetFile); + Solution solution = initAndRun(args); assertTrue(solution.getIndividuals().size() >= 1); + checkStatistics(solution); + assertRPCEndpointResult(solution, TestabilityService.Iface.class.getName()+":getSeparated", RPCCallResultCategory.HANDLED.name()); assertAllContentInResponseForEndpoint(solution,TestabilityService.Iface.class.getName()+":getSeparated" , Arrays.asList("ERROR", "OK")); + + checkSeedingTarget(); }); } + + private void checkStatistics(Solution solution){ + List data = solution.getStatistics(); + + for(String key : Arrays.asList("coveredTargets", "coveredLines", "coveredBranches")){ + List dataByKey = data.stream().filter(s-> s.getHeader().toLowerCase().endsWith(key.toLowerCase())).collect(Collectors.toList()); + assertEquals(4, dataByKey.size()); + int bootTime = Integer.parseInt(dataByKey.stream().filter(s-> s.getHeader().startsWith("bootTime")).findFirst().get().getElement()); + int seedingTime = Integer.parseInt(dataByKey.stream().filter(s-> s.getHeader().startsWith("seedingTime")).findFirst().get().getElement()); + int searchTime = Integer.parseInt(dataByKey.stream().filter(s-> s.getHeader().startsWith("searchTime")).findFirst().get().getElement()); + int total = Integer.parseInt(dataByKey.stream().filter(s-> s.getHeader().startsWith("covered")).findFirst().get().getElement()); + assertTrue(bootTime >= 0); + assertTrue(seedingTime > 0); + assertTrue(searchTime > 0); + assertTrue(total > 0); + assertEquals(total, bootTime + seedingTime + searchTime); + } + + } + + private void checkSeedingTarget(){ + Path path = Paths.get(targetFile); + assertTrue(path.toFile().exists()); + + List targets = null; + List expectedTargets = Arrays.asList( + "Branch_at_com.foo.rpc.examples.spring.testability.TestabilityServiceImp_at_line_00019_position_0_trueBranch", + "Branch_at_com.foo.rpc.examples.spring.testability.TestabilityServiceImp_at_line_00019_position_1_trueBranch", + "Branch_at_com.foo.rpc.examples.spring.testability.TestabilityServiceImp_at_line_00019_position_2_trueBranch", + "Line_at_com.foo.rpc.examples.spring.testability.TestabilityServiceImp_00020" + ); + try { + targets = Files.readAllLines(path); + int first = targets.indexOf(""); + List seedingTimeTargets = targets.subList(0, first); + assertFalse(seedingTimeTargets.isEmpty()); + assertTrue(seedingTimeTargets.containsAll(expectedTargets)); + } catch (IOException e) { + throw new RuntimeException(e); + } + + } } From 4fd6b880e4f872c49fb1c967ff1ba45767a0e9e4 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Fri, 16 Jun 2023 14:55:23 +0200 Subject: [PATCH 087/345] handle driver --- .../java/controller/api/dto/SutRunDto.java | 12 ++++++------ .../client/java/controller/SutHandler.java | 12 +++++++----- .../controller/internal/EMController.java | 4 ++-- .../controller/internal/SutController.java | 19 +++++++++++++++++-- 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutRunDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutRunDto.java index 90af1e93f9..d1cd77bedc 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutRunDto.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/SutRunDto.java @@ -13,12 +13,12 @@ public class SutRunDto { public Boolean resetState; /** - * Whether to reset the mocked external services with customized method + * Whether to reset the mock object with customized method * * this depends on the configuration from core side, * ie, whether to apply customized method to handle external services */ - public Boolean resetMockedExternalServicesWithCustomizedMethod; + public Boolean resetCustomizedMethodForMockObject; /** * Whether SQL heuristics should be computed. @@ -44,18 +44,18 @@ public class SutRunDto { public SutRunDto() { } - public SutRunDto(Boolean run, Boolean resetState, Boolean resetMockedExternalServicesWithCustomizedMethod, Boolean calculateSqlHeuristics, Boolean extractSqlExecutionInfo, String methodReplacementCategories) { + public SutRunDto(Boolean run, Boolean resetState, Boolean resetCustomizedMethodForMockObject, Boolean calculateSqlHeuristics, Boolean extractSqlExecutionInfo, String methodReplacementCategories) { if (calculateSqlHeuristics != null && calculateSqlHeuristics && extractSqlExecutionInfo != null && !extractSqlExecutionInfo) throw new IllegalArgumentException("extractSqlExecutionInfo should be enabled when calculateSqlHeuristics is enabled"); this.run = run; this.resetState = resetState; - this.resetMockedExternalServicesWithCustomizedMethod = resetMockedExternalServicesWithCustomizedMethod; + this.resetCustomizedMethodForMockObject = resetCustomizedMethodForMockObject; this.calculateSqlHeuristics = calculateSqlHeuristics; this.extractSqlExecutionInfo = extractSqlExecutionInfo; this.methodReplacementCategories = methodReplacementCategories; } - public SutRunDto(Boolean run, Boolean resetState, Boolean resetMockedExternalServicesWithCustomizedMethod, Boolean calculateSqlHeuristics, String methodReplacementCategories){ - this(run, resetState, resetMockedExternalServicesWithCustomizedMethod, calculateSqlHeuristics, calculateSqlHeuristics!=null && calculateSqlHeuristics, methodReplacementCategories); + public SutRunDto(Boolean run, Boolean resetState, Boolean resetCustomizedMethodForMockObject, Boolean calculateSqlHeuristics, String methodReplacementCategories){ + this(run, resetState, resetCustomizedMethodForMockObject, calculateSqlHeuristics, calculateSqlHeuristics!=null && calculateSqlHeuristics, methodReplacementCategories); } } diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/SutHandler.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/SutHandler.java index 5b55a9cb70..d8e30124e5 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/SutHandler.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/SutHandler.java @@ -3,12 +3,8 @@ import org.evomaster.client.java.controller.api.dto.database.operations.InsertionDto; import org.evomaster.client.java.controller.api.dto.database.operations.InsertionResultsDto; import org.evomaster.client.java.controller.db.DbCleaner; -import org.evomaster.client.java.controller.db.SqlScriptRunner; -import org.evomaster.client.java.controller.db.SqlScriptRunnerCached; import org.evomaster.client.java.controller.internal.db.DbSpecification; -import java.sql.SQLException; -import java.util.ArrayList; import java.util.List; /** @@ -190,7 +186,7 @@ default void resetDatabase(List tablesToClean){} * a method to reset mocked external services with customized method *

*/ - default boolean resetMockedExternalServicesWithCustomizedMethod(){ + default boolean resetCustomizedMethodForMockObject(){ return false; } @@ -208,4 +204,10 @@ default boolean mockRPCExternalServicesWithCustomizedHandling(String externalSer return false; } + + + default boolean mockDatabasesWithCustomizedHandling(String mockDatabaseObjectDtos, boolean enabled){ + return false; + } + } diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java index 519ceac971..0fb1b46ae8 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java @@ -381,8 +381,8 @@ public Response runSut(SutRunDto dto, @Context HttpServletRequest httpServletReq // clean db with accessed tables noKillSwitchForceCheck(() -> sutController.cleanAccessedTables()); - if (dto.resetMockedExternalServicesWithCustomizedMethod != null && dto.resetMockedExternalServicesWithCustomizedMethod){ - noKillSwitch(()-> sutController.resetMockedExternalServicesWithCustomizedMethod()); + if (dto.resetCustomizedMethodForMockObject != null && dto.resetCustomizedMethodForMockObject){ + noKillSwitch(()-> sutController.resetCustomizedMethodForMockObject()); } /* diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index c35cbf32c4..e70deba454 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -1281,9 +1281,11 @@ public String packagesToSkipInstrumentation(){ *

*/ @Override - public final boolean resetMockedExternalServicesWithCustomizedMethod(){ + public final boolean resetCustomizedMethodForMockObject(){ if (getProblemInfo() instanceof RPCProblem){ - return mockRPCExternalServicesWithCustomizedHandling(null, false); + boolean ok = mockRPCExternalServicesWithCustomizedHandling(null, false); + ok = ok && mockDatabasesWithCustomizedHandling(null, false); + return ok; } return false; } @@ -1311,6 +1313,19 @@ public final boolean mockRPCExternalServicesWithCustomizedHandling(String extern return customizeMockingRPCExternalService(exDto, enabled); } + @Override + public boolean mockDatabasesWithCustomizedHandling(String mockDatabaseObjectDtos, boolean enabled) { + List mockDbObject = null; + try { + if (mockDatabaseObjectDtos != null && !mockDatabaseObjectDtos.isEmpty()) { + mockDbObject = objectMapper.readValue(mockDatabaseObjectDtos, new TypeReference>(){}); + } + } catch (JsonProcessingException e) { + throw new RuntimeException("Fail to handle the given mock object for database with the info:", e); + } + return customizeMockingDatabase(mockDbObject, enabled); + } + /** * * @param fileName the name of file which exist in the same directory of the class From cbed6be1038ccdf5497ee5056759a15593d00229 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Fri, 16 Jun 2023 14:55:35 +0200 Subject: [PATCH 088/345] add unit test --- .../core/output/service/RPCTestCaseWriter.kt | 79 +++++++++++++++---- .../core/output/service/TestSuiteWriter.kt | 7 +- .../rpc/service/RPCEndpointsHandler.kt | 11 +++ .../core/output/EvaluatedIndividualBuilder.kt | 20 +++++ .../output/service/TestSuiteWriterRPCTest.kt | 14 +++- 5 files changed, 112 insertions(+), 19 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/RPCTestCaseWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/RPCTestCaseWriter.kt index 5eeabd63c2..c250863209 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/RPCTestCaseWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/RPCTestCaseWriter.kt @@ -5,6 +5,7 @@ import org.evomaster.core.output.Lines import org.evomaster.core.output.TestCase import org.evomaster.core.output.formatter.OutputFormatter import org.evomaster.core.problem.enterprise.EnterpriseActionGroup +import org.evomaster.core.problem.externalservice.rpc.DbAsExternalServiceAction import org.evomaster.core.problem.externalservice.rpc.RPCExternalServiceAction import org.evomaster.core.problem.rpc.RPCCallAction import org.evomaster.core.problem.rpc.RPCCallResult @@ -33,6 +34,12 @@ class RPCTestCaseWriter : ApiTestCaseWriter() { * name of method for customizing handling of external services for RPC problem */ const val CUSTOMIZED_EXTERNAL_SERVICES = "mockRPCExternalServicesWithCustomizedHandling" + + /** + * name of method for customizing handling of mocking database + * note that it is only for RPC problem now + */ + const val CUSTOMIZED_MOCK_DATABASE_OBJECTS = "mockDatabasesWithCustomizedHandling" } @Inject @@ -71,6 +78,7 @@ class RPCTestCaseWriter : ApiTestCaseWriter() { // generate actions for handling external services with customized methods handleCustomizedExternalServiceHandling(action, index, testCaseName, true, lines, testSuitePath) + handleCustomizedMockDatabaseHandling(action, index, testCaseName, true, lines, testSuitePath) val resVarName = createUniqueResponseVariableName() @@ -90,6 +98,12 @@ class RPCTestCaseWriter : ApiTestCaseWriter() { } } } + + lines.addEmpty() + + // reset mock object if any + handleCustomizedExternalServiceHandling(action, index, testCaseName, false, lines, testSuitePath) + handleCustomizedMockDatabaseHandling(action, index, testCaseName, false, lines, testSuitePath) } private fun handleAssertNull(lines: Lines, resVarName: String){ @@ -183,8 +197,8 @@ class RPCTestCaseWriter : ApiTestCaseWriter() { } - private fun saveJsonAndPrintReadJson(testCaseName: String, actionIndex: Int, json: String, lines: Lines){ - val fileName = getFileNameToSaveMockedResponsesDtoAsJson(testCaseName, actionIndex) + private fun saveJsonAndPrintReadJson(testCaseName: String, actionIndex: Int, json: String, lines: Lines, infoTag: String){ + val fileName = getFileNameToSaveMockedResponsesDtoAsJson(testCaseName, actionIndex, infoTag) val body = if (OutputFormatter.JSON_FORMATTER.isValid(json)) { OutputFormatter.JSON_FORMATTER.getFormatted(json) @@ -199,7 +213,7 @@ class RPCTestCaseWriter : ApiTestCaseWriter() { lines.append("${TestSuiteWriter.controller}.readFileAsStringFromTestResource(\"$fileName\")") } - private fun getFileNameToSaveMockedResponsesDtoAsJson(testCaseName: String, actionIndex: Int) = "${testCaseName}_MockedResponseInfo_$actionIndex.json" + private fun getFileNameToSaveMockedResponsesDtoAsJson(testCaseName: String, actionIndex: Int, infoTag: String) = "${testCaseName}_${infoTag}_$actionIndex.json" private fun printExecutionJson(json: String, lines: Lines) { @@ -272,7 +286,7 @@ class RPCTestCaseWriter : ApiTestCaseWriter() { * @param enable a configuration to enable/disable specified mocking configuration * @param lines are generated lines which save the generated test scripts */ - fun handleCustomizedExternalServiceHandling(action: Action, index: Int, testCaseName: String, enable: Boolean, lines: Lines, testSuitePath: Path?){ + private fun handleCustomizedExternalServiceHandling(action: Action, index: Int, testCaseName: String, enable: Boolean, lines: Lines, testSuitePath: Path?){ if(config.enableCustomizedExternalServiceHandling && action.parent is EnterpriseActionGroup){ val group = action.parent as EnterpriseActionGroup @@ -290,24 +304,55 @@ class RPCTestCaseWriter : ApiTestCaseWriter() { format.isJava() -> lines.add("${TestSuiteWriter.controller}.$CUSTOMIZED_EXTERNAL_SERVICES(") } - val mockedConfigAsJson = rpcHandler.getJsonStringFromDto(exActions) + printJsonForMockObject(exActions, testCaseName, index, lines, enable, infoTag = "MockExternalServiceObjectInfo") + } - if (config.saveMockedResponseAsSeparatedFile){ - if (config.testResourcePathToSaveMockedResponse.isBlank()) - throw IllegalArgumentException("testResourcePathToSaveMockedResponse cannot be empty if it is required to save mocked responses in separated files") - saveJsonAndPrintReadJson(testCaseName,index,mockedConfigAsJson, lines) - }else - printExecutionJson(mockedConfigAsJson, lines) + } + } + + /** + * handle generation of customized external service handling + * @param action is the call to be generated + * @param index the index of action + * @param testCaseName the test which contains the action [action] + * @param enable a configuration to enable/disable specified mocking configuration + * @param lines are generated lines which save the generated test scripts + */ + private fun handleCustomizedMockDatabaseHandling(action: Action, index: Int, testCaseName: String, enable: Boolean, lines: Lines, testSuitePath: Path?){ + if(config.enableCustomizedExternalServiceHandling && action.parent is EnterpriseActionGroup){ + val group = action.parent as EnterpriseActionGroup + + val mockDbActions = group.getExternalServiceActions() + .filterIsInstance() + .map { rpcHandler.transformMockDatabaseDto(it) } + if (mockDbActions.isNotEmpty()){ when { - format.isKotlin() -> lines.append(",$enable)") - format.isJava() -> lines.append(",$enable);") + format.isKotlin() -> lines.add("${TestSuiteWriter.controller}.$CUSTOMIZED_MOCK_DATABASE_OBJECTS(") + format.isJava() -> lines.add("${TestSuiteWriter.controller}.$CUSTOMIZED_MOCK_DATABASE_OBJECTS(") } + + printJsonForMockObject(mockDbActions, testCaseName, index, lines, enable, infoTag = "MockDatabaseObjectInfo") } } } + private fun printJsonForMockObject(mockObj : Any, testCaseName: String, index: Int, lines: Lines, enable: Boolean, infoTag: String){ + val mockedConfigAsJson = rpcHandler.getJsonStringFromDto(mockObj) + + if (config.saveMockedResponseAsSeparatedFile){ + if (config.testResourcePathToSaveMockedResponse.isBlank()) + throw IllegalArgumentException("testResourcePathToSaveMockedResponse cannot be empty if it is required to save mocked responses in separated files") + saveJsonAndPrintReadJson(testCaseName,index,mockedConfigAsJson, lines, infoTag) + }else + printExecutionJson(mockedConfigAsJson, lines) + + when { + format.isKotlin() -> lines.append(",$enable)") + format.isJava() -> lines.append(",$enable);") + } + } fun resetExternalServicesWithCustomizedMethod() : String { if (!format.isJavaOrKotlin()) @@ -315,6 +360,12 @@ class RPCTestCaseWriter : ApiTestCaseWriter() { return "${TestSuiteWriter.controller}.$CUSTOMIZED_EXTERNAL_SERVICES(null,false)${if (format.isJava()) ";" else ""}" } + fun resetMockDatabaseObjectWithCustomizedMethod() : String { + if (!format.isJavaOrKotlin()) + throw IllegalStateException("Only support to generate Java/Kotlin tests with a reset of RPC external services with customized method") + return "${TestSuiteWriter.controller}.$CUSTOMIZED_MOCK_DATABASE_OBJECTS(null,false)${if (format.isJava()) ";" else ""}" + } + /* the inner class in java could be represented with $ in string format for instance org.thrift.ncs.client.NcsService$Client, @@ -332,4 +383,4 @@ class RPCTestCaseWriter : ApiTestCaseWriter() { } } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt index a14c39516a..2a3ce560c9 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt @@ -799,8 +799,11 @@ class TestSuiteWriter { } } - if (config.enableCustomizedExternalServiceHandling && testCaseWriter is RPCTestCaseWriter) + if (config.enableCustomizedExternalServiceHandling && testCaseWriter is RPCTestCaseWriter){ lines.add((testCaseWriter as RPCTestCaseWriter).resetExternalServicesWithCustomizedMethod()) + lines.add((testCaseWriter as RPCTestCaseWriter).resetMockDatabaseObjectWithCustomizedMethod()) + } + } else if (format.isCsharp()) { addStatement("$fixture = fixture", lines) @@ -941,4 +944,4 @@ class TestSuiteWriter { .distinctBy { it.getSignature() }.toList() } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt index d71caae401..cc1e208b58 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt @@ -359,6 +359,17 @@ class RPCEndpointsHandler { } } + fun transformMockDatabaseDto(action: DbAsExternalServiceAction) : MockDatabaseDto{ + val mode = if (action.response.isJson()) GeneUtils.EscapeMode.JSON else throw IllegalStateException("only support response with json type for the monument") + return MockDatabaseDto().apply { + appKey = action.descriptiveInfo + commandName = action.commandName + requests = action.requestRuleIdentifier + response = action.response.responseBody.getValueAsPrintableString(mode = mode) + responseFullType = if ((action.response as? ClassResponseParam)?.className?.isNotBlank() == true) (action.response as ClassResponseParam).className else null + } + } + private fun setAuthInfo(infoDto: SutInfoDto){ infoDto.infoForAuthentication?:return diff --git a/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt b/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt index 6b4a1ef55d..0a6763988e 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt @@ -5,6 +5,7 @@ import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionResult import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.externalservice.ApiExternalServiceAction +import org.evomaster.core.problem.externalservice.rpc.DbAsExternalServiceAction import org.evomaster.core.problem.externalservice.rpc.RPCExternalServiceAction import org.evomaster.core.problem.externalservice.rpc.parm.ClassResponseParam import org.evomaster.core.problem.rest.* @@ -100,6 +101,25 @@ class EvaluatedIndividualBuilder { } } + fun buildFakeDbExternalServiceAction(n : Int): List{ + return (0 until n).map { + + DbAsExternalServiceAction( + descriptiveInfo = "FakeDB_bar", + commandName = "bar", + requestRuleIdentifier = null, + responseParam = ClassResponseParam( + className = "FakeDbReturnDto", + responseType = EnumGene("responseType", listOf("JSON")), + response = OptionalGene("return", + ObjectGene("return", fields = listOf(StringGene("fakeMsg", "This is a fake response from a RPC-based external service"))) ) + ), + active = true, + used = true + ) + } + } + fun buildEvaluatedRPCIndividual( actions: MutableList, externalServicesActions: MutableList>, diff --git a/core/src/test/kotlin/org/evomaster/core/output/service/TestSuiteWriterRPCTest.kt b/core/src/test/kotlin/org/evomaster/core/output/service/TestSuiteWriterRPCTest.kt index 581d457299..62197e9c29 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/service/TestSuiteWriterRPCTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/service/TestSuiteWriterRPCTest.kt @@ -60,8 +60,9 @@ class TestSuiteWriterRPCTest{ EvaluatedIndividualBuilder.buildEvaluatedRPCIndividual( actions = EvaluatedIndividualBuilder.buildFakeRPCAction(expectedJson), externalServicesActions = (0 until expectedJson).map { - EvaluatedIndividualBuilder.buildFakeRPCExternalServiceAction(1) + EvaluatedIndividualBuilder.buildFakeDbExternalServiceAction(1).plus(EvaluatedIndividualBuilder.buildFakeRPCExternalServiceAction(1)) }.toMutableList(), + format = OutputFormat.KOTLIN_JUNIT_5 ) ), @@ -86,9 +87,10 @@ class TestSuiteWriterRPCTest{ val generatedResource = Paths.get(config.testResourcePathToSaveMockedResponse) assertTrue(Files.isDirectory(generatedResource)) assertTrue(Files.exists(generatedResource)) - assertEquals(expectedJson, Files.list(generatedResource).count().toInt()) + assertEquals(expectedJson * 2, Files.list(generatedResource).count().toInt()) val expectedExReset = "controller.mockRPCExternalServicesWithCustomizedHandling(null,false)" + val expectedMockDbReset = "controller.mockDatabasesWithCustomizedHandling(null,false)" /* here, we only check the generated in text @@ -97,8 +99,14 @@ class TestSuiteWriterRPCTest{ */ val testContent = String(Files.readAllBytes(generatedTest)) assertTrue(testContent.contains(expectedExReset)) + assertTrue(testContent.contains(expectedMockDbReset)) + (0 until 5).forEach { - assertTrue(testContent.contains("controller.mockRPCExternalServicesWithCustomizedHandling(controller.readFileAsStringFromTestResource(\"test_0_MockedResponseInfo_${it}.json\"),true)")) + assertTrue(testContent.contains("controller.mockRPCExternalServicesWithCustomizedHandling(controller.readFileAsStringFromTestResource(\"test_0_MockExternalServiceObjectInfo_${it}.json\"),true)")) + assertTrue(testContent.contains("controller.mockRPCExternalServicesWithCustomizedHandling(controller.readFileAsStringFromTestResource(\"test_0_MockExternalServiceObjectInfo_${it}.json\"),false)")) + + assertTrue(testContent.contains("controller.mockDatabasesWithCustomizedHandling(controller.readFileAsStringFromTestResource(\"test_0_MockDatabaseObjectInfo_${it}.json\"),true)")) + assertTrue(testContent.contains("controller.mockDatabasesWithCustomizedHandling(controller.readFileAsStringFromTestResource(\"test_0_MockDatabaseObjectInfo_${it}.json\"),false)")) } } } From a75f15a57ecbc5f13ce64c31b758a196edd926a8 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Fri, 16 Jun 2023 15:57:52 +0200 Subject: [PATCH 089/345] handle db external --- .../problem/rpc/service/RPCEndpointsHandler.kt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt index cc1e208b58..5e64c0976f 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt @@ -605,9 +605,21 @@ class RPCEndpointsHandler { val exActions = (action.parent as EnterpriseActionGroup) .groupsView()!!.getAllInGroup(GroupsOfChildren.EXTERNAL_SERVICES) .flatMap { (it as ActionComponent).flatten() } - .map { e-> transformMockRPCExternalServiceDto((e as ApiExternalServiceAction)) } + .filterIsInstance() + .map { e-> + transformMockRPCExternalServiceDto(e) + } + val mockDbActions = (action.parent as EnterpriseActionGroup) + .groupsView()!!.getAllInGroup(GroupsOfChildren.EXTERNAL_SERVICES) + .flatMap { (it as ActionComponent).flatten() } + .filterIsInstance() + .map { e-> + transformMockDatabaseDto(e) + } if (exActions.isNotEmpty()) rpcAction.mockRPCExternalServiceDtos = exActions + if (mockDbActions.isNotEmpty()) + rpcAction.mockDatabaseDtos = mockDbActions } return rpcAction From 5164c0aafcdd13beb9f2de4180981561d074c9cb Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Fri, 16 Jun 2023 19:38:23 +0200 Subject: [PATCH 090/345] fix a bug --- .../core/problem/rpc/service/RPCEndpointsHandler.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt index 5e64c0976f..ce345c39ef 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt @@ -187,7 +187,7 @@ class RPCEndpointsHandler { val ex = rpcActionDto.mockRPCExternalServiceDtos.map { e-> e.responses.mapIndexed { index, r-> val exAction = seededExternalServiceCluster[ - RPCExternalServiceAction.getRPCExternalServiceActionName(e.interfaceFullName, e.functionName, e.requestRules[index], e.responseTypes[index]) + RPCExternalServiceAction.getRPCExternalServiceActionName(e.interfaceFullName, e.functionName, e.requestRules?.get(index), e.responseTypes[index]) ]!!.copy() as ApiExternalServiceAction try { setGeneBasedOnString(exAction.response.responseBody, r) @@ -256,12 +256,13 @@ class RPCEndpointsHandler { rpcActionDto.mockRPCExternalServiceDtos?.forEach { dto-> - if (dto.requestRules.isNotEmpty() && dto.requestRules.size != dto.responses.size && dto.responses.size != dto.responseTypes.size) + if (dto.requestRules!=null && dto.requestRules.isNotEmpty() && dto.requestRules.size != dto.responses.size && dto.responses.size != dto.responseTypes.size) throw IllegalArgumentException("the size of request identifications and responses should same but ${dto.requestRules.size} vs. ${dto.responses.size} vs. ${dto.responseTypes.size}") dto.responseTypes.forEachIndexed { index, s -> + val exkey = RPCExternalServiceAction.getRPCExternalServiceActionName( - dto.interfaceFullName, dto.functionName, dto.requestRules[index], s + dto.interfaceFullName, dto.functionName, dto.requestRules?.get(index), s ) if (!seededExternalServiceCluster.containsKey(exkey)){ val responseTypeClass = interfaceDto.identifiedResponseTypes?.find { it.type.fullTypeName == s } @@ -290,7 +291,7 @@ class RPCEndpointsHandler { functionName = dto.functionName, descriptiveInfo = dto.appKey, inputParamTypes = dto.inputParameterTypes, - requestRuleIdentifier = dto.requestRules[index], + requestRuleIdentifier = dto.requestRules?.get(index), responseParam = response) Lazy.assert { exkey == externalAction.getName() } seededExternalServiceCluster[exkey] = externalAction From 3a136c0c14c6504b92ba0036374bd6cbd2edad43 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Fri, 16 Jun 2023 19:42:24 +0200 Subject: [PATCH 091/345] add e2e --- .../generated/FakeDatabaseRow.java | 566 ++++ .../generated/FakeMockObjectService.java | 2832 +++++++++++++++++ .../generated/FakeRetrieveData.java | 566 ++++ .../impl/FakeMockObjectApp.java | 32 + .../impl/FakeMockObjectServiceImpl.java | 56 + .../resources/schema/fakemockobject.thrift | 25 + 6 files changed, 4077 insertions(+) create mode 100644 e2e-tests/spring-rpc/spring-rpc-thrift/src/main/java/com/foo/rpc/examples/spring/fakemockobject/generated/FakeDatabaseRow.java create mode 100644 e2e-tests/spring-rpc/spring-rpc-thrift/src/main/java/com/foo/rpc/examples/spring/fakemockobject/generated/FakeMockObjectService.java create mode 100644 e2e-tests/spring-rpc/spring-rpc-thrift/src/main/java/com/foo/rpc/examples/spring/fakemockobject/generated/FakeRetrieveData.java create mode 100644 e2e-tests/spring-rpc/spring-rpc-thrift/src/main/java/com/foo/rpc/examples/spring/fakemockobject/impl/FakeMockObjectApp.java create mode 100644 e2e-tests/spring-rpc/spring-rpc-thrift/src/main/java/com/foo/rpc/examples/spring/fakemockobject/impl/FakeMockObjectServiceImpl.java create mode 100644 e2e-tests/spring-rpc/spring-rpc-thrift/src/main/resources/schema/fakemockobject.thrift diff --git a/e2e-tests/spring-rpc/spring-rpc-thrift/src/main/java/com/foo/rpc/examples/spring/fakemockobject/generated/FakeDatabaseRow.java b/e2e-tests/spring-rpc/spring-rpc-thrift/src/main/java/com/foo/rpc/examples/spring/fakemockobject/generated/FakeDatabaseRow.java new file mode 100644 index 0000000000..d22328e56e --- /dev/null +++ b/e2e-tests/spring-rpc/spring-rpc-thrift/src/main/java/com/foo/rpc/examples/spring/fakemockobject/generated/FakeDatabaseRow.java @@ -0,0 +1,566 @@ +/** + * Autogenerated by Thrift Compiler (0.15.0) + * + * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + * @generated + */ +package com.foo.rpc.examples.spring.fakemockobject.generated; + +@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"}) +@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.15.0)", date = "2023-06-16") +public class FakeDatabaseRow implements org.apache.thrift.TBase, java.io.Serializable, Cloneable, Comparable { + private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("FakeDatabaseRow"); + + private static final org.apache.thrift.protocol.TField ID_FIELD_DESC = new org.apache.thrift.protocol.TField("id", org.apache.thrift.protocol.TType.I32, (short)1); + private static final org.apache.thrift.protocol.TField NAME_FIELD_DESC = new org.apache.thrift.protocol.TField("name", org.apache.thrift.protocol.TType.STRING, (short)2); + private static final org.apache.thrift.protocol.TField INFO_FIELD_DESC = new org.apache.thrift.protocol.TField("info", org.apache.thrift.protocol.TType.STRING, (short)3); + + private static final org.apache.thrift.scheme.SchemeFactory STANDARD_SCHEME_FACTORY = new FakeDatabaseRowStandardSchemeFactory(); + private static final org.apache.thrift.scheme.SchemeFactory TUPLE_SCHEME_FACTORY = new FakeDatabaseRowTupleSchemeFactory(); + + public int id; // required + public @org.apache.thrift.annotation.Nullable java.lang.String name; // required + public @org.apache.thrift.annotation.Nullable java.lang.String info; // required + + /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ + public enum _Fields implements org.apache.thrift.TFieldIdEnum { + ID((short)1, "id"), + NAME((short)2, "name"), + INFO((short)3, "info"); + + private static final java.util.Map byName = new java.util.HashMap(); + + static { + for (_Fields field : java.util.EnumSet.allOf(_Fields.class)) { + byName.put(field.getFieldName(), field); + } + } + + /** + * Find the _Fields constant that matches fieldId, or null if its not found. + */ + @org.apache.thrift.annotation.Nullable + public static _Fields findByThriftId(int fieldId) { + switch(fieldId) { + case 1: // ID + return ID; + case 2: // NAME + return NAME; + case 3: // INFO + return INFO; + default: + return null; + } + } + + /** + * Find the _Fields constant that matches fieldId, throwing an exception + * if it is not found. + */ + public static _Fields findByThriftIdOrThrow(int fieldId) { + _Fields fields = findByThriftId(fieldId); + if (fields == null) throw new java.lang.IllegalArgumentException("Field " + fieldId + " doesn't exist!"); + return fields; + } + + /** + * Find the _Fields constant that matches name, or null if its not found. + */ + @org.apache.thrift.annotation.Nullable + public static _Fields findByName(java.lang.String name) { + return byName.get(name); + } + + private final short _thriftId; + private final java.lang.String _fieldName; + + _Fields(short thriftId, java.lang.String fieldName) { + _thriftId = thriftId; + _fieldName = fieldName; + } + + public short getThriftFieldId() { + return _thriftId; + } + + public java.lang.String getFieldName() { + return _fieldName; + } + } + + // isset id assignments + private static final int __ID_ISSET_ID = 0; + private byte __isset_bitfield = 0; + public static final java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; + static { + java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new java.util.EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); + tmpMap.put(_Fields.ID, new org.apache.thrift.meta_data.FieldMetaData("id", org.apache.thrift.TFieldRequirementType.REQUIRED, + new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I32))); + tmpMap.put(_Fields.NAME, new org.apache.thrift.meta_data.FieldMetaData("name", org.apache.thrift.TFieldRequirementType.REQUIRED, + new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); + tmpMap.put(_Fields.INFO, new org.apache.thrift.meta_data.FieldMetaData("info", org.apache.thrift.TFieldRequirementType.REQUIRED, + new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); + metaDataMap = java.util.Collections.unmodifiableMap(tmpMap); + org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(FakeDatabaseRow.class, metaDataMap); + } + + public FakeDatabaseRow() { + } + + public FakeDatabaseRow( + int id, + java.lang.String name, + java.lang.String info) + { + this(); + this.id = id; + setIdIsSet(true); + this.name = name; + this.info = info; + } + + /** + * Performs a deep copy on other. + */ + public FakeDatabaseRow(FakeDatabaseRow other) { + __isset_bitfield = other.__isset_bitfield; + this.id = other.id; + if (other.isSetName()) { + this.name = other.name; + } + if (other.isSetInfo()) { + this.info = other.info; + } + } + + public FakeDatabaseRow deepCopy() { + return new FakeDatabaseRow(this); + } + + @Override + public void clear() { + setIdIsSet(false); + this.id = 0; + this.name = null; + this.info = null; + } + + public int getId() { + return this.id; + } + + public FakeDatabaseRow setId(int id) { + this.id = id; + setIdIsSet(true); + return this; + } + + public void unsetId() { + __isset_bitfield = org.apache.thrift.EncodingUtils.clearBit(__isset_bitfield, __ID_ISSET_ID); + } + + /** Returns true if field id is set (has been assigned a value) and false otherwise */ + public boolean isSetId() { + return org.apache.thrift.EncodingUtils.testBit(__isset_bitfield, __ID_ISSET_ID); + } + + public void setIdIsSet(boolean value) { + __isset_bitfield = org.apache.thrift.EncodingUtils.setBit(__isset_bitfield, __ID_ISSET_ID, value); + } + + @org.apache.thrift.annotation.Nullable + public java.lang.String getName() { + return this.name; + } + + public FakeDatabaseRow setName(@org.apache.thrift.annotation.Nullable java.lang.String name) { + this.name = name; + return this; + } + + public void unsetName() { + this.name = null; + } + + /** Returns true if field name is set (has been assigned a value) and false otherwise */ + public boolean isSetName() { + return this.name != null; + } + + public void setNameIsSet(boolean value) { + if (!value) { + this.name = null; + } + } + + @org.apache.thrift.annotation.Nullable + public java.lang.String getInfo() { + return this.info; + } + + public FakeDatabaseRow setInfo(@org.apache.thrift.annotation.Nullable java.lang.String info) { + this.info = info; + return this; + } + + public void unsetInfo() { + this.info = null; + } + + /** Returns true if field info is set (has been assigned a value) and false otherwise */ + public boolean isSetInfo() { + return this.info != null; + } + + public void setInfoIsSet(boolean value) { + if (!value) { + this.info = null; + } + } + + public void setFieldValue(_Fields field, @org.apache.thrift.annotation.Nullable java.lang.Object value) { + switch (field) { + case ID: + if (value == null) { + unsetId(); + } else { + setId((java.lang.Integer)value); + } + break; + + case NAME: + if (value == null) { + unsetName(); + } else { + setName((java.lang.String)value); + } + break; + + case INFO: + if (value == null) { + unsetInfo(); + } else { + setInfo((java.lang.String)value); + } + break; + + } + } + + @org.apache.thrift.annotation.Nullable + public java.lang.Object getFieldValue(_Fields field) { + switch (field) { + case ID: + return getId(); + + case NAME: + return getName(); + + case INFO: + return getInfo(); + + } + throw new java.lang.IllegalStateException(); + } + + /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */ + public boolean isSet(_Fields field) { + if (field == null) { + throw new java.lang.IllegalArgumentException(); + } + + switch (field) { + case ID: + return isSetId(); + case NAME: + return isSetName(); + case INFO: + return isSetInfo(); + } + throw new java.lang.IllegalStateException(); + } + + @Override + public boolean equals(java.lang.Object that) { + if (that instanceof FakeDatabaseRow) + return this.equals((FakeDatabaseRow)that); + return false; + } + + public boolean equals(FakeDatabaseRow that) { + if (that == null) + return false; + if (this == that) + return true; + + boolean this_present_id = true; + boolean that_present_id = true; + if (this_present_id || that_present_id) { + if (!(this_present_id && that_present_id)) + return false; + if (this.id != that.id) + return false; + } + + boolean this_present_name = true && this.isSetName(); + boolean that_present_name = true && that.isSetName(); + if (this_present_name || that_present_name) { + if (!(this_present_name && that_present_name)) + return false; + if (!this.name.equals(that.name)) + return false; + } + + boolean this_present_info = true && this.isSetInfo(); + boolean that_present_info = true && that.isSetInfo(); + if (this_present_info || that_present_info) { + if (!(this_present_info && that_present_info)) + return false; + if (!this.info.equals(that.info)) + return false; + } + + return true; + } + + @Override + public int hashCode() { + int hashCode = 1; + + hashCode = hashCode * 8191 + id; + + hashCode = hashCode * 8191 + ((isSetName()) ? 131071 : 524287); + if (isSetName()) + hashCode = hashCode * 8191 + name.hashCode(); + + hashCode = hashCode * 8191 + ((isSetInfo()) ? 131071 : 524287); + if (isSetInfo()) + hashCode = hashCode * 8191 + info.hashCode(); + + return hashCode; + } + + @Override + public int compareTo(FakeDatabaseRow other) { + if (!getClass().equals(other.getClass())) { + return getClass().getName().compareTo(other.getClass().getName()); + } + + int lastComparison = 0; + + lastComparison = java.lang.Boolean.compare(isSetId(), other.isSetId()); + if (lastComparison != 0) { + return lastComparison; + } + if (isSetId()) { + lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.id, other.id); + if (lastComparison != 0) { + return lastComparison; + } + } + lastComparison = java.lang.Boolean.compare(isSetName(), other.isSetName()); + if (lastComparison != 0) { + return lastComparison; + } + if (isSetName()) { + lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.name, other.name); + if (lastComparison != 0) { + return lastComparison; + } + } + lastComparison = java.lang.Boolean.compare(isSetInfo(), other.isSetInfo()); + if (lastComparison != 0) { + return lastComparison; + } + if (isSetInfo()) { + lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.info, other.info); + if (lastComparison != 0) { + return lastComparison; + } + } + return 0; + } + + @org.apache.thrift.annotation.Nullable + public _Fields fieldForId(int fieldId) { + return _Fields.findByThriftId(fieldId); + } + + public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException { + scheme(iprot).read(iprot, this); + } + + public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException { + scheme(oprot).write(oprot, this); + } + + @Override + public java.lang.String toString() { + java.lang.StringBuilder sb = new java.lang.StringBuilder("FakeDatabaseRow("); + boolean first = true; + + sb.append("id:"); + sb.append(this.id); + first = false; + if (!first) sb.append(", "); + sb.append("name:"); + if (this.name == null) { + sb.append("null"); + } else { + sb.append(this.name); + } + first = false; + if (!first) sb.append(", "); + sb.append("info:"); + if (this.info == null) { + sb.append("null"); + } else { + sb.append(this.info); + } + first = false; + sb.append(")"); + return sb.toString(); + } + + public void validate() throws org.apache.thrift.TException { + // check for required fields + // alas, we cannot check 'id' because it's a primitive and you chose the non-beans generator. + if (name == null) { + throw new org.apache.thrift.protocol.TProtocolException("Required field 'name' was not present! Struct: " + toString()); + } + if (info == null) { + throw new org.apache.thrift.protocol.TProtocolException("Required field 'info' was not present! Struct: " + toString()); + } + // check for sub-struct validity + } + + private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { + try { + write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out))); + } catch (org.apache.thrift.TException te) { + throw new java.io.IOException(te); + } + } + + private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, java.lang.ClassNotFoundException { + try { + // it doesn't seem like you should have to do this, but java serialization is wacky, and doesn't call the default constructor. + __isset_bitfield = 0; + read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in))); + } catch (org.apache.thrift.TException te) { + throw new java.io.IOException(te); + } + } + + private static class FakeDatabaseRowStandardSchemeFactory implements org.apache.thrift.scheme.SchemeFactory { + public FakeDatabaseRowStandardScheme getScheme() { + return new FakeDatabaseRowStandardScheme(); + } + } + + private static class FakeDatabaseRowStandardScheme extends org.apache.thrift.scheme.StandardScheme { + + public void read(org.apache.thrift.protocol.TProtocol iprot, FakeDatabaseRow struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TField schemeField; + iprot.readStructBegin(); + while (true) + { + schemeField = iprot.readFieldBegin(); + if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { + break; + } + switch (schemeField.id) { + case 1: // ID + if (schemeField.type == org.apache.thrift.protocol.TType.I32) { + struct.id = iprot.readI32(); + struct.setIdIsSet(true); + } else { + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + break; + case 2: // NAME + if (schemeField.type == org.apache.thrift.protocol.TType.STRING) { + struct.name = iprot.readString(); + struct.setNameIsSet(true); + } else { + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + break; + case 3: // INFO + if (schemeField.type == org.apache.thrift.protocol.TType.STRING) { + struct.info = iprot.readString(); + struct.setInfoIsSet(true); + } else { + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + break; + default: + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + iprot.readFieldEnd(); + } + iprot.readStructEnd(); + + // check for required fields of primitive type, which can't be checked in the validate method + if (!struct.isSetId()) { + throw new org.apache.thrift.protocol.TProtocolException("Required field 'id' was not found in serialized data! Struct: " + toString()); + } + struct.validate(); + } + + public void write(org.apache.thrift.protocol.TProtocol oprot, FakeDatabaseRow struct) throws org.apache.thrift.TException { + struct.validate(); + + oprot.writeStructBegin(STRUCT_DESC); + oprot.writeFieldBegin(ID_FIELD_DESC); + oprot.writeI32(struct.id); + oprot.writeFieldEnd(); + if (struct.name != null) { + oprot.writeFieldBegin(NAME_FIELD_DESC); + oprot.writeString(struct.name); + oprot.writeFieldEnd(); + } + if (struct.info != null) { + oprot.writeFieldBegin(INFO_FIELD_DESC); + oprot.writeString(struct.info); + oprot.writeFieldEnd(); + } + oprot.writeFieldStop(); + oprot.writeStructEnd(); + } + + } + + private static class FakeDatabaseRowTupleSchemeFactory implements org.apache.thrift.scheme.SchemeFactory { + public FakeDatabaseRowTupleScheme getScheme() { + return new FakeDatabaseRowTupleScheme(); + } + } + + private static class FakeDatabaseRowTupleScheme extends org.apache.thrift.scheme.TupleScheme { + + @Override + public void write(org.apache.thrift.protocol.TProtocol prot, FakeDatabaseRow struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TTupleProtocol oprot = (org.apache.thrift.protocol.TTupleProtocol) prot; + oprot.writeI32(struct.id); + oprot.writeString(struct.name); + oprot.writeString(struct.info); + } + + @Override + public void read(org.apache.thrift.protocol.TProtocol prot, FakeDatabaseRow struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TTupleProtocol iprot = (org.apache.thrift.protocol.TTupleProtocol) prot; + struct.id = iprot.readI32(); + struct.setIdIsSet(true); + struct.name = iprot.readString(); + struct.setNameIsSet(true); + struct.info = iprot.readString(); + struct.setInfoIsSet(true); + } + } + + private static S scheme(org.apache.thrift.protocol.TProtocol proto) { + return (org.apache.thrift.scheme.StandardScheme.class.equals(proto.getScheme()) ? STANDARD_SCHEME_FACTORY : TUPLE_SCHEME_FACTORY).getScheme(); + } +} + diff --git a/e2e-tests/spring-rpc/spring-rpc-thrift/src/main/java/com/foo/rpc/examples/spring/fakemockobject/generated/FakeMockObjectService.java b/e2e-tests/spring-rpc/spring-rpc-thrift/src/main/java/com/foo/rpc/examples/spring/fakemockobject/generated/FakeMockObjectService.java new file mode 100644 index 0000000000..333703ac4d --- /dev/null +++ b/e2e-tests/spring-rpc/spring-rpc-thrift/src/main/java/com/foo/rpc/examples/spring/fakemockobject/generated/FakeMockObjectService.java @@ -0,0 +1,2832 @@ +/** + * Autogenerated by Thrift Compiler (0.15.0) + * + * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + * @generated + */ +package com.foo.rpc.examples.spring.fakemockobject.generated; + +@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"}) +@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.15.0)", date = "2023-06-16") +public class FakeMockObjectService { + + public interface Iface { + + public java.lang.String getFooFromExternalService(int id) throws org.apache.thrift.TException; + + public java.lang.String getBarFromDatabase(int id) throws org.apache.thrift.TException; + + public boolean backdoor(FakeRetrieveData exData, FakeDatabaseRow dbData) throws org.apache.thrift.TException; + + } + + public interface AsyncIface { + + public void getFooFromExternalService(int id, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException; + + public void getBarFromDatabase(int id, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException; + + public void backdoor(FakeRetrieveData exData, FakeDatabaseRow dbData, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException; + + } + + public static class Client extends org.apache.thrift.TServiceClient implements Iface { + public static class Factory implements org.apache.thrift.TServiceClientFactory { + public Factory() {} + public Client getClient(org.apache.thrift.protocol.TProtocol prot) { + return new Client(prot); + } + public Client getClient(org.apache.thrift.protocol.TProtocol iprot, org.apache.thrift.protocol.TProtocol oprot) { + return new Client(iprot, oprot); + } + } + + public Client(org.apache.thrift.protocol.TProtocol prot) + { + super(prot, prot); + } + + public Client(org.apache.thrift.protocol.TProtocol iprot, org.apache.thrift.protocol.TProtocol oprot) { + super(iprot, oprot); + } + + public java.lang.String getFooFromExternalService(int id) throws org.apache.thrift.TException + { + send_getFooFromExternalService(id); + return recv_getFooFromExternalService(); + } + + public void send_getFooFromExternalService(int id) throws org.apache.thrift.TException + { + getFooFromExternalService_args args = new getFooFromExternalService_args(); + args.setId(id); + sendBase("getFooFromExternalService", args); + } + + public java.lang.String recv_getFooFromExternalService() throws org.apache.thrift.TException + { + getFooFromExternalService_result result = new getFooFromExternalService_result(); + receiveBase(result, "getFooFromExternalService"); + if (result.isSetSuccess()) { + return result.success; + } + throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "getFooFromExternalService failed: unknown result"); + } + + public java.lang.String getBarFromDatabase(int id) throws org.apache.thrift.TException + { + send_getBarFromDatabase(id); + return recv_getBarFromDatabase(); + } + + public void send_getBarFromDatabase(int id) throws org.apache.thrift.TException + { + getBarFromDatabase_args args = new getBarFromDatabase_args(); + args.setId(id); + sendBase("getBarFromDatabase", args); + } + + public java.lang.String recv_getBarFromDatabase() throws org.apache.thrift.TException + { + getBarFromDatabase_result result = new getBarFromDatabase_result(); + receiveBase(result, "getBarFromDatabase"); + if (result.isSetSuccess()) { + return result.success; + } + throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "getBarFromDatabase failed: unknown result"); + } + + public boolean backdoor(FakeRetrieveData exData, FakeDatabaseRow dbData) throws org.apache.thrift.TException + { + send_backdoor(exData, dbData); + return recv_backdoor(); + } + + public void send_backdoor(FakeRetrieveData exData, FakeDatabaseRow dbData) throws org.apache.thrift.TException + { + backdoor_args args = new backdoor_args(); + args.setExData(exData); + args.setDbData(dbData); + sendBase("backdoor", args); + } + + public boolean recv_backdoor() throws org.apache.thrift.TException + { + backdoor_result result = new backdoor_result(); + receiveBase(result, "backdoor"); + if (result.isSetSuccess()) { + return result.success; + } + throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "backdoor failed: unknown result"); + } + + } + public static class AsyncClient extends org.apache.thrift.async.TAsyncClient implements AsyncIface { + public static class Factory implements org.apache.thrift.async.TAsyncClientFactory { + private org.apache.thrift.async.TAsyncClientManager clientManager; + private org.apache.thrift.protocol.TProtocolFactory protocolFactory; + public Factory(org.apache.thrift.async.TAsyncClientManager clientManager, org.apache.thrift.protocol.TProtocolFactory protocolFactory) { + this.clientManager = clientManager; + this.protocolFactory = protocolFactory; + } + public AsyncClient getAsyncClient(org.apache.thrift.transport.TNonblockingTransport transport) { + return new AsyncClient(protocolFactory, clientManager, transport); + } + } + + public AsyncClient(org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.async.TAsyncClientManager clientManager, org.apache.thrift.transport.TNonblockingTransport transport) { + super(protocolFactory, clientManager, transport); + } + + public void getFooFromExternalService(int id, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException { + checkReady(); + getFooFromExternalService_call method_call = new getFooFromExternalService_call(id, resultHandler, this, ___protocolFactory, ___transport); + this.___currentMethod = method_call; + ___manager.call(method_call); + } + + public static class getFooFromExternalService_call extends org.apache.thrift.async.TAsyncMethodCall { + private int id; + public getFooFromExternalService_call(int id, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException { + super(client, protocolFactory, transport, resultHandler, false); + this.id = id; + } + + public void write_args(org.apache.thrift.protocol.TProtocol prot) throws org.apache.thrift.TException { + prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("getFooFromExternalService", org.apache.thrift.protocol.TMessageType.CALL, 0)); + getFooFromExternalService_args args = new getFooFromExternalService_args(); + args.setId(id); + args.write(prot); + prot.writeMessageEnd(); + } + + public java.lang.String getResult() throws org.apache.thrift.TException { + if (getState() != org.apache.thrift.async.TAsyncMethodCall.State.RESPONSE_READ) { + throw new java.lang.IllegalStateException("Method call not finished!"); + } + org.apache.thrift.transport.TMemoryInputTransport memoryTransport = new org.apache.thrift.transport.TMemoryInputTransport(getFrameBuffer().array()); + org.apache.thrift.protocol.TProtocol prot = client.getProtocolFactory().getProtocol(memoryTransport); + return (new Client(prot)).recv_getFooFromExternalService(); + } + } + + public void getBarFromDatabase(int id, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException { + checkReady(); + getBarFromDatabase_call method_call = new getBarFromDatabase_call(id, resultHandler, this, ___protocolFactory, ___transport); + this.___currentMethod = method_call; + ___manager.call(method_call); + } + + public static class getBarFromDatabase_call extends org.apache.thrift.async.TAsyncMethodCall { + private int id; + public getBarFromDatabase_call(int id, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException { + super(client, protocolFactory, transport, resultHandler, false); + this.id = id; + } + + public void write_args(org.apache.thrift.protocol.TProtocol prot) throws org.apache.thrift.TException { + prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("getBarFromDatabase", org.apache.thrift.protocol.TMessageType.CALL, 0)); + getBarFromDatabase_args args = new getBarFromDatabase_args(); + args.setId(id); + args.write(prot); + prot.writeMessageEnd(); + } + + public java.lang.String getResult() throws org.apache.thrift.TException { + if (getState() != org.apache.thrift.async.TAsyncMethodCall.State.RESPONSE_READ) { + throw new java.lang.IllegalStateException("Method call not finished!"); + } + org.apache.thrift.transport.TMemoryInputTransport memoryTransport = new org.apache.thrift.transport.TMemoryInputTransport(getFrameBuffer().array()); + org.apache.thrift.protocol.TProtocol prot = client.getProtocolFactory().getProtocol(memoryTransport); + return (new Client(prot)).recv_getBarFromDatabase(); + } + } + + public void backdoor(FakeRetrieveData exData, FakeDatabaseRow dbData, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException { + checkReady(); + backdoor_call method_call = new backdoor_call(exData, dbData, resultHandler, this, ___protocolFactory, ___transport); + this.___currentMethod = method_call; + ___manager.call(method_call); + } + + public static class backdoor_call extends org.apache.thrift.async.TAsyncMethodCall { + private FakeRetrieveData exData; + private FakeDatabaseRow dbData; + public backdoor_call(FakeRetrieveData exData, FakeDatabaseRow dbData, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException { + super(client, protocolFactory, transport, resultHandler, false); + this.exData = exData; + this.dbData = dbData; + } + + public void write_args(org.apache.thrift.protocol.TProtocol prot) throws org.apache.thrift.TException { + prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("backdoor", org.apache.thrift.protocol.TMessageType.CALL, 0)); + backdoor_args args = new backdoor_args(); + args.setExData(exData); + args.setDbData(dbData); + args.write(prot); + prot.writeMessageEnd(); + } + + public java.lang.Boolean getResult() throws org.apache.thrift.TException { + if (getState() != org.apache.thrift.async.TAsyncMethodCall.State.RESPONSE_READ) { + throw new java.lang.IllegalStateException("Method call not finished!"); + } + org.apache.thrift.transport.TMemoryInputTransport memoryTransport = new org.apache.thrift.transport.TMemoryInputTransport(getFrameBuffer().array()); + org.apache.thrift.protocol.TProtocol prot = client.getProtocolFactory().getProtocol(memoryTransport); + return (new Client(prot)).recv_backdoor(); + } + } + + } + + public static class Processor extends org.apache.thrift.TBaseProcessor implements org.apache.thrift.TProcessor { + private static final org.slf4j.Logger _LOGGER = org.slf4j.LoggerFactory.getLogger(Processor.class.getName()); + public Processor(I iface) { + super(iface, getProcessMap(new java.util.HashMap>())); + } + + protected Processor(I iface, java.util.Map> processMap) { + super(iface, getProcessMap(processMap)); + } + + private static java.util.Map> getProcessMap(java.util.Map> processMap) { + processMap.put("getFooFromExternalService", new getFooFromExternalService()); + processMap.put("getBarFromDatabase", new getBarFromDatabase()); + processMap.put("backdoor", new backdoor()); + return processMap; + } + + public static class getFooFromExternalService extends org.apache.thrift.ProcessFunction { + public getFooFromExternalService() { + super("getFooFromExternalService"); + } + + public getFooFromExternalService_args getEmptyArgsInstance() { + return new getFooFromExternalService_args(); + } + + protected boolean isOneway() { + return false; + } + + @Override + protected boolean rethrowUnhandledExceptions() { + return false; + } + + public getFooFromExternalService_result getResult(I iface, getFooFromExternalService_args args) throws org.apache.thrift.TException { + getFooFromExternalService_result result = new getFooFromExternalService_result(); + result.success = iface.getFooFromExternalService(args.id); + return result; + } + } + + public static class getBarFromDatabase extends org.apache.thrift.ProcessFunction { + public getBarFromDatabase() { + super("getBarFromDatabase"); + } + + public getBarFromDatabase_args getEmptyArgsInstance() { + return new getBarFromDatabase_args(); + } + + protected boolean isOneway() { + return false; + } + + @Override + protected boolean rethrowUnhandledExceptions() { + return false; + } + + public getBarFromDatabase_result getResult(I iface, getBarFromDatabase_args args) throws org.apache.thrift.TException { + getBarFromDatabase_result result = new getBarFromDatabase_result(); + result.success = iface.getBarFromDatabase(args.id); + return result; + } + } + + public static class backdoor extends org.apache.thrift.ProcessFunction { + public backdoor() { + super("backdoor"); + } + + public backdoor_args getEmptyArgsInstance() { + return new backdoor_args(); + } + + protected boolean isOneway() { + return false; + } + + @Override + protected boolean rethrowUnhandledExceptions() { + return false; + } + + public backdoor_result getResult(I iface, backdoor_args args) throws org.apache.thrift.TException { + backdoor_result result = new backdoor_result(); + result.success = iface.backdoor(args.exData, args.dbData); + result.setSuccessIsSet(true); + return result; + } + } + + } + + public static class AsyncProcessor extends org.apache.thrift.TBaseAsyncProcessor { + private static final org.slf4j.Logger _LOGGER = org.slf4j.LoggerFactory.getLogger(AsyncProcessor.class.getName()); + public AsyncProcessor(I iface) { + super(iface, getProcessMap(new java.util.HashMap>())); + } + + protected AsyncProcessor(I iface, java.util.Map> processMap) { + super(iface, getProcessMap(processMap)); + } + + private static java.util.Map> getProcessMap(java.util.Map> processMap) { + processMap.put("getFooFromExternalService", new getFooFromExternalService()); + processMap.put("getBarFromDatabase", new getBarFromDatabase()); + processMap.put("backdoor", new backdoor()); + return processMap; + } + + public static class getFooFromExternalService extends org.apache.thrift.AsyncProcessFunction { + public getFooFromExternalService() { + super("getFooFromExternalService"); + } + + public getFooFromExternalService_args getEmptyArgsInstance() { + return new getFooFromExternalService_args(); + } + + public org.apache.thrift.async.AsyncMethodCallback getResultHandler(final org.apache.thrift.server.AbstractNonblockingServer.AsyncFrameBuffer fb, final int seqid) { + final org.apache.thrift.AsyncProcessFunction fcall = this; + return new org.apache.thrift.async.AsyncMethodCallback() { + public void onComplete(java.lang.String o) { + getFooFromExternalService_result result = new getFooFromExternalService_result(); + result.success = o; + try { + fcall.sendResponse(fb, result, org.apache.thrift.protocol.TMessageType.REPLY,seqid); + } catch (org.apache.thrift.transport.TTransportException e) { + _LOGGER.error("TTransportException writing to internal frame buffer", e); + fb.close(); + } catch (java.lang.Exception e) { + _LOGGER.error("Exception writing to internal frame buffer", e); + onError(e); + } + } + public void onError(java.lang.Exception e) { + byte msgType = org.apache.thrift.protocol.TMessageType.REPLY; + org.apache.thrift.TSerializable msg; + getFooFromExternalService_result result = new getFooFromExternalService_result(); + if (e instanceof org.apache.thrift.transport.TTransportException) { + _LOGGER.error("TTransportException inside handler", e); + fb.close(); + return; + } else if (e instanceof org.apache.thrift.TApplicationException) { + _LOGGER.error("TApplicationException inside handler", e); + msgType = org.apache.thrift.protocol.TMessageType.EXCEPTION; + msg = (org.apache.thrift.TApplicationException)e; + } else { + _LOGGER.error("Exception inside handler", e); + msgType = org.apache.thrift.protocol.TMessageType.EXCEPTION; + msg = new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.INTERNAL_ERROR, e.getMessage()); + } + try { + fcall.sendResponse(fb,msg,msgType,seqid); + } catch (java.lang.Exception ex) { + _LOGGER.error("Exception writing to internal frame buffer", ex); + fb.close(); + } + } + }; + } + + protected boolean isOneway() { + return false; + } + + public void start(I iface, getFooFromExternalService_args args, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException { + iface.getFooFromExternalService(args.id,resultHandler); + } + } + + public static class getBarFromDatabase extends org.apache.thrift.AsyncProcessFunction { + public getBarFromDatabase() { + super("getBarFromDatabase"); + } + + public getBarFromDatabase_args getEmptyArgsInstance() { + return new getBarFromDatabase_args(); + } + + public org.apache.thrift.async.AsyncMethodCallback getResultHandler(final org.apache.thrift.server.AbstractNonblockingServer.AsyncFrameBuffer fb, final int seqid) { + final org.apache.thrift.AsyncProcessFunction fcall = this; + return new org.apache.thrift.async.AsyncMethodCallback() { + public void onComplete(java.lang.String o) { + getBarFromDatabase_result result = new getBarFromDatabase_result(); + result.success = o; + try { + fcall.sendResponse(fb, result, org.apache.thrift.protocol.TMessageType.REPLY,seqid); + } catch (org.apache.thrift.transport.TTransportException e) { + _LOGGER.error("TTransportException writing to internal frame buffer", e); + fb.close(); + } catch (java.lang.Exception e) { + _LOGGER.error("Exception writing to internal frame buffer", e); + onError(e); + } + } + public void onError(java.lang.Exception e) { + byte msgType = org.apache.thrift.protocol.TMessageType.REPLY; + org.apache.thrift.TSerializable msg; + getBarFromDatabase_result result = new getBarFromDatabase_result(); + if (e instanceof org.apache.thrift.transport.TTransportException) { + _LOGGER.error("TTransportException inside handler", e); + fb.close(); + return; + } else if (e instanceof org.apache.thrift.TApplicationException) { + _LOGGER.error("TApplicationException inside handler", e); + msgType = org.apache.thrift.protocol.TMessageType.EXCEPTION; + msg = (org.apache.thrift.TApplicationException)e; + } else { + _LOGGER.error("Exception inside handler", e); + msgType = org.apache.thrift.protocol.TMessageType.EXCEPTION; + msg = new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.INTERNAL_ERROR, e.getMessage()); + } + try { + fcall.sendResponse(fb,msg,msgType,seqid); + } catch (java.lang.Exception ex) { + _LOGGER.error("Exception writing to internal frame buffer", ex); + fb.close(); + } + } + }; + } + + protected boolean isOneway() { + return false; + } + + public void start(I iface, getBarFromDatabase_args args, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException { + iface.getBarFromDatabase(args.id,resultHandler); + } + } + + public static class backdoor extends org.apache.thrift.AsyncProcessFunction { + public backdoor() { + super("backdoor"); + } + + public backdoor_args getEmptyArgsInstance() { + return new backdoor_args(); + } + + public org.apache.thrift.async.AsyncMethodCallback getResultHandler(final org.apache.thrift.server.AbstractNonblockingServer.AsyncFrameBuffer fb, final int seqid) { + final org.apache.thrift.AsyncProcessFunction fcall = this; + return new org.apache.thrift.async.AsyncMethodCallback() { + public void onComplete(java.lang.Boolean o) { + backdoor_result result = new backdoor_result(); + result.success = o; + result.setSuccessIsSet(true); + try { + fcall.sendResponse(fb, result, org.apache.thrift.protocol.TMessageType.REPLY,seqid); + } catch (org.apache.thrift.transport.TTransportException e) { + _LOGGER.error("TTransportException writing to internal frame buffer", e); + fb.close(); + } catch (java.lang.Exception e) { + _LOGGER.error("Exception writing to internal frame buffer", e); + onError(e); + } + } + public void onError(java.lang.Exception e) { + byte msgType = org.apache.thrift.protocol.TMessageType.REPLY; + org.apache.thrift.TSerializable msg; + backdoor_result result = new backdoor_result(); + if (e instanceof org.apache.thrift.transport.TTransportException) { + _LOGGER.error("TTransportException inside handler", e); + fb.close(); + return; + } else if (e instanceof org.apache.thrift.TApplicationException) { + _LOGGER.error("TApplicationException inside handler", e); + msgType = org.apache.thrift.protocol.TMessageType.EXCEPTION; + msg = (org.apache.thrift.TApplicationException)e; + } else { + _LOGGER.error("Exception inside handler", e); + msgType = org.apache.thrift.protocol.TMessageType.EXCEPTION; + msg = new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.INTERNAL_ERROR, e.getMessage()); + } + try { + fcall.sendResponse(fb,msg,msgType,seqid); + } catch (java.lang.Exception ex) { + _LOGGER.error("Exception writing to internal frame buffer", ex); + fb.close(); + } + } + }; + } + + protected boolean isOneway() { + return false; + } + + public void start(I iface, backdoor_args args, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException { + iface.backdoor(args.exData, args.dbData,resultHandler); + } + } + + } + + public static class getFooFromExternalService_args implements org.apache.thrift.TBase, java.io.Serializable, Cloneable, Comparable { + private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("getFooFromExternalService_args"); + + private static final org.apache.thrift.protocol.TField ID_FIELD_DESC = new org.apache.thrift.protocol.TField("id", org.apache.thrift.protocol.TType.I32, (short)1); + + private static final org.apache.thrift.scheme.SchemeFactory STANDARD_SCHEME_FACTORY = new getFooFromExternalService_argsStandardSchemeFactory(); + private static final org.apache.thrift.scheme.SchemeFactory TUPLE_SCHEME_FACTORY = new getFooFromExternalService_argsTupleSchemeFactory(); + + public int id; // required + + /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ + public enum _Fields implements org.apache.thrift.TFieldIdEnum { + ID((short)1, "id"); + + private static final java.util.Map byName = new java.util.HashMap(); + + static { + for (_Fields field : java.util.EnumSet.allOf(_Fields.class)) { + byName.put(field.getFieldName(), field); + } + } + + /** + * Find the _Fields constant that matches fieldId, or null if its not found. + */ + @org.apache.thrift.annotation.Nullable + public static _Fields findByThriftId(int fieldId) { + switch(fieldId) { + case 1: // ID + return ID; + default: + return null; + } + } + + /** + * Find the _Fields constant that matches fieldId, throwing an exception + * if it is not found. + */ + public static _Fields findByThriftIdOrThrow(int fieldId) { + _Fields fields = findByThriftId(fieldId); + if (fields == null) throw new java.lang.IllegalArgumentException("Field " + fieldId + " doesn't exist!"); + return fields; + } + + /** + * Find the _Fields constant that matches name, or null if its not found. + */ + @org.apache.thrift.annotation.Nullable + public static _Fields findByName(java.lang.String name) { + return byName.get(name); + } + + private final short _thriftId; + private final java.lang.String _fieldName; + + _Fields(short thriftId, java.lang.String fieldName) { + _thriftId = thriftId; + _fieldName = fieldName; + } + + public short getThriftFieldId() { + return _thriftId; + } + + public java.lang.String getFieldName() { + return _fieldName; + } + } + + // isset id assignments + private static final int __ID_ISSET_ID = 0; + private byte __isset_bitfield = 0; + public static final java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; + static { + java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new java.util.EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); + tmpMap.put(_Fields.ID, new org.apache.thrift.meta_data.FieldMetaData("id", org.apache.thrift.TFieldRequirementType.DEFAULT, + new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I32))); + metaDataMap = java.util.Collections.unmodifiableMap(tmpMap); + org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(getFooFromExternalService_args.class, metaDataMap); + } + + public getFooFromExternalService_args() { + } + + public getFooFromExternalService_args( + int id) + { + this(); + this.id = id; + setIdIsSet(true); + } + + /** + * Performs a deep copy on other. + */ + public getFooFromExternalService_args(getFooFromExternalService_args other) { + __isset_bitfield = other.__isset_bitfield; + this.id = other.id; + } + + public getFooFromExternalService_args deepCopy() { + return new getFooFromExternalService_args(this); + } + + @Override + public void clear() { + setIdIsSet(false); + this.id = 0; + } + + public int getId() { + return this.id; + } + + public getFooFromExternalService_args setId(int id) { + this.id = id; + setIdIsSet(true); + return this; + } + + public void unsetId() { + __isset_bitfield = org.apache.thrift.EncodingUtils.clearBit(__isset_bitfield, __ID_ISSET_ID); + } + + /** Returns true if field id is set (has been assigned a value) and false otherwise */ + public boolean isSetId() { + return org.apache.thrift.EncodingUtils.testBit(__isset_bitfield, __ID_ISSET_ID); + } + + public void setIdIsSet(boolean value) { + __isset_bitfield = org.apache.thrift.EncodingUtils.setBit(__isset_bitfield, __ID_ISSET_ID, value); + } + + public void setFieldValue(_Fields field, @org.apache.thrift.annotation.Nullable java.lang.Object value) { + switch (field) { + case ID: + if (value == null) { + unsetId(); + } else { + setId((java.lang.Integer)value); + } + break; + + } + } + + @org.apache.thrift.annotation.Nullable + public java.lang.Object getFieldValue(_Fields field) { + switch (field) { + case ID: + return getId(); + + } + throw new java.lang.IllegalStateException(); + } + + /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */ + public boolean isSet(_Fields field) { + if (field == null) { + throw new java.lang.IllegalArgumentException(); + } + + switch (field) { + case ID: + return isSetId(); + } + throw new java.lang.IllegalStateException(); + } + + @Override + public boolean equals(java.lang.Object that) { + if (that instanceof getFooFromExternalService_args) + return this.equals((getFooFromExternalService_args)that); + return false; + } + + public boolean equals(getFooFromExternalService_args that) { + if (that == null) + return false; + if (this == that) + return true; + + boolean this_present_id = true; + boolean that_present_id = true; + if (this_present_id || that_present_id) { + if (!(this_present_id && that_present_id)) + return false; + if (this.id != that.id) + return false; + } + + return true; + } + + @Override + public int hashCode() { + int hashCode = 1; + + hashCode = hashCode * 8191 + id; + + return hashCode; + } + + @Override + public int compareTo(getFooFromExternalService_args other) { + if (!getClass().equals(other.getClass())) { + return getClass().getName().compareTo(other.getClass().getName()); + } + + int lastComparison = 0; + + lastComparison = java.lang.Boolean.compare(isSetId(), other.isSetId()); + if (lastComparison != 0) { + return lastComparison; + } + if (isSetId()) { + lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.id, other.id); + if (lastComparison != 0) { + return lastComparison; + } + } + return 0; + } + + @org.apache.thrift.annotation.Nullable + public _Fields fieldForId(int fieldId) { + return _Fields.findByThriftId(fieldId); + } + + public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException { + scheme(iprot).read(iprot, this); + } + + public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException { + scheme(oprot).write(oprot, this); + } + + @Override + public java.lang.String toString() { + java.lang.StringBuilder sb = new java.lang.StringBuilder("getFooFromExternalService_args("); + boolean first = true; + + sb.append("id:"); + sb.append(this.id); + first = false; + sb.append(")"); + return sb.toString(); + } + + public void validate() throws org.apache.thrift.TException { + // check for required fields + // check for sub-struct validity + } + + private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { + try { + write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out))); + } catch (org.apache.thrift.TException te) { + throw new java.io.IOException(te); + } + } + + private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, java.lang.ClassNotFoundException { + try { + // it doesn't seem like you should have to do this, but java serialization is wacky, and doesn't call the default constructor. + __isset_bitfield = 0; + read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in))); + } catch (org.apache.thrift.TException te) { + throw new java.io.IOException(te); + } + } + + private static class getFooFromExternalService_argsStandardSchemeFactory implements org.apache.thrift.scheme.SchemeFactory { + public getFooFromExternalService_argsStandardScheme getScheme() { + return new getFooFromExternalService_argsStandardScheme(); + } + } + + private static class getFooFromExternalService_argsStandardScheme extends org.apache.thrift.scheme.StandardScheme { + + public void read(org.apache.thrift.protocol.TProtocol iprot, getFooFromExternalService_args struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TField schemeField; + iprot.readStructBegin(); + while (true) + { + schemeField = iprot.readFieldBegin(); + if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { + break; + } + switch (schemeField.id) { + case 1: // ID + if (schemeField.type == org.apache.thrift.protocol.TType.I32) { + struct.id = iprot.readI32(); + struct.setIdIsSet(true); + } else { + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + break; + default: + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + iprot.readFieldEnd(); + } + iprot.readStructEnd(); + + // check for required fields of primitive type, which can't be checked in the validate method + struct.validate(); + } + + public void write(org.apache.thrift.protocol.TProtocol oprot, getFooFromExternalService_args struct) throws org.apache.thrift.TException { + struct.validate(); + + oprot.writeStructBegin(STRUCT_DESC); + oprot.writeFieldBegin(ID_FIELD_DESC); + oprot.writeI32(struct.id); + oprot.writeFieldEnd(); + oprot.writeFieldStop(); + oprot.writeStructEnd(); + } + + } + + private static class getFooFromExternalService_argsTupleSchemeFactory implements org.apache.thrift.scheme.SchemeFactory { + public getFooFromExternalService_argsTupleScheme getScheme() { + return new getFooFromExternalService_argsTupleScheme(); + } + } + + private static class getFooFromExternalService_argsTupleScheme extends org.apache.thrift.scheme.TupleScheme { + + @Override + public void write(org.apache.thrift.protocol.TProtocol prot, getFooFromExternalService_args struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TTupleProtocol oprot = (org.apache.thrift.protocol.TTupleProtocol) prot; + java.util.BitSet optionals = new java.util.BitSet(); + if (struct.isSetId()) { + optionals.set(0); + } + oprot.writeBitSet(optionals, 1); + if (struct.isSetId()) { + oprot.writeI32(struct.id); + } + } + + @Override + public void read(org.apache.thrift.protocol.TProtocol prot, getFooFromExternalService_args struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TTupleProtocol iprot = (org.apache.thrift.protocol.TTupleProtocol) prot; + java.util.BitSet incoming = iprot.readBitSet(1); + if (incoming.get(0)) { + struct.id = iprot.readI32(); + struct.setIdIsSet(true); + } + } + } + + private static S scheme(org.apache.thrift.protocol.TProtocol proto) { + return (org.apache.thrift.scheme.StandardScheme.class.equals(proto.getScheme()) ? STANDARD_SCHEME_FACTORY : TUPLE_SCHEME_FACTORY).getScheme(); + } + } + + public static class getFooFromExternalService_result implements org.apache.thrift.TBase, java.io.Serializable, Cloneable, Comparable { + private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("getFooFromExternalService_result"); + + private static final org.apache.thrift.protocol.TField SUCCESS_FIELD_DESC = new org.apache.thrift.protocol.TField("success", org.apache.thrift.protocol.TType.STRING, (short)0); + + private static final org.apache.thrift.scheme.SchemeFactory STANDARD_SCHEME_FACTORY = new getFooFromExternalService_resultStandardSchemeFactory(); + private static final org.apache.thrift.scheme.SchemeFactory TUPLE_SCHEME_FACTORY = new getFooFromExternalService_resultTupleSchemeFactory(); + + public @org.apache.thrift.annotation.Nullable java.lang.String success; // required + + /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ + public enum _Fields implements org.apache.thrift.TFieldIdEnum { + SUCCESS((short)0, "success"); + + private static final java.util.Map byName = new java.util.HashMap(); + + static { + for (_Fields field : java.util.EnumSet.allOf(_Fields.class)) { + byName.put(field.getFieldName(), field); + } + } + + /** + * Find the _Fields constant that matches fieldId, or null if its not found. + */ + @org.apache.thrift.annotation.Nullable + public static _Fields findByThriftId(int fieldId) { + switch(fieldId) { + case 0: // SUCCESS + return SUCCESS; + default: + return null; + } + } + + /** + * Find the _Fields constant that matches fieldId, throwing an exception + * if it is not found. + */ + public static _Fields findByThriftIdOrThrow(int fieldId) { + _Fields fields = findByThriftId(fieldId); + if (fields == null) throw new java.lang.IllegalArgumentException("Field " + fieldId + " doesn't exist!"); + return fields; + } + + /** + * Find the _Fields constant that matches name, or null if its not found. + */ + @org.apache.thrift.annotation.Nullable + public static _Fields findByName(java.lang.String name) { + return byName.get(name); + } + + private final short _thriftId; + private final java.lang.String _fieldName; + + _Fields(short thriftId, java.lang.String fieldName) { + _thriftId = thriftId; + _fieldName = fieldName; + } + + public short getThriftFieldId() { + return _thriftId; + } + + public java.lang.String getFieldName() { + return _fieldName; + } + } + + // isset id assignments + public static final java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; + static { + java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new java.util.EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); + tmpMap.put(_Fields.SUCCESS, new org.apache.thrift.meta_data.FieldMetaData("success", org.apache.thrift.TFieldRequirementType.DEFAULT, + new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); + metaDataMap = java.util.Collections.unmodifiableMap(tmpMap); + org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(getFooFromExternalService_result.class, metaDataMap); + } + + public getFooFromExternalService_result() { + } + + public getFooFromExternalService_result( + java.lang.String success) + { + this(); + this.success = success; + } + + /** + * Performs a deep copy on other. + */ + public getFooFromExternalService_result(getFooFromExternalService_result other) { + if (other.isSetSuccess()) { + this.success = other.success; + } + } + + public getFooFromExternalService_result deepCopy() { + return new getFooFromExternalService_result(this); + } + + @Override + public void clear() { + this.success = null; + } + + @org.apache.thrift.annotation.Nullable + public java.lang.String getSuccess() { + return this.success; + } + + public getFooFromExternalService_result setSuccess(@org.apache.thrift.annotation.Nullable java.lang.String success) { + this.success = success; + return this; + } + + public void unsetSuccess() { + this.success = null; + } + + /** Returns true if field success is set (has been assigned a value) and false otherwise */ + public boolean isSetSuccess() { + return this.success != null; + } + + public void setSuccessIsSet(boolean value) { + if (!value) { + this.success = null; + } + } + + public void setFieldValue(_Fields field, @org.apache.thrift.annotation.Nullable java.lang.Object value) { + switch (field) { + case SUCCESS: + if (value == null) { + unsetSuccess(); + } else { + setSuccess((java.lang.String)value); + } + break; + + } + } + + @org.apache.thrift.annotation.Nullable + public java.lang.Object getFieldValue(_Fields field) { + switch (field) { + case SUCCESS: + return getSuccess(); + + } + throw new java.lang.IllegalStateException(); + } + + /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */ + public boolean isSet(_Fields field) { + if (field == null) { + throw new java.lang.IllegalArgumentException(); + } + + switch (field) { + case SUCCESS: + return isSetSuccess(); + } + throw new java.lang.IllegalStateException(); + } + + @Override + public boolean equals(java.lang.Object that) { + if (that instanceof getFooFromExternalService_result) + return this.equals((getFooFromExternalService_result)that); + return false; + } + + public boolean equals(getFooFromExternalService_result that) { + if (that == null) + return false; + if (this == that) + return true; + + boolean this_present_success = true && this.isSetSuccess(); + boolean that_present_success = true && that.isSetSuccess(); + if (this_present_success || that_present_success) { + if (!(this_present_success && that_present_success)) + return false; + if (!this.success.equals(that.success)) + return false; + } + + return true; + } + + @Override + public int hashCode() { + int hashCode = 1; + + hashCode = hashCode * 8191 + ((isSetSuccess()) ? 131071 : 524287); + if (isSetSuccess()) + hashCode = hashCode * 8191 + success.hashCode(); + + return hashCode; + } + + @Override + public int compareTo(getFooFromExternalService_result other) { + if (!getClass().equals(other.getClass())) { + return getClass().getName().compareTo(other.getClass().getName()); + } + + int lastComparison = 0; + + lastComparison = java.lang.Boolean.compare(isSetSuccess(), other.isSetSuccess()); + if (lastComparison != 0) { + return lastComparison; + } + if (isSetSuccess()) { + lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.success, other.success); + if (lastComparison != 0) { + return lastComparison; + } + } + return 0; + } + + @org.apache.thrift.annotation.Nullable + public _Fields fieldForId(int fieldId) { + return _Fields.findByThriftId(fieldId); + } + + public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException { + scheme(iprot).read(iprot, this); + } + + public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException { + scheme(oprot).write(oprot, this); + } + + @Override + public java.lang.String toString() { + java.lang.StringBuilder sb = new java.lang.StringBuilder("getFooFromExternalService_result("); + boolean first = true; + + sb.append("success:"); + if (this.success == null) { + sb.append("null"); + } else { + sb.append(this.success); + } + first = false; + sb.append(")"); + return sb.toString(); + } + + public void validate() throws org.apache.thrift.TException { + // check for required fields + // check for sub-struct validity + } + + private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { + try { + write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out))); + } catch (org.apache.thrift.TException te) { + throw new java.io.IOException(te); + } + } + + private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, java.lang.ClassNotFoundException { + try { + read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in))); + } catch (org.apache.thrift.TException te) { + throw new java.io.IOException(te); + } + } + + private static class getFooFromExternalService_resultStandardSchemeFactory implements org.apache.thrift.scheme.SchemeFactory { + public getFooFromExternalService_resultStandardScheme getScheme() { + return new getFooFromExternalService_resultStandardScheme(); + } + } + + private static class getFooFromExternalService_resultStandardScheme extends org.apache.thrift.scheme.StandardScheme { + + public void read(org.apache.thrift.protocol.TProtocol iprot, getFooFromExternalService_result struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TField schemeField; + iprot.readStructBegin(); + while (true) + { + schemeField = iprot.readFieldBegin(); + if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { + break; + } + switch (schemeField.id) { + case 0: // SUCCESS + if (schemeField.type == org.apache.thrift.protocol.TType.STRING) { + struct.success = iprot.readString(); + struct.setSuccessIsSet(true); + } else { + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + break; + default: + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + iprot.readFieldEnd(); + } + iprot.readStructEnd(); + + // check for required fields of primitive type, which can't be checked in the validate method + struct.validate(); + } + + public void write(org.apache.thrift.protocol.TProtocol oprot, getFooFromExternalService_result struct) throws org.apache.thrift.TException { + struct.validate(); + + oprot.writeStructBegin(STRUCT_DESC); + if (struct.success != null) { + oprot.writeFieldBegin(SUCCESS_FIELD_DESC); + oprot.writeString(struct.success); + oprot.writeFieldEnd(); + } + oprot.writeFieldStop(); + oprot.writeStructEnd(); + } + + } + + private static class getFooFromExternalService_resultTupleSchemeFactory implements org.apache.thrift.scheme.SchemeFactory { + public getFooFromExternalService_resultTupleScheme getScheme() { + return new getFooFromExternalService_resultTupleScheme(); + } + } + + private static class getFooFromExternalService_resultTupleScheme extends org.apache.thrift.scheme.TupleScheme { + + @Override + public void write(org.apache.thrift.protocol.TProtocol prot, getFooFromExternalService_result struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TTupleProtocol oprot = (org.apache.thrift.protocol.TTupleProtocol) prot; + java.util.BitSet optionals = new java.util.BitSet(); + if (struct.isSetSuccess()) { + optionals.set(0); + } + oprot.writeBitSet(optionals, 1); + if (struct.isSetSuccess()) { + oprot.writeString(struct.success); + } + } + + @Override + public void read(org.apache.thrift.protocol.TProtocol prot, getFooFromExternalService_result struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TTupleProtocol iprot = (org.apache.thrift.protocol.TTupleProtocol) prot; + java.util.BitSet incoming = iprot.readBitSet(1); + if (incoming.get(0)) { + struct.success = iprot.readString(); + struct.setSuccessIsSet(true); + } + } + } + + private static S scheme(org.apache.thrift.protocol.TProtocol proto) { + return (org.apache.thrift.scheme.StandardScheme.class.equals(proto.getScheme()) ? STANDARD_SCHEME_FACTORY : TUPLE_SCHEME_FACTORY).getScheme(); + } + } + + public static class getBarFromDatabase_args implements org.apache.thrift.TBase, java.io.Serializable, Cloneable, Comparable { + private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("getBarFromDatabase_args"); + + private static final org.apache.thrift.protocol.TField ID_FIELD_DESC = new org.apache.thrift.protocol.TField("id", org.apache.thrift.protocol.TType.I32, (short)1); + + private static final org.apache.thrift.scheme.SchemeFactory STANDARD_SCHEME_FACTORY = new getBarFromDatabase_argsStandardSchemeFactory(); + private static final org.apache.thrift.scheme.SchemeFactory TUPLE_SCHEME_FACTORY = new getBarFromDatabase_argsTupleSchemeFactory(); + + public int id; // required + + /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ + public enum _Fields implements org.apache.thrift.TFieldIdEnum { + ID((short)1, "id"); + + private static final java.util.Map byName = new java.util.HashMap(); + + static { + for (_Fields field : java.util.EnumSet.allOf(_Fields.class)) { + byName.put(field.getFieldName(), field); + } + } + + /** + * Find the _Fields constant that matches fieldId, or null if its not found. + */ + @org.apache.thrift.annotation.Nullable + public static _Fields findByThriftId(int fieldId) { + switch(fieldId) { + case 1: // ID + return ID; + default: + return null; + } + } + + /** + * Find the _Fields constant that matches fieldId, throwing an exception + * if it is not found. + */ + public static _Fields findByThriftIdOrThrow(int fieldId) { + _Fields fields = findByThriftId(fieldId); + if (fields == null) throw new java.lang.IllegalArgumentException("Field " + fieldId + " doesn't exist!"); + return fields; + } + + /** + * Find the _Fields constant that matches name, or null if its not found. + */ + @org.apache.thrift.annotation.Nullable + public static _Fields findByName(java.lang.String name) { + return byName.get(name); + } + + private final short _thriftId; + private final java.lang.String _fieldName; + + _Fields(short thriftId, java.lang.String fieldName) { + _thriftId = thriftId; + _fieldName = fieldName; + } + + public short getThriftFieldId() { + return _thriftId; + } + + public java.lang.String getFieldName() { + return _fieldName; + } + } + + // isset id assignments + private static final int __ID_ISSET_ID = 0; + private byte __isset_bitfield = 0; + public static final java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; + static { + java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new java.util.EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); + tmpMap.put(_Fields.ID, new org.apache.thrift.meta_data.FieldMetaData("id", org.apache.thrift.TFieldRequirementType.DEFAULT, + new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I32))); + metaDataMap = java.util.Collections.unmodifiableMap(tmpMap); + org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(getBarFromDatabase_args.class, metaDataMap); + } + + public getBarFromDatabase_args() { + } + + public getBarFromDatabase_args( + int id) + { + this(); + this.id = id; + setIdIsSet(true); + } + + /** + * Performs a deep copy on other. + */ + public getBarFromDatabase_args(getBarFromDatabase_args other) { + __isset_bitfield = other.__isset_bitfield; + this.id = other.id; + } + + public getBarFromDatabase_args deepCopy() { + return new getBarFromDatabase_args(this); + } + + @Override + public void clear() { + setIdIsSet(false); + this.id = 0; + } + + public int getId() { + return this.id; + } + + public getBarFromDatabase_args setId(int id) { + this.id = id; + setIdIsSet(true); + return this; + } + + public void unsetId() { + __isset_bitfield = org.apache.thrift.EncodingUtils.clearBit(__isset_bitfield, __ID_ISSET_ID); + } + + /** Returns true if field id is set (has been assigned a value) and false otherwise */ + public boolean isSetId() { + return org.apache.thrift.EncodingUtils.testBit(__isset_bitfield, __ID_ISSET_ID); + } + + public void setIdIsSet(boolean value) { + __isset_bitfield = org.apache.thrift.EncodingUtils.setBit(__isset_bitfield, __ID_ISSET_ID, value); + } + + public void setFieldValue(_Fields field, @org.apache.thrift.annotation.Nullable java.lang.Object value) { + switch (field) { + case ID: + if (value == null) { + unsetId(); + } else { + setId((java.lang.Integer)value); + } + break; + + } + } + + @org.apache.thrift.annotation.Nullable + public java.lang.Object getFieldValue(_Fields field) { + switch (field) { + case ID: + return getId(); + + } + throw new java.lang.IllegalStateException(); + } + + /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */ + public boolean isSet(_Fields field) { + if (field == null) { + throw new java.lang.IllegalArgumentException(); + } + + switch (field) { + case ID: + return isSetId(); + } + throw new java.lang.IllegalStateException(); + } + + @Override + public boolean equals(java.lang.Object that) { + if (that instanceof getBarFromDatabase_args) + return this.equals((getBarFromDatabase_args)that); + return false; + } + + public boolean equals(getBarFromDatabase_args that) { + if (that == null) + return false; + if (this == that) + return true; + + boolean this_present_id = true; + boolean that_present_id = true; + if (this_present_id || that_present_id) { + if (!(this_present_id && that_present_id)) + return false; + if (this.id != that.id) + return false; + } + + return true; + } + + @Override + public int hashCode() { + int hashCode = 1; + + hashCode = hashCode * 8191 + id; + + return hashCode; + } + + @Override + public int compareTo(getBarFromDatabase_args other) { + if (!getClass().equals(other.getClass())) { + return getClass().getName().compareTo(other.getClass().getName()); + } + + int lastComparison = 0; + + lastComparison = java.lang.Boolean.compare(isSetId(), other.isSetId()); + if (lastComparison != 0) { + return lastComparison; + } + if (isSetId()) { + lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.id, other.id); + if (lastComparison != 0) { + return lastComparison; + } + } + return 0; + } + + @org.apache.thrift.annotation.Nullable + public _Fields fieldForId(int fieldId) { + return _Fields.findByThriftId(fieldId); + } + + public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException { + scheme(iprot).read(iprot, this); + } + + public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException { + scheme(oprot).write(oprot, this); + } + + @Override + public java.lang.String toString() { + java.lang.StringBuilder sb = new java.lang.StringBuilder("getBarFromDatabase_args("); + boolean first = true; + + sb.append("id:"); + sb.append(this.id); + first = false; + sb.append(")"); + return sb.toString(); + } + + public void validate() throws org.apache.thrift.TException { + // check for required fields + // check for sub-struct validity + } + + private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { + try { + write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out))); + } catch (org.apache.thrift.TException te) { + throw new java.io.IOException(te); + } + } + + private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, java.lang.ClassNotFoundException { + try { + // it doesn't seem like you should have to do this, but java serialization is wacky, and doesn't call the default constructor. + __isset_bitfield = 0; + read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in))); + } catch (org.apache.thrift.TException te) { + throw new java.io.IOException(te); + } + } + + private static class getBarFromDatabase_argsStandardSchemeFactory implements org.apache.thrift.scheme.SchemeFactory { + public getBarFromDatabase_argsStandardScheme getScheme() { + return new getBarFromDatabase_argsStandardScheme(); + } + } + + private static class getBarFromDatabase_argsStandardScheme extends org.apache.thrift.scheme.StandardScheme { + + public void read(org.apache.thrift.protocol.TProtocol iprot, getBarFromDatabase_args struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TField schemeField; + iprot.readStructBegin(); + while (true) + { + schemeField = iprot.readFieldBegin(); + if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { + break; + } + switch (schemeField.id) { + case 1: // ID + if (schemeField.type == org.apache.thrift.protocol.TType.I32) { + struct.id = iprot.readI32(); + struct.setIdIsSet(true); + } else { + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + break; + default: + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + iprot.readFieldEnd(); + } + iprot.readStructEnd(); + + // check for required fields of primitive type, which can't be checked in the validate method + struct.validate(); + } + + public void write(org.apache.thrift.protocol.TProtocol oprot, getBarFromDatabase_args struct) throws org.apache.thrift.TException { + struct.validate(); + + oprot.writeStructBegin(STRUCT_DESC); + oprot.writeFieldBegin(ID_FIELD_DESC); + oprot.writeI32(struct.id); + oprot.writeFieldEnd(); + oprot.writeFieldStop(); + oprot.writeStructEnd(); + } + + } + + private static class getBarFromDatabase_argsTupleSchemeFactory implements org.apache.thrift.scheme.SchemeFactory { + public getBarFromDatabase_argsTupleScheme getScheme() { + return new getBarFromDatabase_argsTupleScheme(); + } + } + + private static class getBarFromDatabase_argsTupleScheme extends org.apache.thrift.scheme.TupleScheme { + + @Override + public void write(org.apache.thrift.protocol.TProtocol prot, getBarFromDatabase_args struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TTupleProtocol oprot = (org.apache.thrift.protocol.TTupleProtocol) prot; + java.util.BitSet optionals = new java.util.BitSet(); + if (struct.isSetId()) { + optionals.set(0); + } + oprot.writeBitSet(optionals, 1); + if (struct.isSetId()) { + oprot.writeI32(struct.id); + } + } + + @Override + public void read(org.apache.thrift.protocol.TProtocol prot, getBarFromDatabase_args struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TTupleProtocol iprot = (org.apache.thrift.protocol.TTupleProtocol) prot; + java.util.BitSet incoming = iprot.readBitSet(1); + if (incoming.get(0)) { + struct.id = iprot.readI32(); + struct.setIdIsSet(true); + } + } + } + + private static S scheme(org.apache.thrift.protocol.TProtocol proto) { + return (org.apache.thrift.scheme.StandardScheme.class.equals(proto.getScheme()) ? STANDARD_SCHEME_FACTORY : TUPLE_SCHEME_FACTORY).getScheme(); + } + } + + public static class getBarFromDatabase_result implements org.apache.thrift.TBase, java.io.Serializable, Cloneable, Comparable { + private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("getBarFromDatabase_result"); + + private static final org.apache.thrift.protocol.TField SUCCESS_FIELD_DESC = new org.apache.thrift.protocol.TField("success", org.apache.thrift.protocol.TType.STRING, (short)0); + + private static final org.apache.thrift.scheme.SchemeFactory STANDARD_SCHEME_FACTORY = new getBarFromDatabase_resultStandardSchemeFactory(); + private static final org.apache.thrift.scheme.SchemeFactory TUPLE_SCHEME_FACTORY = new getBarFromDatabase_resultTupleSchemeFactory(); + + public @org.apache.thrift.annotation.Nullable java.lang.String success; // required + + /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ + public enum _Fields implements org.apache.thrift.TFieldIdEnum { + SUCCESS((short)0, "success"); + + private static final java.util.Map byName = new java.util.HashMap(); + + static { + for (_Fields field : java.util.EnumSet.allOf(_Fields.class)) { + byName.put(field.getFieldName(), field); + } + } + + /** + * Find the _Fields constant that matches fieldId, or null if its not found. + */ + @org.apache.thrift.annotation.Nullable + public static _Fields findByThriftId(int fieldId) { + switch(fieldId) { + case 0: // SUCCESS + return SUCCESS; + default: + return null; + } + } + + /** + * Find the _Fields constant that matches fieldId, throwing an exception + * if it is not found. + */ + public static _Fields findByThriftIdOrThrow(int fieldId) { + _Fields fields = findByThriftId(fieldId); + if (fields == null) throw new java.lang.IllegalArgumentException("Field " + fieldId + " doesn't exist!"); + return fields; + } + + /** + * Find the _Fields constant that matches name, or null if its not found. + */ + @org.apache.thrift.annotation.Nullable + public static _Fields findByName(java.lang.String name) { + return byName.get(name); + } + + private final short _thriftId; + private final java.lang.String _fieldName; + + _Fields(short thriftId, java.lang.String fieldName) { + _thriftId = thriftId; + _fieldName = fieldName; + } + + public short getThriftFieldId() { + return _thriftId; + } + + public java.lang.String getFieldName() { + return _fieldName; + } + } + + // isset id assignments + public static final java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; + static { + java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new java.util.EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); + tmpMap.put(_Fields.SUCCESS, new org.apache.thrift.meta_data.FieldMetaData("success", org.apache.thrift.TFieldRequirementType.DEFAULT, + new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); + metaDataMap = java.util.Collections.unmodifiableMap(tmpMap); + org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(getBarFromDatabase_result.class, metaDataMap); + } + + public getBarFromDatabase_result() { + } + + public getBarFromDatabase_result( + java.lang.String success) + { + this(); + this.success = success; + } + + /** + * Performs a deep copy on other. + */ + public getBarFromDatabase_result(getBarFromDatabase_result other) { + if (other.isSetSuccess()) { + this.success = other.success; + } + } + + public getBarFromDatabase_result deepCopy() { + return new getBarFromDatabase_result(this); + } + + @Override + public void clear() { + this.success = null; + } + + @org.apache.thrift.annotation.Nullable + public java.lang.String getSuccess() { + return this.success; + } + + public getBarFromDatabase_result setSuccess(@org.apache.thrift.annotation.Nullable java.lang.String success) { + this.success = success; + return this; + } + + public void unsetSuccess() { + this.success = null; + } + + /** Returns true if field success is set (has been assigned a value) and false otherwise */ + public boolean isSetSuccess() { + return this.success != null; + } + + public void setSuccessIsSet(boolean value) { + if (!value) { + this.success = null; + } + } + + public void setFieldValue(_Fields field, @org.apache.thrift.annotation.Nullable java.lang.Object value) { + switch (field) { + case SUCCESS: + if (value == null) { + unsetSuccess(); + } else { + setSuccess((java.lang.String)value); + } + break; + + } + } + + @org.apache.thrift.annotation.Nullable + public java.lang.Object getFieldValue(_Fields field) { + switch (field) { + case SUCCESS: + return getSuccess(); + + } + throw new java.lang.IllegalStateException(); + } + + /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */ + public boolean isSet(_Fields field) { + if (field == null) { + throw new java.lang.IllegalArgumentException(); + } + + switch (field) { + case SUCCESS: + return isSetSuccess(); + } + throw new java.lang.IllegalStateException(); + } + + @Override + public boolean equals(java.lang.Object that) { + if (that instanceof getBarFromDatabase_result) + return this.equals((getBarFromDatabase_result)that); + return false; + } + + public boolean equals(getBarFromDatabase_result that) { + if (that == null) + return false; + if (this == that) + return true; + + boolean this_present_success = true && this.isSetSuccess(); + boolean that_present_success = true && that.isSetSuccess(); + if (this_present_success || that_present_success) { + if (!(this_present_success && that_present_success)) + return false; + if (!this.success.equals(that.success)) + return false; + } + + return true; + } + + @Override + public int hashCode() { + int hashCode = 1; + + hashCode = hashCode * 8191 + ((isSetSuccess()) ? 131071 : 524287); + if (isSetSuccess()) + hashCode = hashCode * 8191 + success.hashCode(); + + return hashCode; + } + + @Override + public int compareTo(getBarFromDatabase_result other) { + if (!getClass().equals(other.getClass())) { + return getClass().getName().compareTo(other.getClass().getName()); + } + + int lastComparison = 0; + + lastComparison = java.lang.Boolean.compare(isSetSuccess(), other.isSetSuccess()); + if (lastComparison != 0) { + return lastComparison; + } + if (isSetSuccess()) { + lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.success, other.success); + if (lastComparison != 0) { + return lastComparison; + } + } + return 0; + } + + @org.apache.thrift.annotation.Nullable + public _Fields fieldForId(int fieldId) { + return _Fields.findByThriftId(fieldId); + } + + public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException { + scheme(iprot).read(iprot, this); + } + + public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException { + scheme(oprot).write(oprot, this); + } + + @Override + public java.lang.String toString() { + java.lang.StringBuilder sb = new java.lang.StringBuilder("getBarFromDatabase_result("); + boolean first = true; + + sb.append("success:"); + if (this.success == null) { + sb.append("null"); + } else { + sb.append(this.success); + } + first = false; + sb.append(")"); + return sb.toString(); + } + + public void validate() throws org.apache.thrift.TException { + // check for required fields + // check for sub-struct validity + } + + private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { + try { + write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out))); + } catch (org.apache.thrift.TException te) { + throw new java.io.IOException(te); + } + } + + private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, java.lang.ClassNotFoundException { + try { + read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in))); + } catch (org.apache.thrift.TException te) { + throw new java.io.IOException(te); + } + } + + private static class getBarFromDatabase_resultStandardSchemeFactory implements org.apache.thrift.scheme.SchemeFactory { + public getBarFromDatabase_resultStandardScheme getScheme() { + return new getBarFromDatabase_resultStandardScheme(); + } + } + + private static class getBarFromDatabase_resultStandardScheme extends org.apache.thrift.scheme.StandardScheme { + + public void read(org.apache.thrift.protocol.TProtocol iprot, getBarFromDatabase_result struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TField schemeField; + iprot.readStructBegin(); + while (true) + { + schemeField = iprot.readFieldBegin(); + if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { + break; + } + switch (schemeField.id) { + case 0: // SUCCESS + if (schemeField.type == org.apache.thrift.protocol.TType.STRING) { + struct.success = iprot.readString(); + struct.setSuccessIsSet(true); + } else { + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + break; + default: + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + iprot.readFieldEnd(); + } + iprot.readStructEnd(); + + // check for required fields of primitive type, which can't be checked in the validate method + struct.validate(); + } + + public void write(org.apache.thrift.protocol.TProtocol oprot, getBarFromDatabase_result struct) throws org.apache.thrift.TException { + struct.validate(); + + oprot.writeStructBegin(STRUCT_DESC); + if (struct.success != null) { + oprot.writeFieldBegin(SUCCESS_FIELD_DESC); + oprot.writeString(struct.success); + oprot.writeFieldEnd(); + } + oprot.writeFieldStop(); + oprot.writeStructEnd(); + } + + } + + private static class getBarFromDatabase_resultTupleSchemeFactory implements org.apache.thrift.scheme.SchemeFactory { + public getBarFromDatabase_resultTupleScheme getScheme() { + return new getBarFromDatabase_resultTupleScheme(); + } + } + + private static class getBarFromDatabase_resultTupleScheme extends org.apache.thrift.scheme.TupleScheme { + + @Override + public void write(org.apache.thrift.protocol.TProtocol prot, getBarFromDatabase_result struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TTupleProtocol oprot = (org.apache.thrift.protocol.TTupleProtocol) prot; + java.util.BitSet optionals = new java.util.BitSet(); + if (struct.isSetSuccess()) { + optionals.set(0); + } + oprot.writeBitSet(optionals, 1); + if (struct.isSetSuccess()) { + oprot.writeString(struct.success); + } + } + + @Override + public void read(org.apache.thrift.protocol.TProtocol prot, getBarFromDatabase_result struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TTupleProtocol iprot = (org.apache.thrift.protocol.TTupleProtocol) prot; + java.util.BitSet incoming = iprot.readBitSet(1); + if (incoming.get(0)) { + struct.success = iprot.readString(); + struct.setSuccessIsSet(true); + } + } + } + + private static S scheme(org.apache.thrift.protocol.TProtocol proto) { + return (org.apache.thrift.scheme.StandardScheme.class.equals(proto.getScheme()) ? STANDARD_SCHEME_FACTORY : TUPLE_SCHEME_FACTORY).getScheme(); + } + } + + public static class backdoor_args implements org.apache.thrift.TBase, java.io.Serializable, Cloneable, Comparable { + private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("backdoor_args"); + + private static final org.apache.thrift.protocol.TField EX_DATA_FIELD_DESC = new org.apache.thrift.protocol.TField("exData", org.apache.thrift.protocol.TType.STRUCT, (short)1); + private static final org.apache.thrift.protocol.TField DB_DATA_FIELD_DESC = new org.apache.thrift.protocol.TField("dbData", org.apache.thrift.protocol.TType.STRUCT, (short)2); + + private static final org.apache.thrift.scheme.SchemeFactory STANDARD_SCHEME_FACTORY = new backdoor_argsStandardSchemeFactory(); + private static final org.apache.thrift.scheme.SchemeFactory TUPLE_SCHEME_FACTORY = new backdoor_argsTupleSchemeFactory(); + + public @org.apache.thrift.annotation.Nullable FakeRetrieveData exData; // required + public @org.apache.thrift.annotation.Nullable FakeDatabaseRow dbData; // required + + /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ + public enum _Fields implements org.apache.thrift.TFieldIdEnum { + EX_DATA((short)1, "exData"), + DB_DATA((short)2, "dbData"); + + private static final java.util.Map byName = new java.util.HashMap(); + + static { + for (_Fields field : java.util.EnumSet.allOf(_Fields.class)) { + byName.put(field.getFieldName(), field); + } + } + + /** + * Find the _Fields constant that matches fieldId, or null if its not found. + */ + @org.apache.thrift.annotation.Nullable + public static _Fields findByThriftId(int fieldId) { + switch(fieldId) { + case 1: // EX_DATA + return EX_DATA; + case 2: // DB_DATA + return DB_DATA; + default: + return null; + } + } + + /** + * Find the _Fields constant that matches fieldId, throwing an exception + * if it is not found. + */ + public static _Fields findByThriftIdOrThrow(int fieldId) { + _Fields fields = findByThriftId(fieldId); + if (fields == null) throw new java.lang.IllegalArgumentException("Field " + fieldId + " doesn't exist!"); + return fields; + } + + /** + * Find the _Fields constant that matches name, or null if its not found. + */ + @org.apache.thrift.annotation.Nullable + public static _Fields findByName(java.lang.String name) { + return byName.get(name); + } + + private final short _thriftId; + private final java.lang.String _fieldName; + + _Fields(short thriftId, java.lang.String fieldName) { + _thriftId = thriftId; + _fieldName = fieldName; + } + + public short getThriftFieldId() { + return _thriftId; + } + + public java.lang.String getFieldName() { + return _fieldName; + } + } + + // isset id assignments + public static final java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; + static { + java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new java.util.EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); + tmpMap.put(_Fields.EX_DATA, new org.apache.thrift.meta_data.FieldMetaData("exData", org.apache.thrift.TFieldRequirementType.DEFAULT, + new org.apache.thrift.meta_data.StructMetaData(org.apache.thrift.protocol.TType.STRUCT, FakeRetrieveData.class))); + tmpMap.put(_Fields.DB_DATA, new org.apache.thrift.meta_data.FieldMetaData("dbData", org.apache.thrift.TFieldRequirementType.DEFAULT, + new org.apache.thrift.meta_data.StructMetaData(org.apache.thrift.protocol.TType.STRUCT, FakeDatabaseRow.class))); + metaDataMap = java.util.Collections.unmodifiableMap(tmpMap); + org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(backdoor_args.class, metaDataMap); + } + + public backdoor_args() { + } + + public backdoor_args( + FakeRetrieveData exData, + FakeDatabaseRow dbData) + { + this(); + this.exData = exData; + this.dbData = dbData; + } + + /** + * Performs a deep copy on other. + */ + public backdoor_args(backdoor_args other) { + if (other.isSetExData()) { + this.exData = new FakeRetrieveData(other.exData); + } + if (other.isSetDbData()) { + this.dbData = new FakeDatabaseRow(other.dbData); + } + } + + public backdoor_args deepCopy() { + return new backdoor_args(this); + } + + @Override + public void clear() { + this.exData = null; + this.dbData = null; + } + + @org.apache.thrift.annotation.Nullable + public FakeRetrieveData getExData() { + return this.exData; + } + + public backdoor_args setExData(@org.apache.thrift.annotation.Nullable FakeRetrieveData exData) { + this.exData = exData; + return this; + } + + public void unsetExData() { + this.exData = null; + } + + /** Returns true if field exData is set (has been assigned a value) and false otherwise */ + public boolean isSetExData() { + return this.exData != null; + } + + public void setExDataIsSet(boolean value) { + if (!value) { + this.exData = null; + } + } + + @org.apache.thrift.annotation.Nullable + public FakeDatabaseRow getDbData() { + return this.dbData; + } + + public backdoor_args setDbData(@org.apache.thrift.annotation.Nullable FakeDatabaseRow dbData) { + this.dbData = dbData; + return this; + } + + public void unsetDbData() { + this.dbData = null; + } + + /** Returns true if field dbData is set (has been assigned a value) and false otherwise */ + public boolean isSetDbData() { + return this.dbData != null; + } + + public void setDbDataIsSet(boolean value) { + if (!value) { + this.dbData = null; + } + } + + public void setFieldValue(_Fields field, @org.apache.thrift.annotation.Nullable java.lang.Object value) { + switch (field) { + case EX_DATA: + if (value == null) { + unsetExData(); + } else { + setExData((FakeRetrieveData)value); + } + break; + + case DB_DATA: + if (value == null) { + unsetDbData(); + } else { + setDbData((FakeDatabaseRow)value); + } + break; + + } + } + + @org.apache.thrift.annotation.Nullable + public java.lang.Object getFieldValue(_Fields field) { + switch (field) { + case EX_DATA: + return getExData(); + + case DB_DATA: + return getDbData(); + + } + throw new java.lang.IllegalStateException(); + } + + /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */ + public boolean isSet(_Fields field) { + if (field == null) { + throw new java.lang.IllegalArgumentException(); + } + + switch (field) { + case EX_DATA: + return isSetExData(); + case DB_DATA: + return isSetDbData(); + } + throw new java.lang.IllegalStateException(); + } + + @Override + public boolean equals(java.lang.Object that) { + if (that instanceof backdoor_args) + return this.equals((backdoor_args)that); + return false; + } + + public boolean equals(backdoor_args that) { + if (that == null) + return false; + if (this == that) + return true; + + boolean this_present_exData = true && this.isSetExData(); + boolean that_present_exData = true && that.isSetExData(); + if (this_present_exData || that_present_exData) { + if (!(this_present_exData && that_present_exData)) + return false; + if (!this.exData.equals(that.exData)) + return false; + } + + boolean this_present_dbData = true && this.isSetDbData(); + boolean that_present_dbData = true && that.isSetDbData(); + if (this_present_dbData || that_present_dbData) { + if (!(this_present_dbData && that_present_dbData)) + return false; + if (!this.dbData.equals(that.dbData)) + return false; + } + + return true; + } + + @Override + public int hashCode() { + int hashCode = 1; + + hashCode = hashCode * 8191 + ((isSetExData()) ? 131071 : 524287); + if (isSetExData()) + hashCode = hashCode * 8191 + exData.hashCode(); + + hashCode = hashCode * 8191 + ((isSetDbData()) ? 131071 : 524287); + if (isSetDbData()) + hashCode = hashCode * 8191 + dbData.hashCode(); + + return hashCode; + } + + @Override + public int compareTo(backdoor_args other) { + if (!getClass().equals(other.getClass())) { + return getClass().getName().compareTo(other.getClass().getName()); + } + + int lastComparison = 0; + + lastComparison = java.lang.Boolean.compare(isSetExData(), other.isSetExData()); + if (lastComparison != 0) { + return lastComparison; + } + if (isSetExData()) { + lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.exData, other.exData); + if (lastComparison != 0) { + return lastComparison; + } + } + lastComparison = java.lang.Boolean.compare(isSetDbData(), other.isSetDbData()); + if (lastComparison != 0) { + return lastComparison; + } + if (isSetDbData()) { + lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.dbData, other.dbData); + if (lastComparison != 0) { + return lastComparison; + } + } + return 0; + } + + @org.apache.thrift.annotation.Nullable + public _Fields fieldForId(int fieldId) { + return _Fields.findByThriftId(fieldId); + } + + public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException { + scheme(iprot).read(iprot, this); + } + + public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException { + scheme(oprot).write(oprot, this); + } + + @Override + public java.lang.String toString() { + java.lang.StringBuilder sb = new java.lang.StringBuilder("backdoor_args("); + boolean first = true; + + sb.append("exData:"); + if (this.exData == null) { + sb.append("null"); + } else { + sb.append(this.exData); + } + first = false; + if (!first) sb.append(", "); + sb.append("dbData:"); + if (this.dbData == null) { + sb.append("null"); + } else { + sb.append(this.dbData); + } + first = false; + sb.append(")"); + return sb.toString(); + } + + public void validate() throws org.apache.thrift.TException { + // check for required fields + // check for sub-struct validity + if (exData != null) { + exData.validate(); + } + if (dbData != null) { + dbData.validate(); + } + } + + private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { + try { + write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out))); + } catch (org.apache.thrift.TException te) { + throw new java.io.IOException(te); + } + } + + private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, java.lang.ClassNotFoundException { + try { + read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in))); + } catch (org.apache.thrift.TException te) { + throw new java.io.IOException(te); + } + } + + private static class backdoor_argsStandardSchemeFactory implements org.apache.thrift.scheme.SchemeFactory { + public backdoor_argsStandardScheme getScheme() { + return new backdoor_argsStandardScheme(); + } + } + + private static class backdoor_argsStandardScheme extends org.apache.thrift.scheme.StandardScheme { + + public void read(org.apache.thrift.protocol.TProtocol iprot, backdoor_args struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TField schemeField; + iprot.readStructBegin(); + while (true) + { + schemeField = iprot.readFieldBegin(); + if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { + break; + } + switch (schemeField.id) { + case 1: // EX_DATA + if (schemeField.type == org.apache.thrift.protocol.TType.STRUCT) { + struct.exData = new FakeRetrieveData(); + struct.exData.read(iprot); + struct.setExDataIsSet(true); + } else { + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + break; + case 2: // DB_DATA + if (schemeField.type == org.apache.thrift.protocol.TType.STRUCT) { + struct.dbData = new FakeDatabaseRow(); + struct.dbData.read(iprot); + struct.setDbDataIsSet(true); + } else { + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + break; + default: + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + iprot.readFieldEnd(); + } + iprot.readStructEnd(); + + // check for required fields of primitive type, which can't be checked in the validate method + struct.validate(); + } + + public void write(org.apache.thrift.protocol.TProtocol oprot, backdoor_args struct) throws org.apache.thrift.TException { + struct.validate(); + + oprot.writeStructBegin(STRUCT_DESC); + if (struct.exData != null) { + oprot.writeFieldBegin(EX_DATA_FIELD_DESC); + struct.exData.write(oprot); + oprot.writeFieldEnd(); + } + if (struct.dbData != null) { + oprot.writeFieldBegin(DB_DATA_FIELD_DESC); + struct.dbData.write(oprot); + oprot.writeFieldEnd(); + } + oprot.writeFieldStop(); + oprot.writeStructEnd(); + } + + } + + private static class backdoor_argsTupleSchemeFactory implements org.apache.thrift.scheme.SchemeFactory { + public backdoor_argsTupleScheme getScheme() { + return new backdoor_argsTupleScheme(); + } + } + + private static class backdoor_argsTupleScheme extends org.apache.thrift.scheme.TupleScheme { + + @Override + public void write(org.apache.thrift.protocol.TProtocol prot, backdoor_args struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TTupleProtocol oprot = (org.apache.thrift.protocol.TTupleProtocol) prot; + java.util.BitSet optionals = new java.util.BitSet(); + if (struct.isSetExData()) { + optionals.set(0); + } + if (struct.isSetDbData()) { + optionals.set(1); + } + oprot.writeBitSet(optionals, 2); + if (struct.isSetExData()) { + struct.exData.write(oprot); + } + if (struct.isSetDbData()) { + struct.dbData.write(oprot); + } + } + + @Override + public void read(org.apache.thrift.protocol.TProtocol prot, backdoor_args struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TTupleProtocol iprot = (org.apache.thrift.protocol.TTupleProtocol) prot; + java.util.BitSet incoming = iprot.readBitSet(2); + if (incoming.get(0)) { + struct.exData = new FakeRetrieveData(); + struct.exData.read(iprot); + struct.setExDataIsSet(true); + } + if (incoming.get(1)) { + struct.dbData = new FakeDatabaseRow(); + struct.dbData.read(iprot); + struct.setDbDataIsSet(true); + } + } + } + + private static S scheme(org.apache.thrift.protocol.TProtocol proto) { + return (org.apache.thrift.scheme.StandardScheme.class.equals(proto.getScheme()) ? STANDARD_SCHEME_FACTORY : TUPLE_SCHEME_FACTORY).getScheme(); + } + } + + public static class backdoor_result implements org.apache.thrift.TBase, java.io.Serializable, Cloneable, Comparable { + private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("backdoor_result"); + + private static final org.apache.thrift.protocol.TField SUCCESS_FIELD_DESC = new org.apache.thrift.protocol.TField("success", org.apache.thrift.protocol.TType.BOOL, (short)0); + + private static final org.apache.thrift.scheme.SchemeFactory STANDARD_SCHEME_FACTORY = new backdoor_resultStandardSchemeFactory(); + private static final org.apache.thrift.scheme.SchemeFactory TUPLE_SCHEME_FACTORY = new backdoor_resultTupleSchemeFactory(); + + public boolean success; // required + + /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ + public enum _Fields implements org.apache.thrift.TFieldIdEnum { + SUCCESS((short)0, "success"); + + private static final java.util.Map byName = new java.util.HashMap(); + + static { + for (_Fields field : java.util.EnumSet.allOf(_Fields.class)) { + byName.put(field.getFieldName(), field); + } + } + + /** + * Find the _Fields constant that matches fieldId, or null if its not found. + */ + @org.apache.thrift.annotation.Nullable + public static _Fields findByThriftId(int fieldId) { + switch(fieldId) { + case 0: // SUCCESS + return SUCCESS; + default: + return null; + } + } + + /** + * Find the _Fields constant that matches fieldId, throwing an exception + * if it is not found. + */ + public static _Fields findByThriftIdOrThrow(int fieldId) { + _Fields fields = findByThriftId(fieldId); + if (fields == null) throw new java.lang.IllegalArgumentException("Field " + fieldId + " doesn't exist!"); + return fields; + } + + /** + * Find the _Fields constant that matches name, or null if its not found. + */ + @org.apache.thrift.annotation.Nullable + public static _Fields findByName(java.lang.String name) { + return byName.get(name); + } + + private final short _thriftId; + private final java.lang.String _fieldName; + + _Fields(short thriftId, java.lang.String fieldName) { + _thriftId = thriftId; + _fieldName = fieldName; + } + + public short getThriftFieldId() { + return _thriftId; + } + + public java.lang.String getFieldName() { + return _fieldName; + } + } + + // isset id assignments + private static final int __SUCCESS_ISSET_ID = 0; + private byte __isset_bitfield = 0; + public static final java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; + static { + java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new java.util.EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); + tmpMap.put(_Fields.SUCCESS, new org.apache.thrift.meta_data.FieldMetaData("success", org.apache.thrift.TFieldRequirementType.DEFAULT, + new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.BOOL))); + metaDataMap = java.util.Collections.unmodifiableMap(tmpMap); + org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(backdoor_result.class, metaDataMap); + } + + public backdoor_result() { + } + + public backdoor_result( + boolean success) + { + this(); + this.success = success; + setSuccessIsSet(true); + } + + /** + * Performs a deep copy on other. + */ + public backdoor_result(backdoor_result other) { + __isset_bitfield = other.__isset_bitfield; + this.success = other.success; + } + + public backdoor_result deepCopy() { + return new backdoor_result(this); + } + + @Override + public void clear() { + setSuccessIsSet(false); + this.success = false; + } + + public boolean isSuccess() { + return this.success; + } + + public backdoor_result setSuccess(boolean success) { + this.success = success; + setSuccessIsSet(true); + return this; + } + + public void unsetSuccess() { + __isset_bitfield = org.apache.thrift.EncodingUtils.clearBit(__isset_bitfield, __SUCCESS_ISSET_ID); + } + + /** Returns true if field success is set (has been assigned a value) and false otherwise */ + public boolean isSetSuccess() { + return org.apache.thrift.EncodingUtils.testBit(__isset_bitfield, __SUCCESS_ISSET_ID); + } + + public void setSuccessIsSet(boolean value) { + __isset_bitfield = org.apache.thrift.EncodingUtils.setBit(__isset_bitfield, __SUCCESS_ISSET_ID, value); + } + + public void setFieldValue(_Fields field, @org.apache.thrift.annotation.Nullable java.lang.Object value) { + switch (field) { + case SUCCESS: + if (value == null) { + unsetSuccess(); + } else { + setSuccess((java.lang.Boolean)value); + } + break; + + } + } + + @org.apache.thrift.annotation.Nullable + public java.lang.Object getFieldValue(_Fields field) { + switch (field) { + case SUCCESS: + return isSuccess(); + + } + throw new java.lang.IllegalStateException(); + } + + /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */ + public boolean isSet(_Fields field) { + if (field == null) { + throw new java.lang.IllegalArgumentException(); + } + + switch (field) { + case SUCCESS: + return isSetSuccess(); + } + throw new java.lang.IllegalStateException(); + } + + @Override + public boolean equals(java.lang.Object that) { + if (that instanceof backdoor_result) + return this.equals((backdoor_result)that); + return false; + } + + public boolean equals(backdoor_result that) { + if (that == null) + return false; + if (this == that) + return true; + + boolean this_present_success = true; + boolean that_present_success = true; + if (this_present_success || that_present_success) { + if (!(this_present_success && that_present_success)) + return false; + if (this.success != that.success) + return false; + } + + return true; + } + + @Override + public int hashCode() { + int hashCode = 1; + + hashCode = hashCode * 8191 + ((success) ? 131071 : 524287); + + return hashCode; + } + + @Override + public int compareTo(backdoor_result other) { + if (!getClass().equals(other.getClass())) { + return getClass().getName().compareTo(other.getClass().getName()); + } + + int lastComparison = 0; + + lastComparison = java.lang.Boolean.compare(isSetSuccess(), other.isSetSuccess()); + if (lastComparison != 0) { + return lastComparison; + } + if (isSetSuccess()) { + lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.success, other.success); + if (lastComparison != 0) { + return lastComparison; + } + } + return 0; + } + + @org.apache.thrift.annotation.Nullable + public _Fields fieldForId(int fieldId) { + return _Fields.findByThriftId(fieldId); + } + + public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException { + scheme(iprot).read(iprot, this); + } + + public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException { + scheme(oprot).write(oprot, this); + } + + @Override + public java.lang.String toString() { + java.lang.StringBuilder sb = new java.lang.StringBuilder("backdoor_result("); + boolean first = true; + + sb.append("success:"); + sb.append(this.success); + first = false; + sb.append(")"); + return sb.toString(); + } + + public void validate() throws org.apache.thrift.TException { + // check for required fields + // check for sub-struct validity + } + + private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { + try { + write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out))); + } catch (org.apache.thrift.TException te) { + throw new java.io.IOException(te); + } + } + + private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, java.lang.ClassNotFoundException { + try { + // it doesn't seem like you should have to do this, but java serialization is wacky, and doesn't call the default constructor. + __isset_bitfield = 0; + read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in))); + } catch (org.apache.thrift.TException te) { + throw new java.io.IOException(te); + } + } + + private static class backdoor_resultStandardSchemeFactory implements org.apache.thrift.scheme.SchemeFactory { + public backdoor_resultStandardScheme getScheme() { + return new backdoor_resultStandardScheme(); + } + } + + private static class backdoor_resultStandardScheme extends org.apache.thrift.scheme.StandardScheme { + + public void read(org.apache.thrift.protocol.TProtocol iprot, backdoor_result struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TField schemeField; + iprot.readStructBegin(); + while (true) + { + schemeField = iprot.readFieldBegin(); + if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { + break; + } + switch (schemeField.id) { + case 0: // SUCCESS + if (schemeField.type == org.apache.thrift.protocol.TType.BOOL) { + struct.success = iprot.readBool(); + struct.setSuccessIsSet(true); + } else { + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + break; + default: + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + iprot.readFieldEnd(); + } + iprot.readStructEnd(); + + // check for required fields of primitive type, which can't be checked in the validate method + struct.validate(); + } + + public void write(org.apache.thrift.protocol.TProtocol oprot, backdoor_result struct) throws org.apache.thrift.TException { + struct.validate(); + + oprot.writeStructBegin(STRUCT_DESC); + if (struct.isSetSuccess()) { + oprot.writeFieldBegin(SUCCESS_FIELD_DESC); + oprot.writeBool(struct.success); + oprot.writeFieldEnd(); + } + oprot.writeFieldStop(); + oprot.writeStructEnd(); + } + + } + + private static class backdoor_resultTupleSchemeFactory implements org.apache.thrift.scheme.SchemeFactory { + public backdoor_resultTupleScheme getScheme() { + return new backdoor_resultTupleScheme(); + } + } + + private static class backdoor_resultTupleScheme extends org.apache.thrift.scheme.TupleScheme { + + @Override + public void write(org.apache.thrift.protocol.TProtocol prot, backdoor_result struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TTupleProtocol oprot = (org.apache.thrift.protocol.TTupleProtocol) prot; + java.util.BitSet optionals = new java.util.BitSet(); + if (struct.isSetSuccess()) { + optionals.set(0); + } + oprot.writeBitSet(optionals, 1); + if (struct.isSetSuccess()) { + oprot.writeBool(struct.success); + } + } + + @Override + public void read(org.apache.thrift.protocol.TProtocol prot, backdoor_result struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TTupleProtocol iprot = (org.apache.thrift.protocol.TTupleProtocol) prot; + java.util.BitSet incoming = iprot.readBitSet(1); + if (incoming.get(0)) { + struct.success = iprot.readBool(); + struct.setSuccessIsSet(true); + } + } + } + + private static S scheme(org.apache.thrift.protocol.TProtocol proto) { + return (org.apache.thrift.scheme.StandardScheme.class.equals(proto.getScheme()) ? STANDARD_SCHEME_FACTORY : TUPLE_SCHEME_FACTORY).getScheme(); + } + } + +} diff --git a/e2e-tests/spring-rpc/spring-rpc-thrift/src/main/java/com/foo/rpc/examples/spring/fakemockobject/generated/FakeRetrieveData.java b/e2e-tests/spring-rpc/spring-rpc-thrift/src/main/java/com/foo/rpc/examples/spring/fakemockobject/generated/FakeRetrieveData.java new file mode 100644 index 0000000000..9488a52192 --- /dev/null +++ b/e2e-tests/spring-rpc/spring-rpc-thrift/src/main/java/com/foo/rpc/examples/spring/fakemockobject/generated/FakeRetrieveData.java @@ -0,0 +1,566 @@ +/** + * Autogenerated by Thrift Compiler (0.15.0) + * + * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + * @generated + */ +package com.foo.rpc.examples.spring.fakemockobject.generated; + +@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"}) +@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.15.0)", date = "2023-06-16") +public class FakeRetrieveData implements org.apache.thrift.TBase, java.io.Serializable, Cloneable, Comparable { + private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("FakeRetrieveData"); + + private static final org.apache.thrift.protocol.TField ID_FIELD_DESC = new org.apache.thrift.protocol.TField("id", org.apache.thrift.protocol.TType.I32, (short)1); + private static final org.apache.thrift.protocol.TField NAME_FIELD_DESC = new org.apache.thrift.protocol.TField("name", org.apache.thrift.protocol.TType.STRING, (short)2); + private static final org.apache.thrift.protocol.TField INFO_FIELD_DESC = new org.apache.thrift.protocol.TField("info", org.apache.thrift.protocol.TType.STRING, (short)3); + + private static final org.apache.thrift.scheme.SchemeFactory STANDARD_SCHEME_FACTORY = new FakeRetrieveDataStandardSchemeFactory(); + private static final org.apache.thrift.scheme.SchemeFactory TUPLE_SCHEME_FACTORY = new FakeRetrieveDataTupleSchemeFactory(); + + public int id; // required + public @org.apache.thrift.annotation.Nullable java.lang.String name; // required + public @org.apache.thrift.annotation.Nullable java.lang.String info; // required + + /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ + public enum _Fields implements org.apache.thrift.TFieldIdEnum { + ID((short)1, "id"), + NAME((short)2, "name"), + INFO((short)3, "info"); + + private static final java.util.Map byName = new java.util.HashMap(); + + static { + for (_Fields field : java.util.EnumSet.allOf(_Fields.class)) { + byName.put(field.getFieldName(), field); + } + } + + /** + * Find the _Fields constant that matches fieldId, or null if its not found. + */ + @org.apache.thrift.annotation.Nullable + public static _Fields findByThriftId(int fieldId) { + switch(fieldId) { + case 1: // ID + return ID; + case 2: // NAME + return NAME; + case 3: // INFO + return INFO; + default: + return null; + } + } + + /** + * Find the _Fields constant that matches fieldId, throwing an exception + * if it is not found. + */ + public static _Fields findByThriftIdOrThrow(int fieldId) { + _Fields fields = findByThriftId(fieldId); + if (fields == null) throw new java.lang.IllegalArgumentException("Field " + fieldId + " doesn't exist!"); + return fields; + } + + /** + * Find the _Fields constant that matches name, or null if its not found. + */ + @org.apache.thrift.annotation.Nullable + public static _Fields findByName(java.lang.String name) { + return byName.get(name); + } + + private final short _thriftId; + private final java.lang.String _fieldName; + + _Fields(short thriftId, java.lang.String fieldName) { + _thriftId = thriftId; + _fieldName = fieldName; + } + + public short getThriftFieldId() { + return _thriftId; + } + + public java.lang.String getFieldName() { + return _fieldName; + } + } + + // isset id assignments + private static final int __ID_ISSET_ID = 0; + private byte __isset_bitfield = 0; + public static final java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; + static { + java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new java.util.EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); + tmpMap.put(_Fields.ID, new org.apache.thrift.meta_data.FieldMetaData("id", org.apache.thrift.TFieldRequirementType.REQUIRED, + new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I32))); + tmpMap.put(_Fields.NAME, new org.apache.thrift.meta_data.FieldMetaData("name", org.apache.thrift.TFieldRequirementType.REQUIRED, + new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); + tmpMap.put(_Fields.INFO, new org.apache.thrift.meta_data.FieldMetaData("info", org.apache.thrift.TFieldRequirementType.REQUIRED, + new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); + metaDataMap = java.util.Collections.unmodifiableMap(tmpMap); + org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(FakeRetrieveData.class, metaDataMap); + } + + public FakeRetrieveData() { + } + + public FakeRetrieveData( + int id, + java.lang.String name, + java.lang.String info) + { + this(); + this.id = id; + setIdIsSet(true); + this.name = name; + this.info = info; + } + + /** + * Performs a deep copy on other. + */ + public FakeRetrieveData(FakeRetrieveData other) { + __isset_bitfield = other.__isset_bitfield; + this.id = other.id; + if (other.isSetName()) { + this.name = other.name; + } + if (other.isSetInfo()) { + this.info = other.info; + } + } + + public FakeRetrieveData deepCopy() { + return new FakeRetrieveData(this); + } + + @Override + public void clear() { + setIdIsSet(false); + this.id = 0; + this.name = null; + this.info = null; + } + + public int getId() { + return this.id; + } + + public FakeRetrieveData setId(int id) { + this.id = id; + setIdIsSet(true); + return this; + } + + public void unsetId() { + __isset_bitfield = org.apache.thrift.EncodingUtils.clearBit(__isset_bitfield, __ID_ISSET_ID); + } + + /** Returns true if field id is set (has been assigned a value) and false otherwise */ + public boolean isSetId() { + return org.apache.thrift.EncodingUtils.testBit(__isset_bitfield, __ID_ISSET_ID); + } + + public void setIdIsSet(boolean value) { + __isset_bitfield = org.apache.thrift.EncodingUtils.setBit(__isset_bitfield, __ID_ISSET_ID, value); + } + + @org.apache.thrift.annotation.Nullable + public java.lang.String getName() { + return this.name; + } + + public FakeRetrieveData setName(@org.apache.thrift.annotation.Nullable java.lang.String name) { + this.name = name; + return this; + } + + public void unsetName() { + this.name = null; + } + + /** Returns true if field name is set (has been assigned a value) and false otherwise */ + public boolean isSetName() { + return this.name != null; + } + + public void setNameIsSet(boolean value) { + if (!value) { + this.name = null; + } + } + + @org.apache.thrift.annotation.Nullable + public java.lang.String getInfo() { + return this.info; + } + + public FakeRetrieveData setInfo(@org.apache.thrift.annotation.Nullable java.lang.String info) { + this.info = info; + return this; + } + + public void unsetInfo() { + this.info = null; + } + + /** Returns true if field info is set (has been assigned a value) and false otherwise */ + public boolean isSetInfo() { + return this.info != null; + } + + public void setInfoIsSet(boolean value) { + if (!value) { + this.info = null; + } + } + + public void setFieldValue(_Fields field, @org.apache.thrift.annotation.Nullable java.lang.Object value) { + switch (field) { + case ID: + if (value == null) { + unsetId(); + } else { + setId((java.lang.Integer)value); + } + break; + + case NAME: + if (value == null) { + unsetName(); + } else { + setName((java.lang.String)value); + } + break; + + case INFO: + if (value == null) { + unsetInfo(); + } else { + setInfo((java.lang.String)value); + } + break; + + } + } + + @org.apache.thrift.annotation.Nullable + public java.lang.Object getFieldValue(_Fields field) { + switch (field) { + case ID: + return getId(); + + case NAME: + return getName(); + + case INFO: + return getInfo(); + + } + throw new java.lang.IllegalStateException(); + } + + /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */ + public boolean isSet(_Fields field) { + if (field == null) { + throw new java.lang.IllegalArgumentException(); + } + + switch (field) { + case ID: + return isSetId(); + case NAME: + return isSetName(); + case INFO: + return isSetInfo(); + } + throw new java.lang.IllegalStateException(); + } + + @Override + public boolean equals(java.lang.Object that) { + if (that instanceof FakeRetrieveData) + return this.equals((FakeRetrieveData)that); + return false; + } + + public boolean equals(FakeRetrieveData that) { + if (that == null) + return false; + if (this == that) + return true; + + boolean this_present_id = true; + boolean that_present_id = true; + if (this_present_id || that_present_id) { + if (!(this_present_id && that_present_id)) + return false; + if (this.id != that.id) + return false; + } + + boolean this_present_name = true && this.isSetName(); + boolean that_present_name = true && that.isSetName(); + if (this_present_name || that_present_name) { + if (!(this_present_name && that_present_name)) + return false; + if (!this.name.equals(that.name)) + return false; + } + + boolean this_present_info = true && this.isSetInfo(); + boolean that_present_info = true && that.isSetInfo(); + if (this_present_info || that_present_info) { + if (!(this_present_info && that_present_info)) + return false; + if (!this.info.equals(that.info)) + return false; + } + + return true; + } + + @Override + public int hashCode() { + int hashCode = 1; + + hashCode = hashCode * 8191 + id; + + hashCode = hashCode * 8191 + ((isSetName()) ? 131071 : 524287); + if (isSetName()) + hashCode = hashCode * 8191 + name.hashCode(); + + hashCode = hashCode * 8191 + ((isSetInfo()) ? 131071 : 524287); + if (isSetInfo()) + hashCode = hashCode * 8191 + info.hashCode(); + + return hashCode; + } + + @Override + public int compareTo(FakeRetrieveData other) { + if (!getClass().equals(other.getClass())) { + return getClass().getName().compareTo(other.getClass().getName()); + } + + int lastComparison = 0; + + lastComparison = java.lang.Boolean.compare(isSetId(), other.isSetId()); + if (lastComparison != 0) { + return lastComparison; + } + if (isSetId()) { + lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.id, other.id); + if (lastComparison != 0) { + return lastComparison; + } + } + lastComparison = java.lang.Boolean.compare(isSetName(), other.isSetName()); + if (lastComparison != 0) { + return lastComparison; + } + if (isSetName()) { + lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.name, other.name); + if (lastComparison != 0) { + return lastComparison; + } + } + lastComparison = java.lang.Boolean.compare(isSetInfo(), other.isSetInfo()); + if (lastComparison != 0) { + return lastComparison; + } + if (isSetInfo()) { + lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.info, other.info); + if (lastComparison != 0) { + return lastComparison; + } + } + return 0; + } + + @org.apache.thrift.annotation.Nullable + public _Fields fieldForId(int fieldId) { + return _Fields.findByThriftId(fieldId); + } + + public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException { + scheme(iprot).read(iprot, this); + } + + public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException { + scheme(oprot).write(oprot, this); + } + + @Override + public java.lang.String toString() { + java.lang.StringBuilder sb = new java.lang.StringBuilder("FakeRetrieveData("); + boolean first = true; + + sb.append("id:"); + sb.append(this.id); + first = false; + if (!first) sb.append(", "); + sb.append("name:"); + if (this.name == null) { + sb.append("null"); + } else { + sb.append(this.name); + } + first = false; + if (!first) sb.append(", "); + sb.append("info:"); + if (this.info == null) { + sb.append("null"); + } else { + sb.append(this.info); + } + first = false; + sb.append(")"); + return sb.toString(); + } + + public void validate() throws org.apache.thrift.TException { + // check for required fields + // alas, we cannot check 'id' because it's a primitive and you chose the non-beans generator. + if (name == null) { + throw new org.apache.thrift.protocol.TProtocolException("Required field 'name' was not present! Struct: " + toString()); + } + if (info == null) { + throw new org.apache.thrift.protocol.TProtocolException("Required field 'info' was not present! Struct: " + toString()); + } + // check for sub-struct validity + } + + private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { + try { + write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out))); + } catch (org.apache.thrift.TException te) { + throw new java.io.IOException(te); + } + } + + private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, java.lang.ClassNotFoundException { + try { + // it doesn't seem like you should have to do this, but java serialization is wacky, and doesn't call the default constructor. + __isset_bitfield = 0; + read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in))); + } catch (org.apache.thrift.TException te) { + throw new java.io.IOException(te); + } + } + + private static class FakeRetrieveDataStandardSchemeFactory implements org.apache.thrift.scheme.SchemeFactory { + public FakeRetrieveDataStandardScheme getScheme() { + return new FakeRetrieveDataStandardScheme(); + } + } + + private static class FakeRetrieveDataStandardScheme extends org.apache.thrift.scheme.StandardScheme { + + public void read(org.apache.thrift.protocol.TProtocol iprot, FakeRetrieveData struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TField schemeField; + iprot.readStructBegin(); + while (true) + { + schemeField = iprot.readFieldBegin(); + if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { + break; + } + switch (schemeField.id) { + case 1: // ID + if (schemeField.type == org.apache.thrift.protocol.TType.I32) { + struct.id = iprot.readI32(); + struct.setIdIsSet(true); + } else { + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + break; + case 2: // NAME + if (schemeField.type == org.apache.thrift.protocol.TType.STRING) { + struct.name = iprot.readString(); + struct.setNameIsSet(true); + } else { + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + break; + case 3: // INFO + if (schemeField.type == org.apache.thrift.protocol.TType.STRING) { + struct.info = iprot.readString(); + struct.setInfoIsSet(true); + } else { + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + break; + default: + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + iprot.readFieldEnd(); + } + iprot.readStructEnd(); + + // check for required fields of primitive type, which can't be checked in the validate method + if (!struct.isSetId()) { + throw new org.apache.thrift.protocol.TProtocolException("Required field 'id' was not found in serialized data! Struct: " + toString()); + } + struct.validate(); + } + + public void write(org.apache.thrift.protocol.TProtocol oprot, FakeRetrieveData struct) throws org.apache.thrift.TException { + struct.validate(); + + oprot.writeStructBegin(STRUCT_DESC); + oprot.writeFieldBegin(ID_FIELD_DESC); + oprot.writeI32(struct.id); + oprot.writeFieldEnd(); + if (struct.name != null) { + oprot.writeFieldBegin(NAME_FIELD_DESC); + oprot.writeString(struct.name); + oprot.writeFieldEnd(); + } + if (struct.info != null) { + oprot.writeFieldBegin(INFO_FIELD_DESC); + oprot.writeString(struct.info); + oprot.writeFieldEnd(); + } + oprot.writeFieldStop(); + oprot.writeStructEnd(); + } + + } + + private static class FakeRetrieveDataTupleSchemeFactory implements org.apache.thrift.scheme.SchemeFactory { + public FakeRetrieveDataTupleScheme getScheme() { + return new FakeRetrieveDataTupleScheme(); + } + } + + private static class FakeRetrieveDataTupleScheme extends org.apache.thrift.scheme.TupleScheme { + + @Override + public void write(org.apache.thrift.protocol.TProtocol prot, FakeRetrieveData struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TTupleProtocol oprot = (org.apache.thrift.protocol.TTupleProtocol) prot; + oprot.writeI32(struct.id); + oprot.writeString(struct.name); + oprot.writeString(struct.info); + } + + @Override + public void read(org.apache.thrift.protocol.TProtocol prot, FakeRetrieveData struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TTupleProtocol iprot = (org.apache.thrift.protocol.TTupleProtocol) prot; + struct.id = iprot.readI32(); + struct.setIdIsSet(true); + struct.name = iprot.readString(); + struct.setNameIsSet(true); + struct.info = iprot.readString(); + struct.setInfoIsSet(true); + } + } + + private static S scheme(org.apache.thrift.protocol.TProtocol proto) { + return (org.apache.thrift.scheme.StandardScheme.class.equals(proto.getScheme()) ? STANDARD_SCHEME_FACTORY : TUPLE_SCHEME_FACTORY).getScheme(); + } +} + diff --git a/e2e-tests/spring-rpc/spring-rpc-thrift/src/main/java/com/foo/rpc/examples/spring/fakemockobject/impl/FakeMockObjectApp.java b/e2e-tests/spring-rpc/spring-rpc-thrift/src/main/java/com/foo/rpc/examples/spring/fakemockobject/impl/FakeMockObjectApp.java new file mode 100644 index 0000000000..45d28c6d2d --- /dev/null +++ b/e2e-tests/spring-rpc/spring-rpc-thrift/src/main/java/com/foo/rpc/examples/spring/fakemockobject/impl/FakeMockObjectApp.java @@ -0,0 +1,32 @@ +package com.foo.rpc.examples.spring.fakemockobject.impl; + +import com.foo.rpc.examples.spring.fakemockobject.generated.FakeMockObjectService; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.server.TServlet; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@SpringBootApplication(exclude = SecurityAutoConfiguration.class) +public class FakeMockObjectApp { + + public static void main(String[] args) { + SpringApplication.run(FakeMockObjectApp.class, args); + } + + @Bean + public TProtocolFactory tProtocolFactory() { + return new TBinaryProtocol.Factory(); + } + + @Bean + public ServletRegistrationBean customizationServlet(TProtocolFactory protocolFactory, FakeMockObjectServiceImpl service) { + TServlet tServlet = new TServlet(new FakeMockObjectService.Processor<>(service), protocolFactory); + return new ServletRegistrationBean(tServlet, "/fakemockobject"); + } +} diff --git a/e2e-tests/spring-rpc/spring-rpc-thrift/src/main/java/com/foo/rpc/examples/spring/fakemockobject/impl/FakeMockObjectServiceImpl.java b/e2e-tests/spring-rpc/spring-rpc-thrift/src/main/java/com/foo/rpc/examples/spring/fakemockobject/impl/FakeMockObjectServiceImpl.java new file mode 100644 index 0000000000..8d6e37f878 --- /dev/null +++ b/e2e-tests/spring-rpc/spring-rpc-thrift/src/main/java/com/foo/rpc/examples/spring/fakemockobject/impl/FakeMockObjectServiceImpl.java @@ -0,0 +1,56 @@ +package com.foo.rpc.examples.spring.fakemockobject.impl; + +import com.foo.rpc.examples.spring.fakemockobject.generated.FakeDatabaseRow; +import com.foo.rpc.examples.spring.fakemockobject.generated.FakeMockObjectService; +import com.foo.rpc.examples.spring.fakemockobject.generated.FakeRetrieveData; +import org.apache.thrift.TException; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class FakeMockObjectServiceImpl implements FakeMockObjectService.Iface{ + + private final Map retrieveDataMap = new HashMap<>(); + private final Map dbDataMap = new HashMap<>(); + + + @Override + public String getFooFromExternalService(int id) throws TException { + if (retrieveDataMap.containsKey(id)){ + FakeRetrieveData row = retrieveDataMap.get(id); + return "EX:::"+(row.name==null?"untitled":row.name)+":::"+(row.info==null?"NONE":row.info); + } + return "NOT FOUND EX"; + } + + @Override + public String getBarFromDatabase(int id) throws TException { + if (dbDataMap.containsKey(id)){ + FakeDatabaseRow row = dbDataMap.get(id); + return "DB:::"+(row.name==null?"untitled":row.name)+":::"+(row.info==null?"NONE":row.info); + } + return "NOT FOUND DB"; + } + + @Override + public boolean backdoor(FakeRetrieveData exData, FakeDatabaseRow dbData) throws TException { + if (exData == null && dbData == null){ + retrieveDataMap.clear(); + dbDataMap.clear(); + return true; + } + if (exData != null){ + retrieveDataMap.put(exData.id, exData); + } + if (dbData != null){ + dbDataMap.put(dbData.id, dbData); + } + + + return true; + } +} diff --git a/e2e-tests/spring-rpc/spring-rpc-thrift/src/main/resources/schema/fakemockobject.thrift b/e2e-tests/spring-rpc/spring-rpc-thrift/src/main/resources/schema/fakemockobject.thrift new file mode 100644 index 0000000000..d4230b8c50 --- /dev/null +++ b/e2e-tests/spring-rpc/spring-rpc-thrift/src/main/resources/schema/fakemockobject.thrift @@ -0,0 +1,25 @@ +namespace java com.foo.rpc.examples.spring.fakemockobject.generated + +struct FakeRetrieveData{ + + 1: required i32 id, + 2: required string name, + 3: required string info +} + +struct FakeDatabaseRow{ + + 1: required i32 id, + 2: required string name, + 3: required string info +} + +service FakeMockObjectService { + + string getFooFromExternalService(1:i32 id), + + string getBarFromDatabase(1:i32 id) + + bool backdoor(1:FakeRetrieveData exData, 2:FakeDatabaseRow dbData) + +} From a70c2e6dd7468cbb85ac5bbb9664ee015ef9314f Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Fri, 16 Jun 2023 20:26:23 +0200 Subject: [PATCH 092/345] add em test --- .../FakeMockObjectController.java | 183 ++++++++++++++++++ .../fakemockobject/FakeMockObjectEMTest.java | 60 ++++++ 2 files changed, 243 insertions(+) create mode 100644 e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/com/foo/rpc/examples/spring/fakemockobject/FakeMockObjectController.java create mode 100644 e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/org/evomaster/e2etests/spring/rpc/examples/fakemockobject/FakeMockObjectEMTest.java diff --git a/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/com/foo/rpc/examples/spring/fakemockobject/FakeMockObjectController.java b/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/com/foo/rpc/examples/spring/fakemockobject/FakeMockObjectController.java new file mode 100644 index 0000000000..845bfd552b --- /dev/null +++ b/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/com/foo/rpc/examples/spring/fakemockobject/FakeMockObjectController.java @@ -0,0 +1,183 @@ +package com.foo.rpc.examples.spring.fakemockobject; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.foo.rpc.examples.spring.SpringController; +import com.foo.rpc.examples.spring.fakemockobject.generated.FakeDatabaseRow; +import com.foo.rpc.examples.spring.fakemockobject.generated.FakeMockObjectService; +import com.foo.rpc.examples.spring.fakemockobject.generated.FakeRetrieveData; +import com.foo.rpc.examples.spring.fakemockobject.impl.FakeMockObjectApp; +import org.apache.thrift.TException; +import org.apache.thrift.TSerializer; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TSimpleJSONProtocol; +import org.apache.thrift.transport.THttpClient; +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; +import org.evomaster.client.java.controller.api.dto.problem.rpc.*; +import org.evomaster.client.java.controller.problem.ProblemInfo; +import org.evomaster.client.java.controller.problem.RPCProblem; + +import java.util.*; + +public class FakeMockObjectController extends SpringController { + + private final static TSerializer serializer; + private final static ObjectMapper mapper = new ObjectMapper(); + + static { + try { + serializer = new TSerializer(new TSimpleJSONProtocol.Factory()); + } catch (TTransportException e) { + throw new RuntimeException(e); + } + } + + private FakeMockObjectService.Client client; + + public FakeMockObjectController() { + super(FakeMockObjectApp.class); + } + + @Override + public ProblemInfo getProblemInfo() { + return new RPCProblem(new HashMap() {{ + put(FakeMockObjectService.Iface.class.getName(), client); + }}, new HashMap>() {{ + put(FakeMockObjectService.Iface.class.getName(), new ArrayList(Collections.singletonList("backdoor"))); + }}, null, null, null, RPCType.GENERAL); + } + + @Override + public String startClient() { + String url = "http://localhost:"+getSutPort()+"/fakemockobject"; + try { + // init client + TTransport transport = new THttpClient(url); + TProtocol protocol = new TBinaryProtocol(transport); + client = new FakeMockObjectService.Client(protocol); + } catch (TTransportException e) { + e.printStackTrace(); + } + + return url; + } + + @Override + public List seedRPCTests() { + + FakeRetrieveData seededData = new FakeRetrieveData(){{ + id = 0; + name = "foo"; + info = "2023-06-16"; + }}; + + FakeDatabaseRow seededRow = new FakeDatabaseRow(){{ + id = 42; + name = "bar"; + info = "2023-06-16"; + }}; + + + String seededDataJson = null; + String seededRowJson = null; + try { + seededDataJson = serializer.toString(seededData); + seededRowJson = serializer.toString(seededRow); + } catch (TException e) { + throw new RuntimeException(e); + } + + String finalSeededDataJson = seededDataJson; + String finalSeededRowJson = seededRowJson; + return Arrays.asList( + new SeededRPCTestDto(){{ + testName = "test_1"; + rpcFunctions = Arrays.asList( + new SeededRPCActionDto(){{ + interfaceName = FakeMockObjectService.Iface.class.getName(); + functionName = "getFooFromExternalService"; + inputParams= Arrays.asList("0"); + inputParamTypes= Arrays.asList(int.class.getName()); + mockRPCExternalServiceDtos= Arrays.asList( + new MockRPCExternalServiceDto(){{ + appKey = "fake.app"; + interfaceFullName = "fake.interface"; + functionName = "fake.func"; + responses = Arrays.asList(finalSeededDataJson); + responseTypes = Arrays.asList( + FakeRetrieveData.class.getName() + ); + }} + ); + }} + ); + }}, + + new SeededRPCTestDto(){{ + testName = "test_2"; + rpcFunctions = Arrays.asList( + new SeededRPCActionDto(){{ + interfaceName = FakeMockObjectService.Iface.class.getName(); + functionName = "getBarFromDatabase"; + inputParams= Arrays.asList("42"); + inputParamTypes= Arrays.asList(int.class.getName()); + mockDatabaseDtos = Arrays.asList( + new MockDatabaseDto(){{ + appKey = "fake.app"; + commandName = "fake.command"; + response = finalSeededRowJson; + responseFullType = FakeDatabaseRow.class.getName(); + }} + ); + }} + ); + }} + ); + } + + @Override + public boolean customizeMockingDatabase(List databaseDtos, boolean enabled) { + try { + if (enabled){ + boolean ok = true; + for (MockDatabaseDto dto: databaseDtos){ + if (dto.response!= null && dto.responseFullType != null){ + Class clazz = Class.forName(dto.responseFullType); + FakeDatabaseRow data = (FakeDatabaseRow) mapper.readValue(dto.response, clazz); + ok = ok && client.backdoor(null, data); + } + } + return ok; + }else { + return client.backdoor(null, null); + } + } catch (TException | ClassNotFoundException | JsonProcessingException e) { + return false; + } + } + + @Override + public boolean customizeMockingRPCExternalService(List externalServiceDtos, boolean enabled) { + + try { + if (enabled){ + boolean ok = true; + for (MockRPCExternalServiceDto dto: externalServiceDtos){ + if (dto.responses!= null && !dto.responses.isEmpty() && dto.responses.size() == dto.responseTypes.size()){ + Class clazz = Class.forName(dto.responseTypes.get(0)); + FakeRetrieveData data = (FakeRetrieveData) mapper.readValue(dto.responses.get(0), clazz); + ok = ok && client.backdoor(data, null); + } + } + return ok; + }else { + return client.backdoor(null, null); + } + } catch (TException | ClassNotFoundException | JsonProcessingException e) { + return false; + } + } +} + diff --git a/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/org/evomaster/e2etests/spring/rpc/examples/fakemockobject/FakeMockObjectEMTest.java b/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/org/evomaster/e2etests/spring/rpc/examples/fakemockobject/FakeMockObjectEMTest.java new file mode 100644 index 0000000000..9a05c5ba24 --- /dev/null +++ b/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/org/evomaster/e2etests/spring/rpc/examples/fakemockobject/FakeMockObjectEMTest.java @@ -0,0 +1,60 @@ +package org.evomaster.e2etests.spring.rpc.examples.fakemockobject; + +import com.foo.rpc.examples.spring.fakemockobject.FakeMockObjectController; +import com.foo.rpc.examples.spring.fakemockobject.generated.FakeMockObjectService; +import org.evomaster.core.problem.rpc.RPCCallResultCategory; +import org.evomaster.core.problem.rpc.RPCIndividual; +import org.evomaster.core.search.Solution; +import org.evomaster.e2etests.spring.rpc.examples.SpringRPCTestBase; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + + +import java.util.Arrays; + + +import static org.junit.jupiter.api.Assertions.*; + +public class FakeMockObjectEMTest extends SpringRPCTestBase { + + @BeforeAll + public static void initClass() throws Exception { + SpringRPCTestBase.initClass(new FakeMockObjectController()); + } + + + @Test + public void testRunEM() throws Throwable { + + + runTestHandlingFlakyAndCompilation( + "FakeMockObjectEM", + "org.bar.FakeMockObjectEM", + 10, + (args) -> { + args.add("--baseTaintAnalysisProbability"); + args.add("0.9"); + args.add("--seedTestCases"); + args.add("true"); + + args.add("--enableCustomizedExternalServiceHandling"); + args.add("true"); + + args.add("--enableBasicAssertions"); + args.add("true"); + + Solution solution = initAndRun(args); + + assertRPCEndpointResult(solution, FakeMockObjectService.Iface.class.getName()+":getFooFromExternalService", RPCCallResultCategory.HANDLED.name()); + assertContentInResponseForEndpoint(solution, FakeMockObjectService.Iface.class.getName()+":getFooFromExternalService", "EX:::"); + assertRPCEndpointResult(solution, FakeMockObjectService.Iface.class.getName()+":getBarFromDatabase", RPCCallResultCategory.HANDLED.name()); + assertContentInResponseForEndpoint(solution, FakeMockObjectService.Iface.class.getName()+":getBarFromDatabase", "DB:::"); + + assertTrue(solution.getIndividuals().size() >= 1); + + }); + } + + +} + From e6c9704d72681ed7e4fa3c74e9ac508d05687144 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Fri, 16 Jun 2023 20:44:04 +0200 Subject: [PATCH 093/345] add doc --- .../api/dto/problem/rpc/MockDatabaseDto.java | 3 +++ .../evomaster/client/java/controller/SutHandler.java | 11 ++++++++++- core/src/main/kotlin/org/evomaster/core/EMConfig.kt | 8 ++------ .../core/output/service/RPCTestCaseWriter.kt | 5 ++--- .../evomaster/core/output/service/TestSuiteWriter.kt | 2 +- .../externalservice/rpc/DbAsExternalServiceAction.kt | 3 +++ .../remote/service/RemoteControllerImplementation.kt | 4 ++-- .../kotlin/org/evomaster/core/search/FitnessValue.kt | 8 ++++++++ .../core/output/service/TestSuiteWriterRPCTest.kt | 2 +- docs/options.md | 3 +-- 10 files changed, 33 insertions(+), 16 deletions(-) diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/MockDatabaseDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/MockDatabaseDto.java index b716a06b5b..421f5a9b25 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/MockDatabaseDto.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/MockDatabaseDto.java @@ -1,5 +1,8 @@ package org.evomaster.client.java.controller.api.dto.problem.rpc; +/** + * mock object for database + */ public class MockDatabaseDto { /** diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/SutHandler.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/SutHandler.java index d8e30124e5..d9e7eb96df 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/SutHandler.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/SutHandler.java @@ -205,7 +205,16 @@ default boolean mockRPCExternalServicesWithCustomizedHandling(String externalSer } - + /** + *

+ * a method to employ customized mocking for database + *

+ * @param mockDatabaseObjectDtos contains info about how to set up mock object for databases with json format, note that the json should + * be able to be converted to a list of MockDatabaseDto + * @param enabled reflect to enable (set it true) or disable (set it false) the specified mock object + * Note that null [mockDatabaseObjectDtos] with false [enabled] means that all existing mock objects for databases should be disabled. + * @return whether the mocked instance starts successfully, + */ default boolean mockDatabasesWithCustomizedHandling(String mockDatabaseObjectDtos, boolean enabled){ return false; } diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index 327d31c8eb..eb4ce37789 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -1822,12 +1822,8 @@ class EMConfig { var externalServiceIP : String = "127.0.0.2" @Experimental - @Cfg("Whether to apply customized method (i.e., implement 'customizeMockingRPCExternalService') to handle external services.") - var enableCustomizedExternalServiceHandling = false - - @Experimental - @Cfg("Whether to apply customized method (i.e., implement 'customizeMockingDatabase') to handle database.") - var enableCustomizedDatabaseHandling = false + @Cfg("Whether to apply customized method (i.e., implement 'customizeMockingRPCExternalService' for external services or 'customizeMockingDatabase' for database) to handle mock object.") + var enableCustomizedMethodForMockObjectHandling = false @Experimental @Cfg("Whether to save mocked responses as separated files") diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/RPCTestCaseWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/RPCTestCaseWriter.kt index c250863209..2e63abedc4 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/RPCTestCaseWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/RPCTestCaseWriter.kt @@ -17,7 +17,6 @@ import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.gene.utils.GeneUtils import org.slf4j.Logger import org.slf4j.LoggerFactory -import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths import kotlin.math.max @@ -287,7 +286,7 @@ class RPCTestCaseWriter : ApiTestCaseWriter() { * @param lines are generated lines which save the generated test scripts */ private fun handleCustomizedExternalServiceHandling(action: Action, index: Int, testCaseName: String, enable: Boolean, lines: Lines, testSuitePath: Path?){ - if(config.enableCustomizedExternalServiceHandling && action.parent is EnterpriseActionGroup){ + if(config.enableCustomizedMethodForMockObjectHandling && action.parent is EnterpriseActionGroup){ val group = action.parent as EnterpriseActionGroup /* @@ -319,7 +318,7 @@ class RPCTestCaseWriter : ApiTestCaseWriter() { * @param lines are generated lines which save the generated test scripts */ private fun handleCustomizedMockDatabaseHandling(action: Action, index: Int, testCaseName: String, enable: Boolean, lines: Lines, testSuitePath: Path?){ - if(config.enableCustomizedExternalServiceHandling && action.parent is EnterpriseActionGroup){ + if(config.enableCustomizedMethodForMockObjectHandling && action.parent is EnterpriseActionGroup){ val group = action.parent as EnterpriseActionGroup val mockDbActions = group.getExternalServiceActions() diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt index 2a3ce560c9..0661502574 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt @@ -799,7 +799,7 @@ class TestSuiteWriter { } } - if (config.enableCustomizedExternalServiceHandling && testCaseWriter is RPCTestCaseWriter){ + if (config.enableCustomizedMethodForMockObjectHandling && testCaseWriter is RPCTestCaseWriter){ lines.add((testCaseWriter as RPCTestCaseWriter).resetExternalServicesWithCustomizedMethod()) lines.add((testCaseWriter as RPCTestCaseWriter).resetMockDatabaseObjectWithCustomizedMethod()) } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/DbAsExternalServiceAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/DbAsExternalServiceAction.kt index 2d8044510f..e4e4fbed77 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/DbAsExternalServiceAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/DbAsExternalServiceAction.kt @@ -5,6 +5,9 @@ import org.evomaster.core.problem.externalservice.rpc.parm.ClassResponseParam import org.evomaster.core.search.StructuralElement import org.evomaster.core.search.gene.Gene +/** + * this is to handle database actions when it is handled with mock object + */ class DbAsExternalServiceAction ( /** * command name which will result in sql execution diff --git a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt index 723da9019e..593c0553e1 100644 --- a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt +++ b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt @@ -232,7 +232,7 @@ class RemoteControllerImplementation() : RemoteController{ getWebTarget() .path(ControllerConstants.RUN_SUT_PATH) .request() - .put(Entity.json(SutRunDto(run, reset, config.enableCustomizedExternalServiceHandling, computeSqlHeuristics, extractSqlExecutionInfo, config.methodReplacementCategories()))) + .put(Entity.json(SutRunDto(run, reset, config.enableCustomizedMethodForMockObjectHandling, computeSqlHeuristics, extractSqlExecutionInfo, config.methodReplacementCategories()))) } } catch (e: Exception) { log.warn("Failed to connect to SUT: ${e.message}") @@ -463,4 +463,4 @@ class RemoteControllerImplementation() : RemoteController{ log.warn("Failed to parse dto", exception) } } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt b/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt index f2363096a8..6a9b003a26 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt @@ -177,7 +177,11 @@ class FitnessValue( .count() } + /** + * set info of targets covered by seeded tests + */ fun setTargetsCoveredBySeeding(coveredTargets: List){ + coveredTargetsDuringSeeding.clear() coveredTargetsDuringSeeding.addAll(coveredTargets) } @@ -189,6 +193,10 @@ class FitnessValue( targets.containsKey(it) && targets[it]!!.distance == MAX_VALUE }.size } + + /** + * @return an amount of targets covered by seeded tests and starting with [prefix] + */ fun coveredTargetsDuringSeeding(prefix: String, idMapper: IdMapper) : Int{ return coveredTargetsDuringSeeding .count { diff --git a/core/src/test/kotlin/org/evomaster/core/output/service/TestSuiteWriterRPCTest.kt b/core/src/test/kotlin/org/evomaster/core/output/service/TestSuiteWriterRPCTest.kt index 62197e9c29..b39662b43e 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/service/TestSuiteWriterRPCTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/service/TestSuiteWriterRPCTest.kt @@ -46,7 +46,7 @@ class TestSuiteWriterRPCTest{ // rpc test generation configuration config.enablePureRPCTestGeneration = true - config.enableCustomizedExternalServiceHandling=true + config.enableCustomizedMethodForMockObjectHandling=true config.enableRPCAssertionWithInstance = true config.enableBasicAssertions = true config.saveMockedResponseAsSeparatedFile = true diff --git a/docs/options.md b/docs/options.md index e028068598..401ba91d03 100644 --- a/docs/options.md +++ b/docs/options.md @@ -161,8 +161,7 @@ There are 3 types of options: |`employResourceSizeHandlingStrategy`| __Enum__. Specify a strategy to determinate a number of resources to be manipulated throughout the search. *Valid values*: `NONE, RANDOM, DPC`. *Default value*: `NONE`.| |`employSmartDbClean`| __Boolean__. Specify whether to employ smart database clean to clear data in the database if the SUT has.`null` represents to employ the setting specified on the EM driver side. *Default value*: `null`.| |`enableAdaptiveResourceStructureMutation`| __Boolean__. Specify whether to decide the resource-based structure mutator and resource to be mutated adaptively based on impacts during focused search.Note that it only works when resource-based solution is enabled for solving REST problem. *Default value*: `false`.| -|`enableCustomizedDatabaseHandling`| __Boolean__. Whether to apply customized method (i.e., implement 'customizeMockingDatabase') to handle database. *Default value*: `false`.| -|`enableCustomizedExternalServiceHandling`| __Boolean__. Whether to apply customized method (i.e., implement 'customizeMockingRPCExternalService') to handle external services. *Default value*: `false`.| +|`enableCustomizedMethodForMockObjectHandling`| __Boolean__. Whether to apply customized method (i.e., implement 'customizeMockingRPCExternalService' for external services or 'customizeMockingDatabase' for database) to handle mock object. *Default value*: `false`.| |`enableNLPParser`| __Boolean__. Whether to employ NLP parser to process text. Note that to enable this parser, it is required to build the EvoMaster with the resource profile, i.e., mvn clean install -Presourceexp -DskipTests. *Default value*: `false`.| |`enableProcessMonitor`| __Boolean__. Whether or not enable a search process monitor for archiving evaluated individuals and Archive regarding an evaluation of search. This is only needed when running experiments with different parameter settings. *Default value*: `false`.| |`enablePureRPCTestGeneration`| __Boolean__. Whether to generate RPC endpoint invocation which is independent from EM driver. *Default value*: `false`.| From 2ce732324a57d9939fb51fa49c40016a6f0664ac Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Fri, 16 Jun 2023 22:54:38 +0200 Subject: [PATCH 094/345] fix failing tests --- .../rpc/examples/fakemockobject/FakeMockObjectEMTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/org/evomaster/e2etests/spring/rpc/examples/fakemockobject/FakeMockObjectEMTest.java b/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/org/evomaster/e2etests/spring/rpc/examples/fakemockobject/FakeMockObjectEMTest.java index 9a05c5ba24..759ca9a2e3 100644 --- a/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/org/evomaster/e2etests/spring/rpc/examples/fakemockobject/FakeMockObjectEMTest.java +++ b/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/org/evomaster/e2etests/spring/rpc/examples/fakemockobject/FakeMockObjectEMTest.java @@ -37,7 +37,7 @@ public void testRunEM() throws Throwable { args.add("--seedTestCases"); args.add("true"); - args.add("--enableCustomizedExternalServiceHandling"); + args.add("--enableCustomizedMethodForMockObjectHandling"); args.add("true"); args.add("--enableBasicAssertions"); From 06aaca630b16b0a371b3bc3ae875ef70e260ca8b Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 19 Jun 2023 13:15:57 +0200 Subject: [PATCH 095/345] address comments --- .../{problem/rpc => }/MockDatabaseDto.java | 8 +++++--- .../api/dto/problem/rpc/RPCActionDto.java | 2 ++ .../dto/problem/rpc/SeededRPCActionDto.java | 3 ++- .../java/controller/CustomizationHandler.java | 2 +- .../controller/internal/SutController.java | 1 + .../rest/service/AbstractRestSampler.kt | 20 +++++++++++-------- .../rpc/service/RPCEndpointsHandler.kt | 3 ++- .../core/problem/rpc/service/RPCSampler.kt | 10 ++++++++-- .../FakeMockObjectController.java | 1 + 9 files changed, 34 insertions(+), 16 deletions(-) rename client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/{problem/rpc => }/MockDatabaseDto.java (76%) diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/MockDatabaseDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/MockDatabaseDto.java similarity index 76% rename from client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/MockDatabaseDto.java rename to client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/MockDatabaseDto.java index 421f5a9b25..4ceea434cd 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/MockDatabaseDto.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/MockDatabaseDto.java @@ -1,4 +1,4 @@ -package org.evomaster.client.java.controller.api.dto.problem.rpc; +package org.evomaster.client.java.controller.api.dto; /** * mock object for database @@ -11,10 +11,12 @@ public class MockDatabaseDto { public String appKey; /** - * it refers to a type of database, eg, mybatis + * it refers to a type of database or related framework, eg, mybatis * nullable + * + * such info might be used by mocking technique for handling mock object */ - public String databaseType; + public String databaseOrFrameworkType; /** * it refers to a type of SQL, eg, select diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/RPCActionDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/RPCActionDto.java index 5d3c88fcf4..563d843fe7 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/RPCActionDto.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/RPCActionDto.java @@ -1,5 +1,7 @@ package org.evomaster.client.java.controller.api.dto.problem.rpc; +import org.evomaster.client.java.controller.api.dto.MockDatabaseDto; + import java.util.ArrayList; import java.util.HashSet; import java.util.List; diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/SeededRPCActionDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/SeededRPCActionDto.java index 15c3396655..94e190faa9 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/SeededRPCActionDto.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/SeededRPCActionDto.java @@ -1,7 +1,8 @@ package org.evomaster.client.java.controller.api.dto.problem.rpc; +import org.evomaster.client.java.controller.api.dto.MockDatabaseDto; + import java.util.List; -import java.util.stream.Collectors; /** * seeded RPC action diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/CustomizationHandler.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/CustomizationHandler.java index f519c0aeaf..ed6baf2fa1 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/CustomizationHandler.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/CustomizationHandler.java @@ -1,9 +1,9 @@ package org.evomaster.client.java.controller; -import org.evomaster.client.java.controller.api.dto.ActionResponseDto; import org.evomaster.client.java.controller.api.dto.CustomizedCallResultCode; import org.evomaster.client.java.controller.api.dto.CustomizedRequestValueDto; +import org.evomaster.client.java.controller.api.dto.MockDatabaseDto; import org.evomaster.client.java.controller.api.dto.problem.rpc.*; import org.evomaster.client.java.controller.problem.rpc.CustomizedNotNullAnnotationForRPCDto; diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index e70deba454..0825134eb7 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -18,6 +18,7 @@ import org.evomaster.client.java.controller.api.dto.database.operations.InsertionResultsDto; import org.evomaster.client.java.controller.api.dto.database.schema.DbSchemaDto; import org.evomaster.client.java.controller.api.dto.database.schema.ExtraConstraintsDto; +import org.evomaster.client.java.controller.api.dto.MockDatabaseDto; import org.evomaster.client.java.controller.api.dto.problem.RPCProblemDto; import org.evomaster.client.java.controller.api.dto.problem.rpc.*; import org.evomaster.client.java.controller.db.DbCleaner; diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt index 8da7df6a57..3a51d0cbc6 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt @@ -112,7 +112,10 @@ abstract class AbstractRestSampler : HttpWsSampler() { } initAdHocInitialIndividuals() - initSeededTests() + + if (config.seedTestCases) + initSeededTests() + postInits() updateConfigBasedOnSutInfoDto(infoDto) @@ -173,14 +176,15 @@ abstract class AbstractRestSampler : HttpWsSampler() { override fun initSeededTests(infoDto: SutInfoDto?) { // if test case seeding is enabled, add those test cases too - if (config.seedTestCases) { - val parser = getParser() - val seededTestCases = parser.parseTestCases(config.seedTestCasesPath) - seededIndividuals.addAll(seededTestCases.map { - it.forEach { a -> a.doInitialize() } - createIndividual(SampleType.SEEDED, it) - }) + if (!config.seedTestCases) { + throw IllegalStateException("'seedTestCases' should be true when initializing seeded tests") } + val parser = getParser() + val seededTestCases = parser.parseTestCases(config.seedTestCasesPath) + seededIndividuals.addAll(seededTestCases.map { + it.forEach { a -> a.doInitialize() } + createIndividual(SampleType.SEEDED, it) + }) } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt index ce345c39ef..6eda696ef3 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper import com.google.inject.Inject import org.evomaster.client.java.controller.api.dto.* +import org.evomaster.client.java.controller.api.dto.MockDatabaseDto import org.evomaster.client.java.controller.api.dto.problem.RPCProblemDto import org.evomaster.client.java.controller.api.dto.problem.rpc.* import org.evomaster.core.EMConfig @@ -360,7 +361,7 @@ class RPCEndpointsHandler { } } - fun transformMockDatabaseDto(action: DbAsExternalServiceAction) : MockDatabaseDto{ + fun transformMockDatabaseDto(action: DbAsExternalServiceAction) : MockDatabaseDto { val mode = if (action.response.isJson()) GeneUtils.EscapeMode.JSON else throw IllegalStateException("only support response with json type for the monument") return MockDatabaseDto().apply { appKey = action.descriptiveInfo diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCSampler.kt index 635d49cd8c..f3f25987ca 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCSampler.kt @@ -59,7 +59,9 @@ class RPCSampler: ApiWsSampler() { initSqlInfo(infoDto) initAdHocInitialIndividuals(infoDto) - initSeededTests(infoDto) + + if (config.seedTestCases) + initSeededTests(infoDto) updateConfigBasedOnSutInfoDto(infoDto) log.debug("Done initializing {}", RPCSampler::class.simpleName) @@ -118,8 +120,12 @@ class RPCSampler: ApiWsSampler() { } override fun initSeededTests(infoDto: SutInfoDto?) { + if (!config.seedTestCases) { + throw IllegalStateException("'seedTestCases' should be true when initializing seeded tests") + } + - if (config.seedTestCases && infoDto?.rpcProblem?.seededTestDtos?.isNotEmpty() == true){ + if (infoDto?.rpcProblem?.seededTestDtos?.isNotEmpty() == true){ seededIndividuals.addAll( rpcHandler.handledSeededTests(infoDto.rpcProblem.seededTestDtos) .map{ diff --git a/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/com/foo/rpc/examples/spring/fakemockobject/FakeMockObjectController.java b/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/com/foo/rpc/examples/spring/fakemockobject/FakeMockObjectController.java index 845bfd552b..098a9de4f0 100644 --- a/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/com/foo/rpc/examples/spring/fakemockobject/FakeMockObjectController.java +++ b/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/com/foo/rpc/examples/spring/fakemockobject/FakeMockObjectController.java @@ -15,6 +15,7 @@ import org.apache.thrift.transport.THttpClient; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TTransportException; +import org.evomaster.client.java.controller.api.dto.MockDatabaseDto; import org.evomaster.client.java.controller.api.dto.problem.rpc.*; import org.evomaster.client.java.controller.problem.ProblemInfo; import org.evomaster.client.java.controller.problem.RPCProblem; From 9561c2018816be6e21ffdffdb429cc142f2d22f6 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 19 Jun 2023 16:31:29 +0200 Subject: [PATCH 096/345] fix failing test --- .../rest/service/resource/model/SimpleResourceSampler.kt | 3 ++- .../evomaster/core/problem/rest/service/AbstractRestSampler.kt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/model/SimpleResourceSampler.kt b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/model/SimpleResourceSampler.kt index 5ab8a87d0e..8f856268d3 100644 --- a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/model/SimpleResourceSampler.kt +++ b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/model/SimpleResourceSampler.kt @@ -22,7 +22,8 @@ class SimpleResourceSampler : ResourceSampler() { } initAdHocInitialIndividuals() - initSeededTests() + if (config.seedTestCases) + initSeededTests() postInits() } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt index 3a51d0cbc6..627ba9daef 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt @@ -281,7 +281,8 @@ abstract class AbstractRestSampler : HttpWsSampler() { RestActionBuilderV3.addActionsFromSwagger(swagger, actionCluster, listOf(), enableConstraintHandling = config.enableSchemaConstraintHandling) initAdHocInitialIndividuals() - initSeededTests() + if (config.seedTestCases) + initSeededTests() addAuthFromConfig() From d9b4417a1e8ae84fc06981f04b616f6937d117ce Mon Sep 17 00:00:00 2001 From: hghianni Date: Tue, 20 Jun 2023 00:02:44 -0300 Subject: [PATCH 097/345] Continue with the repository type handling --- .../dto/database/execution/FailedQuery.java | 7 +- .../controller/internal/EMController.java | 1 + .../controller/internal/SutController.java | 22 ++++- .../controller/internal/db/MongoHandler.java | 53 ++++++++--- .../java/instrumentation/AdditionalInfo.java | 10 +++ .../instrumentation/MongoCollectionInfo.java | 20 +++++ .../methodreplacement/ReplacementList.java | 2 +- .../ThirdPartyMethodReplacementClass.java | 9 ++ ...ongoEntityInformationClassReplacement.java | 87 +++++++++++++++++++ .../MongoRepositoryClassReplacement.java | 48 ---------- .../staticstate/ExecutionTracer.java | 5 ++ .../core/mongo/MongoActionGeneBuilder.kt | 54 +++++++++--- .../org/evomaster/core/mongo/MongoDbAction.kt | 35 +++----- .../core/mongo/MongoInsertBuilder.kt | 2 +- .../enterprise/service/EnterpriseSampler.kt | 2 +- .../core/search/gene/numeric/DoubleGene.kt | 3 +- .../core/search/gene/numeric/IntegerGene.kt | 3 +- .../core/search/gene/numeric/LongGene.kt | 3 +- .../core/search/gene/utils/GeneUtils.kt | 3 +- 19 files changed, 260 insertions(+), 109 deletions(-) create mode 100644 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/MongoCollectionInfo.java create mode 100644 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MappingMongoEntityInformationClassReplacement.java delete mode 100644 client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MongoRepositoryClassReplacement.java diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/FailedQuery.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/FailedQuery.java index 98be5552ba..5a955ce8d1 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/FailedQuery.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/FailedQuery.java @@ -3,8 +3,7 @@ import java.util.Map; public class FailedQuery { - // Add database - public FailedQuery(String database, String collection, Class documentsType, Map accessedFields) { + public FailedQuery(String database, String collection, Class documentsType, Map > accessedFields) { this.database = database; this.collection = collection; this.documentsType = documentsType; @@ -19,11 +18,11 @@ public FailedQuery(){ private final String database; private final String collection; private Class documentsType; - private Map accessedFields; + private Map> accessedFields; public String getDatabase() {return database;} public String getCollection() {return collection;} - public Map getAccessedFields() { + public Map> getAccessedFields() { return accessedFields; } public Class getDocumentsType() { diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java index b47d41e59c..7c04329ec4 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java @@ -369,6 +369,7 @@ public Response runSut(SutRunDto dto, @Context HttpServletRequest httpServletReq return Response.status(500).entity(WrappedResponseDto.withError(msg)).build(); } noKillSwitch(() -> sutController.initSqlHandler()); + noKillSwitch(() -> sutController.initMongoHandler()); } else { //TODO as starting should be blocking, need to check //if initialized, and wait if not diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index 4cbe6f180d..ca5c61d28d 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -275,6 +275,16 @@ public final void initSqlHandler() { sqlHandler.setSchema(getSqlDatabaseSchema()); } + public final void initMongoHandler() { + // This is needed because the replacement use to get this info occurs during the start of the SUT. + + List list = getAdditionalInfoList(); + if(!list.isEmpty()) { + AdditionalInfo last = list.get(list.size() - 1); + last.getMongoCollectionInfoData().forEach(mongoHandler::handle); + } + } + /** * TODO further handle multiple connections @@ -364,9 +374,9 @@ private void computeSQLHeuristics(ExtraHeuristicsDto dto) { } public final void computeMongoHeuristics(ExtraHeuristicsDto dto){ - if(mongoHandler.isCalculateHeuristics()){ + List list = getAdditionalInfoList(); - List list = getAdditionalInfoList(); + if(mongoHandler.isCalculateHeuristics()){ if(!list.isEmpty()) { AdditionalInfo last = list.get(list.size() - 1); last.getMongoInfoData().forEach(it -> { @@ -390,7 +400,13 @@ public final void computeMongoHeuristics(ExtraHeuristicsDto dto){ .forEach(h -> dto.heuristics.add(h)); } - if(mongoHandler.isExtractMongoExecution()){dto.mongoExecutionDto = mongoHandler.getExecutionDto();} + if(mongoHandler.isExtractMongoExecution()){ + if(!list.isEmpty()) { + AdditionalInfo last = list.get(list.size() - 1); + last.getMongoCollectionInfoData().forEach(mongoHandler::handle); + } + dto.mongoExecutionDto = mongoHandler.getExecutionDto(); + } } /** diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/MongoHandler.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/MongoHandler.java index 6fbacf2ba9..5827beab02 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/MongoHandler.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/MongoHandler.java @@ -6,6 +6,7 @@ import org.evomaster.client.java.controller.mongo.MongoOperation; import org.evomaster.client.java.controller.mongo.QueryParser; import org.evomaster.client.java.controller.mongo.operations.*; +import org.evomaster.client.java.instrumentation.MongoCollectionInfo; import org.evomaster.client.java.instrumentation.MongoInfo; import java.lang.reflect.InvocationTargetException; @@ -42,10 +43,13 @@ public class MongoHandler { private final List failedQueries; + private final HashMap> collectionInfo; + public MongoHandler() { distances = new ArrayList<>(); operations = new ArrayList<>(); failedQueries = new ArrayList<>(); + collectionInfo = new HashMap<>(); extractMongoExecution = true; calculateHeuristics = true; } @@ -54,6 +58,7 @@ public void reset() { operations.clear(); distances.clear(); failedQueries.clear(); + // collectionInfo is not cleared to avoid losing the info as it's retrieved when SUT is started } public void handle(MongoInfo info) { @@ -64,6 +69,14 @@ public void handle(MongoInfo info) { operations.add(info); } + public void handle(MongoCollectionInfo info) { + if (!extractMongoExecution) { + return; + } + + collectionInfo.put(info.getCollectionName(), info.getDocumentsType()); + } + public List getDistances() { operations.stream().filter(info -> info.getQuery() != null).forEach(mongoInfo -> { @@ -121,8 +134,8 @@ private FailedQuery extractRelevantInfo(MongoOperation operation) { QueryOperation query = new QueryParser().parse(operation.getQuery()); Object collection = operation.getCollection(); - Map accessedFields = extractFieldsInQuery(query); - Class documentsType = extractDocumentsType(collection); + Map> accessedFields = extractFieldsInQuery(query); + Class documentsType; String collectionName; String databaseName; @@ -136,6 +149,24 @@ private FailedQuery extractRelevantInfo(MongoOperation operation) { throw new RuntimeException(e); } + // I should be as specific as I can with the type the collection's documents should have. + // There are a few ways to get that info: + + // 1) Using collection.getDocumentsClass(). + // Spring for example "ignore" this and store type info inside repository. + + // 2) When using Spring, retrieving the type of the repository associated with the collection. + // The MongoEntityInformation replacement makes this possible. + + // 3) Extract from the query the fields used and type of each of them. This probably won't + // work as expected as usually a subset of fields is used in a query. + + if(collectionInfo.containsKey(collectionName)){ + documentsType = collectionInfo.get(collectionName); + }else{ + documentsType = extractDocumentsType(collection); + } + return new FailedQuery(databaseName, collectionName, documentsType, accessedFields); } @@ -149,12 +180,12 @@ private static Class extractDocumentsType(Object collection) { } } - private static Map extractFieldsInQuery(QueryOperation operation) { - Map accessedFields = new HashMap<>(); + private static Map> extractFieldsInQuery(QueryOperation operation) { + Map> accessedFields = new HashMap<>(); if(operation instanceof ComparisonOperation) { ComparisonOperation op = (ComparisonOperation) operation; - accessedFields.put(op.getFieldName(), op.getValue()); + accessedFields.put(op.getFieldName(), op.getValue().getClass()); } if(operation instanceof AndOperation) { @@ -174,22 +205,22 @@ private static Map extractFieldsInQuery(QueryOperation operation if(operation instanceof InOperation) { InOperation op = (InOperation) operation; - accessedFields.put(op.getFieldName(), op.getValues().get(0)); + accessedFields.put(op.getFieldName(), op.getValues().get(0).getClass()); } if(operation instanceof NotInOperation) { NotInOperation op = (NotInOperation) operation; - accessedFields.put(op.getFieldName(), op.getValues().get(0)); + accessedFields.put(op.getFieldName(), op.getValues().get(0).getClass()); } if(operation instanceof AllOperation) { AllOperation op = (AllOperation) operation; - accessedFields.put(op.getFieldName(), op.getValues()); + accessedFields.put(op.getFieldName(), op.getValues().getClass()); } if(operation instanceof SizeOperation) { SizeOperation op = (SizeOperation) operation; - accessedFields.put(op.getFieldName(), op.getValue()); + accessedFields.put(op.getFieldName(), op.getValue().getClass()); } if(operation instanceof ExistsOperation) { @@ -199,12 +230,12 @@ private static Map extractFieldsInQuery(QueryOperation operation if(operation instanceof ModOperation) { ModOperation op = (ModOperation) operation; - accessedFields.put(op.getFieldName(), op.getDivisor()); + accessedFields.put(op.getFieldName(), op.getDivisor().getClass()); } if(operation instanceof TypeOperation) { TypeOperation op = (TypeOperation) operation; - accessedFields.put(op.getFieldName(), op.getType()); + accessedFields.put(op.getFieldName(), op.getType().getClass()); } /* diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AdditionalInfo.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AdditionalInfo.java index bfdb1d0b8c..667a6d8028 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AdditionalInfo.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/AdditionalInfo.java @@ -104,6 +104,8 @@ public StatementDescription(String line, String method) { private final Set mongoInfoData = new CopyOnWriteArraySet<>(); + private final Set mongoCollectionInfoData = new CopyOnWriteArraySet<>(); + public Set getSqlInfoData(){ return Collections.unmodifiableSet(sqlInfoData); } @@ -112,6 +114,10 @@ public Set getMongoInfoData(){ return Collections.unmodifiableSet(mongoInfoData); } + public Set getMongoCollectionInfoData(){ + return Collections.unmodifiableSet(mongoCollectionInfoData); + } + public void addSqlInfo(SqlInfo info){ sqlInfoData.add(info); } @@ -120,6 +126,10 @@ public void addMongoInfo(MongoInfo info){ mongoInfoData.add(info); } + public void addMongoCollectionInfo(MongoCollectionInfo info){ + mongoCollectionInfoData.add(info); + } + public Set getParsedDtoNamesView(){ return Collections.unmodifiableSet(parsedDtoNames); } diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/MongoCollectionInfo.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/MongoCollectionInfo.java new file mode 100644 index 0000000000..63f72f750c --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/MongoCollectionInfo.java @@ -0,0 +1,20 @@ +package org.evomaster.client.java.instrumentation; + +import java.io.Serializable; + +/** + * Info about the type of documents in the collection. + */ +public class MongoCollectionInfo implements Serializable { + private final String collectionName; + private final Class documentsType; + + public MongoCollectionInfo(String collectionName, Class collectionType) { + this.collectionName = collectionName; + this.documentsType = collectionType; + } + + public String getCollectionName() {return collectionName;} + + public Class getDocumentsType() {return documentsType;} +} diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/ReplacementList.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/ReplacementList.java index d3772b4dd9..4dce7ecb60 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/ReplacementList.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/ReplacementList.java @@ -49,7 +49,7 @@ public static List getList() { new MatcherClassReplacement(), new MethodClassReplacement(), new MongoCollectionClassReplacement(), - new MongoRepositoryClassReplacement(), + new MappingMongoEntityInformationClassReplacement(), new OkHttpClient3BuilderClassReplacement(), new OkHttpClient3ClassReplacement(), new OkHttpClientClassReplacement(), diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/ThirdPartyMethodReplacementClass.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/ThirdPartyMethodReplacementClass.java index d245406a81..efa8514701 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/ThirdPartyMethodReplacementClass.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/ThirdPartyMethodReplacementClass.java @@ -163,6 +163,15 @@ private void initConstructors(ClassLoader loader, StateInfo info) { } Class[] reducedInputs = Arrays.copyOfRange(inputs, start, end); + Annotation[][] annotations = m.getParameterAnnotations(); + + for (int i = start; i < end; i++) { + if (annotations[i].length > 0) { + Class klazz = ReplacementUtils.getCastedToThirdParty(loader,annotations[i]); + if (klazz != null) + reducedInputs[i - start] = klazz; + } + } Constructor targetConstructor = null; try { diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MappingMongoEntityInformationClassReplacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MappingMongoEntityInformationClassReplacement.java new file mode 100644 index 0000000000..9020836f3c --- /dev/null +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MappingMongoEntityInformationClassReplacement.java @@ -0,0 +1,87 @@ +package org.evomaster.client.java.instrumentation.coverage.methodreplacement.classes; + +import org.evomaster.client.java.instrumentation.MongoCollectionInfo; +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.ThirdPartyMethodReplacementClass; +import org.evomaster.client.java.instrumentation.shared.ReplacementCategory; +import org.evomaster.client.java.instrumentation.shared.ReplacementType; +import org.evomaster.client.java.instrumentation.staticstate.ExecutionTracer; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + + +public class MappingMongoEntityInformationClassReplacement extends ThirdPartyMethodReplacementClass { + private static final MappingMongoEntityInformationClassReplacement singleton = new MappingMongoEntityInformationClassReplacement(); + private static ThreadLocal instance = new ThreadLocal<>(); + + @Override + protected String getNameOfThirdPartyTargetClass() { + return "org.springframework.data.mongodb.repository.support.MappingMongoEntityInformation"; + } + + public static Object consumeInstance() { + Object mappingMongoEntityInformation = instance.get(); + if (mappingMongoEntityInformation == null) { + throw new IllegalStateException("No instance to consume"); + } + instance.set(null); + return mappingMongoEntityInformation; + } + + private static void addInstance(Object x) { + Object mappingMongoEntityInformation = instance.get(); + if (mappingMongoEntityInformation != null) { + throw new IllegalStateException("Previous instance was not consumed"); + } + instance.set(x); + } + + @Replacement( + replacingConstructor = true, + type = ReplacementType.TRACKER, + category = ReplacementCategory.SQL, + id = "constructorEntity" + ) + public static void MappingMongoEntityInformation(@ThirdPartyCast(actualType = "org.springframework.data.mongodb.core.mapping.MongoPersistentEntity") Object entity) { + handleMappingMongoEntityInformationConstructor("constructorEntity", Collections.singletonList(entity)); + } + + @Replacement( + replacingConstructor = true, + type = ReplacementType.TRACKER, + category = ReplacementCategory.SQL, + id = "constructorEntityCustomCollectionName" + ) + public static void MappingMongoEntityInformation(@ThirdPartyCast(actualType = "org.springframework.data.mongodb.core.mapping.MongoPersistentEntity") Object entity, String customCollectionName) { + handleMappingMongoEntityInformationConstructor("constructorEntityCustomCollectionName", Arrays.asList(entity, customCollectionName)); + } + + @Replacement( + replacingConstructor = true, + type = ReplacementType.TRACKER, + category = ReplacementCategory.SQL, + id = "constructorEntityFallbackIdType" + ) + public static void MappingMongoEntityInformation(@ThirdPartyCast(actualType = "org.springframework.data.mongodb.core.mapping.MongoPersistentEntity") Object entity, Class fallbackIdType) { + handleMappingMongoEntityInformationConstructor("constructorEntityFallbackIdType", Arrays.asList(entity, fallbackIdType)); + } + + private static void handleMappingMongoEntityInformationConstructor(String id, List args) { + Constructor original = getOriginalConstructor(singleton, id); + + try { + Object mappingMongoEntityInformation = original.newInstance(args.toArray()); + addInstance(mappingMongoEntityInformation); + String collectionName = (String) mappingMongoEntityInformation.getClass().getMethod("getCollectionName").invoke(mappingMongoEntityInformation); + Class repositoryType = (Class) mappingMongoEntityInformation.getClass().getMethod("getJavaType").invoke(mappingMongoEntityInformation); + ExecutionTracer.addMongoCollectionInfo(new MongoCollectionInfo(collectionName, repositoryType)); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } +} diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MongoRepositoryClassReplacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MongoRepositoryClassReplacement.java deleted file mode 100644 index 3f5b6220ec..0000000000 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MongoRepositoryClassReplacement.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.evomaster.client.java.instrumentation.coverage.methodreplacement.classes; - -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.ThirdPartyMethodReplacementClass; -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.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - - -public class MongoRepositoryClassReplacement extends ThirdPartyMethodReplacementClass { - private static final MongoRepositoryClassReplacement singleton = new MongoRepositoryClassReplacement(); - - @Override - protected String getNameOfThirdPartyTargetClass() { - return "org.springframework.data.mongodb.repository.support.SimpleMongoRepository"; - } - - // This is not working. It may be related to the fact that this constructor seems to be called using reflection. - // When a MongoRepository is created in Spring it uses a MongoCollection under the hood. - // The type of the collection is the default (Document) despite probably the repository was created of some custom type like Person. - // The fact that elements of the collection should be Person is stored in Spring (SimpleMongoRepository). - // The idea is to instrument the constructor of SimpleMongoRepository to obtain the actual type of the collection. - - // Change category to MONGO - @Replacement(replacingStatic = false, - replacingConstructor = true, - type = ReplacementType.TRACKER, id = "SimpleMongoRepository", - usageFilter = UsageFilter.ANY, - category = ReplacementCategory.SQL) - public static void SimpleMongoRepository( - Object mongoRepository, - @ThirdPartyCast(actualType = "org.springframework.data.mongodb.repository.query.MongoEntityInformation") Object metadata, - @ThirdPartyCast(actualType = "org.springframework.data.mongodb.repository.core.MongoOperations") Object mongoOperations) { - try { - Method method = getOriginal(singleton, "SimpleMongoRepository", mongoRepository); - method.invoke(mongoRepository, metadata, mongoOperations); - // Store info - } catch (IllegalAccessException e){ - throw new RuntimeException(e); - } catch (InvocationTargetException e){ - throw (RuntimeException) e.getCause(); - } - } -} diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/staticstate/ExecutionTracer.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/staticstate/ExecutionTracer.java index 4e73901a6d..c27c224350 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/staticstate/ExecutionTracer.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/staticstate/ExecutionTracer.java @@ -421,6 +421,11 @@ public static void addMongoInfo(MongoInfo info){ getCurrentAdditionalInfo().addMongoInfo(info); } + public static void addMongoCollectionInfo(MongoCollectionInfo info){ + if (!executingInitMongo) + getCurrentAdditionalInfo().addMongoCollectionInfo(info); + } + public static void markLastExecutedStatement(String lastLine, String lastMethod) { getCurrentAdditionalInfo().pushLastExecutedStatement(lastLine, lastMethod); } diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilder.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilder.kt index 601c5336e9..8542695715 100644 --- a/core/src/main/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilder.kt @@ -4,23 +4,49 @@ import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.collection.ArrayGene import org.evomaster.core.search.gene.datetime.DateGene import org.evomaster.core.search.gene.numeric.* +import org.evomaster.core.search.gene.optional.NullableGene import org.evomaster.core.search.gene.string.StringGene -import java.util.Date class MongoActionGeneBuilder { - fun buildGene(fieldName: String, value: T): Gene { - return when (value) { - is Int -> IntegerGene(fieldName, min = Int.MIN_VALUE, max = Int.MAX_VALUE) - is Long -> LongGene(fieldName, min = Long.MIN_VALUE, max = Long.MAX_VALUE) - is Double -> DoubleGene(fieldName, min = Double.MIN_VALUE, max = Double.MAX_VALUE) - is String -> StringGene(name = fieldName, minLength = Int.MIN_VALUE) - is Boolean -> BooleanGene(name = fieldName) - //is Array<*> -> ArrayGene<*>(name = fieldName) - //is Object -> ObjectGene - //is Date -> DateGene - //is null -> NullGene - //Unhandled Types: Binary Data, Object Id, Regular Expression, Javascript, Timestamp, Decimal128, Min/max key - else -> throw IllegalArgumentException("Cannot handle: $fieldName.") + fun buildGene(fieldName: String, valueType: Class): Gene { + val typeName = valueType.simpleName; + + if(typeName == "String"){ + return StringGene(name = fieldName) + } + + if(typeName == "int" || typeName == "Integer"){ + return IntegerGene(fieldName, min = Int.MIN_VALUE, max = Int.MAX_VALUE) + } + + if(typeName == "long" || typeName == "Long"){ + return LongGene(fieldName, min = Long.MIN_VALUE, max = Long.MAX_VALUE) + } + + if(typeName == "double" || typeName == "Double"){ + return DoubleGene(fieldName, min = Double.MIN_VALUE, max = Double.MAX_VALUE) + } + + if(typeName == "boolean" || typeName == "Boolean") { + return BooleanGene(name = fieldName) } + + if(typeName == "Date") { + return DateGene(name = fieldName) + } + + + /* + if(valueType == List<*>::javaClass ) { + return ArrayGene(name = fieldName, template = IntegerGene(fieldName, min = Int.MIN_VALUE, max = Int.MAX_VALUE)) + } + + */ + + //Unhandled Types: Null, Document, Binary Data, Object Id, Regular Expression, Javascript, Timestamp, Decimal128, Min/max key + + return ObjectGene(fieldName, valueType.fields.map { field -> buildGene(field.name, field.type) }) + + //throw IllegalArgumentException("Cannot handle: $fieldName.") } } diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt index e29dfb17f2..c24cdefa8d 100644 --- a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt @@ -5,32 +5,23 @@ import org.evomaster.core.search.gene.Gene import org.evomaster.core.search.gene.ObjectGene import java.util.* -class MongoDbAction(val database: String, val collection: String, val documentsType: Class<*>, val accessedFields: Map, computedGenes: List? = null) : Action(listOf()) { +class MongoDbAction( + val database: String, + val collection: String, + val documentsType: Class<*>, + val accessedFields: Map>, + computedGenes: List? = null +) : Action(listOf()) { - val genes: List = (computedGenes ?: computeGenes()) .also { addChildren(it) } + val genes: List = (computedGenes ?: computeGenes()).also { addChildren(it) } private fun computeGenes(): List { - // I should be as specific as I can with the type the collection's documents should have. - // There are a few ways to get that info: - - // 1) Using , which is the result of collection.getDocumentsClass() - // Spring for example "ignore" this and store type info inside SampleMongoRepository - - // 2) Instrument the constructor of SampleMongoRepository and retrieve the info - // Probably can reuse something from GsonClassReplacement. - - // 3) Extract from the query the fields () used and type of each of them. This probably won't - // work fine as usually a subset of fields is used in a query. But is better than creating a - // Document. - val genes = - if(documentsType.typeName == "org.bson.Document"){ - // 3) - accessedFields.map { MongoActionGeneBuilder().buildGene(it.key, it.value) } - }else{ - // 1) - documentsType.declaredFields.map { MongoActionGeneBuilder().buildGene(it.name, it.type) } - } + if (documentsType.typeName == "org.bson.Document") { + accessedFields.map { MongoActionGeneBuilder().buildGene(it.key, it.value) } + } else { + documentsType.declaredFields.map { MongoActionGeneBuilder().buildGene(it.name, it.type) } + } return Collections.singletonList(ObjectGene("BSON", genes)) } diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoInsertBuilder.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoInsertBuilder.kt index 32aa37835d..743e43545c 100644 --- a/core/src/main/kotlin/org/evomaster/core/mongo/MongoInsertBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoInsertBuilder.kt @@ -2,7 +2,7 @@ package org.evomaster.core.mongo class MongoInsertBuilder { - fun createMongoInsertionAction(database: String, collection: String, documentsType: Class<*>, accessedFields: Map): List { + fun createMongoInsertionAction(database: String, collection: String, documentsType: Class<*>, accessedFields: Map>): List { return mutableListOf(MongoDbAction(database, collection, documentsType, accessedFields)) } } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt index 8340d803a6..0a3f3cb5e9 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt @@ -78,7 +78,7 @@ abstract class EnterpriseSampler : Sampler() where T : Individual { return actions } - fun sampleMongoInsertion(database: String, collection: String, documentsType: Class<*>, accessedFields: Map): List { + fun sampleMongoInsertion(database: String, collection: String, documentsType: Class<*>, accessedFields: Map>): List { // Should I use something like this? //val extraConstraints = randomness.nextBoolean(apc.getExtraSqlDbConstraintsProbability()) diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/DoubleGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/DoubleGene.kt index 936e1e2a81..826aa45352 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/DoubleGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/DoubleGene.kt @@ -82,7 +82,8 @@ class DoubleGene(name: String, } override fun getValueAsPrintableString(previousGenes: List, mode: GeneUtils.EscapeMode?, targetFormat: OutputFormat?, extraCheck: Boolean): String { - return getFormattedValue().toString() + val stringValue = getFormattedValue().toString() + return if(mode==GeneUtils.EscapeMode.EJSON) "{ \"\$numberInt\": \"$stringValue\" }" else stringValue } override fun copyValueFrom(other: Gene): Boolean { diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/IntegerGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/IntegerGene.kt index d6472bc101..8afd998639 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/IntegerGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/IntegerGene.kt @@ -143,7 +143,8 @@ class IntegerGene( targetFormat: OutputFormat?, extraCheck: Boolean ): String { - return value.toString() + val stringValue = value.toString() + return if(mode==GeneUtils.EscapeMode.EJSON) "{ \"\$numberInt\": \"$stringValue\" }" else stringValue } diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/LongGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/LongGene.kt index 1402aae1e4..d9028b97bf 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/LongGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/LongGene.kt @@ -68,7 +68,8 @@ class LongGene( } override fun getValueAsPrintableString(previousGenes: List, mode: GeneUtils.EscapeMode?, targetFormat: OutputFormat?, extraCheck: Boolean): String { - return value.toString() + val stringValue = value.toString() + return if(mode==GeneUtils.EscapeMode.EJSON) "{ \"\$numberLong\": \"$stringValue\" }" else stringValue } override fun copyValueFrom(other: Gene): Boolean { diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/utils/GeneUtils.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/utils/GeneUtils.kt index c36a169ea2..7747b918e5 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/utils/GeneUtils.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/utils/GeneUtils.kt @@ -54,6 +54,7 @@ object GeneUtils { SQL, ASSERTION, EXPECTATION, + EJSON, JSON, TEXT, XML, @@ -172,7 +173,7 @@ object GeneUtils { EscapeMode.SQL -> applySqlEscapes(string, format) EscapeMode.ASSERTION -> applyAssertionEscapes(string, format) EscapeMode.EXPECTATION -> applyExpectationEscapes(string, format) - EscapeMode.JSON -> applyJsonEscapes(string, format) + EscapeMode.JSON, EscapeMode.EJSON-> applyJsonEscapes(string, format) EscapeMode.TEXT -> applyTextEscapes(string, format) EscapeMode.NONE, EscapeMode.X_WWW_FORM_URLENCODED, From 864d73d831f2a88b0cc4174c857589629e5298ca Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 20 Jun 2023 16:29:44 +0200 Subject: [PATCH 098/345] init grpc support --- .../problem/rpc/RPCEndpointsBuilder.java | 11 +- e2e-tests/spring-rpc/pom.xml | 3 +- e2e-tests/spring-rpc/spring-rpc-grpc/pom.xml | 107 ++++++++++++++++++ .../spring/branches/BranchGRPCService.java | 38 +++++++ .../src/main/proto/branches.proto | 26 +++++ .../examples/spring/GRPCServerController.java | 101 +++++++++++++++++ .../branches/BranchGRPCServiceController.java | 34 ++++++ .../rpc/grpc/examples/GRPCServerTestBase.java | 12 ++ .../examples/branches/BranchesGRPCEMTest.java | 37 ++++++ pom.xml | 32 +++++- 10 files changed, 394 insertions(+), 7 deletions(-) create mode 100644 e2e-tests/spring-rpc/spring-rpc-grpc/pom.xml create mode 100644 e2e-tests/spring-rpc/spring-rpc-grpc/src/main/java/com/foo/rpc/grpc/examples/spring/branches/BranchGRPCService.java create mode 100644 e2e-tests/spring-rpc/spring-rpc-grpc/src/main/proto/branches.proto create mode 100644 e2e-tests/spring-rpc/spring-rpc-grpc/src/test/java/com/foo/rpc/grpc/examples/spring/GRPCServerController.java create mode 100644 e2e-tests/spring-rpc/spring-rpc-grpc/src/test/java/com/foo/rpc/grpc/examples/spring/branches/BranchGRPCServiceController.java create mode 100644 e2e-tests/spring-rpc/spring-rpc-grpc/src/test/java/org/evomaster/e2etests/spring/rpc/grpc/examples/GRPCServerTestBase.java create mode 100644 e2e-tests/spring-rpc/spring-rpc-grpc/src/test/java/org/evomaster/e2etests/spring/rpc/grpc/examples/branches/BranchesGRPCEMTest.java diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java index cafd95fe34..f0b368bded 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java @@ -191,7 +191,7 @@ public static InterfaceSchema build(String interfaceName, RPCType rpcType, Objec InterfaceSchema schema = new InterfaceSchema(interfaceName, endpoints, getClientClass(client) , rpcType, skippedEndpoints, authEndpoints, endpointsForAuth); for (Method m : interfaze.getDeclaredMethods()) { - if (filterMethod(m, skipEndpointsByName, skipEndpointsByAnnotation, involveEndpointsByName, involveEndpointsByAnnotation)){ + if (filterRPCFunctionMethod(m, skipEndpointsByName, skipEndpointsByAnnotation, involveEndpointsByName, involveEndpointsByAnnotation)){ try{ EndpointSchema endpointSchema = build(schema, m, rpcType, authenticationDtoList, customizedRequestValueDtos, notNullAnnotations); endpoints.add(endpointSchema); @@ -322,9 +322,9 @@ private static List getAuthEndpointInInterface(List skipEndpointsByName, List skipEndpointsByAnnotation, - List involveEndpointsByName, List involveEndpointsByAnnotation){ + private static boolean filterRPCFunctionMethod(Method endpoint, + List skipEndpointsByName, List skipEndpointsByAnnotation, + List involveEndpointsByName, List involveEndpointsByAnnotation){ if (skipEndpointsByName != null && involveEndpointsByName != null) throw new IllegalArgumentException("Driver Config Error: skipEndpointsByName and involveEndpointsByName should not be specified at same time."); if (skipEndpointsByAnnotation != null && involveEndpointsByAnnotation != null) @@ -336,7 +336,8 @@ private static boolean filterMethod(Method endpoint, if (involveEndpointsByName != null || involveEndpointsByAnnotation != null) return anyMatchByNameAndAnnotation(endpoint, involveEndpointsByName, involveEndpointsByAnnotation); - return true; + // only handle public method + return Modifier.isPublic(endpoint.getModifiers()); } private static boolean anyMatchByNameAndAnnotation(Method endpoint, List names, List annotations){ diff --git a/e2e-tests/spring-rpc/pom.xml b/e2e-tests/spring-rpc/pom.xml index d2b45e6763..fe44f134cb 100644 --- a/e2e-tests/spring-rpc/pom.xml +++ b/e2e-tests/spring-rpc/pom.xml @@ -14,6 +14,7 @@ spring-rpc-thrift + spring-rpc-grpc - \ No newline at end of file + diff --git a/e2e-tests/spring-rpc/spring-rpc-grpc/pom.xml b/e2e-tests/spring-rpc/spring-rpc-grpc/pom.xml new file mode 100644 index 0000000000..1e9de2a965 --- /dev/null +++ b/e2e-tests/spring-rpc/spring-rpc-grpc/pom.xml @@ -0,0 +1,107 @@ + + + + evomaster-e2e-tests-spring-rpc + org.evomaster + 1.6.2-SNAPSHOT + + 4.0.0 + + evomaster-e2e-tests-spring-rpc-grpc + jar + + + + + + + javax.validation + validation-api + 2.0.1.Final + + + + + javax.ws.rs + javax.ws.rs-api + + + org.evomaster + evomaster-e2e-tests-utils + test-jar + + + org.evomaster + evomaster-client-java-controller + + + org.evomaster + evomaster-core + test + + + org.evomaster + evomaster-client-java-instrumentation + test-jar + + + + io.grpc + grpc-netty-shaded + + + io.grpc + grpc-protobuf + + + io.grpc + grpc-stub + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + + + + kr.motd.maven + os-maven-plugin + ${kr.motd.maven.version} + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + + com.google.protobuf:protoc:${com.google.protobuf.protoc.version}:exe:${os.detected.classifier} + grpc-java + io.grpc:protoc-gen-grpc-java:${io.grpc.version}:exe:${os.detected.classifier} + + + + + compile + compile-custom + + + + + + + + diff --git a/e2e-tests/spring-rpc/spring-rpc-grpc/src/main/java/com/foo/rpc/grpc/examples/spring/branches/BranchGRPCService.java b/e2e-tests/spring-rpc/spring-rpc-grpc/src/main/java/com/foo/rpc/grpc/examples/spring/branches/BranchGRPCService.java new file mode 100644 index 0000000000..7afc9cfa7e --- /dev/null +++ b/e2e-tests/spring-rpc/spring-rpc-grpc/src/main/java/com/foo/rpc/grpc/examples/spring/branches/BranchGRPCService.java @@ -0,0 +1,38 @@ +package com.foo.rpc.grpc.examples.spring.branches; + +import com.foo.rpc.grpc.examples.spring.branches.generated.BranchGRPCServiceGrpc; +import com.foo.rpc.grpc.examples.spring.branches.generated.BranchesPostDto; +import com.foo.rpc.grpc.examples.spring.branches.generated.BranchesResponseDto; +import com.foo.somedifferentpackage.examples.branches.BranchesImp; +import io.grpc.stub.StreamObserver; +import org.evomaster.client.java.instrumentation.example.branches.Branches; + +public class BranchGRPCService extends BranchGRPCServiceGrpc.BranchGRPCServiceImplBase { + + @Override + public void pos(BranchesPostDto request, StreamObserver responseObserver) { + Branches b = new BranchesImp(); + int value = b.pos(request.getX(), request.getY()); + BranchesResponseDto response = BranchesResponseDto.newBuilder().setValue(value).build(); + responseObserver.onNext(response); + responseObserver.onCompleted(); + } + + @Override + public void neg(BranchesPostDto request, StreamObserver responseObserver) { + Branches b = new BranchesImp(); + int value = b.neg(request.getX(), request.getY()); + BranchesResponseDto response = BranchesResponseDto.newBuilder().setValue(value).build(); + responseObserver.onNext(response); + responseObserver.onCompleted(); + } + + @Override + public void eq(BranchesPostDto request, StreamObserver responseObserver) { + Branches b = new BranchesImp(); + int value = b.eq(request.getX(), request.getY()); + BranchesResponseDto response = BranchesResponseDto.newBuilder().setValue(value).build(); + responseObserver.onNext(response); + responseObserver.onCompleted(); + } +} diff --git a/e2e-tests/spring-rpc/spring-rpc-grpc/src/main/proto/branches.proto b/e2e-tests/spring-rpc/spring-rpc-grpc/src/main/proto/branches.proto new file mode 100644 index 0000000000..7d0276958f --- /dev/null +++ b/e2e-tests/spring-rpc/spring-rpc-grpc/src/main/proto/branches.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "com.foo.rpc.grpc.examples.spring.branches.generated"; + + +service BranchGRPCService { + + + rpc pos(BranchesPostDto) returns (BranchesResponseDto) {} + + rpc neg(BranchesPostDto) returns (BranchesResponseDto) {} + + rpc eq(BranchesPostDto) returns (BranchesResponseDto) {} + + +} + +message BranchesResponseDto { + int32 value = 1; +} + +message BranchesPostDto { + int32 x = 1; + int32 y = 2; +} diff --git a/e2e-tests/spring-rpc/spring-rpc-grpc/src/test/java/com/foo/rpc/grpc/examples/spring/GRPCServerController.java b/e2e-tests/spring-rpc/spring-rpc-grpc/src/test/java/com/foo/rpc/grpc/examples/spring/GRPCServerController.java new file mode 100644 index 0000000000..c2fba0bfd4 --- /dev/null +++ b/e2e-tests/spring-rpc/spring-rpc-grpc/src/test/java/com/foo/rpc/grpc/examples/spring/GRPCServerController.java @@ -0,0 +1,101 @@ +package com.foo.rpc.grpc.examples.spring; + +import com.foo.rpc.grpc.examples.spring.branches.generated.BranchGRPCServiceGrpc; +import io.grpc.BindableService; +import io.grpc.ManagedChannel; +import io.grpc.Server; +import io.grpc.ServerBuilder; +import org.evomaster.client.java.controller.EmbeddedSutController; +import org.evomaster.client.java.controller.api.dto.AuthenticationDto; +import org.evomaster.client.java.controller.api.dto.SutInfoDto; +import org.evomaster.client.java.controller.internal.db.DbSpecification; + + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public abstract class GRPCServerController extends EmbeddedSutController { + + protected ManagedChannel channel; + private Server server; + + private final BindableService registeredService; + + public GRPCServerController(BindableService service){ + registeredService = service; + } + + abstract public String startClient(); + + @Override + public String startSut() { + + try { + server = ServerBuilder.forPort(0).addService(registeredService).build(); + server.start(); + + startClient(); + return "http://localhost:"+server.getPort(); + + } catch (IOException e) { + throw new RuntimeException(e); + } + + + } + + protected int getSutPort() { + return server.getPort(); + } + + @Override + public boolean isSutRunning() { + return (server != null && !server.isShutdown() && !server.isTerminated()); + } + + @Override + public void stopSut() { + + try { + if (channel != null) + channel.shutdown().awaitTermination(2, TimeUnit.SECONDS); + if (server != null) + server.shutdown().awaitTermination(2, TimeUnit.SECONDS); + + server = null; + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + } + + + + @Override + public String getPackagePrefixesToCover() { + return "com.foo."; + } + + @Override + public void resetStateOfSUT() { + //nothing to do + } + + @Override + public List getInfoForAuthentication() { + return null; + } + + @Override + public List getDbSpecifications() { + return null; + } + + + @Override + public SutInfoDto.OutputFormat getPreferredOutputFormat() { + return SutInfoDto.OutputFormat.JAVA_JUNIT_5; + } + +} diff --git a/e2e-tests/spring-rpc/spring-rpc-grpc/src/test/java/com/foo/rpc/grpc/examples/spring/branches/BranchGRPCServiceController.java b/e2e-tests/spring-rpc/spring-rpc-grpc/src/test/java/com/foo/rpc/grpc/examples/spring/branches/BranchGRPCServiceController.java new file mode 100644 index 0000000000..5e43058255 --- /dev/null +++ b/e2e-tests/spring-rpc/spring-rpc-grpc/src/test/java/com/foo/rpc/grpc/examples/spring/branches/BranchGRPCServiceController.java @@ -0,0 +1,34 @@ +package com.foo.rpc.grpc.examples.spring.branches; + +import com.foo.rpc.grpc.examples.spring.GRPCServerController; +import com.foo.rpc.grpc.examples.spring.branches.generated.BranchGRPCServiceGrpc; +import io.grpc.ManagedChannelBuilder; +import org.evomaster.client.java.controller.problem.ProblemInfo; +import org.evomaster.client.java.controller.problem.RPCProblem; + +import java.util.HashMap; + +public class BranchGRPCServiceController extends GRPCServerController { + + private BranchGRPCServiceGrpc.BranchGRPCServiceBlockingStub stub; + + public BranchGRPCServiceController() { + super(new BranchGRPCService()); + } + + @Override + public ProblemInfo getProblemInfo() { + return new RPCProblem(new HashMap() {{ + put(BranchGRPCServiceGrpc.BranchGRPCServiceBlockingStub.class.getName(), stub); + }}); + } + + @Override + public String startClient() { + channel = ManagedChannelBuilder.forAddress("localhost", getSutPort()).usePlaintext().build(); + stub = BranchGRPCServiceGrpc.newBlockingStub(channel); + + + return "started:"+!(channel.isShutdown() || channel.isTerminated()); + } +} diff --git a/e2e-tests/spring-rpc/spring-rpc-grpc/src/test/java/org/evomaster/e2etests/spring/rpc/grpc/examples/GRPCServerTestBase.java b/e2e-tests/spring-rpc/spring-rpc-grpc/src/test/java/org/evomaster/e2etests/spring/rpc/grpc/examples/GRPCServerTestBase.java new file mode 100644 index 0000000000..a7f5ecc443 --- /dev/null +++ b/e2e-tests/spring-rpc/spring-rpc-grpc/src/test/java/org/evomaster/e2etests/spring/rpc/grpc/examples/GRPCServerTestBase.java @@ -0,0 +1,12 @@ +package org.evomaster.e2etests.spring.rpc.grpc.examples; + +import org.evomaster.client.java.controller.EmbeddedSutController; +import org.evomaster.e2etests.utils.RPCTestBase; + +public class GRPCServerTestBase extends RPCTestBase { + + protected static void initClass(EmbeddedSutController controller) throws Exception { + + RPCTestBase.initClass(controller); + } +} diff --git a/e2e-tests/spring-rpc/spring-rpc-grpc/src/test/java/org/evomaster/e2etests/spring/rpc/grpc/examples/branches/BranchesGRPCEMTest.java b/e2e-tests/spring-rpc/spring-rpc-grpc/src/test/java/org/evomaster/e2etests/spring/rpc/grpc/examples/branches/BranchesGRPCEMTest.java new file mode 100644 index 0000000000..2562c966bb --- /dev/null +++ b/e2e-tests/spring-rpc/spring-rpc-grpc/src/test/java/org/evomaster/e2etests/spring/rpc/grpc/examples/branches/BranchesGRPCEMTest.java @@ -0,0 +1,37 @@ +package org.evomaster.e2etests.spring.rpc.grpc.examples.branches; + +import com.foo.rpc.grpc.examples.spring.branches.BranchGRPCServiceController; +import org.evomaster.core.problem.rpc.RPCIndividual; +import org.evomaster.core.search.Solution; +import org.evomaster.e2etests.spring.rpc.grpc.examples.GRPCServerTestBase; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class BranchesGRPCEMTest extends GRPCServerTestBase { + + @BeforeAll + public static void initClass() throws Exception { + + BranchGRPCServiceController controller = new BranchGRPCServiceController(); + GRPCServerTestBase.initClass(controller); + } + +// @Test +// public void testRunEM() throws Throwable { +// +// runTestHandlingFlakyAndCompilation( +// "BranchesGRPCEM", +// "org.foo.grpc.BranchesEM", +// 5000, +// (args) -> { +// +// Solution solution = initAndRun(args); +// +// assertTrue(solution.getIndividuals().size() >= 1); +// +// }); +// } +} diff --git a/pom.xml b/pom.xml index adc73d12a3..36a0f7f7a6 100644 --- a/pom.xml +++ b/pom.xml @@ -148,6 +148,10 @@ 9.2.1.jre8 2.2.0 0.15.0 + 1.41.0 + 0.6.1 + 3.17.3 + 1.6.2 2.32.0 4.5.13 4.9.3 @@ -812,12 +816,31 @@ + org.apache.thrift libthrift ${thrift.libthrift.version} + + + + io.grpc + grpc-netty-shaded + ${io.grpc.version} + + + io.grpc + grpc-protobuf + ${io.grpc.version} + + + io.grpc + grpc-stub + ${io.grpc.version} + + com.github.tomakehurst @@ -1240,6 +1263,13 @@ + + org.xolstice.maven.plugins + protobuf-maven-plugin + ${org.xolsticeprotobuf.version} + + + @@ -1258,4 +1288,4 @@ - \ No newline at end of file + From c93a04d519ee153af86f47c42c53e3506b1c24b4 Mon Sep 17 00:00:00 2001 From: hghianni Date: Sun, 25 Jun 2023 23:55:01 -0300 Subject: [PATCH 099/345] Add tests, refactors, docs --- .../dto/database/execution/FailedQuery.java | 8 +- .../operations/MongoInsertionDto.java | 5 +- .../operations/MongoInsertionEntryDto.java | 6 - .../controller/internal/EMController.java | 6 +- .../controller/internal/SutController.java | 2 +- .../controller/internal/db/MongoHandler.java | 181 +++++---------- .../controller/mongo/MongoScriptRunner.java | 37 +++- .../java/controller/mongo/dsl/MongoDsl.java | 14 +- .../mongo/dsl/MongoSequenceDsl.java | 3 +- .../mongo/dsl/MongoStatementDsl.java | 2 +- .../mongo/MongoScriptRunnerTest.java | 46 ++++ client-java/instrumentation/pom.xml | 5 + ...ongoEntityInformationClassReplacement.java | 25 ++- .../MappingMongoEntityOperationsImpl.java | 13 ++ .../MappingMongoEntityInstrumentedTest.java | 49 +++++ .../mongo/MappingMongoEntityOperations.java | 9 + .../mongo/MongoPersistentEntityMock.java | 208 ++++++++++++++++++ core/pom.xml | 6 + .../kotlin/org/evomaster/core/EMConfig.kt | 3 +- .../core/mongo/MongoActionGeneBuilder.kt | 101 +++++++-- .../org/evomaster/core/mongo/MongoDbAction.kt | 27 ++- .../core/mongo/MongoDbActionResult.kt | 16 +- .../core/mongo/MongoDbActionTransformer.kt | 36 +-- .../core/mongo/MongoInsertBuilder.kt | 5 +- .../org/evomaster/core/output/MongoWriter.kt | 6 +- .../api/service/ApiWsStructureMutator.kt | 6 +- .../enterprise/EnterpriseIndividual.kt | 5 +- .../enterprise/service/EnterpriseSampler.kt | 24 +- .../service/RemoteControllerImplementation.kt | 2 +- .../core/search/EvaluatedIndividual.kt | 34 +-- .../evomaster/core/search/gene/ObjectGene.kt | 2 +- .../core/search/gene/datetime/DateGene.kt | 2 +- .../core/search/gene/numeric/DoubleGene.kt | 2 +- .../core/search/gene/numeric/IntegerGene.kt | 2 +- .../core/search/gene/numeric/LongGene.kt | 2 +- .../core/mongo/MongoActionGeneBuilderTest.kt | 82 +++++++ .../evomaster/core/mongo/MongoActionTest.kt | 31 +++ .../mongo/MongoDbActionTransformerTest.kt | 36 +++ .../core/mongo/MongoInsertBuilderTest.kt | 21 ++ .../search/gene/mongo/EJSONOutputModeTest.kt | 73 ++++++ docs/options.md | 2 + .../foo/EvoMasterSampleGenerationTest.kt | 193 ++++++++++++++++ .../rest/mongo/foo/MongoEMGenerationTest.java | 8 +- 43 files changed, 1046 insertions(+), 300 deletions(-) delete mode 100644 client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionEntryDto.java create mode 100644 client-java/controller/src/test/java/org/evomaster/client/java/controller/mongo/MongoScriptRunnerTest.java rename client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/{classes => thirdpartyclasses}/MappingMongoEntityInformationClassReplacement.java (79%) create mode 100644 client-java/instrumentation/src/test/java/com/foo/somedifferentpackage/examples/methodreplacement/MappingMongoEntityOperationsImpl.java create mode 100644 client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/example/mongo/MappingMongoEntityInstrumentedTest.java create mode 100644 client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/example/mongo/MappingMongoEntityOperations.java create mode 100644 client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/example/mongo/MongoPersistentEntityMock.java create mode 100644 core/src/test/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilderTest.kt create mode 100644 core/src/test/kotlin/org/evomaster/core/mongo/MongoActionTest.kt create mode 100644 core/src/test/kotlin/org/evomaster/core/mongo/MongoDbActionTransformerTest.kt create mode 100644 core/src/test/kotlin/org/evomaster/core/mongo/MongoInsertBuilderTest.kt create mode 100644 core/src/test/kotlin/org/evomaster/core/search/gene/mongo/EJSONOutputModeTest.kt create mode 100644 e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/EvoMasterSampleGenerationTest.kt diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/FailedQuery.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/FailedQuery.java index 5a955ce8d1..f4a2ef6be7 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/FailedQuery.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/FailedQuery.java @@ -1,13 +1,11 @@ package org.evomaster.client.java.controller.api.dto.database.execution; -import java.util.Map; public class FailedQuery { - public FailedQuery(String database, String collection, Class documentsType, Map > accessedFields) { + public FailedQuery(String database, String collection, Class documentsType) { this.database = database; this.collection = collection; this.documentsType = documentsType; - this.accessedFields = accessedFields; } public FailedQuery(){ @@ -18,13 +16,9 @@ public FailedQuery(){ private final String database; private final String collection; private Class documentsType; - private Map> accessedFields; public String getDatabase() {return database;} public String getCollection() {return collection;} - public Map> getAccessedFields() { - return accessedFields; - } public Class getDocumentsType() { return documentsType; } diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionDto.java index d0e7896a4a..9fe5274807 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionDto.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionDto.java @@ -1,13 +1,10 @@ package org.evomaster.client.java.controller.api.dto.database.operations; -import java.util.ArrayList; -import java.util.List; - public class MongoInsertionDto { public String databaseName; public String collectionName; - public List data = new ArrayList<>(); + public String data; } diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionEntryDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionEntryDto.java deleted file mode 100644 index a23e0c3706..0000000000 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/operations/MongoInsertionEntryDto.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.evomaster.client.java.controller.api.dto.database.operations; - -public class MongoInsertionEntryDto { - public String fieldName; - public String value; -} diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java index 7c04329ec4..3377585789 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java @@ -790,8 +790,8 @@ public Response executeMongoInsertion(MongoDatabaseCommandDto dto, @Context Http return Response.status(400).entity(WrappedResponseDto.withError(msg)).build(); } - if (dto.insertions.stream().anyMatch(i -> i.collectionName == null || i.collectionName.isEmpty())) { - String msg = "Insertion with no target collection"; + if (dto.insertions.stream().anyMatch(i -> i.collectionName.isEmpty() || i.databaseName.isEmpty())) { + String msg = "Insertion with no target collection or database"; SimpleLogger.warn(msg); return Response.status(400).entity(WrappedResponseDto.withError(msg)).build(); } @@ -800,7 +800,7 @@ public Response executeMongoInsertion(MongoDatabaseCommandDto dto, @Context Http try { - mongoInsertionResultsDto = MongoScriptRunner.execInsert(connection, dto.insertions); + mongoInsertionResultsDto = MongoScriptRunner.executeInsert(connection, dto.insertions); } catch (Exception e) { String msg = "Failed to execute database command: " + e.getMessage(); SimpleLogger.warn(msg); diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index ca5c61d28d..e082349228 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -237,7 +237,7 @@ public MongoInsertionResultsDto execInsertionsIntoMongoDatabase(List failedQueries; - private final HashMap> collectionInfo; + /** + * Info about types of the documents of collections + */ + private final Map> collectionInfo; public MongoHandler() { distances = new ArrayList<>(); @@ -58,23 +62,31 @@ public void reset() { operations.clear(); distances.clear(); failedQueries.clear(); - // collectionInfo is not cleared to avoid losing the info as it's retrieved when SUT is started + // collectionInfo is not cleared to avoid losing the info as it's retrieved while SUT is starting } - public void handle(MongoInfo info) { - if (!extractMongoExecution) { - return; - } + public boolean isCalculateHeuristics() { + return calculateHeuristics; + } - operations.add(info); + public boolean isExtractMongoExecution() { + return extractMongoExecution; } - public void handle(MongoCollectionInfo info) { - if (!extractMongoExecution) { - return; - } + public void setCalculateHeuristics(boolean calculateHeuristics) { + this.calculateHeuristics = calculateHeuristics; + } + + public void setExtractMongoExecution(boolean extractMongoExecution) { + this.extractMongoExecution = extractMongoExecution; + } + + public void handle(MongoInfo info) { + if (extractMongoExecution) operations.add(info); + } - collectionInfo.put(info.getCollectionName(), info.getDocumentsType()); + public void handle(MongoCollectionInfo info) { + if (extractMongoExecution) collectionInfo.put(info.getCollectionName(), info.getDocumentsType()); } public List getDistances() { @@ -88,7 +100,7 @@ public List getDistances() { } distances.add(new MongoOperationDistance(mongoInfo.getQuery(), dist)); - if (dist > 0 ) { + if (dist > 0) { Object collection = mongoInfo.getCollection(); failedQueries.add(new MongoOperation(collection, mongoInfo.getQuery())); } @@ -98,6 +110,12 @@ public List getDistances() { return distances; } + public MongoExecutionDto getExecutionDto() { + MongoExecutionDto dto = new MongoExecutionDto(); + dto.failedQueries = failedQueries.stream().map(this::extractRelevantInfo).collect(Collectors.toList()); + return dto; + } + private double computeDistance(MongoInfo info) { Object collection = info.getCollection(); Iterable documents = getDocuments(collection); @@ -124,144 +142,43 @@ private static Iterable getDocuments(Object collection) { } } - public MongoExecutionDto getExecutionDto(){ - MongoExecutionDto dto = new MongoExecutionDto(); - dto.failedQueries = failedQueries.stream().map(this::extractRelevantInfo).collect(Collectors.toList()); - return dto; - } - private FailedQuery extractRelevantInfo(MongoOperation operation) { - QueryOperation query = new QueryParser().parse(operation.getQuery()); Object collection = operation.getCollection(); - Map> accessedFields = extractFieldsInQuery(query); - Class documentsType; - - String collectionName; String databaseName; + String collectionName; + Class documentsType; try { Class collectionClass = collection.getClass().getClassLoader().loadClass("com.mongodb.client.MongoCollection"); Object namespace = collectionClass.getMethod("getNamespace").invoke(collection); - collectionName = (String) namespace.getClass().getMethod("getCollectionName").invoke(namespace); databaseName = (String) namespace.getClass().getMethod("getDatabaseName").invoke(namespace); - } catch (ClassNotFoundException |IllegalAccessException | InvocationTargetException | NoSuchMethodException e){ - throw new RuntimeException(e); + collectionName = (String) namespace.getClass().getMethod("getCollectionName").invoke(namespace); + } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException e) { + throw new RuntimeException("Failed to retrieve collection name or database name", e); } - // I should be as specific as I can with the type the collection's documents should have. - // There are a few ways to get that info: - - // 1) Using collection.getDocumentsClass(). - // Spring for example "ignore" this and store type info inside repository. - - // 2) When using Spring, retrieving the type of the repository associated with the collection. - // The MongoEntityInformation replacement makes this possible. - - // 3) Extract from the query the fields used and type of each of them. This probably won't - // work as expected as usually a subset of fields is used in a query. - - if(collectionInfo.containsKey(collectionName)){ + if (collectionTypeIsRegistered(collectionName)) { documentsType = collectionInfo.get(collectionName); - }else{ + } else { documentsType = extractDocumentsType(collection); } - return new FailedQuery(databaseName, collectionName, documentsType, accessedFields); + return new FailedQuery(databaseName, collectionName, documentsType); + } + + private boolean collectionTypeIsRegistered(String collectionName) { + return collectionInfo.containsKey(collectionName); } private static Class extractDocumentsType(Object collection) { try { Class collectionClass = collection.getClass().getClassLoader().loadClass("com.mongodb.client.MongoCollection"); return (Class) collectionClass.getMethod("getDocumentClass").invoke(collection); - - } catch (NoSuchMethodException | ClassNotFoundException | InvocationTargetException | IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - private static Map> extractFieldsInQuery(QueryOperation operation) { - Map> accessedFields = new HashMap<>(); - - if(operation instanceof ComparisonOperation) { - ComparisonOperation op = (ComparisonOperation) operation; - accessedFields.put(op.getFieldName(), op.getValue().getClass()); - } - - if(operation instanceof AndOperation) { - AndOperation op = (AndOperation) operation; - op.getConditions().forEach(cond -> accessedFields.putAll(extractFieldsInQuery(cond))); - } - - if(operation instanceof OrOperation) { - OrOperation op = (OrOperation) operation; - op.getConditions().forEach(cond -> accessedFields.putAll(extractFieldsInQuery(cond))); - } - - if(operation instanceof NorOperation) { - NorOperation op = (NorOperation) operation; - op.getConditions().forEach(cond -> accessedFields.putAll(extractFieldsInQuery(cond))); - } - - if(operation instanceof InOperation) { - InOperation op = (InOperation) operation; - accessedFields.put(op.getFieldName(), op.getValues().get(0).getClass()); - } - - if(operation instanceof NotInOperation) { - NotInOperation op = (NotInOperation) operation; - accessedFields.put(op.getFieldName(), op.getValues().get(0).getClass()); - } - - if(operation instanceof AllOperation) { - AllOperation op = (AllOperation) operation; - accessedFields.put(op.getFieldName(), op.getValues().getClass()); - } - - if(operation instanceof SizeOperation) { - SizeOperation op = (SizeOperation) operation; - accessedFields.put(op.getFieldName(), op.getValue().getClass()); - } - - if(operation instanceof ExistsOperation) { - ExistsOperation op = (ExistsOperation) operation; - accessedFields.put(op.getFieldName(), null); - } - - if(operation instanceof ModOperation) { - ModOperation op = (ModOperation) operation; - accessedFields.put(op.getFieldName(), op.getDivisor().getClass()); - } - - if(operation instanceof TypeOperation) { - TypeOperation op = (TypeOperation) operation; - accessedFields.put(op.getFieldName(), op.getType().getClass()); + } catch (NoSuchMethodException | ClassNotFoundException | InvocationTargetException | + IllegalAccessException e) { + throw new RuntimeException("Failed to retrieve document's type from collection", e); } - - /* - if(operation instanceof ElemMatchOperation) { - ElemMatchOperation op = (ElemMatchOperation) operation; - accessedFields.put(op.getFieldName(), op.getValue()); - } - - */ - - return accessedFields; - } - - public boolean isCalculateHeuristics() { - return calculateHeuristics; - } - - public boolean isExtractMongoExecution() { - return extractMongoExecution; - } - - public void setCalculateHeuristics(boolean calculateHeuristics) { - this.calculateHeuristics = calculateHeuristics; - } - - public void setExtractMongoExecution(boolean extractMongoExecution) { - this.extractMongoExecution = extractMongoExecution; } } diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoScriptRunner.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoScriptRunner.java index d4855e61ae..cd6e266b31 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoScriptRunner.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoScriptRunner.java @@ -3,6 +3,7 @@ import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionDto; import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionResultsDto; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -15,10 +16,17 @@ public class MongoScriptRunner { /** * Default constructor */ - public MongoScriptRunner() { - } + public MongoScriptRunner() {} - public static MongoInsertionResultsDto execInsert(Object conn, List insertions){ + /** + * Execute the different Mongo insertions. + * + * @param connection a connection to the database (MongoClient) + * @param insertions the Mongo insertions to execute + * + * @return a MongoInsertionResultsDto + */ + public static MongoInsertionResultsDto executeInsert(Object connection, List insertions) { if (insertions == null || insertions.isEmpty()) { throw new IllegalArgumentException("No data to insert"); @@ -28,15 +36,11 @@ public static MongoInsertionResultsDto execInsert(Object conn, List documentClass = Class.forName("org.bson.Document"); - Object document = documentClass.getDeclaredConstructor().newInstance(); - document = document.getClass().getMethod("parse", String.class).invoke(document, insDto.data.get(0).value); - Object database = conn.getClass().getMethod("getDatabase", String.class).invoke(conn,insDto.databaseName); - Object collection = database.getClass().getMethod("getCollection", String.class).invoke(database, insDto.collectionName); - Class.forName("com.mongodb.client.MongoCollection").getMethod("insertOne", Object.class).invoke(collection, document); + Object document = parseEJSON(insertionDto.data); + insertDocument(connection, insertionDto.databaseName, insertionDto.collectionName, document); mongoResults.set(i, true); } catch (Exception e) { String msg = "Failed to execute insertion with index " + i + " with Mongo. Error: " + e.getMessage(); @@ -49,4 +53,17 @@ public static MongoInsertionResultsDto execInsert(Object conn, List documentClass = Class.forName("org.bson.Document"); + Object document = Class.forName("org.bson.Document").getDeclaredConstructor().newInstance(); + document = documentClass.getMethod("parse", String.class).invoke(document, documentAsEJSON); + return document; + } + } diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoDsl.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoDsl.java index 83c8162678..dece6570ef 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoDsl.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoDsl.java @@ -1,7 +1,6 @@ package org.evomaster.client.java.controller.mongo.dsl; import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionDto; -import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionEntryDto; import java.util.ArrayList; import java.util.Arrays; @@ -11,7 +10,7 @@ * DSL (Domain Specific Language) for operations on * the Mongo Database */ -public class MongoDsl implements MongoSequenceDsl, MongoStatementDsl{ +public class MongoDsl implements MongoSequenceDsl, MongoStatementDsl{ private List list = new ArrayList<>(); @@ -27,10 +26,9 @@ private MongoDsl(List... previous) { } /** - * @return a DSL object to create SQL operations + * @return a DSL object to create MONGO operations */ public static MongoSequenceDsl mongo() { - return new MongoDsl(); } @@ -65,14 +63,8 @@ public MongoStatementDsl insertInto(String databaseName, String collectionName) @Override public MongoStatementDsl d(String printableValue) { - checkDsl(); - - MongoInsertionEntryDto entry = new MongoInsertionEntryDto(); - entry.value = printableValue; - - current().data.add(entry); - + current().data = printableValue; return this; } diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoSequenceDsl.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoSequenceDsl.java index a69afb0cc6..99bee8820b 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoSequenceDsl.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoSequenceDsl.java @@ -5,7 +5,8 @@ public interface MongoSequenceDsl { /** * An insertion operation on the Mongo Database (MongoDB) * - * @param collectionName the target table in the DB + * @param databaseName the target database in the MongoDB + * @param collectionName the target collection in the MongoDB * @return a statement in which it can be specified the values to add */ MongoStatementDsl insertInto(String databaseName, String collectionName); diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoStatementDsl.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoStatementDsl.java index 8f9e24437e..464843f4a2 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoStatementDsl.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/dsl/MongoStatementDsl.java @@ -26,7 +26,7 @@ public interface MongoStatementDsl { /** * Build the DTOs (Data Transfer Object) from this DSL, * closing it (ie, not usable any longer). - * @return a list of DTOs representing all the insertion SQL commands defined in this DSL. + * @return a list of DTOs representing all the insertion MONGO commands defined in this DSL. */ List dtos(); diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/mongo/MongoScriptRunnerTest.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/mongo/MongoScriptRunnerTest.java new file mode 100644 index 0000000000..e06163fa25 --- /dev/null +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/mongo/MongoScriptRunnerTest.java @@ -0,0 +1,46 @@ +package org.evomaster.client.java.controller.mongo; + +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; +import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionDto; +import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionResultsDto; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.GenericContainer; + +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.*; + +// Is this test possible? Docker should be running I think +public class MongoScriptRunnerTest { + + private static MongoClient connection; + private static final int MONGODB_PORT = 27017; + private static final GenericContainer mongodb = new GenericContainer<>("mongo:6.0") + .withExposedPorts(MONGODB_PORT); + + @BeforeAll + public static void initClass() throws Exception { + mongodb.start(); + int port = mongodb.getMappedPort(MONGODB_PORT); + + connection = MongoClients.create("mongodb://localhost:" + port + "/" + "aDatabase"); + } + + @Test + public void testInsert() { + assertFalse(connection.getDatabase("aDatabase").getCollection("aCollection").find().cursor().hasNext()); + MongoInsertionDto insertionDto = new MongoInsertionDto(); + insertionDto.databaseName = "aDatabase"; + insertionDto.collectionName = "aCollection"; + insertionDto.data = "{\"aField\":\"aString\"}"; + MongoInsertionResultsDto resultsDto = MongoScriptRunner.executeInsert(getConnection(), Collections.singletonList(insertionDto)); + assertTrue(resultsDto.executionResults.get(0)); + assertTrue(connection.getDatabase("aDatabase").getCollection("aCollection").find().cursor().hasNext()); + } + + public Object getConnection() { + return connection; + } +} diff --git a/client-java/instrumentation/pom.xml b/client-java/instrumentation/pom.xml index 4e7b023b3f..01ce07607e 100644 --- a/client-java/instrumentation/pom.xml +++ b/client-java/instrumentation/pom.xml @@ -149,6 +149,11 @@ mongodb-driver-sync test + + org.springframework.boot + spring-boot-starter-data-mongodb + test + diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MappingMongoEntityInformationClassReplacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MappingMongoEntityInformationClassReplacement.java similarity index 79% rename from client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MappingMongoEntityInformationClassReplacement.java rename to client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MappingMongoEntityInformationClassReplacement.java index 9020836f3c..5ddc9d4f8c 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/classes/MappingMongoEntityInformationClassReplacement.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MappingMongoEntityInformationClassReplacement.java @@ -1,4 +1,4 @@ -package org.evomaster.client.java.instrumentation.coverage.methodreplacement.classes; +package org.evomaster.client.java.instrumentation.coverage.methodreplacement.thirdpartyclasses; import org.evomaster.client.java.instrumentation.MongoCollectionInfo; import org.evomaster.client.java.instrumentation.coverage.methodreplacement.Replacement; @@ -14,7 +14,13 @@ import java.util.Collections; import java.util.List; - +/** + * When a MongoRepository is created in Spring, a MongoCollection is used under the hood. + * But information about the type of the repository's documents is not transferred to the collection. + * That info is retained on Spring side. + * So the intention of this replacement is to retrieve that type info. + * This will allow us to create and insert documents of the correct type in the collection (and the repository). + */ public class MappingMongoEntityInformationClassReplacement extends ThirdPartyMethodReplacementClass { private static final MappingMongoEntityInformationClassReplacement singleton = new MappingMongoEntityInformationClassReplacement(); private static ThreadLocal instance = new ThreadLocal<>(); @@ -44,8 +50,9 @@ private static void addInstance(Object x) { @Replacement( replacingConstructor = true, type = ReplacementType.TRACKER, - category = ReplacementCategory.SQL, - id = "constructorEntity" + category = ReplacementCategory.MONGO, + id = "constructorEntity", + castTo = "org.springframework.data.mongodb.repository.support.MappingMongoEntityInformation" ) public static void MappingMongoEntityInformation(@ThirdPartyCast(actualType = "org.springframework.data.mongodb.core.mapping.MongoPersistentEntity") Object entity) { handleMappingMongoEntityInformationConstructor("constructorEntity", Collections.singletonList(entity)); @@ -54,8 +61,9 @@ public static void MappingMongoEntityInformation(@ThirdPartyCast(actualType = "o @Replacement( replacingConstructor = true, type = ReplacementType.TRACKER, - category = ReplacementCategory.SQL, - id = "constructorEntityCustomCollectionName" + category = ReplacementCategory.MONGO, + id = "constructorEntityCustomCollectionName", + castTo = "org.springframework.data.mongodb.repository.support.MappingMongoEntityInformation" ) public static void MappingMongoEntityInformation(@ThirdPartyCast(actualType = "org.springframework.data.mongodb.core.mapping.MongoPersistentEntity") Object entity, String customCollectionName) { handleMappingMongoEntityInformationConstructor("constructorEntityCustomCollectionName", Arrays.asList(entity, customCollectionName)); @@ -64,8 +72,9 @@ public static void MappingMongoEntityInformation(@ThirdPartyCast(actualType = "o @Replacement( replacingConstructor = true, type = ReplacementType.TRACKER, - category = ReplacementCategory.SQL, - id = "constructorEntityFallbackIdType" + category = ReplacementCategory.MONGO, + id = "constructorEntityFallbackIdType", + castTo = "org.springframework.data.mongodb.repository.support.MappingMongoEntityInformation" ) public static void MappingMongoEntityInformation(@ThirdPartyCast(actualType = "org.springframework.data.mongodb.core.mapping.MongoPersistentEntity") Object entity, Class fallbackIdType) { handleMappingMongoEntityInformationConstructor("constructorEntityFallbackIdType", Arrays.asList(entity, fallbackIdType)); diff --git a/client-java/instrumentation/src/test/java/com/foo/somedifferentpackage/examples/methodreplacement/MappingMongoEntityOperationsImpl.java b/client-java/instrumentation/src/test/java/com/foo/somedifferentpackage/examples/methodreplacement/MappingMongoEntityOperationsImpl.java new file mode 100644 index 0000000000..6abef79a52 --- /dev/null +++ b/client-java/instrumentation/src/test/java/com/foo/somedifferentpackage/examples/methodreplacement/MappingMongoEntityOperationsImpl.java @@ -0,0 +1,13 @@ +package com.foo.somedifferentpackage.examples.methodreplacement; + +import org.evomaster.client.java.instrumentation.example.mongo.MappingMongoEntityOperations; +import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; +import org.springframework.data.mongodb.repository.support.MappingMongoEntityInformation; +import org.springframework.lang.Nullable; + +public class MappingMongoEntityOperationsImpl implements MappingMongoEntityOperations { + @Override + public MappingMongoEntityInformation callMappingMongoEntityInformation(MongoPersistentEntity entity, @Nullable Class fallbackIdType){ + return new MappingMongoEntityInformation<>(entity, fallbackIdType); + } +} diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/example/mongo/MappingMongoEntityInstrumentedTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/example/mongo/MappingMongoEntityInstrumentedTest.java new file mode 100644 index 0000000000..6a6fe47831 --- /dev/null +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/example/mongo/MappingMongoEntityInstrumentedTest.java @@ -0,0 +1,49 @@ +package org.evomaster.client.java.instrumentation.example.mongo; + +import com.foo.somedifferentpackage.examples.methodreplacement.MappingMongoEntityOperationsImpl; +import org.evomaster.client.java.instrumentation.InstrumentingClassLoader; +import org.evomaster.client.java.instrumentation.staticstate.ExecutionTracer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; +import org.springframework.data.mongodb.repository.support.MappingMongoEntityInformation; + +import static org.junit.jupiter.api.Assertions.*; + + +public class MappingMongoEntityInstrumentedTest { + + public class SomeType { + public String someField; + } + + protected MappingMongoEntityOperations getInstance() throws Exception { + + InstrumentingClassLoader cl = new InstrumentingClassLoader("com.foo"); + + return (MappingMongoEntityOperations) + cl.loadClass(MappingMongoEntityOperationsImpl.class.getName()) + .newInstance(); + } + + @BeforeEach + public void init(){ + ExecutionTracer.reset(); + assertEquals(0 , ExecutionTracer.getNumberOfObjectives()); + } + + @AfterEach + public void checkInstrumentation(){ + assertTrue(ExecutionTracer.getNumberOfObjectives() > 0); + } + + @Test + public void testConstructor() throws Exception { + MappingMongoEntityOperations mongoInstrumented = getInstance(); + MongoPersistentEntity entity = new MongoPersistentEntityMock<>(); + MappingMongoEntityInformation mappingMongoEntityInformation = mongoInstrumented.callMappingMongoEntityInformation(entity, SomeType.class); + assertNotNull(mappingMongoEntityInformation); + } +} + diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/example/mongo/MappingMongoEntityOperations.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/example/mongo/MappingMongoEntityOperations.java new file mode 100644 index 0000000000..af5e844398 --- /dev/null +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/example/mongo/MappingMongoEntityOperations.java @@ -0,0 +1,9 @@ +package org.evomaster.client.java.instrumentation.example.mongo; + +import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; +import org.springframework.data.mongodb.repository.support.MappingMongoEntityInformation; +import org.springframework.lang.Nullable; + +public interface MappingMongoEntityOperations { + MappingMongoEntityInformation callMappingMongoEntityInformation(MongoPersistentEntity entity, @Nullable Class fallbackIdType); +} diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/example/mongo/MongoPersistentEntityMock.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/example/mongo/MongoPersistentEntityMock.java new file mode 100644 index 0000000000..b4f6062eae --- /dev/null +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/example/mongo/MongoPersistentEntityMock.java @@ -0,0 +1,208 @@ +package org.evomaster.client.java.instrumentation.example.mongo; + +import org.jetbrains.annotations.NotNull; +import org.springframework.data.mapping.*; +import org.springframework.data.mapping.model.PersistentPropertyAccessorFactory; +import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; +import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; +import org.springframework.data.mongodb.core.mapping.ShardKey; +import org.springframework.data.mongodb.core.query.Collation; +import org.springframework.data.spel.EvaluationContextProvider; +import org.springframework.data.util.TypeInformation; + +import java.lang.annotation.Annotation; +import java.util.Iterator; + +public class MongoPersistentEntityMock implements MongoPersistentEntity{ + + @Override + public String getCollection() { + return null; + } + + @Override + public String getLanguage() { + return null; + } + + @Override + public MongoPersistentProperty getTextScoreProperty() { + return null; + } + + @Override + public boolean hasTextScoreProperty() { + return false; + } + + @Override + public Collation getCollation() { + return null; + } + + @Override + public ShardKey getShardKey() { + return null; + } + + @Override + public void addPersistentProperty(MongoPersistentProperty mongoPersistentProperty) { + + } + + @Override + public void addAssociation(Association association) { + + } + + @Override + public void verify() throws MappingException { + + } + + @Override + public void setPersistentPropertyAccessorFactory(PersistentPropertyAccessorFactory persistentPropertyAccessorFactory) { + + } + + @Override + public void setEvaluationContextProvider(EvaluationContextProvider evaluationContextProvider) { + + } + + @Override + public String getName() { + return null; + } + + @Override + public PreferredConstructor getPersistenceConstructor() { + return null; + } + + @Override + public boolean isConstructorArgument(PersistentProperty persistentProperty) { + return false; + } + + @Override + public boolean isIdProperty(PersistentProperty persistentProperty) { + return false; + } + + @Override + public boolean isVersionProperty(PersistentProperty persistentProperty) { + return false; + } + + @Override + public MongoPersistentProperty getIdProperty() { + return null; + } + + @Override + public MongoPersistentProperty getVersionProperty() { + return null; + } + + @Override + public MongoPersistentProperty getPersistentProperty(String s) { + return null; + } + + @Override + public Iterable getPersistentProperties(Class aClass) { + return null; + } + + @Override + public boolean hasIdProperty() { + return false; + } + + @Override + public boolean hasVersionProperty() { + return false; + } + + @Override + public Class getType() { + return null; + } + + @Override + public Alias getTypeAlias() { + return null; + } + + @Override + public TypeInformation getTypeInformation() { + return null; + } + + @Override + public void doWithProperties(PropertyHandler propertyHandler) { + + } + + @Override + public void doWithProperties(SimplePropertyHandler simplePropertyHandler) { + + } + + @Override + public void doWithAssociations(AssociationHandler associationHandler) { + + } + + @Override + public void doWithAssociations(SimpleAssociationHandler simpleAssociationHandler) { + + } + + @Override + public A findAnnotation(Class aClass) { + return null; + } + + @Override + public boolean isAnnotationPresent(Class aClass) { + return false; + } + + @Override + public PersistentPropertyAccessor getPropertyAccessor(B b) { + return null; + } + + @Override + public PersistentPropertyPathAccessor getPropertyPathAccessor(B b) { + return null; + } + + @Override + public IdentifierAccessor getIdentifierAccessor(Object o) { + return null; + } + + @Override + public boolean isNew(Object o) { + return false; + } + + @Override + public boolean isImmutable() { + return false; + } + + @Override + public boolean requiresPropertyPopulation() { + return false; + } + + @NotNull + @Override + public Iterator iterator() { + return null; + } +} \ No newline at end of file diff --git a/core/pom.xml b/core/pom.xml index 331d928a2c..4cbe6c6654 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -222,6 +222,12 @@ mockserver-client-java-no-dependencies test + + + org.mongodb + bson + test + diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index 757dabdd4a..0b699e9804 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -1061,8 +1061,9 @@ class EMConfig { @Cfg("Enable EvoMaster to generate SQL data with direct accesses to the database. Use a search algorithm") var generateSqlDataWithSearch = true + @Experimental @Cfg("Enable EvoMaster to generate Mongo data with direct accesses to the database") - var generateMongoData = true + var generateMongoData = false @Cfg("When generating SQL data, how many new rows (max) to generate for each specific SQL Select") @Min(1.0) diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilder.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilder.kt index 8542695715..ddffb67343 100644 --- a/core/src/main/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilder.kt @@ -4,49 +4,110 @@ import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.collection.ArrayGene import org.evomaster.core.search.gene.datetime.DateGene import org.evomaster.core.search.gene.numeric.* -import org.evomaster.core.search.gene.optional.NullableGene import org.evomaster.core.search.gene.string.StringGene +import org.slf4j.Logger +import org.slf4j.LoggerFactory +// Default Mapping of Java Classes to BSON types: +// https://mongodb.github.io/mongo-java-driver/3.6/javadoc/?org/bson/codecs/BsonTypeClassMap.html class MongoActionGeneBuilder { - fun buildGene(fieldName: String, valueType: Class): Gene { - val typeName = valueType.simpleName; + private val log: Logger = LoggerFactory.getLogger(MongoActionGeneBuilder::class.java) - if(typeName == "String"){ + fun buildGene(fieldName: String, valueType: Class): Gene? { + val typeName = valueType.name; + + if(typeName == "java.lang.String"){ return StringGene(name = fieldName) } - if(typeName == "int" || typeName == "Integer"){ - return IntegerGene(fieldName, min = Int.MIN_VALUE, max = Int.MAX_VALUE) + if(typeName == "int" || typeName == "java.lang.Integer"){ + return IntegerGene(name = fieldName, min = Int.MIN_VALUE, max = Int.MAX_VALUE) } - if(typeName == "long" || typeName == "Long"){ - return LongGene(fieldName, min = Long.MIN_VALUE, max = Long.MAX_VALUE) + if(typeName == "long" || typeName == "java.lang.Long"){ + return LongGene(name = fieldName, min = Long.MIN_VALUE, max = Long.MAX_VALUE) } - if(typeName == "double" || typeName == "Double"){ - return DoubleGene(fieldName, min = Double.MIN_VALUE, max = Double.MAX_VALUE) + if(typeName == "double" || typeName == "java.lang.Double"){ + return DoubleGene(name = fieldName, min = Double.MIN_VALUE, max = Double.MAX_VALUE) } - if(typeName == "boolean" || typeName == "Boolean") { + if(typeName == "boolean" || typeName == "java.lang.Boolean") { return BooleanGene(name = fieldName) } - if(typeName == "Date") { - return DateGene(name = fieldName) + if(typeName == "java.util.Date") { + return DateGene(name = fieldName, onlyValidDates = true) + } + + if(isAListImplementation(valueType)){ + val elementsGene = buildGene("", valueType.componentType) + return if(elementsGene != null) ArrayGene(name = fieldName, template = elementsGene) else null + } + + if(typeName == "org.bson.types.Decimal128") { + return unhandledValueType(fieldName) + } + + if(typeName == "org.bson.types.Binary") { + return unhandledValueType(fieldName) + } + + if(typeName == "org.bson.types.ObjectId") { + return unhandledValueType(fieldName) + } + + if(typeName == "org.bson.types.RegularExpression") { + return unhandledValueType(fieldName) + } + + // Deprecated + if(typeName == "org.bson.types.Symbol") { + return unhandledValueType(fieldName) } + // Deprecated + if(typeName == "org.bson.types.DBPointer") { + return unhandledValueType(fieldName) + } - /* - if(valueType == List<*>::javaClass ) { - return ArrayGene(name = fieldName, template = IntegerGene(fieldName, min = Int.MIN_VALUE, max = Int.MAX_VALUE)) + if(typeName == "org.bson.types.MaxKey") { + return unhandledValueType(fieldName) } - */ + if(typeName == "org.bson.types.MinKey") { + return unhandledValueType(fieldName) + } - //Unhandled Types: Null, Document, Binary Data, Object Id, Regular Expression, Javascript, Timestamp, Decimal128, Min/max key + if(typeName == "org.bson.types.Code") { + return unhandledValueType(fieldName) + } - return ObjectGene(fieldName, valueType.fields.map { field -> buildGene(field.name, field.type) }) + // Deprecated + if(typeName == "org.bson.types.CodeWithScope") { + return unhandledValueType(fieldName) + } - //throw IllegalArgumentException("Cannot handle: $fieldName.") + if(typeName == "org.bson.types.BSONTimestamp") { + return unhandledValueType(fieldName) + } + + // Deprecated + if(typeName == "org.bson.types.Undefined") { + return unhandledValueType(fieldName) + } + + if(typeName == "org.bson.Document") { + return ObjectGene(fieldName, listOf()) + } + + return ObjectGene(fieldName, valueType.fields.mapNotNull { field -> buildGene(field.name, field.type) }) } + + private fun unhandledValueType(fieldName: String): Gene? { + log.warn(("Cannot convert field: $fieldName to gene")) + return null + } + + private fun isAListImplementation(valueType: Class) = valueType.interfaces.any { i -> i.typeName == "java.util.List" } } diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt index c24cdefa8d..a8a6656179 100644 --- a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt @@ -3,30 +3,41 @@ package org.evomaster.core.mongo import org.evomaster.core.search.Action import org.evomaster.core.search.gene.Gene import org.evomaster.core.search.gene.ObjectGene +import java.lang.reflect.Modifier import java.util.* class MongoDbAction( + /** + * The database to insert document into + */ val database: String, + /** + * The collection to insert document into + */ val collection: String, + /** + * The type of the new document. Should map the type of the documents of the collection + */ val documentsType: Class<*>, - val accessedFields: Map>, computedGenes: List? = null ) : Action(listOf()) { - val genes: List = (computedGenes ?: computeGenes()).also { addChildren(it) } + private val genes: List = (computedGenes ?: computeGenes()).also { addChildren(it) } private fun computeGenes(): List { val genes = - if (documentsType.typeName == "org.bson.Document") { - accessedFields.map { MongoActionGeneBuilder().buildGene(it.key, it.value) } + if (documentsType.name == "org.bson.Document") { + listOf() } else { - documentsType.declaredFields.map { MongoActionGeneBuilder().buildGene(it.name, it.type) } + getFieldsFromType().mapNotNull { MongoActionGeneBuilder().buildGene(it.name, it.type) } } return Collections.singletonList(ObjectGene("BSON", genes)) } override fun getName(): String { - return "MONGO_Find_${collection}_${accessedFields.map { it.key }.sorted().joinToString("_")}" + return "MONGO_Insert_${database}_${collection}_${ + getFieldsFromType().map { it.name }.sorted().joinToString("_") + }" } override fun seeTopGenes(): List { @@ -38,6 +49,8 @@ class MongoDbAction( } override fun copyContent(): Action { - return MongoDbAction(database, collection, documentsType, accessedFields, genes.map(Gene::copy)) + return MongoDbAction(database, collection, documentsType, genes.map(Gene::copy)) } + + private fun getFieldsFromType() = documentsType.declaredFields.filter { Modifier.isPublic(it.modifiers) } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionResult.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionResult.kt index 09f68ce633..a2a31848e3 100644 --- a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionResult.kt +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionResult.kt @@ -1,4 +1,5 @@ package org.evomaster.core.mongo + import org.evomaster.core.search.Action import org.evomaster.core.search.ActionResult @@ -8,9 +9,9 @@ import org.evomaster.core.search.ActionResult class MongoDbActionResult : ActionResult { constructor(stopping: Boolean = false) : super(stopping) - constructor(other: MongoDbActionResult): super(other) + constructor(other: MongoDbActionResult) : super(other) - companion object{ + companion object { const val INSERT_MONGO_EXECUTE_SUCCESSFULLY = "INSERT_MONGO_EXECUTE_SUCCESSFULLY" } @@ -19,16 +20,15 @@ class MongoDbActionResult : ActionResult { } /** - * @param success specifies whether the INSERT SQL executed successfully - * - * NOTE THAT here for SELECT, the execution result is false by default. + * @param success specifies whether the INSERT MONGO executed successfully */ - fun setInsertExecutionResult(success: Boolean) = addResultValue(INSERT_MONGO_EXECUTE_SUCCESSFULLY, success.toString()) + fun setInsertExecutionResult(success: Boolean) = + addResultValue(INSERT_MONGO_EXECUTE_SUCCESSFULLY, success.toString()) /** - * @return whether the db action executed successfully + * @return whether the MongoDB action executed successfully */ - fun getInsertExecutionResult() = getResultValue(INSERT_MONGO_EXECUTE_SUCCESSFULLY)?.toBoolean()?:false + fun getInsertExecutionResult() = getResultValue(INSERT_MONGO_EXECUTE_SUCCESSFULLY)?.toBoolean() ?: false override fun matchedType(action: Action): Boolean { return action is MongoDbAction diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionTransformer.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionTransformer.kt index 99b904f687..2377164b2e 100644 --- a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionTransformer.kt +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionTransformer.kt @@ -1,38 +1,26 @@ package org.evomaster.core.mongo import org.evomaster.client.java.controller.api.dto.database.operations.* +import org.evomaster.core.search.gene.utils.GeneUtils object MongoDbActionTransformer { - fun transform(insertions: List) : MongoDatabaseCommandDto { + fun transform(actions: List) : MongoDatabaseCommandDto { - val list = mutableListOf() + val insertionDtos = mutableListOf() - for (element in insertions) { + for (action in actions) { + val genes = action.seeTopGenes().first() - val insertion = MongoInsertionDto().apply { - databaseName = element.database - collectionName = element.collection } + val insertionDto = MongoInsertionDto().apply { + databaseName = action.database + collectionName = action.collection + data = genes.getValueAsPrintableString(mode = GeneUtils.EscapeMode.EJSON) + } - val g = element.seeTopGenes().first() - val entry = MongoInsertionEntryDto() - - // If is printed as JSON there might a problem - // A Document(from Mongo) can be created from a JSON but some info might be lost - // Preferably is created from an EJSON (Extended JSON) as JSON can only directly represent a - // subset of the types supported by BSON. - // Maybe we can create a new OutputFormat - - entry.value = g.getValueAsPrintableString() - - // This is ignored for now - entry.fieldName = g.getVariableName() - - insertion.data.add(entry) - - list.add(insertion) + insertionDtos.add(insertionDto) } - return MongoDatabaseCommandDto().apply { this.insertions = list } + return MongoDatabaseCommandDto().apply { this.insertions = insertionDtos } } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoInsertBuilder.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoInsertBuilder.kt index 743e43545c..fc916af983 100644 --- a/core/src/main/kotlin/org/evomaster/core/mongo/MongoInsertBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoInsertBuilder.kt @@ -1,8 +1,7 @@ package org.evomaster.core.mongo class MongoInsertBuilder { - - fun createMongoInsertionAction(database: String, collection: String, documentsType: Class<*>, accessedFields: Map>): List { - return mutableListOf(MongoDbAction(database, collection, documentsType, accessedFields)) + fun createMongoInsertionAction(database: String, collection: String, documentsType: Class<*>): MongoDbAction{ + return MongoDbAction(database, collection, documentsType) } } diff --git a/core/src/main/kotlin/org/evomaster/core/output/MongoWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/MongoWriter.kt index d7fc99f124..1bcac528d7 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/MongoWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/MongoWriter.kt @@ -23,13 +23,11 @@ object MongoWriter { format: OutputFormat, mongoDbInitialization: List, lines: Lines, - groupIndex: String ="", + groupIndex: String = "", insertionVars: MutableList>, skipFailure: Boolean) { - //if (dbInitialization.isEmpty() || dbInitialization.none { !it.action.representExistingData && (!skipFailure || it.result.getInsertExecutionResult())}) { - //return - //} + if (mongoDbInitialization.isEmpty()) return val insertionVar = "insertions${groupIndex}" val insertionVarResult = "${insertionVar}result" diff --git a/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt b/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt index ed2f1feba8..554db1eea6 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt @@ -323,9 +323,9 @@ abstract class ApiWsStructureMutator : StructureMutator() { val addedInsertions = if (mutatedGenes != null) mutableListOf>() else null ff.forEach { - val insertions = sampler.sampleMongoInsertion(it.database, it.collection, it.documentsType, it.accessedFields) - ind.addInitializingMongoDbActions(actions = insertions) - addedInsertions?.add(insertions) + val insertion = listOf(sampler.sampleMongoInsertion(it.database, it.collection, it.documentsType)) + ind.addInitializingMongoDbActions(actions = insertion) + addedInsertions?.add(insertion) } return addedInsertions diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt index b9783c43d1..5057774c9f 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt @@ -105,9 +105,8 @@ abstract class EnterpriseIndividual( ActionFilter.MAIN_EXECUTABLE -> groupsView()!!.getAllInGroup(GroupsOfChildren.MAIN) .flatMap { (it as ActionComponent).flatten() } .filter { it !is DbAction && it !is ApiExternalServiceAction } - //CHANGE: This is momentary for testing. Needs refactor to handle multiple databases - ActionFilter.INIT -> groupsView()!!.getAllInGroup(GroupsOfChildren.INITIALIZATION_MONGO).flatMap { (it as ActionComponent).flatten() } - // WARNING: this can still return DbAction and External ones... + ActionFilter.INIT -> seeAllActions().filter { it is DbAction || it is MongoDbAction} + // WARNING: this can still return DbAction, MongoDbAction and External ones... ActionFilter.NO_INIT -> groupsView()!!.getAllInGroup(GroupsOfChildren.MAIN).flatMap { (it as ActionComponent).flatten() } ActionFilter.ONLY_SQL -> seeAllActions().filterIsInstance() ActionFilter.ONLY_MONGO -> seeAllActions().filterIsInstance() diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt index 0a3f3cb5e9..648d976620 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt @@ -78,26 +78,10 @@ abstract class EnterpriseSampler : Sampler() where T : Individual { return actions } - fun sampleMongoInsertion(database: String, collection: String, documentsType: Class<*>, accessedFields: Map>): List { - - // Should I use something like this? - //val extraConstraints = randomness.nextBoolean(apc.getExtraSqlDbConstraintsProbability()) - - val actions = MongoInsertBuilder().createMongoInsertionAction(database, collection, documentsType, accessedFields) - ?: throw IllegalStateException("No MongoDB schema is available") - actions.flatMap{it.seeTopGenes()}.forEach{it.doInitialize(randomness)} - - /* - if (log.isTraceEnabled){ - log.trace("at sampleMongoInsertion, {} insertions are added, and they are {}", actions.size, - actions.joinToString(",") { - if (it is MongoDbAction) it.getResolvedName() else it.getName() - }) - } - - */ - - return actions + fun sampleMongoInsertion(database: String, collection: String, documentsType: Class<*>): MongoDbAction { + val action = MongoInsertBuilder().createMongoInsertionAction(database, collection, documentsType) + action.seeTopGenes().forEach{it.doInitialize(randomness)} + return action } fun canInsertInto(tableName: String) : Boolean { diff --git a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt index a0a99ed014..375f83728a 100644 --- a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt +++ b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteControllerImplementation.kt @@ -456,7 +456,7 @@ class RemoteControllerImplementation() : RemoteController{ val dto = getDtoFromResponse(response, type) - if (!checkResponse(response, dto, "Failed to execute database command")) { + if (!checkResponse(response, dto, "Failed to execute MongoDB insertion")) { return null } diff --git a/core/src/main/kotlin/org/evomaster/core/search/EvaluatedIndividual.kt b/core/src/main/kotlin/org/evomaster/core/search/EvaluatedIndividual.kt index 36960f9cfa..f4493f57a0 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/EvaluatedIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/EvaluatedIndividual.kt @@ -12,7 +12,6 @@ import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionResult import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.mongo.MongoDbAction -import org.evomaster.core.mongo.MongoDbActionResult import org.evomaster.core.problem.externalservice.ApiExternalServiceAction import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestCallResult @@ -24,6 +23,8 @@ import org.evomaster.core.search.service.mutator.EvaluatedMutation import org.evomaster.core.search.tracer.TrackingHistory import org.slf4j.Logger import org.slf4j.LoggerFactory +import javax.security.sasl.AuthorizeCallback +import kotlin.reflect.KClass /** * EvaluatedIndividual allows to tracking its evolution. @@ -711,21 +712,21 @@ class EvaluatedIndividual( ) } - /* - if there exist other types of action (ie, not DbAction), this might need to be extended - */ - //CHANGE: This is momentary for testing. Needs refactor to handle multiple databases - action = individual.seeInitializingActions().filterIsInstance().find { it.seeTopGenes().contains(gene) } + initializingActionClasses().forEach { initializingActionClass -> + action = individual.seeInitializingActions().filter { initializingActionClass.isInstance(it)} + .find { it.seeTopGenes().contains(gene) } - if (action != null) { - return impactInfo.getGene( - localId = null, - fixedIndexedAction = true, - actionName = action.getName(), - actionIndex = individual.seeInitializingActions().indexOf(action), - geneId = id, - fromInitialization = true - ) + if (action != null) { + action as Action + return impactInfo.getGene( + localId = null, + fixedIndexedAction = true, + actionName = action!!.getName(), + actionIndex = individual.seeInitializingActions().indexOf(action), + geneId = id, + fromInitialization = true + ) + } } return impactInfo.getGene( @@ -890,4 +891,7 @@ class EvaluatedIndividual( } return !invalid } + private fun initializingActionClasses(): List> { + return listOf(MongoDbAction::class, DbAction::class) + } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/ObjectGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/ObjectGene.kt index 366632fde0..3c0077bbe2 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/ObjectGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/ObjectGene.kt @@ -331,7 +331,7 @@ class ObjectGene( } .filter { it.isPrintable() } - if (shouldPrintAsJSON(mode)) { + if (shouldPrintAsJSON(mode) || mode == GeneUtils.EscapeMode.EJSON) { buffer.append("{") includedFields.map { diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/datetime/DateGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/datetime/DateGene.kt index 1a836f1752..f8a1ebaeae 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/datetime/DateGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/datetime/DateGene.kt @@ -114,7 +114,7 @@ class DateGene( targetFormat: OutputFormat?, extraCheck: Boolean ): String { - return "\"${getValueAsRawString()}\"" + return if(mode == GeneUtils.EscapeMode.EJSON) "{\"\$date\":\"${getValueAsRawString()}\"}" else "\"${getValueAsRawString()}\"" } override fun getValueAsRawString(): String { diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/DoubleGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/DoubleGene.kt index 826aa45352..f2e556b1bf 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/DoubleGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/DoubleGene.kt @@ -83,7 +83,7 @@ class DoubleGene(name: String, override fun getValueAsPrintableString(previousGenes: List, mode: GeneUtils.EscapeMode?, targetFormat: OutputFormat?, extraCheck: Boolean): String { val stringValue = getFormattedValue().toString() - return if(mode==GeneUtils.EscapeMode.EJSON) "{ \"\$numberInt\": \"$stringValue\" }" else stringValue + return if(mode==GeneUtils.EscapeMode.EJSON) "{\"\$numberDouble\":\"$stringValue\"}" else stringValue } override fun copyValueFrom(other: Gene): Boolean { diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/IntegerGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/IntegerGene.kt index 8afd998639..48b4f149fb 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/IntegerGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/IntegerGene.kt @@ -144,7 +144,7 @@ class IntegerGene( extraCheck: Boolean ): String { val stringValue = value.toString() - return if(mode==GeneUtils.EscapeMode.EJSON) "{ \"\$numberInt\": \"$stringValue\" }" else stringValue + return if(mode==GeneUtils.EscapeMode.EJSON) "{\"\$numberInt\":\"$stringValue\"}" else stringValue } diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/LongGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/LongGene.kt index d9028b97bf..5819706fe0 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/LongGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/numeric/LongGene.kt @@ -69,7 +69,7 @@ class LongGene( override fun getValueAsPrintableString(previousGenes: List, mode: GeneUtils.EscapeMode?, targetFormat: OutputFormat?, extraCheck: Boolean): String { val stringValue = value.toString() - return if(mode==GeneUtils.EscapeMode.EJSON) "{ \"\$numberLong\": \"$stringValue\" }" else stringValue + return if(mode==GeneUtils.EscapeMode.EJSON) "{\"\$numberLong\":\"$stringValue\"}" else stringValue } override fun copyValueFrom(other: Gene): Boolean { diff --git a/core/src/test/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilderTest.kt b/core/src/test/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilderTest.kt new file mode 100644 index 0000000000..264c0c00c3 --- /dev/null +++ b/core/src/test/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilderTest.kt @@ -0,0 +1,82 @@ +package org.evomaster.core.mongo + +import org.evomaster.core.search.gene.BooleanGene +import org.evomaster.core.search.gene.ObjectGene +import org.evomaster.core.search.gene.collection.ArrayGene +import org.evomaster.core.search.gene.datetime.DateGene +import org.evomaster.core.search.gene.numeric.DoubleGene +import org.evomaster.core.search.gene.numeric.IntegerGene +import org.evomaster.core.search.gene.numeric.LongGene +import org.evomaster.core.search.gene.string.StringGene +import org.junit.jupiter.api.Test +import java.util.* + +class MongoActionGeneBuilderTest { + + inner class Object { + var someField: Int = 0 + } + + @Test + fun testStringField() { + val gene = MongoActionGeneBuilder().buildGene("someField", String::class.java) + assert(StringGene::class.isInstance(gene)) + } + + @Test + fun testIntegerField() { + val gene = MongoActionGeneBuilder().buildGene("someField", Integer::class.java) + assert(IntegerGene::class.isInstance(gene)) + } + + @Test + fun testLongField() { + val gene = MongoActionGeneBuilder().buildGene("someField", Long::class.java) + assert(LongGene::class.isInstance(gene)) + } + + @Test + fun testDoubleField() { + val gene = MongoActionGeneBuilder().buildGene("someField", Double::class.java) + assert(DoubleGene::class.isInstance(gene)) + } + + @Test + fun testDateField() { + val gene = MongoActionGeneBuilder().buildGene("someField", Date::class.java) + assert(DateGene::class.isInstance(gene)) + } + + @Test + fun testBooleanField() { + val gene = MongoActionGeneBuilder().buildGene("someField", Boolean::class.java) + assert(BooleanGene::class.isInstance(gene)) + } + + @Test + fun testObjectField() { + val gene = MongoActionGeneBuilder().buildGene("someField", Object::class.java) + assert(ObjectGene::class.isInstance(gene)) + } + + @Test + fun testUnhandledTypeField() { + val gene = MongoActionGeneBuilder().buildGene("someField", Class.forName("org.bson.types.Decimal128")) + assert(gene == null) + } + + @Test + fun testDocumentField() { + val gene = MongoActionGeneBuilder().buildGene("someField", Class.forName("org.bson.Document")) + assert(ObjectGene::class.isInstance(gene)) + } + + /* + @Test + fun testListField() { + val gene = MongoActionGeneBuilder().buildGene("someField", ArrayList()::class.java) + assert(ArrayGene::class.isInstance(gene)) + } + + */ +} \ No newline at end of file diff --git a/core/src/test/kotlin/org/evomaster/core/mongo/MongoActionTest.kt b/core/src/test/kotlin/org/evomaster/core/mongo/MongoActionTest.kt new file mode 100644 index 0000000000..07752dc6cc --- /dev/null +++ b/core/src/test/kotlin/org/evomaster/core/mongo/MongoActionTest.kt @@ -0,0 +1,31 @@ +package org.evomaster.core.mongo + +import org.evomaster.core.search.gene.ObjectGene +import org.evomaster.core.search.gene.numeric.IntegerGene +import org.junit.jupiter.api.Test + +class MongoActionTest { + + inner class Object { + @JvmField + var someField: Int = 0 + } + + @Test + fun testGenesWhenDocument() { + val action = MongoDbAction("someDatabase", "someCollection", Class.forName("org.bson.Document")) + val gene = action.seeTopGenes().first() + assert(ObjectGene::class.isInstance(gene)) + gene as ObjectGene + assert(gene.fields.isEmpty()) + } + + @Test + fun testGenesWhenNotDocument() { + val action = MongoDbAction("someDatabase", "someCollection", Object::class.java) + val gene = action.seeTopGenes().first() + assert(ObjectGene::class.isInstance(gene)) + gene as ObjectGene + assert(IntegerGene::class.isInstance(gene.fields.first())) + } +} \ No newline at end of file diff --git a/core/src/test/kotlin/org/evomaster/core/mongo/MongoDbActionTransformerTest.kt b/core/src/test/kotlin/org/evomaster/core/mongo/MongoDbActionTransformerTest.kt new file mode 100644 index 0000000000..e8b8537ef6 --- /dev/null +++ b/core/src/test/kotlin/org/evomaster/core/mongo/MongoDbActionTransformerTest.kt @@ -0,0 +1,36 @@ +package org.evomaster.core.mongo + +import org.evomaster.core.search.gene.utils.GeneUtils +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test + +class MongoDbActionTransformerTest { + + class CustomType(field: Int) { + val aField = field + } + + @Test + fun testEmpty() { + val actions = listOf() + val dto = MongoDbActionTransformer.transform(actions) + assertTrue(dto.insertions.isEmpty()) + } + + @Test + fun testNotEmpty() { + val database = "aDatabase" + val collection = "aCollection" + val action = MongoDbAction(database, collection, CustomType::class.java) + val actions = listOf(action) + val dto = MongoDbActionTransformer.transform(actions) + assertFalse(dto.insertions.isEmpty()) + assertTrue(dto.insertions[0].databaseName == action.database) + assertTrue(dto.insertions[0].collectionName == action.collection) + assertTrue( + dto.insertions[0].data == action.seeTopGenes().first() + .getValueAsPrintableString(mode = GeneUtils.EscapeMode.EJSON) + ) + } + +} \ No newline at end of file diff --git a/core/src/test/kotlin/org/evomaster/core/mongo/MongoInsertBuilderTest.kt b/core/src/test/kotlin/org/evomaster/core/mongo/MongoInsertBuilderTest.kt new file mode 100644 index 0000000000..5795bfb4a8 --- /dev/null +++ b/core/src/test/kotlin/org/evomaster/core/mongo/MongoInsertBuilderTest.kt @@ -0,0 +1,21 @@ +package org.evomaster.core.mongo + +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +class MongoInsertBuilderTest { + class CustomType(field: Int) { + val aField = field + } + @Test + fun testInsert() { + val database = "aDatabase" + val collection = "aCollection" + val documentsType = CustomType::class.java + val builder = MongoInsertBuilder() + val action = builder.createMongoInsertionAction(database, collection, documentsType) + + assertEquals(database, action.database) + assertEquals(collection, action.collection) + assertEquals(documentsType, action.documentsType) + } +} \ No newline at end of file diff --git a/core/src/test/kotlin/org/evomaster/core/search/gene/mongo/EJSONOutputModeTest.kt b/core/src/test/kotlin/org/evomaster/core/search/gene/mongo/EJSONOutputModeTest.kt new file mode 100644 index 0000000000..a786b87e36 --- /dev/null +++ b/core/src/test/kotlin/org/evomaster/core/search/gene/mongo/EJSONOutputModeTest.kt @@ -0,0 +1,73 @@ +package org.evomaster.core.search.gene.mongo + +import org.evomaster.core.search.gene.BooleanGene +import org.evomaster.core.search.gene.ObjectGene +import org.evomaster.core.search.gene.collection.ArrayGene +import org.evomaster.core.search.gene.datetime.DateGene +import org.evomaster.core.search.gene.numeric.DoubleGene +import org.evomaster.core.search.gene.numeric.IntegerGene +import org.evomaster.core.search.gene.numeric.LongGene +import org.evomaster.core.search.gene.string.StringGene +import org.evomaster.core.search.gene.utils.GeneUtils +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class EJSONOutputModeTest { + @Test + fun testStringGene() { + val gene = StringGene("someField", value = "someValue") + assertEquals("\"someValue\"", gene.getValueAsPrintableString(mode = GeneUtils.EscapeMode.EJSON)) + } + + @Test + fun testIntegerGene() { + val gene = IntegerGene("someField", value = 1) + assertEquals("{\"\$numberInt\":\"1\"}", gene.getValueAsPrintableString(mode = GeneUtils.EscapeMode.EJSON)) + } + + @Test + fun testDoubleGene() { + val gene = DoubleGene("someField", value = 1.0) + assertEquals("{\"\$numberDouble\":\"1.0\"}", gene.getValueAsPrintableString(mode = GeneUtils.EscapeMode.EJSON)) + } + + @Test + fun testLongGene() { + val gene = LongGene("someField", value = 1L) + assertEquals("{\"\$numberLong\":\"1\"}", gene.getValueAsPrintableString(mode = GeneUtils.EscapeMode.EJSON)) + } + + @Test + fun testBooleanGene() { + val gene = BooleanGene("Boolean", value = false) + assertEquals( + "false", + gene.getValueAsPrintableString(mode = GeneUtils.EscapeMode.EJSON) + ) + } + + @Test + fun testDateGene() { + val gene = DateGene("someField") + assertEquals("{\"\$date\":\"2016-03-12\"}", gene.getValueAsPrintableString(mode = GeneUtils.EscapeMode.EJSON)) + } + + @Test + fun testObjectGene() { + val gene = ObjectGene("Object", listOf(LongGene("someField", value = 1L))) + assertEquals( + "{\"someField\":{\"\$numberLong\":\"1\"}}", + gene.getValueAsPrintableString(mode = GeneUtils.EscapeMode.EJSON) + ) + } + + @Test + fun testArrayGene() { + val gene = ArrayGene("Array", template = LongGene("Long", 1L), elements = mutableListOf(LongGene("Long", 1L))) + assertEquals( + "[{\"\$numberLong\":\"1\"}]", + gene.getValueAsPrintableString(mode = GeneUtils.EscapeMode.EJSON) + ) + } +} + diff --git a/docs/options.md b/docs/options.md index c0e6849dd7..301a061d40 100644 --- a/docs/options.md +++ b/docs/options.md @@ -79,6 +79,7 @@ There are 3 types of options: |`extraHeader`| __Boolean__. Add an extra HTTP header, to analyze how it is used/read by the SUT. Needed to discover new headers that were not specified in the schema. *Default value*: `true`.| |`extraHeuristicsFile`| __String__. Where the extra heuristics file (if any) is going to be written (in CSV format). *Default value*: `extra_heuristics.csv`.| |`extraQueryParam`| __Boolean__. Add an extra query param, to analyze how it is used/read by the SUT. Needed to discover new query params that were not specified in the schema. *Default value*: `true`.| +|`extractMongoExecutionInfo`| __Boolean__. Enable extracting Mongo execution info. *Default value*: `true`.| |`extractSqlExecutionInfo`| __Boolean__. Enable extracting SQL execution info. *Default value*: `true`.| |`feedbackDirectedSampling`| __Enum__. Specify whether when we sample from archive we do look at the most promising targets for which we have had a recent improvement. *Valid values*: `NONE, LAST, FOCUSED_QUICKEST`. *Default value*: `LAST`.| |`focusedSearchActivationTime`| __Double__. The percentage of passed search before starting a more focused, less exploratory one. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.5`.| @@ -181,6 +182,7 @@ There are 3 types of options: |`externalRequestResponseSelectionStrategy`| __Enum__. Harvested external request response selection strategy. *Valid values*: `EXACT, CLOSEST_SAME_DOMAIN, CLOSEST_SAME_PATH, RANDOM`. *Default value*: `EXACT`.| |`externalServiceIP`| __String__. User provided external service IP. *Constraints*: `regex ^127\.((25[0-5]\|2[0-4][0-9]\|[01]?[0-9][0-9]?)\.){2}(25[0-5]\|2[0-4][0-9]\|[01]?[0-9][0-9]?)$`. *Default value*: `127.0.0.2`.| |`externalServiceIPSelectionStrategy`| __Enum__. Specify a method to select the first external service spoof IP address. *Valid values*: `NONE, DEFAULT, USER, RANDOM`. *Default value*: `NONE`.| +|`generateMongoData`| __Boolean__. Enable EvoMaster to generate Mongo data with direct accesses to the database. *Default value*: `false`.| |`generateSqlDataWithDSE`| __Boolean__. Enable EvoMaster to generate SQL data with direct accesses to the database. Use Dynamic Symbolic Execution. *Default value*: `false`.| |`heuristicsForMongo`| __Boolean__. Tracking of Mongo commands to improve test generation. *Default value*: `false`.| |`impactAfterMutationFile`| __String__. Specify a path to save collected impact info after each mutation during search, only useful for debugging. *Default value*: `impactSnapshot.csv`.| diff --git a/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/EvoMasterSampleGenerationTest.kt b/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/EvoMasterSampleGenerationTest.kt new file mode 100644 index 0000000000..aaad8f12ca --- /dev/null +++ b/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/EvoMasterSampleGenerationTest.kt @@ -0,0 +1,193 @@ +package org.evomaster.e2etests.spring.rest.mongo.foo + + +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.Timeout +import org.junit.jupiter.api.Assertions.* +import java.util.List +import org.evomaster.client.java.controller.api.EMTestUtils.* +import org.evomaster.client.java.controller.SutHandler +import io.restassured.RestAssured +import io.restassured.RestAssured.given +import io.restassured.response.ValidatableResponse +import org.evomaster.client.java.controller.mongo.dsl.MongoDsl.mongo +import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionResultsDto +import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionDto +import org.hamcrest.Matchers.* +import io.restassured.config.JsonConfig +import io.restassured.path.json.config.JsonPathConfig +import io.restassured.config.RedirectConfig.redirectConfig +import org.evomaster.client.java.controller.contentMatchers.NumberMatcher.* +import org.evomaster.client.java.controller.contentMatchers.StringMatcher.* +import org.evomaster.client.java.controller.contentMatchers.SubStringMatcher.* +import org.evomaster.client.java.controller.expect.ExpectationHandler.expectationHandler +import org.evomaster.client.java.controller.expect.ExpectationHandler +import io.restassured.path.json.JsonPath +import java.util.Arrays + + + + +/** + * This file was automatically generated by EvoMaster on 2023-06-26T22:42:10.888704-03:00\[America/Argentina/Buenos_Aires\] + * + * The generated test suite contains 3 tests + * + * Covered targets: 26 + * + * Used time: 0h 1m 55s + * + * Needed budget for current results: 100% + * + * + */ +internal class MongoEMGeneration { + + + companion object { + private val controller : SutHandler = com.foo.spring.rest.mongo.MongoPersonsWithoutPostAppController() + private lateinit var baseUrlOfSut: String + /** [ems] - expectations master switch - is the variable that activates/deactivates expectations individual test cases + * by default, expectations are turned off. The variable needs to be set to [true] to enable expectations + */ + private val ems = false + /** + * sco - supported code oracle - checking that the response status code is among those supported according to the schema + */ + private val sco = false + + + @BeforeAll + @JvmStatic + fun initClass() { + controller.setupForGeneratedTest() + baseUrlOfSut = controller.startSut() + assertNotNull(baseUrlOfSut) + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails() + RestAssured.useRelaxedHTTPSValidation() + RestAssured.urlEncodingEnabled = false + RestAssured.config = RestAssured.config() + .jsonConfig(JsonConfig.jsonConfig().numberReturnType(JsonPathConfig.NumberReturnType.DOUBLE)) + .redirect(redirectConfig().followRedirects(false)) + } + + + @AfterAll + @JvmStatic + fun tearDown() { + controller.stopSut() + } + } + + + @BeforeEach + fun initTest() { + controller.resetStateOfSUT() + } + + + + + @Test @Timeout(60) + fun test_0() { + val expectationHandler: ExpectationHandler = expectationHandler() + + val res_0: ValidatableResponse = given().accept("*/*") + .get("${baseUrlOfSut}/v2/api-docs") + .then() + .statusCode(200) + .assertThat() + .contentType("application/json") + .body("'swagger'", containsString("2.0")) + .body("'info'.'description'", containsString("Some description")) + .body("'info'.'version'", containsString("1.0")) + .body("'info'.'title'", containsString("API")) + .body("'basePath'", containsString("/")) + .body("'tags'.size()", equalTo(1)) + .body("'tags'[0].'name'", containsString("person-without-post-controller")) + .body("'tags'[0].'description'", containsString("Person Without Post Controller")) + .body("'paths'.'/persons/list18'.'get'.'tags'.size()", equalTo(1)) + .body("'paths'.'/persons/list18'.'get'.'tags'", hasItems("person-without-post-controller")) + .body("'paths'.'/persons/list18'.'get'.'summary'", containsString("find18s")) + .body("'paths'.'/persons/list18'.'get'.'operationId'", containsString("find18sUsingGET")) + .body("'paths'.'/persons/list18'.'get'.'produces'.size()", equalTo(1)) + .body("'paths'.'/persons/list18'.'get'.'produces'", hasItems("*/*")) + .body("'paths'.'/persons/list18'.'get'.'responses'.'200'.'description'", containsString("OK")) + .body("'paths'.'/persons/list18'.'get'.'responses'.'401'.'description'", containsString("Unauthorized")) + .body("'paths'.'/persons/list18'.'get'.'responses'.'403'.'description'", containsString("Forbidden")) + .body("'paths'.'/persons/list18'.'get'.'responses'.'404'.'description'", containsString("Not Found")) + .body("'paths'.'/persons/list18'.'get'.'responsesObject'.'200'.'description'", containsString("OK")) + .body("'paths'.'/persons/list18'.'get'.'responsesObject'.'401'.'description'", containsString("Unauthorized")) + .body("'paths'.'/persons/list18'.'get'.'responsesObject'.'403'.'description'", containsString("Forbidden")) + .body("'paths'.'/persons/list18'.'get'.'responsesObject'.'404'.'description'", containsString("Not Found")) + + expectationHandler.expect(ems) + /* + Note: No supported codes appear to be defined. https://swagger.io/docs/specification/describing-responses/. + This is somewhat unexpected, so the code below is likely to lead to a failed expectation + */ + .that(sco, listOf().contains(res_0.extract().statusCode())) + } + + + @Test @Timeout(60) + fun test_1() { + val insertions = mongo().insertInto("persons", "person") + .d("{\"age\":16}") + .and().insertInto("persons", "person") + .d("{\"age\":-512}") + .and().insertInto("persons", "person") + .d("{\"age\":18}") + .and().insertInto("persons", "person") + .d("{\"age\":0}") + .and().insertInto("persons", "person") + .d("{\"age\":131072}") + .and().insertInto("persons", "person") + .d("{\"age\":-2048}") + .and().insertInto("persons", "person") + .d("{\"age\":301793848}") + .and().insertInto("persons", "person") + .d("{\"age\":-16252321}") + .and().insertInto("persons", "person") + .d("{\"age\":692}") + .and().insertInto("persons", "person") + .d("{\"age\":332656646}") + .and().insertInto("persons", "person") + .d("{\"age\":67109689}") + .and().insertInto("persons", "person") + .d("{\"age\":90362404}") + .dtos() + val insertionsresult = controller.execInsertionsIntoMongoDatabase(insertions) + + given().accept("*/*") + .header("x-EMextraHeader123", "") + .get("${baseUrlOfSut}/persons/list18?EMextraParam123=_EM_0_XYZ_") + .then() + .statusCode(200) + .assertThat() + .body(isEmptyOrNullString()) + + } + + + @Test @Timeout(60) + fun test_2() { + val expectationHandler: ExpectationHandler = expectationHandler() + + val res_0: ValidatableResponse = given().accept("*/*") + .header("x-EMextraHeader123", "") + .get("${baseUrlOfSut}/persons/list18?EMextraParam123=_EM_0_XYZ_") + .then() + .statusCode(400) + .assertThat() + .body(isEmptyOrNullString()) + + expectationHandler.expect(ems) + .that(sco, listOf(200, 401, 403, 404).contains(res_0.extract().statusCode())) + } + + +} diff --git a/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java b/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java index 14218ebbb3..2d4545f48b 100644 --- a/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java +++ b/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java @@ -1,7 +1,7 @@ package org.evomaster.e2etests.spring.rest.mongo.foo; -import com.foo.spring.rest.mongo.MongoPersonsAppController; import com.foo.spring.rest.mongo.MongoPersonsWithoutPostAppController; +import org.evomaster.core.EMConfig; import org.evomaster.core.problem.rest.HttpVerb; import org.evomaster.core.problem.rest.RestIndividual; import org.evomaster.core.search.Solution; @@ -18,7 +18,9 @@ @BeforeAll public static void initClass() throws Exception { - RestTestBase.initClass(new MongoPersonsWithoutPostAppController()); + EMConfig config = new EMConfig(); + config.setInstrumentMR_MONGO(true); + RestTestBase.initClass(new MongoPersonsWithoutPostAppController(), config); } @Test @@ -35,6 +37,8 @@ public void testRunEM() throws Throwable { args.add("true"); args.add("--instrumentMR_MONGO"); args.add("true"); + args.add("--generateMongoData"); + args.add("true"); Solution solution = initAndRun(args); From 3bd7250cd1b03485804da340a8a1667a9433162f Mon Sep 17 00:00:00 2001 From: hghianni Date: Wed, 28 Jun 2023 14:27:29 -0300 Subject: [PATCH 100/345] Momentary fix to handle multiple databases --- .../controller/mongo/MongoScriptRunner.java | 3 ++- .../org/evomaster/core/mongo/MongoExecution.kt | 4 ++-- .../problem/enterprise/EnterpriseIndividual.kt | 18 ++++++++++++++---- .../core/problem/rest/RestIndividual.kt | 2 +- .../org/evomaster/core/search/FitnessValue.kt | 2 +- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoScriptRunner.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoScriptRunner.java index cd6e266b31..b2510c9046 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoScriptRunner.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoScriptRunner.java @@ -2,6 +2,7 @@ import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionDto; import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionResultsDto; +import org.evomaster.client.java.utils.SimpleLogger; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; @@ -23,7 +24,6 @@ public MongoScriptRunner() {} * * @param connection a connection to the database (MongoClient) * @param insertions the Mongo insertions to execute - * * @return a MongoInsertionResultsDto */ public static MongoInsertionResultsDto executeInsert(Object connection, List insertions) { @@ -42,6 +42,7 @@ public static MongoInsertionResultsDto executeInsert(Object connection, List) { +class MongoExecution(val failedQueries: MutableList?) { companion object { fun fromDto(dto: MongoExecutionDto?): MongoExecution { - return MongoExecution(dto!!.failedQueries) + return MongoExecution(dto?.failedQueries) } } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt index 5057774c9f..101d65d143 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt @@ -77,15 +77,25 @@ abstract class EnterpriseIndividual( } //TODO in future ll need to refactor to handle multiple databases and NoSQL ones - //CHANGE: This is momentary for testing. Needs refactor to handle multiple databases - val db = ChildGroup(GroupsOfChildren.INITIALIZATION_MONGO,{e -> e is ActionComponent && e.flatten().all { a -> a is MongoDbAction }}, - if(sizeDb==0) -1 else 0 , if(sizeDb==0) -1 else sizeDb-1 + //CHANGE: This is momentary. Needs refactor to handle multiple databases + + val startIndexSQL = children.indexOfFirst { a -> a is DbAction } + val endIndexSQL = children.indexOfLast { a -> a is DbAction } + val startIndexMongo = children.indexOfFirst { a -> a is MongoDbAction } + val endIndexMongo = children.indexOfLast { a -> a is MongoDbAction } + + val db = ChildGroup(GroupsOfChildren.INITIALIZATION_SQL,{e -> e is ActionComponent && e.flatten().all { a -> a is DbAction }}, + if(sizeDb==0) -1 else startIndexSQL , if(sizeDb==0) -1 else endIndexSQL + ) + + val mongodb = ChildGroup(GroupsOfChildren.INITIALIZATION_MONGO,{e -> e is ActionComponent && e.flatten().all { a -> a is MongoDbAction }}, + if(sizeDb==0) -1 else startIndexMongo , if(sizeDb==0) -1 else endIndexMongo ) val main = ChildGroup(GroupsOfChildren.MAIN, {e -> e !is DbAction && e !is ApiExternalServiceAction }, if(sizeMain == 0) -1 else sizeDb, if(sizeMain == 0) -1 else sizeDb + sizeMain - 1) - return GroupsOfChildren(children, listOf(db, main)) + return GroupsOfChildren(children, listOf(db, mongodb, main)) } } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt index 9c9c45ae4f..8e00d1187b 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt @@ -79,7 +79,7 @@ class RestIndividual( children.map { it.copy() }.toMutableList() as MutableList, mainSize = groupsView()!!.sizeOfGroup(GroupsOfChildren.MAIN), //CHANGE: This is momentary for testing. Needs refactor to handle multiple databases - dbSize = groupsView()!!.sizeOfGroup(GroupsOfChildren.INITIALIZATION_MONGO) + dbSize = groupsView()!!.sizeOfGroup(GroupsOfChildren.INITIALIZATION_MONGO ) + groupsView()!!.sizeOfGroup(GroupsOfChildren.INITIALIZATION_SQL ) ) } diff --git a/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt b/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt index 66006fa51c..1c17d5cdb0 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt @@ -144,7 +144,7 @@ class FitnessValue( } fun aggregateMongoDatabaseData(){ aggregatedFailedFind.clear() - mongoExecutions.values.map { aggregatedFailedFind.addAll(it.failedQueries) } + mongoExecutions.values.map { it.failedQueries?.let { it1 -> aggregatedFailedFind.addAll(it1) } } } fun setExtraToMinimize(actionIndex: Int, list: List) { From cf8f90093ae90311a56c172480e4bc7b04c15592 Mon Sep 17 00:00:00 2001 From: hghianni Date: Wed, 28 Jun 2023 15:34:53 -0300 Subject: [PATCH 101/345] Change option extractMongoExecutionInfo to be false by default --- core/src/main/kotlin/org/evomaster/core/EMConfig.kt | 3 ++- .../e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index 191121ce42..a5a91800e1 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -1051,8 +1051,9 @@ class EMConfig { @Cfg("Enable extracting SQL execution info") var extractSqlExecutionInfo = true + @Experimental @Cfg("Enable extracting Mongo execution info") - var extractMongoExecutionInfo = true + var extractMongoExecutionInfo = false @Experimental @Cfg("Enable EvoMaster to generate SQL data with direct accesses to the database. Use Dynamic Symbolic Execution") diff --git a/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java b/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java index 2d4545f48b..dd0fba65bd 100644 --- a/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java +++ b/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java @@ -39,6 +39,8 @@ public void testRunEM() throws Throwable { args.add("true"); args.add("--generateMongoData"); args.add("true"); + args.add("--extractMongoExecutionInfo"); + args.add("true"); Solution solution = initAndRun(args); From dac92645309bb09bccdc6f2f1ff708287e034008 Mon Sep 17 00:00:00 2001 From: hghianni Date: Wed, 28 Jun 2023 15:35:37 -0300 Subject: [PATCH 102/345] Solve failing tests --- .../org/evomaster/core/output/service/ApiTestCaseWriter.kt | 2 +- .../evomaster/core/problem/enterprise/EnterpriseIndividual.kt | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/ApiTestCaseWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/ApiTestCaseWriter.kt index 7e8537c51d..b5635e1460 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/ApiTestCaseWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/ApiTestCaseWriter.kt @@ -49,7 +49,7 @@ abstract class ApiTestCaseWriter : TestCaseWriter() { SqlWriter.handleDbInitialization( format, initializingSqlActions.indices.map { - EvaluatedDbAction(initializingSqlActions[it], initializingSqlActions[it] as DbActionResult) + EvaluatedDbAction(initializingSqlActions[it], initializingSqlActionResults[it] as DbActionResult) }, lines, insertionVars = insertionVars, skipFailure = config.skipFailureSQLInTestFile) } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt index 101d65d143..2285f28459 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt @@ -115,7 +115,9 @@ abstract class EnterpriseIndividual( ActionFilter.MAIN_EXECUTABLE -> groupsView()!!.getAllInGroup(GroupsOfChildren.MAIN) .flatMap { (it as ActionComponent).flatten() } .filter { it !is DbAction && it !is ApiExternalServiceAction } - ActionFilter.INIT -> seeAllActions().filter { it is DbAction || it is MongoDbAction} + ActionFilter.INIT -> groupsView()!!.getAllInGroup(GroupsOfChildren.INITIALIZATION_SQL) + .flatMap { (it as ActionComponent).flatten() } + groupsView()!!.getAllInGroup(GroupsOfChildren.INITIALIZATION_MONGO) + .flatMap { (it as ActionComponent).flatten() } // WARNING: this can still return DbAction, MongoDbAction and External ones... ActionFilter.NO_INIT -> groupsView()!!.getAllInGroup(GroupsOfChildren.MAIN).flatMap { (it as ActionComponent).flatten() } ActionFilter.ONLY_SQL -> seeAllActions().filterIsInstance() From d39a3d6556660947f2f60b77ca549b7c2d993461 Mon Sep 17 00:00:00 2001 From: hghianni Date: Wed, 28 Jun 2023 16:36:29 -0300 Subject: [PATCH 103/345] Update options file --- docs/options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/options.md b/docs/options.md index 08ee2789c4..cce3d6f87b 100644 --- a/docs/options.md +++ b/docs/options.md @@ -79,7 +79,6 @@ There are 3 types of options: |`extraHeader`| __Boolean__. Add an extra HTTP header, to analyze how it is used/read by the SUT. Needed to discover new headers that were not specified in the schema. *Default value*: `true`.| |`extraHeuristicsFile`| __String__. Where the extra heuristics file (if any) is going to be written (in CSV format). *Default value*: `extra_heuristics.csv`.| |`extraQueryParam`| __Boolean__. Add an extra query param, to analyze how it is used/read by the SUT. Needed to discover new query params that were not specified in the schema. *Default value*: `true`.| -|`extractMongoExecutionInfo`| __Boolean__. Enable extracting Mongo execution info. *Default value*: `true`.| |`extractSqlExecutionInfo`| __Boolean__. Enable extracting SQL execution info. *Default value*: `true`.| |`feedbackDirectedSampling`| __Enum__. Specify whether when we sample from archive we do look at the most promising targets for which we have had a recent improvement. *Valid values*: `NONE, LAST, FOCUSED_QUICKEST`. *Default value*: `LAST`.| |`focusedSearchActivationTime`| __Double__. The percentage of passed search before starting a more focused, less exploratory one. *Constraints*: `probability 0.0-1.0`. *Default value*: `0.5`.| @@ -182,6 +181,7 @@ There are 3 types of options: |`externalRequestResponseSelectionStrategy`| __Enum__. Harvested external request response selection strategy. *Valid values*: `EXACT, CLOSEST_SAME_DOMAIN, CLOSEST_SAME_PATH, RANDOM`. *Default value*: `EXACT`.| |`externalServiceIP`| __String__. User provided external service IP. *Constraints*: `regex ^127\.((25[0-5]\|2[0-4][0-9]\|[01]?[0-9][0-9]?)\.){2}(25[0-5]\|2[0-4][0-9]\|[01]?[0-9][0-9]?)$`. *Default value*: `127.0.0.2`.| |`externalServiceIPSelectionStrategy`| __Enum__. Specify a method to select the first external service spoof IP address. *Valid values*: `NONE, DEFAULT, USER, RANDOM`. *Default value*: `NONE`.| +|`extractMongoExecutionInfo`| __Boolean__. Enable extracting Mongo execution info. *Default value*: `false`.| |`generateMongoData`| __Boolean__. Enable EvoMaster to generate Mongo data with direct accesses to the database. *Default value*: `false`.| |`generateSqlDataWithDSE`| __Boolean__. Enable EvoMaster to generate SQL data with direct accesses to the database. Use Dynamic Symbolic Execution. *Default value*: `false`.| |`heuristicsForMongo`| __Boolean__. Tracking of Mongo commands to improve test generation. *Default value*: `false`.| From ea421846db3ebd8b9376266d27bebbc2200cf17c Mon Sep 17 00:00:00 2001 From: hghianni Date: Wed, 28 Jun 2023 19:20:55 -0300 Subject: [PATCH 104/345] Decrease iterations of MongoEMGenerationTest to avoid timeout --- .../e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java b/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java index dd0fba65bd..6b4f9f766e 100644 --- a/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java +++ b/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java @@ -29,7 +29,7 @@ public void testRunEM() throws Throwable { runTestHandlingFlakyAndCompilation( "MongoEMGeneration", "org.foo.spring.rest.mongo.MongoEMGeneration", - 10000, + 1000, (args) -> { args.add("--enableWeightBasedMutationRateSelectionForGene"); args.add("false"); From d06f9c3378088ec4956bd81c050b2a5f1fcd8059 Mon Sep 17 00:00:00 2001 From: hghianni Date: Thu, 29 Jun 2023 14:16:48 -0300 Subject: [PATCH 105/345] Change failed queries to be only the queries executed in an empty collection --- .../controller/internal/db/MongoHandler.java | 8 +- .../foo/EvoMasterSampleGenerationTest.kt | 172 ++++++++---------- .../rest/mongo/foo/MongoEMFitnessTest.java | 5 +- .../rest/mongo/foo/MongoEMGenerationTest.java | 5 +- 4 files changed, 80 insertions(+), 110 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/MongoHandler.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/MongoHandler.java index 3128f75ad4..4ef3d7b357 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/MongoHandler.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/MongoHandler.java @@ -99,11 +99,6 @@ public List getDistances() { dist = Double.MAX_VALUE; } distances.add(new MongoOperationDistance(mongoInfo.getQuery(), dist)); - - if (dist > 0) { - Object collection = mongoInfo.getCollection(); - failedQueries.add(new MongoOperation(collection, mongoInfo.getQuery())); - } }); operations.clear(); @@ -119,6 +114,9 @@ public MongoExecutionDto getExecutionDto() { private double computeDistance(MongoInfo info) { Object collection = info.getCollection(); Iterable documents = getDocuments(collection); + boolean collectionIsEmpty = !documents.iterator().hasNext(); + + if (collectionIsEmpty) failedQueries.add(new MongoOperation(collection, info.getQuery())); MongoHeuristicsCalculator calculator = new MongoHeuristicsCalculator(); diff --git a/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/EvoMasterSampleGenerationTest.kt b/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/EvoMasterSampleGenerationTest.kt index aaad8f12ca..dc96979d22 100644 --- a/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/EvoMasterSampleGenerationTest.kt +++ b/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/EvoMasterSampleGenerationTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.e2etests.spring.rest.mongo.foo +package org.foo.spring.rest.mongo import org.junit.jupiter.api.AfterAll @@ -32,34 +32,34 @@ import java.util.Arrays /** - * This file was automatically generated by EvoMaster on 2023-06-26T22:42:10.888704-03:00\[America/Argentina/Buenos_Aires\] - * + * This file was automatically generated by EvoMaster on 2023-06-29T13:40:21.894829-03:00\[America/Argentina/Buenos_Aires\] + * * The generated test suite contains 3 tests - * - * Covered targets: 26 - * - * Used time: 0h 1m 55s - * + * + * Covered targets: 29 + * + * Used time: 0h 0m 10s + * * Needed budget for current results: 100% - * - * + * + * */ internal class MongoEMGeneration { - + companion object { private val controller : SutHandler = com.foo.spring.rest.mongo.MongoPersonsWithoutPostAppController() private lateinit var baseUrlOfSut: String /** [ems] - expectations master switch - is the variable that activates/deactivates expectations individual test cases - * by default, expectations are turned off. The variable needs to be set to [true] to enable expectations - */ + * by default, expectations are turned off. The variable needs to be set to [true] to enable expectations + */ private val ems = false /** - * sco - supported code oracle - checking that the response status code is among those supported according to the schema - */ + * sco - supported code oracle - checking that the response status code is among those supported according to the schema + */ private val sco = false - - + + @BeforeAll @JvmStatic fun initClass() { @@ -73,57 +73,57 @@ internal class MongoEMGeneration { .jsonConfig(JsonConfig.jsonConfig().numberReturnType(JsonPathConfig.NumberReturnType.DOUBLE)) .redirect(redirectConfig().followRedirects(false)) } - - + + @AfterAll @JvmStatic fun tearDown() { controller.stopSut() } } - - + + @BeforeEach fun initTest() { controller.resetStateOfSUT() } - - - - + + + + @Test @Timeout(60) fun test_0() { val expectationHandler: ExpectationHandler = expectationHandler() - + val res_0: ValidatableResponse = given().accept("*/*") - .get("${baseUrlOfSut}/v2/api-docs") - .then() - .statusCode(200) - .assertThat() - .contentType("application/json") - .body("'swagger'", containsString("2.0")) - .body("'info'.'description'", containsString("Some description")) - .body("'info'.'version'", containsString("1.0")) - .body("'info'.'title'", containsString("API")) - .body("'basePath'", containsString("/")) - .body("'tags'.size()", equalTo(1)) - .body("'tags'[0].'name'", containsString("person-without-post-controller")) - .body("'tags'[0].'description'", containsString("Person Without Post Controller")) - .body("'paths'.'/persons/list18'.'get'.'tags'.size()", equalTo(1)) - .body("'paths'.'/persons/list18'.'get'.'tags'", hasItems("person-without-post-controller")) - .body("'paths'.'/persons/list18'.'get'.'summary'", containsString("find18s")) - .body("'paths'.'/persons/list18'.'get'.'operationId'", containsString("find18sUsingGET")) - .body("'paths'.'/persons/list18'.'get'.'produces'.size()", equalTo(1)) - .body("'paths'.'/persons/list18'.'get'.'produces'", hasItems("*/*")) - .body("'paths'.'/persons/list18'.'get'.'responses'.'200'.'description'", containsString("OK")) - .body("'paths'.'/persons/list18'.'get'.'responses'.'401'.'description'", containsString("Unauthorized")) - .body("'paths'.'/persons/list18'.'get'.'responses'.'403'.'description'", containsString("Forbidden")) - .body("'paths'.'/persons/list18'.'get'.'responses'.'404'.'description'", containsString("Not Found")) - .body("'paths'.'/persons/list18'.'get'.'responsesObject'.'200'.'description'", containsString("OK")) - .body("'paths'.'/persons/list18'.'get'.'responsesObject'.'401'.'description'", containsString("Unauthorized")) - .body("'paths'.'/persons/list18'.'get'.'responsesObject'.'403'.'description'", containsString("Forbidden")) - .body("'paths'.'/persons/list18'.'get'.'responsesObject'.'404'.'description'", containsString("Not Found")) - + .get("${baseUrlOfSut}/v2/api-docs") + .then() + .statusCode(200) + .assertThat() + .contentType("application/json") + .body("'swagger'", containsString("2.0")) + .body("'info'.'description'", containsString("Some description")) + .body("'info'.'version'", containsString("1.0")) + .body("'info'.'title'", containsString("API")) + .body("'basePath'", containsString("/")) + .body("'tags'.size()", equalTo(1)) + .body("'tags'[0].'name'", containsString("person-without-post-controller")) + .body("'tags'[0].'description'", containsString("Person Without Post Controller")) + .body("'paths'.'/persons/list18'.'get'.'tags'.size()", equalTo(1)) + .body("'paths'.'/persons/list18'.'get'.'tags'", hasItems("person-without-post-controller")) + .body("'paths'.'/persons/list18'.'get'.'summary'", containsString("find18s")) + .body("'paths'.'/persons/list18'.'get'.'operationId'", containsString("find18sUsingGET")) + .body("'paths'.'/persons/list18'.'get'.'produces'.size()", equalTo(1)) + .body("'paths'.'/persons/list18'.'get'.'produces'", hasItems("*/*")) + .body("'paths'.'/persons/list18'.'get'.'responses'.'200'.'description'", containsString("OK")) + .body("'paths'.'/persons/list18'.'get'.'responses'.'401'.'description'", containsString("Unauthorized")) + .body("'paths'.'/persons/list18'.'get'.'responses'.'403'.'description'", containsString("Forbidden")) + .body("'paths'.'/persons/list18'.'get'.'responses'.'404'.'description'", containsString("Not Found")) + .body("'paths'.'/persons/list18'.'get'.'responsesObject'.'200'.'description'", containsString("OK")) + .body("'paths'.'/persons/list18'.'get'.'responsesObject'.'401'.'description'", containsString("Unauthorized")) + .body("'paths'.'/persons/list18'.'get'.'responsesObject'.'403'.'description'", containsString("Forbidden")) + .body("'paths'.'/persons/list18'.'get'.'responsesObject'.'404'.'description'", containsString("Not Found")) + expectationHandler.expect(ems) /* Note: No supported codes appear to be defined. https://swagger.io/docs/specification/describing-responses/. @@ -131,60 +131,38 @@ internal class MongoEMGeneration { */ .that(sco, listOf().contains(res_0.extract().statusCode())) } - - + + @Test @Timeout(60) fun test_1() { val insertions = mongo().insertInto("persons", "person") - .d("{\"age\":16}") - .and().insertInto("persons", "person") - .d("{\"age\":-512}") - .and().insertInto("persons", "person") - .d("{\"age\":18}") - .and().insertInto("persons", "person") - .d("{\"age\":0}") - .and().insertInto("persons", "person") - .d("{\"age\":131072}") - .and().insertInto("persons", "person") - .d("{\"age\":-2048}") - .and().insertInto("persons", "person") - .d("{\"age\":301793848}") - .and().insertInto("persons", "person") - .d("{\"age\":-16252321}") - .and().insertInto("persons", "person") - .d("{\"age\":692}") - .and().insertInto("persons", "person") - .d("{\"age\":332656646}") - .and().insertInto("persons", "person") - .d("{\"age\":67109689}") - .and().insertInto("persons", "person") - .d("{\"age\":90362404}") + .d("{\"id\":\"IMU_V4ERbZxCSaw\", \"age\":18}") .dtos() val insertionsresult = controller.execInsertionsIntoMongoDatabase(insertions) - + given().accept("*/*") - .header("x-EMextraHeader123", "") - .get("${baseUrlOfSut}/persons/list18?EMextraParam123=_EM_0_XYZ_") - .then() - .statusCode(200) - .assertThat() - .body(isEmptyOrNullString()) - + .header("x-EMextraHeader123", "") + .get("${baseUrlOfSut}/persons/list18") + .then() + .statusCode(200) + .assertThat() + .body(isEmptyOrNullString()) + } - - + + @Test @Timeout(60) fun test_2() { val expectationHandler: ExpectationHandler = expectationHandler() - + val res_0: ValidatableResponse = given().accept("*/*") - .header("x-EMextraHeader123", "") - .get("${baseUrlOfSut}/persons/list18?EMextraParam123=_EM_0_XYZ_") - .then() - .statusCode(400) - .assertThat() - .body(isEmptyOrNullString()) - + .header("x-EMextraHeader123", "") + .get("${baseUrlOfSut}/persons/list18?EMextraParam123=_EM_0_XYZ_") + .then() + .statusCode(400) + .assertThat() + .body(isEmptyOrNullString()) + expectationHandler.expect(ems) .that(sco, listOf(200, 401, 403, 404).contains(res_0.extract().statusCode())) } diff --git a/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMFitnessTest.java b/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMFitnessTest.java index 6267c23c89..126af94500 100644 --- a/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMFitnessTest.java +++ b/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMFitnessTest.java @@ -10,10 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; -public class - - -MongoEMFitnessTest extends RestTestBase { +public class MongoEMFitnessTest extends RestTestBase { @BeforeAll public static void initClass() throws Exception { diff --git a/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java b/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java index 6b4f9f766e..1396201c73 100644 --- a/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java +++ b/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/MongoEMGenerationTest.java @@ -11,10 +11,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; -public class - - -MongoEMGenerationTest extends RestTestBase { +public class MongoEMGenerationTest extends RestTestBase { @BeforeAll public static void initClass() throws Exception { From 1c385b008be9ccf1c97766521d74fb3bf922795e Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Thu, 29 Jun 2023 21:59:35 +0200 Subject: [PATCH 106/345] support protobuf3 parser --- .../problem/rpc/Protobuf3Field.java | 11 ++ .../problem/rpc/RPCEndpointsBuilder.java | 155 +++++++++++++----- 2 files changed, 126 insertions(+), 40 deletions(-) create mode 100644 client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/Protobuf3Field.java diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/Protobuf3Field.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/Protobuf3Field.java new file mode 100644 index 0000000000..d7cec79da6 --- /dev/null +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/Protobuf3Field.java @@ -0,0 +1,11 @@ +package org.evomaster.client.java.controller.problem.rpc; + +public class Protobuf3Field { + + public String fieldName; + public Class fieldType; + public String setterName; + public String getterName; + + public Protobuf3Field(){} +} diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java index f0b368bded..06861cfdc6 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java @@ -32,6 +32,10 @@ public class RPCEndpointsBuilder { private final static String OBJECT_FLAG = "OBJECT"; private final static String OBJECT_FLAG_SEPARATOR = ":"; + private final static String PROTOBUF_PACKAGE = "com.google.protobuf"; + + private final static String PROTOBUF_BUILDER = "Builder"; + private static String getObjectTypeNameWithFlag(Class clazz, String name, int level) { if (isNotCustomizedObject(clazz)) return name; if (level < 0) @@ -557,54 +561,71 @@ private static NamedTypedValue build(InterfaceSchema schema, Class clazz, Typ Map objRelatedCustomizationDtos = getCustomizationBasedOnSpecifiedType(customizationDtos, clazz.getName()); int flevel = level + 1; - // field list - List fieldList = new ArrayList<>(); - getAllFields(clazz, fieldList, rpcType); - - for(Field f: fieldList){ - // skip final field - if (Modifier.isFinal(f.getModifiers())) - continue; - - if (doSkipReflection(f.getName())) - continue; - - // always try to find the setter and getter - AccessibleSchema faccessSchema = new AccessibleSchema(Modifier.isPublic(f.getModifiers()), findGetterOrSetter(clazz, f, false), findGetterOrSetter(clazz, f, true)); - //check accessible - if (!Modifier.isPublic(f.getModifiers())){ - if (faccessSchema.getterMethodName == null || faccessSchema.setterMethodName == null){ - SimpleLogger.recordErrorMessage("Error: skip the field "+f.getName()+" since its setter/getter is not found"); + + if (rpcType == RPCType.gRPC || isProtobuf(clazz)){ + List pfList = getProtobuf3FieldsAndType(clazz); + for (Protobuf3Field pf : pfList){ + AccessibleSchema faccessSchema = new AccessibleSchema(false, pf.setterName, pf.getterName); + + // TODO genericType + + NamedTypedValue field = build(schema, pf.fieldType, null, pf.fieldName, rpcType, flattenDepth, flevel, objRelatedCustomizationDtos, relatedCustomization, faccessSchema, notNullAnnotations, null, genericTypeMap, isTypeToIdentify); + + fields.add(field); + } + + }else{ + // field list + List fieldList = new ArrayList<>(); + getAllFields(clazz, fieldList, rpcType); + + for(Field f: fieldList){ + // skip final field + if (Modifier.isFinal(f.getModifiers())) continue; + + if (doSkipReflection(f.getName())) + continue; + + // always try to find the setter and getter + AccessibleSchema faccessSchema = new AccessibleSchema(Modifier.isPublic(f.getModifiers()), findGetterOrSetter(clazz, f, false), findGetterOrSetter(clazz, f, true)); + //check accessible + if (!Modifier.isPublic(f.getModifiers())){ + if (faccessSchema.getterMethodName == null || faccessSchema.setterMethodName == null){ + SimpleLogger.recordErrorMessage("Error: skip the field "+f.getName()+" since its setter/getter is not found"); + continue; + } } - } - Class fType = f.getType(); - Class foriginalType = null; - Type fGType = f.getGenericType(); - - if (f.getGenericType() instanceof TypeVariable){ - foriginalType = f.getType(); - Type actualType = getActualType(genericTypeMap, (TypeVariable) f.getGenericType()); - if (actualType instanceof Class){ - fType = (Class) actualType; - fGType = fType; - }else if (actualType instanceof ParameterizedType){ - fGType = actualType; - if (((ParameterizedType) actualType).getRawType() instanceof Class) - fType = (Class) ((ParameterizedType) actualType).getRawType(); - else - throw new RuntimeException("Error: Fail to handle actual type of a generic type"); + Class fType = f.getType(); + Class foriginalType = null; + Type fGType = f.getGenericType(); + + if (f.getGenericType() instanceof TypeVariable){ + foriginalType = f.getType(); + Type actualType = getActualType(genericTypeMap, (TypeVariable) f.getGenericType()); + if (actualType instanceof Class){ + fType = (Class) actualType; + fGType = fType; + }else if (actualType instanceof ParameterizedType){ + fGType = actualType; + if (((ParameterizedType) actualType).getRawType() instanceof Class) + fType = (Class) ((ParameterizedType) actualType).getRawType(); + else + throw new RuntimeException("Error: Fail to handle actual type of a generic type"); + } } - } - NamedTypedValue field = build(schema, fType, fGType,f.getName(), rpcType, flattenDepth, flevel, objRelatedCustomizationDtos, relatedCustomization, faccessSchema, notNullAnnotations, foriginalType, genericTypeMap, isTypeToIdentify); - for (Annotation annotation : f.getAnnotations()){ - handleConstraint(field, annotation, notNullAnnotations); + NamedTypedValue field = build(schema, fType, fGType,f.getName(), rpcType, flattenDepth, flevel, objRelatedCustomizationDtos, relatedCustomization, faccessSchema, notNullAnnotations, foriginalType, genericTypeMap, isTypeToIdentify); + for (Annotation annotation : f.getAnnotations()){ + handleConstraint(field, annotation, notNullAnnotations); + } + fields.add(field); } - fields.add(field); } + + handleNativeRPCConstraints(clazz, fields, rpcType); ObjectType otype = new ObjectType(clazz.getSimpleName(), clazz.getName(), fields, clazz, genericTypes); @@ -646,6 +667,34 @@ private static int getCycleSize(List flattenDepth,Class clazz, String return cycle; } + private static boolean isProtobuf(Class clazz){ + if (clazz == null) return false; + boolean isProtobuf= clazz.getName().startsWith(PROTOBUF_PACKAGE); + if (isProtobuf) return isProtobuf; + + Class pclazz = clazz.getSuperclass(); + if (pclazz != null){ + return isProtobuf(pclazz); + } + + return false; + } + + private static List getProtobuf3FieldsAndType(Class clazz){ + Optional> op = Arrays.stream(clazz.getDeclaredClasses()).filter(s-> s.getSimpleName().equals(PROTOBUF_BUILDER)).findFirst(); + if (!op.isPresent()) return null; + + List list = new ArrayList<>(); + + for (Field f : op.get().getDeclaredFields()){ + String fieldName = formatProtobuf3FieldName(f.getName()); + Protobuf3Field pf = findProtobuf3FieldType(op.get(), fieldName); + if (pf != null) + list.add(pf); + } + return list; + } + private static String getNameEnumConstant(Object object) { try { Method name = object.getClass().getMethod("name"); @@ -772,6 +821,32 @@ private static boolean isGetter(String fieldName, String methodName, String type || (isBoolean && fieldName.startsWith("is") && methodName.equalsIgnoreCase(fieldName.replaceFirst("is", "get"))); } + private static String formatProtobuf3FieldName(String fieldName){ + if (fieldName.endsWith("_")) + return fieldName.substring(0, fieldName.length()-1); + return fieldName; + } + + private static Protobuf3Field findProtobuf3FieldType(Class clazz, String fieldName){ + Method setter = null; + Method getter = null; + for (Method m : clazz.getDeclaredMethods()){ + if (m.getName().equalsIgnoreCase("set"+fieldName)) + setter = m; + else if (m.getName().equalsIgnoreCase("get"+fieldName)) + getter = m; + } + if (getter!=null && setter != null) { + Protobuf3Field pf = new Protobuf3Field(); + pf.fieldName = fieldName; + pf.fieldType = getter.getReturnType(); + pf.getterName = getter.getName(); + pf.setterName = setter.getName(); + return pf; + } + return null; + } + private static void handleNamedValueWithCustomizedDto(NamedTypedValue namedTypedValue, Map customizationDtos, Set relatedCustomization){ List candidateReferences = new ArrayList<>(); From 30bb9ac96e995ff2133cc02d0b0f23e268c59a3a Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Thu, 29 Jun 2023 22:02:48 +0200 Subject: [PATCH 107/345] add unit tests --- .../api/dto/problem/rpc/RPCType.java | 5 +- client-java/controller/pom.xml | 46 ++++++- .../rpc/RPCEndpointsBuilderTestBase.java | 6 + .../HelloWorldgRPCEndpointsBuilderTest.java | 49 ++++++++ .../thrift/FacebookEndpointsBuilderTest.java | 5 +- .../src/test/proto/helloworld.proto | 37 ++++++ .../src/test/proto/route_guide.proto | 115 ++++++++++++++++++ 7 files changed, 255 insertions(+), 8 deletions(-) create mode 100644 client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/HelloWorldgRPCEndpointsBuilderTest.java create mode 100644 client-java/controller/src/test/proto/helloworld.proto create mode 100644 client-java/controller/src/test/proto/route_guide.proto diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/RPCType.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/RPCType.java index 95bffd3a8c..b064f22157 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/RPCType.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/rpc/RPCType.java @@ -18,11 +18,10 @@ public enum RPCType { /** * thrift */ - THRIFT + THRIFT, /** * gRPC - * TODO, NOT SUPPORT YET */ - //gRPC + gRPC } diff --git a/client-java/controller/pom.xml b/client-java/controller/pom.xml index f0bf4b7cbc..9ce13dfca0 100644 --- a/client-java/controller/pom.xml +++ b/client-java/controller/pom.xml @@ -196,10 +196,54 @@ test + + + io.grpc + grpc-netty-shaded + test + + + io.grpc + grpc-protobuf + test + + + io.grpc + grpc-stub + test + + + + + + kr.motd.maven + os-maven-plugin + ${kr.motd.maven.version} + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + + com.google.protobuf:protoc:${com.google.protobuf.protoc.version}:exe:${os.detected.classifier} + grpc-java + io.grpc:protoc-gen-grpc-java:${io.grpc.version}:exe:${os.detected.classifier} + + + + + test-compile + test-compile-custom + + + + + org.apache.maven.plugins maven-compiler-plugin @@ -295,4 +339,4 @@ - \ No newline at end of file + diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilderTestBase.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilderTestBase.java index 0243600cc7..5b477e5de3 100644 --- a/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilderTestBase.java +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilderTestBase.java @@ -8,6 +8,7 @@ import org.evomaster.client.java.controller.problem.rpc.schema.InterfaceSchema; import org.evomaster.client.java.controller.api.dto.problem.rpc.RPCType; import org.evomaster.client.java.controller.problem.rpc.schema.params.NamedTypedValue; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; @@ -65,4 +66,9 @@ public List getListEndpoint(String name, int expectedSize){ public boolean containType(List params, String fullTypeName){ return params.stream().anyMatch(s-> s.getType().getFullTypeNameWithGenericType().equals(fullTypeName)); } + + @Test + public void testEndpointsLoad(){ + assertEquals(expectedNumberOfEndpoints(), schema.getEndpoints().size()); + } } diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/HelloWorldgRPCEndpointsBuilderTest.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/HelloWorldgRPCEndpointsBuilderTest.java new file mode 100644 index 0000000000..f982262e2e --- /dev/null +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/HelloWorldgRPCEndpointsBuilderTest.java @@ -0,0 +1,49 @@ +package org.evomaster.client.java.controller.problem.rpc.grpc; + +import io.grpc.examples.helloworld.GreeterGrpc; +import io.grpc.examples.helloworld.HelloRequest; +import org.evomaster.client.java.controller.problem.rpc.RPCEndpointsBuilderTestBase; +import org.evomaster.client.java.controller.problem.rpc.schema.EndpointSchema; +import org.evomaster.client.java.controller.problem.rpc.schema.params.NamedTypedValue; +import org.evomaster.client.java.controller.problem.rpc.schema.params.ObjectParam; +import org.evomaster.client.java.controller.problem.rpc.schema.types.ObjectType; +import org.junit.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class HelloWorldgRPCEndpointsBuilderTest extends RPCEndpointsBuilderTestBase { + @Override + public String getInterfaceName() { + return GreeterGrpc.GreeterBlockingStub.class.getName(); + } + + @Override + public int expectedNumberOfEndpoints() { + return 1; + } + + @Test + public void testProtobuf(){ + + EndpointSchema endpoint = getOneEndpoint("sayHello"); + NamedTypedValue response = endpoint.getResponse(); + assertTrue(response instanceof ObjectParam); + ObjectType returnType = (ObjectType)response.getType(); + assertEquals(1, returnType.getFields().size()); + assertEquals("message", returnType.getFields().get(0).getName()); + assertEquals(String.class.getName(), returnType.getFields().get(0).getType().getFullTypeName()); + + + List params = endpoint.getRequestParams(); + assertEquals(1, params.size()); + NamedTypedValue inputParam = params.get(0); + assertTrue(inputParam instanceof ObjectParam); + ObjectType paramType = ((ObjectParam) inputParam).getType(); + assertEquals(1, paramType.getFields().size()); + assertEquals("name", paramType.getFields().get(0).getName()); + assertEquals(String.class.getName(), paramType.getFields().get(0).getType().getFullTypeName()); + } +} diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/thrift/FacebookEndpointsBuilderTest.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/thrift/FacebookEndpointsBuilderTest.java index 72ab209f5a..e74f8be664 100644 --- a/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/thrift/FacebookEndpointsBuilderTest.java +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/thrift/FacebookEndpointsBuilderTest.java @@ -24,10 +24,7 @@ public int expectedNumberOfEndpoints() { return 13; } - @Test - public void testEndpointsLoad(){ - assertEquals(expectedNumberOfEndpoints(), schema.getEndpoints().size()); - } + @Test public void testEnum(){ diff --git a/client-java/controller/src/test/proto/helloworld.proto b/client-java/controller/src/test/proto/helloworld.proto new file mode 100644 index 0000000000..c60d9416f1 --- /dev/null +++ b/client-java/controller/src/test/proto/helloworld.proto @@ -0,0 +1,37 @@ +// Copyright 2015 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "io.grpc.examples.helloworld"; +option java_outer_classname = "HelloWorldProto"; +option objc_class_prefix = "HLW"; + +package helloworld; + +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} diff --git a/client-java/controller/src/test/proto/route_guide.proto b/client-java/controller/src/test/proto/route_guide.proto new file mode 100644 index 0000000000..3c179f2ff1 --- /dev/null +++ b/client-java/controller/src/test/proto/route_guide.proto @@ -0,0 +1,115 @@ +// Copyright 2015 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "io.grpc.examples.routeguide"; +option java_outer_classname = "RouteGuideProto"; +option objc_class_prefix = "RTG"; + +package routeguide; + +// Interface exported by the server. +service RouteGuide { + // A simple RPC. + // + // Obtains the feature at a given position. + // + // A feature with an empty name is returned if there's no feature at the given + // position. + rpc GetFeature(Point) returns (Feature) {} + + // A server-to-client streaming RPC. + // + // Obtains the Features available within the given Rectangle. Results are + // streamed rather than returned at once (e.g. in a response message with a + // repeated field), as the rectangle may cover a large area and contain a + // huge number of features. + rpc ListFeatures(Rectangle) returns (stream Feature) {} + + // A client-to-server streaming RPC. + // + // Accepts a stream of Points on a route being traversed, returning a + // RouteSummary when traversal is completed. + rpc RecordRoute(stream Point) returns (RouteSummary) {} + + // A Bidirectional streaming RPC. + // + // Accepts a stream of RouteNotes sent while a route is being traversed, + // while receiving other RouteNotes (e.g. from other users). + rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} +} + +// Points are represented as latitude-longitude pairs in the E7 representation +// (degrees multiplied by 10**7 and rounded to the nearest integer). +// Latitudes should be in the range +/- 90 degrees and longitude should be in +// the range +/- 180 degrees (inclusive). +message Point { + int32 latitude = 1; + int32 longitude = 2; +} + +// A latitude-longitude rectangle, represented as two diagonally opposite +// points "lo" and "hi". +message Rectangle { + // One corner of the rectangle. + Point lo = 1; + + // The other corner of the rectangle. + Point hi = 2; +} + +// A feature names something at a given point. +// +// If a feature could not be named, the name is empty. +message Feature { + // The name of the feature. + string name = 1; + + // The point where the feature is detected. + Point location = 2; +} + +// Not used in the RPC. Instead, this is here for the form serialized to disk. +message FeatureDatabase { + repeated Feature feature = 1; +} + +// A RouteNote is a message sent while at a given point. +message RouteNote { + // The location from which the message is sent. + Point location = 1; + + // The message to be sent. + string message = 2; +} + +// A RouteSummary is received in response to a RecordRoute rpc. +// +// It contains the number of individual points received, the number of +// detected features, and the total distance covered as the cumulative sum of +// the distance between each point. +message RouteSummary { + // The number of points received. + int32 point_count = 1; + + // The number of known features passed while traversing the route. + int32 feature_count = 2; + + // The distance covered in metres. + int32 distance = 3; + + // The duration of the traversal in seconds. + int32 elapsed_time = 4; +} From 55303990fa7998879fb2b94990b528ece703768c Mon Sep 17 00:00:00 2001 From: hghianni Date: Fri, 30 Jun 2023 12:10:44 -0300 Subject: [PATCH 108/345] Change surefire config to debug why MongoEMGenerationTest fails --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index adc73d12a3..7c85370580 100644 --- a/pom.xml +++ b/pom.xml @@ -968,7 +968,7 @@ 3.0.0-M5 1 - true + false 2 false false - + \ No newline at end of file diff --git a/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/EvoMasterSampleGenerationTest.kt b/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/EvoMasterSampleGenerationTest.kt deleted file mode 100644 index dc96979d22..0000000000 --- a/e2e-tests/spring-rest-mongo/src/test/java/org/evomaster/e2etests/spring/rest/mongo/foo/EvoMasterSampleGenerationTest.kt +++ /dev/null @@ -1,171 +0,0 @@ -package org.foo.spring.rest.mongo - - -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.Timeout -import org.junit.jupiter.api.Assertions.* -import java.util.List -import org.evomaster.client.java.controller.api.EMTestUtils.* -import org.evomaster.client.java.controller.SutHandler -import io.restassured.RestAssured -import io.restassured.RestAssured.given -import io.restassured.response.ValidatableResponse -import org.evomaster.client.java.controller.mongo.dsl.MongoDsl.mongo -import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionResultsDto -import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionDto -import org.hamcrest.Matchers.* -import io.restassured.config.JsonConfig -import io.restassured.path.json.config.JsonPathConfig -import io.restassured.config.RedirectConfig.redirectConfig -import org.evomaster.client.java.controller.contentMatchers.NumberMatcher.* -import org.evomaster.client.java.controller.contentMatchers.StringMatcher.* -import org.evomaster.client.java.controller.contentMatchers.SubStringMatcher.* -import org.evomaster.client.java.controller.expect.ExpectationHandler.expectationHandler -import org.evomaster.client.java.controller.expect.ExpectationHandler -import io.restassured.path.json.JsonPath -import java.util.Arrays - - - - -/** - * This file was automatically generated by EvoMaster on 2023-06-29T13:40:21.894829-03:00\[America/Argentina/Buenos_Aires\] - * - * The generated test suite contains 3 tests - * - * Covered targets: 29 - * - * Used time: 0h 0m 10s - * - * Needed budget for current results: 100% - * - * - */ -internal class MongoEMGeneration { - - - companion object { - private val controller : SutHandler = com.foo.spring.rest.mongo.MongoPersonsWithoutPostAppController() - private lateinit var baseUrlOfSut: String - /** [ems] - expectations master switch - is the variable that activates/deactivates expectations individual test cases - * by default, expectations are turned off. The variable needs to be set to [true] to enable expectations - */ - private val ems = false - /** - * sco - supported code oracle - checking that the response status code is among those supported according to the schema - */ - private val sco = false - - - @BeforeAll - @JvmStatic - fun initClass() { - controller.setupForGeneratedTest() - baseUrlOfSut = controller.startSut() - assertNotNull(baseUrlOfSut) - RestAssured.enableLoggingOfRequestAndResponseIfValidationFails() - RestAssured.useRelaxedHTTPSValidation() - RestAssured.urlEncodingEnabled = false - RestAssured.config = RestAssured.config() - .jsonConfig(JsonConfig.jsonConfig().numberReturnType(JsonPathConfig.NumberReturnType.DOUBLE)) - .redirect(redirectConfig().followRedirects(false)) - } - - - @AfterAll - @JvmStatic - fun tearDown() { - controller.stopSut() - } - } - - - @BeforeEach - fun initTest() { - controller.resetStateOfSUT() - } - - - - - @Test @Timeout(60) - fun test_0() { - val expectationHandler: ExpectationHandler = expectationHandler() - - val res_0: ValidatableResponse = given().accept("*/*") - .get("${baseUrlOfSut}/v2/api-docs") - .then() - .statusCode(200) - .assertThat() - .contentType("application/json") - .body("'swagger'", containsString("2.0")) - .body("'info'.'description'", containsString("Some description")) - .body("'info'.'version'", containsString("1.0")) - .body("'info'.'title'", containsString("API")) - .body("'basePath'", containsString("/")) - .body("'tags'.size()", equalTo(1)) - .body("'tags'[0].'name'", containsString("person-without-post-controller")) - .body("'tags'[0].'description'", containsString("Person Without Post Controller")) - .body("'paths'.'/persons/list18'.'get'.'tags'.size()", equalTo(1)) - .body("'paths'.'/persons/list18'.'get'.'tags'", hasItems("person-without-post-controller")) - .body("'paths'.'/persons/list18'.'get'.'summary'", containsString("find18s")) - .body("'paths'.'/persons/list18'.'get'.'operationId'", containsString("find18sUsingGET")) - .body("'paths'.'/persons/list18'.'get'.'produces'.size()", equalTo(1)) - .body("'paths'.'/persons/list18'.'get'.'produces'", hasItems("*/*")) - .body("'paths'.'/persons/list18'.'get'.'responses'.'200'.'description'", containsString("OK")) - .body("'paths'.'/persons/list18'.'get'.'responses'.'401'.'description'", containsString("Unauthorized")) - .body("'paths'.'/persons/list18'.'get'.'responses'.'403'.'description'", containsString("Forbidden")) - .body("'paths'.'/persons/list18'.'get'.'responses'.'404'.'description'", containsString("Not Found")) - .body("'paths'.'/persons/list18'.'get'.'responsesObject'.'200'.'description'", containsString("OK")) - .body("'paths'.'/persons/list18'.'get'.'responsesObject'.'401'.'description'", containsString("Unauthorized")) - .body("'paths'.'/persons/list18'.'get'.'responsesObject'.'403'.'description'", containsString("Forbidden")) - .body("'paths'.'/persons/list18'.'get'.'responsesObject'.'404'.'description'", containsString("Not Found")) - - expectationHandler.expect(ems) - /* - Note: No supported codes appear to be defined. https://swagger.io/docs/specification/describing-responses/. - This is somewhat unexpected, so the code below is likely to lead to a failed expectation - */ - .that(sco, listOf().contains(res_0.extract().statusCode())) - } - - - @Test @Timeout(60) - fun test_1() { - val insertions = mongo().insertInto("persons", "person") - .d("{\"id\":\"IMU_V4ERbZxCSaw\", \"age\":18}") - .dtos() - val insertionsresult = controller.execInsertionsIntoMongoDatabase(insertions) - - given().accept("*/*") - .header("x-EMextraHeader123", "") - .get("${baseUrlOfSut}/persons/list18") - .then() - .statusCode(200) - .assertThat() - .body(isEmptyOrNullString()) - - } - - - @Test @Timeout(60) - fun test_2() { - val expectationHandler: ExpectationHandler = expectationHandler() - - val res_0: ValidatableResponse = given().accept("*/*") - .header("x-EMextraHeader123", "") - .get("${baseUrlOfSut}/persons/list18?EMextraParam123=_EM_0_XYZ_") - .then() - .statusCode(400) - .assertThat() - .body(isEmptyOrNullString()) - - expectationHandler.expect(ems) - .that(sco, listOf(200, 401, 403, 404).contains(res_0.extract().statusCode())) - } - - -} From 2937079a55a435c0fc548c8661b42f046c02a0b1 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 3 Jul 2023 21:56:55 +0200 Subject: [PATCH 117/345] construct proto3 dto --- .../rpc/schema/params/ObjectParam.java | 67 ++++++++++++------- ...DataTypesTestgRPCEndpointsBuilderTest.java | 41 ++++++++++++ 2 files changed, 85 insertions(+), 23 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java index 08c3a80fd3..83b47654d8 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java @@ -3,10 +3,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import org.evomaster.client.java.controller.api.dto.problem.rpc.ParamDto; import org.evomaster.client.java.controller.problem.rpc.CodeJavaGenerator; -import org.evomaster.client.java.controller.problem.rpc.schema.types.AccessibleSchema; -import org.evomaster.client.java.controller.problem.rpc.schema.types.ObjectType; -import org.evomaster.client.java.controller.problem.rpc.schema.types.PrimitiveOrWrapperType; -import org.evomaster.client.java.controller.problem.rpc.schema.types.TypeSchema; +import org.evomaster.client.java.controller.problem.rpc.schema.types.*; import org.evomaster.client.java.utils.SimpleLogger; import java.lang.reflect.Field; @@ -23,6 +20,10 @@ */ public class ObjectParam extends NamedTypedValue> { + public static final String PROTO3_BUILDER_METHOD = "newBuilder"; + + public static final String PROTO3_OBJECT_BUILD_METHOD = "build"; + public ObjectParam(String name, ObjectType type, AccessibleSchema accessibleSchema) { super(name, type, accessibleSchema); } @@ -33,34 +34,52 @@ public Object newInstance() throws ClassNotFoundException { String clazzName = getType().getFullTypeName(); Class clazz = Class.forName(clazzName); try { - Object instance = clazz.newInstance(); - for (NamedTypedValue v: getValue()){ - boolean setWithSetter = false; + Object instance = null; + + if (getType().spec == JavaDtoSpec.PROTO3){ + Object instanceBuilder = null; + + Method builderMethod = clazz.getMethod(PROTO3_BUILDER_METHOD); + instanceBuilder = builderMethod.invoke(null); + Class builderClazz = instanceBuilder.getClass(); + for (NamedTypedValue v: getValue()){ + Method builderSetter = builderClazz.getMethod(v.accessibleSchema.setterMethodName,v.getType().getClazz()); + builderSetter.invoke(instanceBuilder, v.newInstance()); + } + Method buildMethod = builderClazz.getMethod(PROTO3_OBJECT_BUILD_METHOD); + instance = buildMethod.invoke(instanceBuilder); + + } else if (getType().spec == JavaDtoSpec.PURE){ + instance = clazz.newInstance(); + + for (NamedTypedValue v: getValue()){ + boolean setWithSetter = false; /* if setter exists, we should prioritize the usage of the setter for thrift, the setter contains additional info */ - if(v.accessibleSchema != null && v.accessibleSchema.setterMethodName != null){ - Method m = getSetter(clazz, v.accessibleSchema.setterMethodName, v.getType(), v.getType().getClazz(), 0); - //clazz.getMethod(v.accessibleSchema.setterMethodName, v.getType().getClazz()); - try { - m.invoke(instance, v.newInstance()); - setWithSetter = true; - } catch (InvocationTargetException e) { - SimpleLogger.uniqueWarn("fail to access the method:"+clazzName+" with error msg:"+e.getMessage()); + if(v.accessibleSchema != null && v.accessibleSchema.setterMethodName != null){ + Method m = getSetter(clazz, v.accessibleSchema.setterMethodName, v.getType(), v.getType().getClazz(), 0); + //clazz.getMethod(v.accessibleSchema.setterMethodName, v.getType().getClazz()); + try { + m.invoke(instance, v.newInstance()); + setWithSetter = true; + } catch (InvocationTargetException e) { + SimpleLogger.uniqueWarn("fail to access the method:"+clazzName+" with error msg:"+e.getMessage()); + } } - } - if (!setWithSetter){ - Field f = clazz.getField(v.getName()); - f.setAccessible(true); - Object vins = v.newInstance(); - if (vins != null) - f.set(instance, vins); + if (!setWithSetter){ + Field f = clazz.getField(v.getName()); + f.setAccessible(true); + Object vins = v.newInstance(); + if (vins != null) + f.set(instance, vins); + } } - } + return instance; } catch (InstantiationException e) { throw new RuntimeException("fail to construct the class:"+clazzName+" with error msg:"+e.getMessage()); @@ -70,6 +89,8 @@ public Object newInstance() throws ClassNotFoundException { throw new RuntimeException("fail to access the field:"+clazzName+" with error msg:"+e.getMessage()); } catch (NoSuchMethodException e) { throw new RuntimeException("fail to access the method:"+clazzName+" with error msg:"+e.getMessage()); + } catch (InvocationTargetException e) { + throw new RuntimeException("fail to find the builder:"+clazzName+" with error msg:"+e.getMessage()); } } diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/DataTypesTestgRPCEndpointsBuilderTest.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/DataTypesTestgRPCEndpointsBuilderTest.java index 668713ad99..9e188d4237 100644 --- a/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/DataTypesTestgRPCEndpointsBuilderTest.java +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/DataTypesTestgRPCEndpointsBuilderTest.java @@ -1,7 +1,18 @@ package org.evomaster.client.java.controller.problem.rpc.grpc; import io.grpc.examples.evotests.datatypes.DataTypesTestGrpc; +import io.grpc.examples.evotests.datatypes.GetInfo; +import io.grpc.examples.evotests.datatypes.SimpleObj; +import org.evomaster.client.java.controller.api.dto.problem.rpc.ParamDto; import org.evomaster.client.java.controller.problem.rpc.RPCEndpointsBuilderTestBase; +import org.evomaster.client.java.controller.problem.rpc.schema.EndpointSchema; +import org.evomaster.client.java.controller.problem.rpc.schema.params.NamedTypedValue; +import org.evomaster.client.java.controller.problem.rpc.schema.params.ObjectParam; +import org.evomaster.client.java.controller.problem.rpc.schema.types.ObjectType; +import org.junit.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class DataTypesTestgRPCEndpointsBuilderTest extends RPCEndpointsBuilderTestBase { @Override @@ -13,4 +24,34 @@ public String getInterfaceName() { public int expectedNumberOfEndpoints() { return 7; } + + @Test + public void testGetSimpleObj() throws ClassNotFoundException { + EndpointSchema endpoint = getOneEndpoint("getSimpleObj"); + assertEquals(1, endpoint.getRequestParams().size()); + NamedTypedValue param1 = endpoint.getRequestParams().get(0); + assertTrue(param1 instanceof ObjectParam); + ObjectType returnType = (ObjectType)param1.getType(); + assertEquals(1, returnType.getFields().size()); + assertEquals("name", returnType.getFields().get(0).getName()); + assertEquals(String.class.getName(), returnType.getFields().get(0).getType().getFullTypeName()); + + ParamDto param1Dto = param1.getDto(); + assertEquals(GetInfo.class.getName(), param1Dto.type.fullTypeNameWithGenericType); + assertEquals(1, param1Dto.innerContent.size()); + assertEquals(String.class.getName(), param1Dto.innerContent.get(0).type.fullTypeNameWithGenericType); + param1Dto.setNotNullValue(); + String name = "foo"; + + param1Dto.innerContent.get(0).stringValue = name; + + param1.setValueBasedOnDto(param1Dto); + + Object param1Instance = param1.newInstance(); + + assertTrue(param1Instance instanceof GetInfo); + assertEquals(name, ((GetInfo) param1Instance).getName()); + + + } } From 403c0a1c9bc1a312bfcd30777e1d71691f8be932 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 3 Jul 2023 22:37:20 +0200 Subject: [PATCH 118/345] java construct proto3 --- .../problem/rpc/CodeJavaGenerator.java | 5 ++++ .../rpc/schema/params/ObjectParam.java | 23 +++++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/CodeJavaGenerator.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/CodeJavaGenerator.java index 48a12decd9..b045208a6c 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/CodeJavaGenerator.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/CodeJavaGenerator.java @@ -131,6 +131,11 @@ public static String setInstanceObject(String fullName, String varName){ return String.format("%s = %s;", varName, newObject(fullName)); } + + public static String newBuilderProto3(String fullName, String varBuilderName){ + return String.format("%s.Builder %s = %s.newBuilder();", fullName, varBuilderName, fullName); + } + /** * process [varName] = [instance] * @param varName specifies the variable name diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java index 83b47654d8..b99e538168 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java @@ -235,11 +235,21 @@ public List newInstanceWithJava(boolean isDeclaration, boolean doesInclu if (isNull) return codes; CodeJavaGenerator.addCode(codes, "{", indent); - // new obj - CodeJavaGenerator.addCode(codes, CodeJavaGenerator.setInstanceObject(typeName, varName), indent+1); + + String ownVarName = null; + if (getType().spec == JavaDtoSpec.PURE){ + // new obj + CodeJavaGenerator.addCode(codes, CodeJavaGenerator.setInstanceObject(typeName, varName), indent + 1 ); + ownVarName = varName; + }else{ + String varBuilderName = varName+"builder"; + CodeJavaGenerator.addCode(codes, CodeJavaGenerator.newBuilderProto3(typeName, varBuilderName), indent + 1); + ownVarName = varBuilderName; + } + for (NamedTypedValue f : getValue()){ if (f.accessibleSchema != null && f.accessibleSchema.setterMethodName != null){ - String fName = varName; + String fName = ownVarName; boolean fdeclar = false; if (f instanceof ObjectParam || f instanceof MapParam || f instanceof CollectionParam || f instanceof DateParam || f instanceof BigDecimalParam || f instanceof BigIntegerParam){ fName = varName+"_"+f.getName(); @@ -248,7 +258,12 @@ public List newInstanceWithJava(boolean isDeclaration, boolean doesInclu codes.addAll(f.newInstanceWithJava(fdeclar, true, fName, indent+1)); if (f instanceof ObjectParam || f instanceof MapParam || f instanceof CollectionParam || f instanceof DateParam || f instanceof BigDecimalParam || f instanceof BigIntegerParam){ - CodeJavaGenerator.addCode(codes, CodeJavaGenerator.methodInvocation(varName, f.accessibleSchema.setterMethodName, fName)+CodeJavaGenerator.appendLast(),indent+1); + CodeJavaGenerator.addCode(codes, CodeJavaGenerator.methodInvocation(ownVarName, f.accessibleSchema.setterMethodName, fName)+CodeJavaGenerator.appendLast(),indent+1); + } + + if (getType().spec == JavaDtoSpec.PROTO3){ + + CodeJavaGenerator.addCode(codes, CodeJavaGenerator.setInstance(true, varName, CodeJavaGenerator.methodInvocation(ownVarName, PROTO3_OBJECT_BUILD_METHOD, "")),indent+1); } }else { String fName = varName+"."+f.getName(); From 55d1e862c7d1c3a4f5242967cab732ee45e4a5df Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 3 Jul 2023 22:37:32 +0200 Subject: [PATCH 119/345] add unit test --- ...DataTypesTestgRPCEndpointsBuilderTest.java | 52 ++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/DataTypesTestgRPCEndpointsBuilderTest.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/DataTypesTestgRPCEndpointsBuilderTest.java index 9e188d4237..e3b6f23197 100644 --- a/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/DataTypesTestgRPCEndpointsBuilderTest.java +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/DataTypesTestgRPCEndpointsBuilderTest.java @@ -11,8 +11,10 @@ import org.evomaster.client.java.controller.problem.rpc.schema.types.ObjectType; import org.junit.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; public class DataTypesTestgRPCEndpointsBuilderTest extends RPCEndpointsBuilderTestBase { @Override @@ -25,6 +27,25 @@ public int expectedNumberOfEndpoints() { return 7; } + @Test + public void testAllFunctions(){ + List expectedFunctions = Arrays.asList( + "getSimpleObj", + "getNestedObj", + "setNestedObj", + "getMapObj", + "addMapObj", + "getListObj", + "addListObj" + ); + + for (String fun : expectedFunctions){ + EndpointSchema endpoint = getOneEndpoint(fun); + assertNotNull(endpoint); + } + + } + @Test public void testGetSimpleObj() throws ClassNotFoundException { EndpointSchema endpoint = getOneEndpoint("getSimpleObj"); @@ -41,17 +62,34 @@ public void testGetSimpleObj() throws ClassNotFoundException { assertEquals(1, param1Dto.innerContent.size()); assertEquals(String.class.getName(), param1Dto.innerContent.get(0).type.fullTypeNameWithGenericType); param1Dto.setNotNullValue(); - String name = "foo"; - - param1Dto.innerContent.get(0).stringValue = name; - + param1Dto.innerContent.get(0).stringValue = "foo"; param1.setValueBasedOnDto(param1Dto); Object param1Instance = param1.newInstance(); + assertTrue(param1Instance instanceof GetInfo); + assertEquals("foo", ((GetInfo) param1Instance).getName()); + GetInfo param1Bar = GetInfo.newBuilder().setName("bar").build(); + param1.setValueBasedOnInstance(param1Bar); + + param1Instance = param1.newInstance(); assertTrue(param1Instance instanceof GetInfo); - assertEquals(name, ((GetInfo) param1Instance).getName()); + assertEquals("bar", ((GetInfo) param1Instance).getName()); + + List param1InstanceJava = param1.newInstanceWithJava(0); + List expectedContents = Arrays.asList( + "io.grpc.examples.evotests.datatypes.GetInfo arg0 = null;", + "{", + " io.grpc.examples.evotests.datatypes.GetInfo.Builder arg0builder = io.grpc.examples.evotests.datatypes.GetInfo.newBuilder();", + " arg0builder.setName(\"bar\");", + " arg0 = arg0builder.build();", + "}" + ); + assertEquals(expectedContents.size(), param1InstanceJava.size()); + for (int i = 0; i < param1InstanceJava.size(); i++) + assertEquals(expectedContents.get(i), param1InstanceJava.get(i)); } + } From 2e90ecafb020905e3e9ac36d7b4402ff6e8451cf Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 4 Jul 2023 00:06:48 +0200 Subject: [PATCH 120/345] fix jsonresponse --- .../client/java/controller/internal/SutController.java | 10 ++++++++-- .../grpc/DataTypesTestgRPCEndpointsBuilderTest.java | 8 +++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index 0825134eb7..7b96d4549b 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -835,8 +835,14 @@ public final void executeAction(RPCActionDto dto, ActionResponseDto responseDto) responseDto.rpcResponse = resSchema.getDto(); if (dto.doGenerateAssertions && dto.responseVariable != null) responseDto.assertionScript = resSchema.newAssertionWithJava(dto.responseVariable, dto.maxAssertionForDataInCollection); - else - responseDto.jsonResponse = objectMapper.writeValueAsString(response); + else{ + try { + responseDto.jsonResponse = objectMapper.writeValueAsString(response); + }catch (JsonProcessingException e){ + // cannot convert to json + } + } + } catch (Exception e){ SimpleLogger.error("ERROR: fail to set successful response instance value to dto "+ e.getMessage()); //throw new RuntimeException("ERROR: fail to set successful response instance value to dto "+ e.getMessage()); diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/DataTypesTestgRPCEndpointsBuilderTest.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/DataTypesTestgRPCEndpointsBuilderTest.java index e3b6f23197..80573a2efd 100644 --- a/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/DataTypesTestgRPCEndpointsBuilderTest.java +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/DataTypesTestgRPCEndpointsBuilderTest.java @@ -47,7 +47,7 @@ public void testAllFunctions(){ } @Test - public void testGetSimpleObj() throws ClassNotFoundException { + public void testGetInfo() throws ClassNotFoundException { EndpointSchema endpoint = getOneEndpoint("getSimpleObj"); assertEquals(1, endpoint.getRequestParams().size()); NamedTypedValue param1 = endpoint.getRequestParams().get(0); @@ -92,4 +92,10 @@ public void testGetSimpleObj() throws ClassNotFoundException { assertEquals(expectedContents.get(i), param1InstanceJava.get(i)); } + @Test + public void testSimpleObj(){ + + } + + } From d9d789929b62db6a97f8d4fc0264cf394e477f32 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 4 Jul 2023 00:07:01 +0200 Subject: [PATCH 121/345] add e2e tests --- e2e-tests/spring-rpc/spring-rpc-grpc/pom.xml | 5 ++ .../examples/branches/BranchesGRPCEMTest.java | 53 +++++++++++++------ 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/e2e-tests/spring-rpc/spring-rpc-grpc/pom.xml b/e2e-tests/spring-rpc/spring-rpc-grpc/pom.xml index 1e9de2a965..621594254e 100644 --- a/e2e-tests/spring-rpc/spring-rpc-grpc/pom.xml +++ b/e2e-tests/spring-rpc/spring-rpc-grpc/pom.xml @@ -62,6 +62,11 @@ grpc-stub + + org.hamcrest + hamcrest-all + + org.junit.jupiter junit-jupiter-api diff --git a/e2e-tests/spring-rpc/spring-rpc-grpc/src/test/java/org/evomaster/e2etests/spring/rpc/grpc/examples/branches/BranchesGRPCEMTest.java b/e2e-tests/spring-rpc/spring-rpc-grpc/src/test/java/org/evomaster/e2etests/spring/rpc/grpc/examples/branches/BranchesGRPCEMTest.java index 2562c966bb..90e373835e 100644 --- a/e2e-tests/spring-rpc/spring-rpc-grpc/src/test/java/org/evomaster/e2etests/spring/rpc/grpc/examples/branches/BranchesGRPCEMTest.java +++ b/e2e-tests/spring-rpc/spring-rpc-grpc/src/test/java/org/evomaster/e2etests/spring/rpc/grpc/examples/branches/BranchesGRPCEMTest.java @@ -1,13 +1,20 @@ package org.evomaster.e2etests.spring.rpc.grpc.examples.branches; import com.foo.rpc.grpc.examples.spring.branches.BranchGRPCServiceController; +import org.evomaster.core.problem.rpc.RPCCallAction; import org.evomaster.core.problem.rpc.RPCIndividual; +import org.evomaster.core.problem.util.ParamUtil; +import org.evomaster.core.search.EvaluatedIndividual; import org.evomaster.core.search.Solution; +import org.evomaster.core.search.gene.Gene; +import org.evomaster.core.search.gene.ObjectGene; +import org.evomaster.core.search.gene.numeric.IntegerGene; import org.evomaster.e2etests.spring.rpc.grpc.examples.GRPCServerTestBase; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; public class BranchesGRPCEMTest extends GRPCServerTestBase { @@ -19,19 +26,35 @@ public static void initClass() throws Exception { GRPCServerTestBase.initClass(controller); } -// @Test -// public void testRunEM() throws Throwable { -// -// runTestHandlingFlakyAndCompilation( -// "BranchesGRPCEM", -// "org.foo.grpc.BranchesEM", -// 5000, -// (args) -> { -// -// Solution solution = initAndRun(args); -// -// assertTrue(solution.getIndividuals().size() >= 1); -// -// }); -// } + @Test + public void testRunEM() throws Throwable { + + runTestHandlingFlakyAndCompilation( + "BranchesGRPCEM", + "org.foo.grpc.BranchesEM", + 5000, + (args) -> { + + Solution solution = initAndRun(args); + + assertTrue(solution.getIndividuals().size() >= 1); + + int n =solution.getIndividuals().stream().map(EvaluatedIndividual::getIndividual) + .flatMapToInt(i-> i.seeAllActions().stream().filter(a-> + { + RPCCallAction action = (RPCCallAction) a; + if (action.getResponse() == null) return false; + Gene g = ParamUtil.Companion.getValueGene(action.getResponse().getGene()); + if (g instanceof ObjectGene){ + return ((ObjectGene)g).getFields().size() == 1 && ((ObjectGene)g).getFields().get(0) instanceof IntegerGene; + }else return false; + } + ).mapToInt(a-> ((IntegerGene)((ObjectGene)ParamUtil.Companion.getValueGene(((RPCCallAction)a).getResponse().getGene())).getFields().get(0)).getValue())) + .distinct() + .sorted().toArray().length; + + assertEquals(9, n); + + }); + } } From 92a7bb2be9d44906387ac3631b02d50655d7a644 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 4 Jul 2023 09:36:07 +0200 Subject: [PATCH 122/345] handle setter params --- .../controller/problem/rpc/Protobuf3Field.java | 3 +++ .../problem/rpc/RPCEndpointsBuilder.java | 9 ++++----- .../problem/rpc/schema/params/ObjectParam.java | 18 +++++++++++------- .../rpc/schema/types/AccessibleSchema.java | 14 ++++++++++++-- 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/Protobuf3Field.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/Protobuf3Field.java index 4d8308f963..a90ea5f30f 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/Protobuf3Field.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/Protobuf3Field.java @@ -11,6 +11,9 @@ public class Protobuf3Field { public Type genericType; public String setterName; + + public Class[] setterInputParams; + public String getterName; public Protobuf3Field(){} diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java index 388e43f61f..4ce9ba9727 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java @@ -607,9 +607,7 @@ else if(Set.class.isAssignableFrom(clazz)) if (rpcType == RPCType.gRPC || isProtobuf(clazz)){ List pfList = getProtobuf3FieldsAndType(clazz); for (Protobuf3Field pf : pfList){ - AccessibleSchema faccessSchema = new AccessibleSchema(false, pf.setterName, pf.getterName); - - // TODO genericType + AccessibleSchema faccessSchema = new AccessibleSchema(false, pf.setterName, pf.getterName, pf.setterInputParams); NamedTypedValue field = build(schema, pf.fieldType, pf.genericType, pf.fieldName, rpcType, flattenDepth, flevel, objRelatedCustomizationDtos, relatedCustomization, faccessSchema, notNullAnnotations, null, genericTypeMap, isTypeToIdentify); @@ -903,9 +901,9 @@ private static Protobuf3Field findProtobuf3FieldType(Class clazz, String fiel if (getter != null && filterProtobuf3Type(getter.getReturnType())){ String setterName = "set"+fieldName; if (Map.class.isAssignableFrom(getter.getReturnType())){ - setterName = "putAll"+fieldName; + setterName = PROTOBUF_MAP_SETTER_PREFIX + fieldName; }else if (List.class.isAssignableFrom(getter.getReturnType())){ - setterName = "addAll" + fieldName; + setterName = PROTOBUF_LIST_SETTER_PREFIX + fieldName; } for (Method m : clazz.getDeclaredMethods()){ if (m.getName().equalsIgnoreCase(setterName) @@ -924,6 +922,7 @@ private static Protobuf3Field findProtobuf3FieldType(Class clazz, String fiel pf.genericType = getter.getGenericReturnType(); pf.getterName = getter.getName(); pf.setterName = setter.getName(); + pf.setterInputParams = setter.getParameterTypes(); return pf; } return null; diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java index b99e538168..80dd99c07b 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java @@ -43,7 +43,12 @@ public Object newInstance() throws ClassNotFoundException { instanceBuilder = builderMethod.invoke(null); Class builderClazz = instanceBuilder.getClass(); for (NamedTypedValue v: getValue()){ - Method builderSetter = builderClazz.getMethod(v.accessibleSchema.setterMethodName,v.getType().getClazz()); + Class setterInputClazz = v.getType().getClazz(); + if (v.accessibleSchema.setterInputParams != null && v.accessibleSchema.setterInputParams.length > 0){ + setterInputClazz = v.accessibleSchema.setterInputParams[0]; + } + + Method builderSetter = builderClazz.getMethod(v.accessibleSchema.setterMethodName,setterInputClazz); builderSetter.invoke(instanceBuilder, v.newInstance()); } Method buildMethod = builderClazz.getMethod(PROTO3_OBJECT_BUILD_METHOD); @@ -97,7 +102,7 @@ public Object newInstance() throws ClassNotFoundException { private Method getSetter(Class clazz, String setterName, TypeSchema type, Class typeClass, int attemptTimes) throws NoSuchMethodException { try { - Method m = clazz.getMethod(setterName, type.getClazz()); + Method m = clazz.getMethod(setterName, typeClass); return m; } catch (NoSuchMethodException e) { if (type instanceof PrimitiveOrWrapperType && attemptTimes == 0){ @@ -260,17 +265,16 @@ public List newInstanceWithJava(boolean isDeclaration, boolean doesInclu if (f instanceof ObjectParam || f instanceof MapParam || f instanceof CollectionParam || f instanceof DateParam || f instanceof BigDecimalParam || f instanceof BigIntegerParam){ CodeJavaGenerator.addCode(codes, CodeJavaGenerator.methodInvocation(ownVarName, f.accessibleSchema.setterMethodName, fName)+CodeJavaGenerator.appendLast(),indent+1); } - - if (getType().spec == JavaDtoSpec.PROTO3){ - - CodeJavaGenerator.addCode(codes, CodeJavaGenerator.setInstance(true, varName, CodeJavaGenerator.methodInvocation(ownVarName, PROTO3_OBJECT_BUILD_METHOD, "")),indent+1); - } }else { String fName = varName+"."+f.getName(); codes.addAll(f.newInstanceWithJava(false, true, fName, indent+1)); } } + if (getType().spec == JavaDtoSpec.PROTO3){ + + CodeJavaGenerator.addCode(codes, CodeJavaGenerator.setInstance(true, varName, CodeJavaGenerator.methodInvocation(ownVarName, PROTO3_OBJECT_BUILD_METHOD, "")),indent+1); + } CodeJavaGenerator.addCode(codes, "}", indent); return codes; } diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/types/AccessibleSchema.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/types/AccessibleSchema.java index c9587eb37e..8908221fdc 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/types/AccessibleSchema.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/types/AccessibleSchema.java @@ -21,19 +21,29 @@ public class AccessibleSchema { */ public final String getterMethodName; + /** + * input params for setter + */ + public final Class[] setterInputParams; + public AccessibleSchema(){ - this(true, null, null); + this(true, null, null, null); } + public AccessibleSchema(boolean isAccessible, String setterMethodName, String getterMethodName){ + this(isAccessible, setterMethodName, getterMethodName, null); + } /** * * @param isAccessible specifies if the field is public * @param setterMethodName specifies the setter method name if it exists * @param getterMethodName specifies the getter method name if it exists + * @param setterInputParams specifies types of input params */ - public AccessibleSchema(boolean isAccessible, String setterMethodName, String getterMethodName){ + public AccessibleSchema(boolean isAccessible, String setterMethodName, String getterMethodName, Class[] setterInputParams){ this.getterMethodName = getterMethodName; this.isAccessible = isAccessible; this.setterMethodName = setterMethodName; + this.setterInputParams = setterInputParams; } } From 53a51d00312571b5b6e0413e2ddbd3e91d4e9ae1 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 4 Jul 2023 09:36:29 +0200 Subject: [PATCH 123/345] add more unit tests for various datatype --- ...DataTypesTestgRPCEndpointsBuilderTest.java | 368 +++++++++++++++++- 1 file changed, 360 insertions(+), 8 deletions(-) diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/DataTypesTestgRPCEndpointsBuilderTest.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/DataTypesTestgRPCEndpointsBuilderTest.java index 80573a2efd..1d1e4bd7b0 100644 --- a/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/DataTypesTestgRPCEndpointsBuilderTest.java +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/DataTypesTestgRPCEndpointsBuilderTest.java @@ -1,8 +1,6 @@ package org.evomaster.client.java.controller.problem.rpc.grpc; -import io.grpc.examples.evotests.datatypes.DataTypesTestGrpc; -import io.grpc.examples.evotests.datatypes.GetInfo; -import io.grpc.examples.evotests.datatypes.SimpleObj; +import io.grpc.examples.evotests.datatypes.*; import org.evomaster.client.java.controller.api.dto.problem.rpc.ParamDto; import org.evomaster.client.java.controller.problem.rpc.RPCEndpointsBuilderTestBase; import org.evomaster.client.java.controller.problem.rpc.schema.EndpointSchema; @@ -13,6 +11,7 @@ import java.util.Arrays; import java.util.List; +import java.util.Map; import static org.junit.jupiter.api.Assertions.*; @@ -52,10 +51,10 @@ public void testGetInfo() throws ClassNotFoundException { assertEquals(1, endpoint.getRequestParams().size()); NamedTypedValue param1 = endpoint.getRequestParams().get(0); assertTrue(param1 instanceof ObjectParam); - ObjectType returnType = (ObjectType)param1.getType(); - assertEquals(1, returnType.getFields().size()); - assertEquals("name", returnType.getFields().get(0).getName()); - assertEquals(String.class.getName(), returnType.getFields().get(0).getType().getFullTypeName()); + ObjectType param1Type = (ObjectType)param1.getType(); + assertEquals(1, param1Type.getFields().size()); + assertEquals("name", param1Type.getFields().get(0).getName()); + assertEquals(String.class.getName(), param1Type.getFields().get(0).getType().getFullTypeName()); ParamDto param1Dto = param1.getDto(); assertEquals(GetInfo.class.getName(), param1Dto.type.fullTypeNameWithGenericType); @@ -93,9 +92,362 @@ public void testGetInfo() throws ClassNotFoundException { } @Test - public void testSimpleObj(){ + public void testSimpleObj() throws ClassNotFoundException { + EndpointSchema endpoint = getOneEndpoint("setNestedObj"); + assertEquals(1, endpoint.getRequestParams().size()); + NamedTypedValue param1 = endpoint.getRequestParams().get(0); + assertTrue(param1 instanceof ObjectParam); + ObjectType param1Type = (ObjectType)param1.getType(); + assertEquals(7, param1Type.getFields().size()); + assertEquals("name", param1Type.getFields().get(0).getName()); + assertEquals(String.class.getName(), param1Type.getFields().get(0).getType().getFullTypeName()); + assertEquals("doublevalue", param1Type.getFields().get(1).getName()); + assertEquals(double.class.getName(), param1Type.getFields().get(1).getType().getFullTypeName()); + assertEquals("int32Value", param1Type.getFields().get(2).getName()); + assertEquals(int.class.getName(), param1Type.getFields().get(2).getType().getFullTypeName()); + assertEquals("int64Value", param1Type.getFields().get(3).getName()); + assertEquals(long.class.getName(), param1Type.getFields().get(3).getType().getFullTypeName()); + assertEquals("floatvalue", param1Type.getFields().get(4).getName()); + assertEquals(float.class.getName(), param1Type.getFields().get(4).getType().getFullTypeName()); + assertEquals("boolvalue", param1Type.getFields().get(5).getName()); + assertEquals(boolean.class.getName(), param1Type.getFields().get(5).getType().getFullTypeName()); + // bytes not support yet + assertEquals("enumvalue", param1Type.getFields().get(6).getName()); + assertEquals(SimpleEnum.class.getName(), param1Type.getFields().get(6).getType().getFullTypeName()); + + + SimpleObj objInstance = SimpleObj + .newBuilder() + .setName("foo") + .setDoublevalue(0.42) + .setInt32Value(42) + .setInt64Value(42L) + .setFloatvalue(0.42f) + .setBoolvalue(false) + .setEnumvalue(SimpleEnum.ONE) + .build(); + + param1.setValueBasedOnInstance(objInstance); + + Object param1Instance = param1.newInstance(); + assertTrue(param1Instance instanceof SimpleObj); + assertEquals(42, ((SimpleObj) param1Instance).getInt32Value()); + + + List param1InstanceJava = param1.newInstanceWithJava(0); + String[] expectedContents = ("io.grpc.examples.evotests.datatypes.SimpleObj arg0 = null;\n" + + "{\n" + + " io.grpc.examples.evotests.datatypes.SimpleObj.Builder arg0builder = io.grpc.examples.evotests.datatypes.SimpleObj.newBuilder();\n" + + " arg0builder.setName(\"foo\");\n" + + " arg0builder.setDoublevalue(0.42);\n" + + " arg0builder.setInt32Value(42);\n" + + " arg0builder.setInt64Value(42L);\n" + + " arg0builder.setFloatvalue(0.42f);\n" + + " arg0builder.setBoolvalue(false);\n" + + " arg0builder.setEnumvalue(((io.grpc.examples.evotests.datatypes.SimpleEnum)(io.grpc.examples.evotests.datatypes.SimpleEnum.ONE)));\n" + + " arg0 = arg0builder.build();\n" + + "}").split("\n"); + + assertEquals(expectedContents.length, param1InstanceJava.size()); + + for (int i = 0; i < param1InstanceJava.size(); i++) + assertEquals(expectedContents[i], param1InstanceJava.get(i)); + + } + @Test + public void testNestedObj() throws ClassNotFoundException { + EndpointSchema endpoint = getOneEndpoint("setNestedObj"); + NamedTypedValue response = endpoint.getResponse(); + assertTrue(response instanceof ObjectParam); + ObjectType responseType = (ObjectType)response.getType(); + assertEquals(2, responseType.getFields().size()); + assertEquals("name", responseType.getFields().get(0).getName()); + assertEquals(String.class.getName(), responseType.getFields().get(0).getType().getFullTypeName()); + assertEquals("objvalue", responseType.getFields().get(1).getName()); + assertEquals(SimpleObj.class.getName(), responseType.getFields().get(1).getType().getFullTypeName()); + + + SimpleObj objInstance = SimpleObj + .newBuilder() + .setName("foo") + .setDoublevalue(0.42) + .setInt32Value(42) + .setInt64Value(42L) + .setFloatvalue(0.42f) + .setBoolvalue(false) + .setEnumvalue(SimpleEnum.ONE) + .build(); + + NestedObj nestedObjInstance = NestedObj + .newBuilder() + .setName("bar") + .setObjvalue(objInstance).build(); + + response.setValueBasedOnInstance(nestedObjInstance); + + Object param1Instance = response.newInstance(); + assertTrue(param1Instance instanceof NestedObj); + assertEquals(42, ((NestedObj) param1Instance).getObjvalue().getInt32Value()); + + + List param1InstanceJava = response.newInstanceWithJava(true, true, "res", 0); + + String[] expectedContents = ("io.grpc.examples.evotests.datatypes.NestedObj res = null;\n" + + "{\n" + + " io.grpc.examples.evotests.datatypes.NestedObj.Builder resbuilder = io.grpc.examples.evotests.datatypes.NestedObj.newBuilder();\n" + + " resbuilder.setName(\"bar\");\n" + + " io.grpc.examples.evotests.datatypes.SimpleObj res_objvalue = null;\n" + + " {\n" + + " io.grpc.examples.evotests.datatypes.SimpleObj.Builder res_objvaluebuilder = io.grpc.examples.evotests.datatypes.SimpleObj.newBuilder();\n" + + " res_objvaluebuilder.setName(\"foo\");\n" + + " res_objvaluebuilder.setDoublevalue(0.42);\n" + + " res_objvaluebuilder.setInt32Value(42);\n" + + " res_objvaluebuilder.setInt64Value(42L);\n" + + " res_objvaluebuilder.setFloatvalue(0.42f);\n" + + " res_objvaluebuilder.setBoolvalue(false);\n" + + " res_objvaluebuilder.setEnumvalue(((io.grpc.examples.evotests.datatypes.SimpleEnum)(io.grpc.examples.evotests.datatypes.SimpleEnum.ONE)));\n" + + " res_objvalue = res_objvaluebuilder.build();\n" + + " }\n" + + " resbuilder.setObjvalue(res_objvalue);\n" + + " res = resbuilder.build();\n" + + "}").split("\n"); + + assertEquals(expectedContents.length, param1InstanceJava.size()); + + for (int i = 0; i < param1InstanceJava.size(); i++) + assertEquals(expectedContents[i], param1InstanceJava.get(i)); } + @Test + public void testMapObj() throws ClassNotFoundException { + EndpointSchema endpoint = getOneEndpoint("getMapObj"); + NamedTypedValue response = endpoint.getResponse(); + assertTrue(response instanceof ObjectParam); + ObjectType responseType = (ObjectType)response.getType(); + assertEquals(3, responseType.getFields().size()); + assertEquals("name", responseType.getFields().get(0).getName()); + assertEquals(String.class.getName(), responseType.getFields().get(0).getType().getFullTypeName()); + assertEquals("intkeymapvalue", responseType.getFields().get(1).getName()); + assertEquals(Map.class.getName(), responseType.getFields().get(1).getType().getFullTypeName()); + assertEquals("stringkeymapvalue", responseType.getFields().get(2).getName()); + assertEquals(Map.class.getName(), responseType.getFields().get(2).getType().getFullTypeName()); + + + SimpleObj int1ObjInstance = SimpleObj + .newBuilder() + .setName("int1Foo") + .setDoublevalue(0.42) + .setInt32Value(42) + .setInt64Value(42L) + .setFloatvalue(0.42f) + .setBoolvalue(false) + .setEnumvalue(SimpleEnum.ONE) + .build(); + + SimpleObj strbarObjInstance = SimpleObj + .newBuilder() + .setName("strbarFoo") + .setDoublevalue(0.42) + .setInt32Value(42) + .setInt64Value(42L) + .setFloatvalue(0.42f) + .setBoolvalue(false) + .setEnumvalue(SimpleEnum.TWO) + .build(); + + MapObj mapObjInstance = MapObj + .newBuilder() + .setName("bar") + .putIntkeymapvalue(1, int1ObjInstance) + .putStringkeymapvalue("strbar", strbarObjInstance) + .build(); + + + response.setValueBasedOnInstance(mapObjInstance); + + Object param1Instance = response.newInstance(); + assertTrue(param1Instance instanceof MapObj); + assertEquals(42, ((MapObj) param1Instance).getIntkeymapvalueMap().get(1).getInt32Value()); + + + List param1InstanceJava = response.newInstanceWithJava(true, true, "res", 0); + + String[] expectedContents = ("io.grpc.examples.evotests.datatypes.MapObj res = null;\n" + + "{\n" + + " io.grpc.examples.evotests.datatypes.MapObj.Builder resbuilder = io.grpc.examples.evotests.datatypes.MapObj.newBuilder();\n" + + " resbuilder.setName(\"bar\");\n" + + " java.util.Map res_intkeymapvalue = null;\n" + + " {\n" + + " res_intkeymapvalue = new java.util.HashMap<>();\n" + + " java.lang.Integer res_intkeymapvalue_key_0 = 1;\n" + + " io.grpc.examples.evotests.datatypes.SimpleObj res_intkeymapvalue_value_0 = null;\n" + + " {\n" + + " io.grpc.examples.evotests.datatypes.SimpleObj.Builder res_intkeymapvalue_value_0builder = io.grpc.examples.evotests.datatypes.SimpleObj.newBuilder();\n" + + " res_intkeymapvalue_value_0builder.setName(\"int1Foo\");\n" + + " res_intkeymapvalue_value_0builder.setDoublevalue(0.42);\n" + + " res_intkeymapvalue_value_0builder.setInt32Value(42);\n" + + " res_intkeymapvalue_value_0builder.setInt64Value(42L);\n" + + " res_intkeymapvalue_value_0builder.setFloatvalue(0.42f);\n" + + " res_intkeymapvalue_value_0builder.setBoolvalue(false);\n" + + " res_intkeymapvalue_value_0builder.setEnumvalue(((io.grpc.examples.evotests.datatypes.SimpleEnum)(io.grpc.examples.evotests.datatypes.SimpleEnum.ONE)));\n" + + " res_intkeymapvalue_value_0 = res_intkeymapvalue_value_0builder.build();\n" + + " }\n" + + " res_intkeymapvalue.put(res_intkeymapvalue_key_0,res_intkeymapvalue_value_0);\n" + + " }\n" + + " resbuilder.putAllIntkeymapvalue(res_intkeymapvalue);\n" + + " java.util.Map res_stringkeymapvalue = null;\n" + + " {\n" + + " res_stringkeymapvalue = new java.util.HashMap<>();\n" + + " java.lang.String res_stringkeymapvalue_key_0 = \"strbar\";\n" + + " io.grpc.examples.evotests.datatypes.SimpleObj res_stringkeymapvalue_value_0 = null;\n" + + " {\n" + + " io.grpc.examples.evotests.datatypes.SimpleObj.Builder res_stringkeymapvalue_value_0builder = io.grpc.examples.evotests.datatypes.SimpleObj.newBuilder();\n" + + " res_stringkeymapvalue_value_0builder.setName(\"strbarFoo\");\n" + + " res_stringkeymapvalue_value_0builder.setDoublevalue(0.42);\n" + + " res_stringkeymapvalue_value_0builder.setInt32Value(42);\n" + + " res_stringkeymapvalue_value_0builder.setInt64Value(42L);\n" + + " res_stringkeymapvalue_value_0builder.setFloatvalue(0.42f);\n" + + " res_stringkeymapvalue_value_0builder.setBoolvalue(false);\n" + + " res_stringkeymapvalue_value_0builder.setEnumvalue(((io.grpc.examples.evotests.datatypes.SimpleEnum)(io.grpc.examples.evotests.datatypes.SimpleEnum.TWO)));\n" + + " res_stringkeymapvalue_value_0 = res_stringkeymapvalue_value_0builder.build();\n" + + " }\n" + + " res_stringkeymapvalue.put(res_stringkeymapvalue_key_0,res_stringkeymapvalue_value_0);\n" + + " }\n" + + " resbuilder.putAllStringkeymapvalue(res_stringkeymapvalue);\n" + + " res = resbuilder.build();\n" + + "}").split("\n"); + + assertEquals(expectedContents.length, param1InstanceJava.size()); + + for (int i = 0; i < param1InstanceJava.size(); i++) + assertEquals(expectedContents[i], param1InstanceJava.get(i)); + + } + + + @Test + public void testListObj() throws ClassNotFoundException { + EndpointSchema endpoint = getOneEndpoint("addListObj"); + NamedTypedValue response = endpoint.getResponse(); + assertTrue(response instanceof ObjectParam); + ObjectType responseType = (ObjectType)response.getType(); + assertEquals(4, responseType.getFields().size()); + assertEquals("name", responseType.getFields().get(0).getName()); + assertEquals(String.class.getName(), responseType.getFields().get(0).getType().getFullTypeName()); + assertEquals("intlistvalue", responseType.getFields().get(1).getName()); + assertEquals(List.class.getName(), responseType.getFields().get(1).getType().getFullTypeName()); + assertEquals("stringlistvalue", responseType.getFields().get(2).getName()); + assertEquals(List.class.getName(), responseType.getFields().get(2).getType().getFullTypeName()); + assertEquals("objlistvalue", responseType.getFields().get(3).getName()); + assertEquals(List.class.getName(), responseType.getFields().get(3).getType().getFullTypeName()); + + SimpleObj int1ObjInstance = SimpleObj + .newBuilder() + .setName("int1Foo") + .setDoublevalue(0.42) + .setInt32Value(42) + .setInt64Value(42L) + .setFloatvalue(0.42f) + .setBoolvalue(false) + .setEnumvalue(SimpleEnum.ONE) + .build(); + + SimpleObj strbarObjInstance = SimpleObj + .newBuilder() + .setName("strbarFoo") + .setDoublevalue(0.42) + .setInt32Value(42) + .setInt64Value(42L) + .setFloatvalue(0.42f) + .setBoolvalue(false) + .setEnumvalue(SimpleEnum.TWO) + .build(); + + ListObj listObj = ListObj + .newBuilder() + .setName("bar") + .addIntlistvalue(42) + .addStringlistvalue("barInList") + .addStringlistvalue("fooInList") + .addObjlistvalue(int1ObjInstance) + .addObjlistvalue(strbarObjInstance) + .build(); + + + response.setValueBasedOnInstance(listObj); + + Object param1Instance = response.newInstance(); + assertTrue(param1Instance instanceof ListObj); + ListObj param1InstanceValue = (ListObj) param1Instance; + assertEquals(1, param1InstanceValue.getIntlistvalueList().size()); + assertEquals(2, param1InstanceValue.getStringlistvalueList().size()); + assertEquals(2, param1InstanceValue.getObjlistvalueList().size()); + + + List param1InstanceJava = response.newInstanceWithJava(true, true, "res", 0); + + String[] expectedContents = ("io.grpc.examples.evotests.datatypes.ListObj res = null;\n" + + "{\n" + + " io.grpc.examples.evotests.datatypes.ListObj.Builder resbuilder = io.grpc.examples.evotests.datatypes.ListObj.newBuilder();\n" + + " resbuilder.setName(\"bar\");\n" + + " java.util.List res_intlistvalue = null;\n" + + " {\n" + + " res_intlistvalue = new java.util.ArrayList<>();\n" + + " java.lang.Integer res_intlistvalue_e_0 = 42;\n" + + " res_intlistvalue.add(res_intlistvalue_e_0);\n" + + " }\n" + + " resbuilder.addAllIntlistvalue(res_intlistvalue);\n" + + " java.util.List res_stringlistvalue = null;\n" + + " {\n" + + " res_stringlistvalue = new java.util.ArrayList<>();\n" + + " java.lang.String res_stringlistvalue_e_0 = \"barInList\";\n" + + " res_stringlistvalue.add(res_stringlistvalue_e_0);\n" + + " java.lang.String res_stringlistvalue_e_1 = \"fooInList\";\n" + + " res_stringlistvalue.add(res_stringlistvalue_e_1);\n" + + " }\n" + + " resbuilder.addAllStringlistvalue(res_stringlistvalue);\n" + + " java.util.List res_objlistvalue = null;\n" + + " {\n" + + " res_objlistvalue = new java.util.ArrayList<>();\n" + + " io.grpc.examples.evotests.datatypes.SimpleObj res_objlistvalue_e_0 = null;\n" + + " {\n" + + " io.grpc.examples.evotests.datatypes.SimpleObj.Builder res_objlistvalue_e_0builder = io.grpc.examples.evotests.datatypes.SimpleObj.newBuilder();\n" + + " res_objlistvalue_e_0builder.setName(\"int1Foo\");\n" + + " res_objlistvalue_e_0builder.setDoublevalue(0.42);\n" + + " res_objlistvalue_e_0builder.setInt32Value(42);\n" + + " res_objlistvalue_e_0builder.setInt64Value(42L);\n" + + " res_objlistvalue_e_0builder.setFloatvalue(0.42f);\n" + + " res_objlistvalue_e_0builder.setBoolvalue(false);\n" + + " res_objlistvalue_e_0builder.setEnumvalue(((io.grpc.examples.evotests.datatypes.SimpleEnum)(io.grpc.examples.evotests.datatypes.SimpleEnum.ONE)));\n" + + " res_objlistvalue_e_0 = res_objlistvalue_e_0builder.build();\n" + + " }\n" + + " res_objlistvalue.add(res_objlistvalue_e_0);\n" + + " io.grpc.examples.evotests.datatypes.SimpleObj res_objlistvalue_e_1 = null;\n" + + " {\n" + + " io.grpc.examples.evotests.datatypes.SimpleObj.Builder res_objlistvalue_e_1builder = io.grpc.examples.evotests.datatypes.SimpleObj.newBuilder();\n" + + " res_objlistvalue_e_1builder.setName(\"strbarFoo\");\n" + + " res_objlistvalue_e_1builder.setDoublevalue(0.42);\n" + + " res_objlistvalue_e_1builder.setInt32Value(42);\n" + + " res_objlistvalue_e_1builder.setInt64Value(42L);\n" + + " res_objlistvalue_e_1builder.setFloatvalue(0.42f);\n" + + " res_objlistvalue_e_1builder.setBoolvalue(false);\n" + + " res_objlistvalue_e_1builder.setEnumvalue(((io.grpc.examples.evotests.datatypes.SimpleEnum)(io.grpc.examples.evotests.datatypes.SimpleEnum.TWO)));\n" + + " res_objlistvalue_e_1 = res_objlistvalue_e_1builder.build();\n" + + " }\n" + + " res_objlistvalue.add(res_objlistvalue_e_1);\n" + + " }\n" + + " resbuilder.addAllObjlistvalue(res_objlistvalue);\n" + + " res = resbuilder.build();\n" + + "}").split("\n"); + + assertEquals(expectedContents.length, param1InstanceJava.size()); + + for (int i = 0; i < param1InstanceJava.size(); i++) + assertEquals(expectedContents[i], param1InstanceJava.get(i)); + + + + } } From c86633dd0db3e003877185602467d012690df22d Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 4 Jul 2023 12:08:16 +0200 Subject: [PATCH 124/345] add doc --- .../problem/rpc/CodeJavaGenerator.java | 6 ++++ .../problem/rpc/Protobuf3Field.java | 28 +++++++++++++++++++ .../problem/rpc/schema/types/JavaDtoSpec.java | 13 ++++++++- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/CodeJavaGenerator.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/CodeJavaGenerator.java index b045208a6c..f7e6e9ea5c 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/CodeJavaGenerator.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/CodeJavaGenerator.java @@ -132,6 +132,12 @@ public static String setInstanceObject(String fullName, String varName){ } + /** + * + * @param fullName is the full name of the dto + * @param varBuilderName is the variable name for the builder + * @return code to instantiate builder for proto3 dto + */ public static String newBuilderProto3(String fullName, String varBuilderName){ return String.format("%s.Builder %s = %s.newBuilder();", fullName, varBuilderName, fullName); } diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/Protobuf3Field.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/Protobuf3Field.java index a90ea5f30f..97b4e0a886 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/Protobuf3Field.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/Protobuf3Field.java @@ -2,18 +2,46 @@ import java.lang.reflect.Type; +/** + * this class presents extracted info of a field of protobuf dto + */ public class Protobuf3Field { + /** + * name + */ public String fieldName; + /** + * type + */ public Class fieldType; + /** + * generic type if it exists, such as for Map, List + */ public Type genericType; + /** + * name of corresponding setter + */ public String setterName; + /** + * types of input parameters of the setter + * + * the input parameter might not be consistent with the fieldType in gRPC + * the type could be defined with its supertype + * then we need such explicit info in order to get the setter + * + * For instance, the fieldType is List, and the input type is Iterator + * + */ public Class[] setterInputParams; + /** + * name of getter + */ public String getterName; public Protobuf3Field(){} diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/types/JavaDtoSpec.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/types/JavaDtoSpec.java index e35e36fbc6..b4e8b29a1f 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/types/JavaDtoSpec.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/types/JavaDtoSpec.java @@ -1,5 +1,16 @@ package org.evomaster.client.java.controller.problem.rpc.schema.types; +/** + * supported specification of Java Dto + */ public enum JavaDtoSpec { - PURE, PROTO3 + /** + * pure Java + */ + PURE, + + /** + * using proto3 + */ + PROTO3 } From 88dd05f718ce0c727adc77c539aeb4824e2a68f6 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 4 Jul 2023 15:59:59 +0200 Subject: [PATCH 125/345] address comments --- client-java/controller/pom.xml | 1 + .../controller/internal/SutController.java | 19 ++++++++++++------- .../problem/rpc/RPCEndpointsBuilder.java | 2 +- .../rpc/schema/LocalAuthSetupSchema.java | 2 +- .../rpc/schema/params/ObjectParam.java | 4 ++-- .../problem/rpc/schema/types/JavaDtoSpec.java | 9 ++++++++- 6 files changed, 25 insertions(+), 12 deletions(-) diff --git a/client-java/controller/pom.xml b/client-java/controller/pom.xml index 9ce13dfca0..0220b96a73 100644 --- a/client-java/controller/pom.xml +++ b/client-java/controller/pom.xml @@ -219,6 +219,7 @@ + kr.motd.maven os-maven-plugin ${kr.motd.maven.version} diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index 7b96d4549b..82904f2b56 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -835,13 +835,18 @@ public final void executeAction(RPCActionDto dto, ActionResponseDto responseDto) responseDto.rpcResponse = resSchema.getDto(); if (dto.doGenerateAssertions && dto.responseVariable != null) responseDto.assertionScript = resSchema.newAssertionWithJava(dto.responseVariable, dto.maxAssertionForDataInCollection); - else{ - try { - responseDto.jsonResponse = objectMapper.writeValueAsString(response); - }catch (JsonProcessingException e){ - // cannot convert to json - } - } + /* + ActionResponseDto.jsonResponse could be used to generate assertions in core side + however, as we do not support the test generate in core side yet and not all DTO can be converted into json, + we comment out this code + */ +// else{ +// try { +// responseDto.jsonResponse = objectMapper.writeValueAsString(response); +// }catch (JsonProcessingException e){ +// // cannot convert to json +// } +// } } catch (Exception e){ SimpleLogger.error("ERROR: fail to set successful response instance value to dto "+ e.getMessage()); diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java index 4ce9ba9727..b10b671a13 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java @@ -507,7 +507,7 @@ private static NamedTypedValue build(InterfaceSchema schema, Class clazz, Typ flattenDepth.add(getObjectTypeNameWithFlag(clazz, clazzWithGenericTypes, level)); NamedTypedValue namedValue = null; - JavaDtoSpec spec = JavaDtoSpec.PURE; + JavaDtoSpec spec = JavaDtoSpec.DEFAULT; if (rpcType == RPCType.gRPC || isProtobuf(clazz)) spec = JavaDtoSpec.PROTO3; diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/LocalAuthSetupSchema.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/LocalAuthSetupSchema.java index adb72e2480..749a1f3e48 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/LocalAuthSetupSchema.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/LocalAuthSetupSchema.java @@ -21,7 +21,7 @@ public class LocalAuthSetupSchema extends EndpointSchema{ public LocalAuthSetupSchema() { super(HANDLE_LOCAL_AUTHENTICATION_SETUP_METHOD_NAME, - EM_LOCAL_METHOD, null, Arrays.asList(new StringParam("arg0", new StringType(JavaDtoSpec.PURE), new AccessibleSchema())), null, null, false, null, null); + EM_LOCAL_METHOD, null, Arrays.asList(new StringParam("arg0", new StringType(JavaDtoSpec.DEFAULT), new AccessibleSchema())), null, null, false, null, null); } /** diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java index 80dd99c07b..d84fd08b3a 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java @@ -54,7 +54,7 @@ public Object newInstance() throws ClassNotFoundException { Method buildMethod = builderClazz.getMethod(PROTO3_OBJECT_BUILD_METHOD); instance = buildMethod.invoke(instanceBuilder); - } else if (getType().spec == JavaDtoSpec.PURE){ + } else if (getType().spec == JavaDtoSpec.DEFAULT){ instance = clazz.newInstance(); for (NamedTypedValue v: getValue()){ @@ -242,7 +242,7 @@ public List newInstanceWithJava(boolean isDeclaration, boolean doesInclu CodeJavaGenerator.addCode(codes, "{", indent); String ownVarName = null; - if (getType().spec == JavaDtoSpec.PURE){ + if (getType().spec == JavaDtoSpec.DEFAULT){ // new obj CodeJavaGenerator.addCode(codes, CodeJavaGenerator.setInstanceObject(typeName, varName), indent + 1 ); ownVarName = varName; diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/types/JavaDtoSpec.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/types/JavaDtoSpec.java index b4e8b29a1f..d78f1381d2 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/types/JavaDtoSpec.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/types/JavaDtoSpec.java @@ -2,15 +2,22 @@ /** * supported specification of Java Dto + * + * the specification would relate to how to construct dto */ public enum JavaDtoSpec { /** * pure Java */ - PURE, + DEFAULT, /** * using proto3 + * + * if the specification is proto3, + * then dto is needed to construct with the builder + * see info of Java Generated Code + * */ PROTO3 } From aa1963e27c9169380ff2b14639def0e23a0f1603 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Thu, 6 Jul 2023 21:28:48 +0200 Subject: [PATCH 126/345] .NET update --- docs/publications.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/publications.md b/docs/publications.md index e244af0bc2..39a04fe809 100644 --- a/docs/publications.md +++ b/docs/publications.md @@ -31,6 +31,10 @@ Also, some of these papers provides full replication packages, which are linked ### 2023 +* A. Golmohammadi, M. Zhang, A. Arcuri + *.NET/C# Instrumentation for Search-Based Software Testing*. + Software Quality Journal (SQJ). (to appear) + * A. Belhadi, M. Zhang, A. Arcuri. *Random Testing and Evolutionary Testing for Fuzzing GraphQL APIs*. ACM Transactions on the Web (TWEB). (to appear) From 17bf2827d38d7f17e683d025f589d62be2d425cd Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 10 Jul 2023 17:55:18 +0200 Subject: [PATCH 127/345] handle ByteString for proto3 --- .../problem/rpc/RPCEndpointsBuilder.java | 15 ++- .../rpc/schema/params/ObjectParam.java | 4 +- .../params/Protobuf3ByteStringParam.java | 122 ++++++++++++++++++ .../schema/types/Protobuf3ByteStringType.java | 33 +++++ 4 files changed, 167 insertions(+), 7 deletions(-) create mode 100644 client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/Protobuf3ByteStringParam.java create mode 100644 client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/types/Protobuf3ByteStringType.java diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java index b10b671a13..abea9dde97 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.sun.xml.internal.bind.v2.TODO; import org.evomaster.client.java.controller.api.dto.AuthenticationDto; import org.evomaster.client.java.controller.api.dto.CustomizedRequestValueDto; import org.evomaster.client.java.controller.api.dto.JsonAuthRPCEndpointDto; @@ -552,6 +553,9 @@ private static NamedTypedValue build(InterfaceSchema schema, Class clazz, Typ } else if (clazz == ByteBuffer.class){ // handle binary of thrift namedValue = new ByteBufferParam(name, accessibleSchema, spec); + } else if (clazz.getName().equals(Protobuf3ByteStringType.PROTOBUF3_BYTE_STRING_TYPE_NAME)){ + Protobuf3ByteStringType type = Protobuf3ByteStringType.getInstance(spec, clazz); + namedValue = new Protobuf3ByteStringParam(name, type, accessibleSchema); } else if (List.class.isAssignableFrom(clazz) || Set.class.isAssignableFrom(clazz)){ if (genericType == null) throw new RuntimeException("genericType should not be null for List and Set class"); @@ -932,12 +936,13 @@ private static boolean filterProtobuf3Field(Field field){ return (!field.getName().equals("bitField0_")); } - /** - * TODO need to support com.google.protobuf.ByteString - * - */ + private static boolean filterProtobuf3Type(Class clazz){ - return !clazz.getName().equals("com.google.protobuf.ByteString"); + /* + support all types for proto 3 + might add if we found any type is not supported yet + */ + return true; } private static void handleNamedValueWithCustomizedDto(NamedTypedValue namedTypedValue, Map customizationDtos, Set relatedCustomization){ diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java index d84fd08b3a..5d71b80655 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java @@ -256,13 +256,13 @@ public List newInstanceWithJava(boolean isDeclaration, boolean doesInclu if (f.accessibleSchema != null && f.accessibleSchema.setterMethodName != null){ String fName = ownVarName; boolean fdeclar = false; - if (f instanceof ObjectParam || f instanceof MapParam || f instanceof CollectionParam || f instanceof DateParam || f instanceof BigDecimalParam || f instanceof BigIntegerParam){ + if (f instanceof ObjectParam || f instanceof MapParam || f instanceof CollectionParam || f instanceof DateParam || f instanceof BigDecimalParam || f instanceof BigIntegerParam || f instanceof Protobuf3ByteStringParam){ fName = varName+"_"+f.getName(); fdeclar = true; } codes.addAll(f.newInstanceWithJava(fdeclar, true, fName, indent+1)); - if (f instanceof ObjectParam || f instanceof MapParam || f instanceof CollectionParam || f instanceof DateParam || f instanceof BigDecimalParam || f instanceof BigIntegerParam){ + if (f instanceof ObjectParam || f instanceof MapParam || f instanceof CollectionParam || f instanceof DateParam || f instanceof BigDecimalParam || f instanceof BigIntegerParam || f instanceof Protobuf3ByteStringParam){ CodeJavaGenerator.addCode(codes, CodeJavaGenerator.methodInvocation(ownVarName, f.accessibleSchema.setterMethodName, fName)+CodeJavaGenerator.appendLast(),indent+1); } }else { diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/Protobuf3ByteStringParam.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/Protobuf3ByteStringParam.java new file mode 100644 index 0000000000..af12952fd9 --- /dev/null +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/Protobuf3ByteStringParam.java @@ -0,0 +1,122 @@ +package org.evomaster.client.java.controller.problem.rpc.schema.params; + +import org.evomaster.client.java.controller.api.dto.problem.rpc.ParamDto; +import org.evomaster.client.java.controller.problem.rpc.CodeJavaGenerator; +import org.evomaster.client.java.controller.problem.rpc.schema.types.AccessibleSchema; +import org.evomaster.client.java.controller.problem.rpc.schema.types.Protobuf3ByteStringType; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * this is for handling bytes in gRPC with protobuf3 ByteString + * + * handle it as string with utf8 now + * copyFromUtf8 + * toStringUtf8 + */ +public class Protobuf3ByteStringParam extends NamedTypedValue{ + + + public final static String PROTOBUF3_BYTE_STRING_METHOD_COPY_FROM_METHOD = "copyFromUtf8"; + + public final static String PROTOBUF3_BYTE_STRING_METHOD_TO_STRING_UTF8 = "toStringUtf8"; + + public Protobuf3ByteStringParam(String name, Protobuf3ByteStringType type, AccessibleSchema accessibleSchema){ + super(name, type, accessibleSchema); + } + + @Override + public Object newInstance() throws ClassNotFoundException { + if (getValue() == null) return null; + + try { + Method setMethod = getType().getClazz().getMethod(PROTOBUF3_BYTE_STRING_METHOD_COPY_FROM_METHOD, String.class); + Object instance = setMethod.invoke(null, getValue()); + return instance; + } catch (NoSuchMethodException e) { + throw new RuntimeException("fail to find method copyFromUtf8 in the class com.google.protobuf.ByteString with error msg:", e); + } catch (InvocationTargetException e) { + throw new RuntimeException("fail to invoke method copyFromUtf8 in the class com.google.protobuf.ByteString with error msg:", e); + } catch (IllegalAccessException e) { + throw new RuntimeException("fail to access method copyFromUtf8 in the class com.google.protobuf.ByteString with error msg:", e); + } + } + + @Override + public ParamDto getDto() { + ParamDto dto = super.getDto(); + if (getValue() != null){ + dto.stringValue = getValue(); + } + + return dto; + } + + + @Override + public Protobuf3ByteStringParam copyStructure() { + return new Protobuf3ByteStringParam(getName(), getType(), accessibleSchema); + } + + @Override + public void setValueBasedOnDto(ParamDto dto) { + if (dto.stringValue != null) + setValue(dto.stringValue); + } + + @Override + protected void setValueBasedOnValidInstance(Object instance) { + if (instance == null) setValue(null); + try { + Method getMethod = getType().getClazz().getMethod(PROTOBUF3_BYTE_STRING_METHOD_TO_STRING_UTF8); + Object strutf8 = getMethod.invoke(instance); + if (strutf8 instanceof String) + setValue((String) strutf8); + else{ + throw new RuntimeException("fail to get string value of ByteString with toStringUtf8"); + } + } catch (NoSuchMethodException e) { + throw new RuntimeException("fail to find method toStringUtf8 in the class com.google.protobuf.ByteString with error msg:", e); + } catch (InvocationTargetException e) { + throw new RuntimeException("fail to invoke method toStringUtf8 in the class com.google.protobuf.ByteString with error msg:", e); + } catch (IllegalAccessException e) { + throw new RuntimeException("fail to access method toStringUtf8 in the class com.google.protobuf.ByteString with error msg:", e); + } + + } + + @Override + public List newInstanceWithJava(boolean isDeclaration, boolean doesIncludeName, String variableName, int indent) { + List codes = new ArrayList<>(); + String var = CodeJavaGenerator.oneLineInstance(isDeclaration, doesIncludeName, getType().getFullTypeName(), variableName, null); + CodeJavaGenerator.addCode(codes, var, indent); + if (getValue() == null) return codes; + CodeJavaGenerator.addCode(codes, "{", indent); + CodeJavaGenerator.addCode(codes, + CodeJavaGenerator.oneLineInstance(false, true, getType().getFullTypeName(), variableName, getType().getFullTypeName()+"."+PROTOBUF3_BYTE_STRING_METHOD_COPY_FROM_METHOD+"(\""+ getValue() + "\")"), indent + 1); + CodeJavaGenerator.addCode(codes, "}", indent); + return codes; + } + + @Override + public List newAssertionWithJava(int indent, String responseVarName, int maxAssertionForDataInCollection) { + StringBuilder sb = new StringBuilder(); + sb.append(CodeJavaGenerator.getIndent(indent)); + if (getValue() == null) + sb.append(CodeJavaGenerator.junitAssertNull(responseVarName)); + else{ + sb.append(CodeJavaGenerator.junitAssertEquals("\""+getValueAsJavaString()+"\"", responseVarName+"."+PROTOBUF3_BYTE_STRING_METHOD_TO_STRING_UTF8+"()")); + } + + return Collections.singletonList(sb.toString()); + } + + @Override + public String getValueAsJavaString() { + return getValue(); + } +} diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/types/Protobuf3ByteStringType.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/types/Protobuf3ByteStringType.java new file mode 100644 index 0000000000..a15b73a4e9 --- /dev/null +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/types/Protobuf3ByteStringType.java @@ -0,0 +1,33 @@ +package org.evomaster.client.java.controller.problem.rpc.schema.types; + +import org.evomaster.client.java.controller.api.dto.problem.rpc.RPCSupportedDataType; +import org.evomaster.client.java.controller.api.dto.problem.rpc.TypeDto; + + +public class Protobuf3ByteStringType extends TypeSchema { + public final static String PROTOBUF3_BYTE_STRING_SIMPLE_TYPE_NAME = "ByteString"; + public final static String PROTOBUF3_BYTE_STRING_TYPE_NAME = "com.google.protobuf.ByteString"; + + private static Protobuf3ByteStringType instance; + public static Protobuf3ByteStringType getInstance(JavaDtoSpec spec, Class clazz){ + if (instance == null) + instance = new Protobuf3ByteStringType(spec, clazz); + return instance; + } + + public Protobuf3ByteStringType(JavaDtoSpec spec, Class clazz) { + super(PROTOBUF3_BYTE_STRING_SIMPLE_TYPE_NAME, PROTOBUF3_BYTE_STRING_TYPE_NAME, clazz, spec); + } + + @Override + public TypeDto getDto() { + TypeDto dto = super.getDto(); + dto.type = RPCSupportedDataType.BYTEBUFFER; + return dto; + } + + @Override + public Protobuf3ByteStringType copy() { + return new Protobuf3ByteStringType(spec, getClazz()); + } +} From 92e9a90342d4a1f67fac65c68a245aca05056823 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 10 Jul 2023 17:57:54 +0200 Subject: [PATCH 128/345] add unit test --- ...rationServicegRPCEndpointsBuilderTest.java | 111 +++++ .../src/test/proto/registration_service.proto | 418 ++++++++++++++++++ 2 files changed, 529 insertions(+) create mode 100644 client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/RegistrationServicegRPCEndpointsBuilderTest.java create mode 100644 client-java/controller/src/test/proto/registration_service.proto diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/RegistrationServicegRPCEndpointsBuilderTest.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/RegistrationServicegRPCEndpointsBuilderTest.java new file mode 100644 index 0000000000..f478fbf0a4 --- /dev/null +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/RegistrationServicegRPCEndpointsBuilderTest.java @@ -0,0 +1,111 @@ +package org.evomaster.client.java.controller.problem.rpc.grpc; + +import com.google.protobuf.ByteString; +import org.evomaster.client.java.controller.problem.rpc.RPCEndpointsBuilderTestBase; +import org.evomaster.client.java.controller.problem.rpc.schema.EndpointSchema; +import org.evomaster.client.java.controller.problem.rpc.schema.params.NamedTypedValue; +import org.evomaster.client.java.controller.problem.rpc.schema.params.ObjectParam; +import org.evomaster.client.java.controller.problem.rpc.schema.types.ObjectType; +import org.evomaster.client.java.controller.problem.rpc.schema.types.Protobuf3ByteStringType; +import org.junit.Test; +import org.signal.registration.rpc.GetRegistrationSessionMetadataRequest; +import org.signal.registration.rpc.RegistrationServiceGrpc; +import org.signal.registration.rpc.RegistrationSessionMetadata; +import org.signal.registration.rpc.RegistrationSessionMetadataOrBuilder; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class RegistrationServicegRPCEndpointsBuilderTest extends RPCEndpointsBuilderTestBase { + @Override + public String getInterfaceName() { + return RegistrationServiceGrpc.RegistrationServiceBlockingStub.class.getName(); + } + + @Override + public int expectedNumberOfEndpoints() { + return 5; + } + + + @Test + public void testAllFunctions(){ + List expectedFunctions = Arrays.asList( + "createSession", + "getSessionMetadata", + "sendVerificationCode", + "checkVerificationCode", + "legacyCheckVerificationCode" + ); + + for (String fun : expectedFunctions){ + EndpointSchema endpoint = getOneEndpoint(fun); + assertNotNull(endpoint); + } + + } + + @Test + public void testGetSessionMetadata() throws ClassNotFoundException { + EndpointSchema endpoint = getOneEndpoint("getSessionMetadata"); + + List params = endpoint.getRequestParams(); + assertEquals(1, params.size()); + NamedTypedValue inputParam = params.get(0); + assertTrue(inputParam instanceof ObjectParam); + ObjectType paramType = ((ObjectParam) inputParam).getType(); + assertEquals(1, paramType.getFields().size()); + assertEquals("sessionId", paramType.getFields().get(0).getName()); + assertEquals(Protobuf3ByteStringType.PROTOBUF3_BYTE_STRING_TYPE_NAME, paramType.getFields().get(0).getType().getFullTypeName()); + + ByteString sb = ByteString.copyFromUtf8("foo"); + GetRegistrationSessionMetadataRequest obj = GetRegistrationSessionMetadataRequest.newBuilder().setSessionId(sb).build(); + inputParam.setValueBasedOnInstance(obj); + Object inputInstance = inputParam.newInstance(); + assertTrue(inputInstance instanceof GetRegistrationSessionMetadataRequest); + assertEquals("foo", ((GetRegistrationSessionMetadataRequest) inputInstance).getSessionId().toStringUtf8()); + + List param1InstanceJava = inputParam.newInstanceWithJava(true, true, "request", 0); + + String[] expectedContents = ("org.signal.registration.rpc.GetRegistrationSessionMetadataRequest request = null;\n" + + "{\n" + + " org.signal.registration.rpc.GetRegistrationSessionMetadataRequest.Builder requestbuilder = org.signal.registration.rpc.GetRegistrationSessionMetadataRequest.newBuilder();\n" + + " com.google.protobuf.ByteString request_sessionId = null;\n" + + " {\n" + + " request_sessionId = com.google.protobuf.ByteString.copyFromUtf8(\"foo\");\n" + + " }\n" + + " requestbuilder.setSessionId(request_sessionId);\n" + + " request = requestbuilder.build();\n" + + "}").split("\n"); + + assertEquals(expectedContents.length, param1InstanceJava.size()); + + for (int i = 0; i < param1InstanceJava.size(); i++) + assertEquals(expectedContents[i], param1InstanceJava.get(i)); + + List param1Assertions = inputParam.newAssertionWithJava("request", -1); + + String[] expectedAssertions = ("assertEquals(\"foo\", request.getSessionId().toStringUtf8());").split("\n"); + + assertEquals(expectedAssertions.length, param1Assertions.size()); + + for (int i = 0; i < param1Assertions.size(); i++) + assertEquals(expectedAssertions[i], param1Assertions.get(i)); + + org.signal.registration.rpc.GetRegistrationSessionMetadataRequest request = null; + { + org.signal.registration.rpc.GetRegistrationSessionMetadataRequest.Builder requestbuilder = org.signal.registration.rpc.GetRegistrationSessionMetadataRequest.newBuilder(); + com.google.protobuf.ByteString request_sessionId = null; + { + request_sessionId = com.google.protobuf.ByteString.copyFromUtf8("foo"); + } + requestbuilder.setSessionId(request_sessionId); + request = requestbuilder.build(); + } + assertEquals("foo", request.getSessionId().toStringUtf8()); + + } +} diff --git a/client-java/controller/src/test/proto/registration_service.proto b/client-java/controller/src/test/proto/registration_service.proto new file mode 100644 index 0000000000..ea3f33ca26 --- /dev/null +++ b/client-java/controller/src/test/proto/registration_service.proto @@ -0,0 +1,418 @@ +syntax = "proto3"; + +option java_multiple_files = true; + +package org.signal.registration.rpc; + +service RegistrationService { + /** + * Create a new registration session for a given destination phone number. + */ + rpc create_session (CreateRegistrationSessionRequest) returns (CreateRegistrationSessionResponse) {} + + /** + * Retrieves session metadata for a given session. + */ + rpc get_session_metadata (GetRegistrationSessionMetadataRequest) returns (GetRegistrationSessionMetadataResponse) {} + + /** + * Sends a verification code to a destination phone number within the context + * of a previously-created registration session. + */ + rpc send_verification_code (SendVerificationCodeRequest) returns (SendVerificationCodeResponse) {} + + /** + * Checks a client-provided verification code for a given registration + * session. + */ + rpc check_verification_code (CheckVerificationCodeRequest) returns (CheckVerificationCodeResponse) {} + + rpc legacy_check_verification_code (CheckVerificationCodeRequest) returns (LegacyCheckVerificationCodeResponse) {} +} + +message CreateRegistrationSessionRequest { + /** + * The phone number for which to create a new registration session. + */ + uint64 e164 = 1; + + /** + * Indicates whether an account already exists with the given e164 (i.e. this + * session represents a "re-registration" attempt). + */ + bool account_exists_with_e164 = 2; +} + +message CreateRegistrationSessionResponse { + oneof response { + /** + * Metadata for the newly-created session. + */ + RegistrationSessionMetadata session_metadata = 1; + + /** + * A response explaining why a session could not be created as requested. + */ + CreateRegistrationSessionError error = 2; + } +} + +message RegistrationSessionMetadata { + /** + * An opaque sequence of bytes that uniquely identifies the registration + * session associated with this registration attempt. + */ + bytes session_id = 1; + + /** + * Indicates whether a valid verification code has been submitted in the scope + * of this session. + */ + bool verified = 2; + + /** + * The phone number associated with this registration session. + */ + uint64 e164 = 3; + + /** + * Indicates whether the caller may request delivery of a verification code + * via SMS now or at some time in the future. If true, the time a caller must + * wait before requesting a verification code via SMS is given in the + * `next_sms_seconds` field. + */ + bool may_request_sms = 4; + + /** + * The duration, in seconds, after which a caller will next be allowed to + * request delivery of a verification code via SMS if `may_request_sms` is + * true. If zero, a caller may request a verification code via SMS + * immediately. If `may_request_sms` is false, this field has no meaning. + */ + uint64 next_sms_seconds = 5; + + /** + * Indicates whether the caller may request delivery of a verification code + * via a phone call now or at some time in the future. If true, the time a + * caller must wait before requesting a verification code via SMS is given in + * the `next_voice_call_seconds` field. If false, simply waiting will not + * allow the caller to request a phone call and the caller may need to + * perform some other action (like attempting verification code delivery via + * SMS) before requesting a voice call. + */ + bool may_request_voice_call = 6; + + /** + * The duration, in seconds, after which a caller will next be allowed to + * request delivery of a verification code via a phone call if + * `may_request_voice_call` is true. If zero, a caller may request a + * verification code via a phone call immediately. If `may_request_voice_call` + * is false, this field has no meaning. + */ + uint64 next_voice_call_seconds = 7; + + /** + * Indicates whether the caller may submit new verification codes now or at + * some time in the future. If true, the time a caller must wait before + * submitting a verification code is given in the `next_code_check_seconds` + * field. If false, simply waiting will not allow the caller to submit a + * verification code and the caller may need to perform some other action + * (like requesting delivery of a verification code) before checking a + * verification code. + */ + bool may_check_code = 8; + + /** + * The duration, in seconds, after which a caller will next be allowed to + * submit a verification code if `may_check_code` is true. If zero, a caller + * may submit a verification code immediately. If `may_check_code` is false, + * this field has no meaning. + */ + uint64 next_code_check_seconds = 9; + + /** + * The duration, in seconds, after which this session will expire. + */ + uint64 expiration_seconds = 10; +} + +message CreateRegistrationSessionError { + /** + * The type of error that prevented a session from being created. + */ + CreateRegistrationSessionErrorType error_type = 1; + + /** + * Indicates that this error may succeed if retried without modification after + * a delay indicated by `retry_after_seconds`. If false, callers should not + * retry the request without modification. + */ + bool may_retry = 2; + + /** + * If this error may be retried,, indicates the duration in seconds from the + * present after which the request may be retried without modification. This + * value has no meaning otherwise. + */ + uint64 retry_after_seconds = 3; +} + +enum CreateRegistrationSessionErrorType { + CREATE_REGISTRATION_SESSION_ERROR_TYPE_UNSPECIFIED = 0; + + /** + * Indicates that a session could not be created because too many requests to + * create a session for the given phone number have been received in some + * window of time. Callers should wait and try again later. + */ + CREATE_REGISTRATION_SESSION_ERROR_TYPE_RATE_LIMITED = 1; + + /** + * Indicates that the provided phone number could not be parsed. + */ + CREATE_REGISTRATION_SESSION_ERROR_TYPE_ILLEGAL_PHONE_NUMBER = 2; +} + +message GetRegistrationSessionMetadataRequest { + /** + * The ID of the session for which to retrieve metadata. + */ + bytes session_id = 1; +} + +message GetRegistrationSessionMetadataResponse { + oneof response { + RegistrationSessionMetadata session_metadata = 1; + GetRegistrationSessionMetadataError error = 2; + } +} + +message GetRegistrationSessionMetadataError { + GetRegistrationSessionMetadataErrorType error_type = 1; +} + +enum GetRegistrationSessionMetadataErrorType { + GET_REGISTRATION_SESSION_METADATA_ERROR_TYPE_UNSPECIFIED = 0; + + /** + * No session was found with the given identifier. + */ + GET_REGISTRATION_SESSION_METADATA_ERROR_TYPE_NOT_FOUND = 1; +} + +message SendVerificationCodeRequest { + + reserved 1; + + /** + * The message transport to use to send a verification code to the destination + * phone number. + */ + MessageTransport transport = 2; + + /** + * A prioritized list of languages accepted by the destination; should be + * provided in the same format as the value of an HTTP Accept-Language header. + */ + string accept_language = 3; + + /** + * The type of client requesting a verification code. + */ + ClientType client_type = 4; + + /** + * The ID of a session within which to send (or re-send) a verification code. + */ + bytes session_id = 5; + + /** + * If provided, always attempt to use the specified sender to send + * this message. + */ + string sender_name = 6; +} + +enum MessageTransport { + MESSAGE_TRANSPORT_UNSPECIFIED = 0; + MESSAGE_TRANSPORT_SMS = 1; + MESSAGE_TRANSPORT_VOICE = 2; +} + +enum ClientType { + CLIENT_TYPE_UNSPECIFIED = 0; + CLIENT_TYPE_IOS = 1; + CLIENT_TYPE_ANDROID_WITH_FCM = 2; + CLIENT_TYPE_ANDROID_WITHOUT_FCM = 3; +} + +message SendVerificationCodeResponse { + reserved 1; + + /** + * Metadata for the named session. May be absent if the session could not be + * found or has expired. + */ + RegistrationSessionMetadata session_metadata = 2; + + /** + * If a code could not be sent, explains the underlying error. Will be absent + * if a code was sent successfully. Note that both an error and session + * metadata may be present in the same response because the session metadata + * may include information helpful for resolving the underlying error (i.e. + * "next attempt" times). + */ + SendVerificationCodeError error = 3; +} + +message SendVerificationCodeError { + /** + * The type of error that prevented a verification code from being sent. + */ + SendVerificationCodeErrorType error_type = 1; + + /** + * Indicates that this error may succeed if retried without modification after + * a delay indicated by `retry_after_seconds`. If false, callers should not + * retry the request without modification. + */ + bool may_retry = 2; + + /** + * If this error may be retried,, indicates the duration in seconds from the + * present after which the request may be retried without modification. This + * value has no meaning otherwise. + */ + uint64 retry_after_seconds = 3; +} + +enum SendVerificationCodeErrorType { + SEND_VERIFICATION_CODE_ERROR_TYPE_UNSPECIFIED = 0; + + /** + * The sender received and understood the request to send a verification code, + * but declined to do so (i.e. due to rate limits or suspected fraud). + */ + SEND_VERIFICATION_CODE_ERROR_TYPE_SENDER_REJECTED = 1; + + /** + * The sender could not process or would not accept some part of a request + * (e.g. a valid phone number that cannot receive SMS messages). + */ + SEND_VERIFICATION_CODE_ERROR_TYPE_SENDER_ILLEGAL_ARGUMENT = 2; + + /** + * A verification could could not be sent via the requested channel due to + * timing/rate restrictions. The response object containing this error should + * include session metadata that indicates when the next attempt is allowed. + */ + SEND_VERIFICATION_CODE_ERROR_TYPE_RATE_LIMITED = 3; + + /** + * No session was found with the given ID. + */ + SEND_VERIFICATION_CODE_ERROR_TYPE_SESSION_NOT_FOUND = 4; + + /** + * A new verification could could not be sent because the session has already + * been verified. + */ + SEND_VERIFICATION_CODE_ERROR_TYPE_SESSION_ALREADY_VERIFIED = 5; +} + +message CheckVerificationCodeRequest { + /** + * The session ID returned when sending a verification code. + */ + bytes session_id = 1; + + /** + * The client-provided verification code. + */ + string verification_code = 2; +} + +message CheckVerificationCodeResponse { + reserved 1; + + /** + * Metadata for the named session. May be absent if the session could not be + * found or has expired. + */ + RegistrationSessionMetadata session_metadata = 2; + + /** + * If a code could not be checked, explains the underlying error. Will be + * absent if no error occurred. Note that both an error and session + * metadata may be present in the same response because the session metadata + * may include information helpful for resolving the underlying error (i.e. + * "next attempt" times). + */ + CheckVerificationCodeError error = 3; +} + +message LegacyCheckVerificationCodeResponse { + /** + * Indicates whether the verification code given in the request that produced + * this response was correct. + */ + bool verified = 1; + + /** + * If a code could not be checked, explains the underlying error. Will be + * absent if no error occurred. + */ + CheckVerificationCodeError error = 2; +} + +message CheckVerificationCodeError { + /** + * The type of error that prevented a verification code from being checked. + */ + CheckVerificationCodeErrorType error_type = 1; + + /** + * Indicates that this error may succeed if retried without modification after + * a delay indicated by `retry_after_seconds`. If false, callers should not + * retry the request without modification. + */ + bool may_retry = 2; + + /** + * If this error may be retried,, indicates the duration in seconds from the + * present after which the request may be retried without modification. This + * value has no meaning otherwise. + */ + uint64 retry_after_seconds = 3; +} + +enum CheckVerificationCodeErrorType { + CHECK_VERIFICATION_CODE_ERROR_TYPE_UNSPECIFIED = 0; + + /** + * The caller has attempted to submit a verification code even though no + * verification codes have been sent within the scope of this session. The + * caller must issue a "send code" request before trying again. + */ + CHECK_VERIFICATION_CODE_ERROR_TYPE_NO_CODE_SENT = 1; + + /** + * The caller has made too many guesses within some period of time. Callers + * should wait for the duration prescribed in the session metadata object + * elsewhere in the response before trying again. + */ + CHECK_VERIFICATION_CODE_ERROR_TYPE_RATE_LIMITED = 2; + + /** + * The session identified in this request could not be found (possibly due to + * session expiration). + */ + CHECK_VERIFICATION_CODE_ERROR_TYPE_SESSION_NOT_FOUND = 3; + + /** + * The session identified in this request is still active, but the most + * recently-sent code has expired. Callers should request a new code, then + * try again. + */ + CHECK_VERIFICATION_CODE_ERROR_TYPE_ATTEMPT_EXPIRED = 4; +} From decde0b7de40971ef3a2a7cb38c9babe522e8472 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 10 Jul 2023 18:07:15 +0200 Subject: [PATCH 129/345] fix failing tests --- ...DataTypesTestgRPCEndpointsBuilderTest.java | 189 +++++++++++++++++- 1 file changed, 185 insertions(+), 4 deletions(-) diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/DataTypesTestgRPCEndpointsBuilderTest.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/DataTypesTestgRPCEndpointsBuilderTest.java index 1d1e4bd7b0..056fd3d45d 100644 --- a/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/DataTypesTestgRPCEndpointsBuilderTest.java +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/problem/rpc/grpc/DataTypesTestgRPCEndpointsBuilderTest.java @@ -1,5 +1,6 @@ package org.evomaster.client.java.controller.problem.rpc.grpc; +import com.google.protobuf.ByteString; import io.grpc.examples.evotests.datatypes.*; import org.evomaster.client.java.controller.api.dto.problem.rpc.ParamDto; import org.evomaster.client.java.controller.problem.rpc.RPCEndpointsBuilderTestBase; @@ -7,6 +8,7 @@ import org.evomaster.client.java.controller.problem.rpc.schema.params.NamedTypedValue; import org.evomaster.client.java.controller.problem.rpc.schema.params.ObjectParam; import org.evomaster.client.java.controller.problem.rpc.schema.types.ObjectType; +import org.evomaster.client.java.controller.problem.rpc.schema.types.Protobuf3ByteStringType; import org.junit.Test; import java.util.Arrays; @@ -98,7 +100,7 @@ public void testSimpleObj() throws ClassNotFoundException { NamedTypedValue param1 = endpoint.getRequestParams().get(0); assertTrue(param1 instanceof ObjectParam); ObjectType param1Type = (ObjectType)param1.getType(); - assertEquals(7, param1Type.getFields().size()); + assertEquals(8, param1Type.getFields().size()); assertEquals("name", param1Type.getFields().get(0).getName()); assertEquals(String.class.getName(), param1Type.getFields().get(0).getType().getFullTypeName()); assertEquals("doublevalue", param1Type.getFields().get(1).getName()); @@ -111,9 +113,10 @@ public void testSimpleObj() throws ClassNotFoundException { assertEquals(float.class.getName(), param1Type.getFields().get(4).getType().getFullTypeName()); assertEquals("boolvalue", param1Type.getFields().get(5).getName()); assertEquals(boolean.class.getName(), param1Type.getFields().get(5).getType().getFullTypeName()); - // bytes not support yet - assertEquals("enumvalue", param1Type.getFields().get(6).getName()); - assertEquals(SimpleEnum.class.getName(), param1Type.getFields().get(6).getType().getFullTypeName()); + assertEquals("bytesvalue", param1Type.getFields().get(6).getName()); + assertEquals(Protobuf3ByteStringType.PROTOBUF3_BYTE_STRING_TYPE_NAME, param1Type.getFields().get(6).getType().getFullTypeName()); + assertEquals("enumvalue", param1Type.getFields().get(7).getName()); + assertEquals(SimpleEnum.class.getName(), param1Type.getFields().get(7).getType().getFullTypeName()); SimpleObj objInstance = SimpleObj @@ -124,6 +127,7 @@ public void testSimpleObj() throws ClassNotFoundException { .setInt64Value(42L) .setFloatvalue(0.42f) .setBoolvalue(false) + .setBytesvalue(ByteString.copyFromUtf8("helloworld")) .setEnumvalue(SimpleEnum.ONE) .build(); @@ -144,6 +148,11 @@ public void testSimpleObj() throws ClassNotFoundException { " arg0builder.setInt64Value(42L);\n" + " arg0builder.setFloatvalue(0.42f);\n" + " arg0builder.setBoolvalue(false);\n" + + " com.google.protobuf.ByteString arg0_bytesvalue = null;\n" + + " {\n" + + " arg0_bytesvalue = com.google.protobuf.ByteString.copyFromUtf8(\"helloworld\");\n" + + " }\n" + + " arg0builder.setBytesvalue(arg0_bytesvalue);\n" + " arg0builder.setEnumvalue(((io.grpc.examples.evotests.datatypes.SimpleEnum)(io.grpc.examples.evotests.datatypes.SimpleEnum.ONE)));\n" + " arg0 = arg0builder.build();\n" + "}").split("\n"); @@ -176,6 +185,7 @@ public void testNestedObj() throws ClassNotFoundException { .setInt64Value(42L) .setFloatvalue(0.42f) .setBoolvalue(false) + .setBytesvalue(ByteString.copyFromUtf8("helloworld")) .setEnumvalue(SimpleEnum.ONE) .build(); @@ -206,6 +216,11 @@ public void testNestedObj() throws ClassNotFoundException { " res_objvaluebuilder.setInt64Value(42L);\n" + " res_objvaluebuilder.setFloatvalue(0.42f);\n" + " res_objvaluebuilder.setBoolvalue(false);\n" + + " com.google.protobuf.ByteString res_objvalue_bytesvalue = null;\n" + + " {\n" + + " res_objvalue_bytesvalue = com.google.protobuf.ByteString.copyFromUtf8(\"helloworld\");\n" + + " }\n" + + " res_objvaluebuilder.setBytesvalue(res_objvalue_bytesvalue);\n" + " res_objvaluebuilder.setEnumvalue(((io.grpc.examples.evotests.datatypes.SimpleEnum)(io.grpc.examples.evotests.datatypes.SimpleEnum.ONE)));\n" + " res_objvalue = res_objvaluebuilder.build();\n" + " }\n" + @@ -217,6 +232,31 @@ public void testNestedObj() throws ClassNotFoundException { for (int i = 0; i < param1InstanceJava.size(); i++) assertEquals(expectedContents[i], param1InstanceJava.get(i)); + + io.grpc.examples.evotests.datatypes.NestedObj res = null; + { + io.grpc.examples.evotests.datatypes.NestedObj.Builder resbuilder = io.grpc.examples.evotests.datatypes.NestedObj.newBuilder(); + resbuilder.setName("bar"); + io.grpc.examples.evotests.datatypes.SimpleObj res_objvalue = null; + { + io.grpc.examples.evotests.datatypes.SimpleObj.Builder res_objvaluebuilder = io.grpc.examples.evotests.datatypes.SimpleObj.newBuilder(); + res_objvaluebuilder.setName("foo"); + res_objvaluebuilder.setDoublevalue(0.42); + res_objvaluebuilder.setInt32Value(42); + res_objvaluebuilder.setInt64Value(42L); + res_objvaluebuilder.setFloatvalue(0.42f); + res_objvaluebuilder.setBoolvalue(false); + com.google.protobuf.ByteString res_objvalue_bytesvalue = null; + { + res_objvalue_bytesvalue = com.google.protobuf.ByteString.copyFromUtf8("helloworld"); + } + res_objvaluebuilder.setBytesvalue(res_objvalue_bytesvalue); + res_objvaluebuilder.setEnumvalue(((io.grpc.examples.evotests.datatypes.SimpleEnum)(io.grpc.examples.evotests.datatypes.SimpleEnum.ONE))); + res_objvalue = res_objvaluebuilder.build(); + } + resbuilder.setObjvalue(res_objvalue); + res = resbuilder.build(); + } } @@ -243,6 +283,7 @@ public void testMapObj() throws ClassNotFoundException { .setInt64Value(42L) .setFloatvalue(0.42f) .setBoolvalue(false) + .setBytesvalue(ByteString.copyFromUtf8("helloworld")) .setEnumvalue(SimpleEnum.ONE) .build(); @@ -254,6 +295,7 @@ public void testMapObj() throws ClassNotFoundException { .setInt64Value(42L) .setFloatvalue(0.42f) .setBoolvalue(false) + .setBytesvalue(ByteString.copyFromUtf8("helloworld")) .setEnumvalue(SimpleEnum.TWO) .build(); @@ -291,6 +333,11 @@ public void testMapObj() throws ClassNotFoundException { " res_intkeymapvalue_value_0builder.setInt64Value(42L);\n" + " res_intkeymapvalue_value_0builder.setFloatvalue(0.42f);\n" + " res_intkeymapvalue_value_0builder.setBoolvalue(false);\n" + + " com.google.protobuf.ByteString res_intkeymapvalue_value_0_bytesvalue = null;\n" + + " {\n" + + " res_intkeymapvalue_value_0_bytesvalue = com.google.protobuf.ByteString.copyFromUtf8(\"helloworld\");\n" + + " }\n" + + " res_intkeymapvalue_value_0builder.setBytesvalue(res_intkeymapvalue_value_0_bytesvalue);\n" + " res_intkeymapvalue_value_0builder.setEnumvalue(((io.grpc.examples.evotests.datatypes.SimpleEnum)(io.grpc.examples.evotests.datatypes.SimpleEnum.ONE)));\n" + " res_intkeymapvalue_value_0 = res_intkeymapvalue_value_0builder.build();\n" + " }\n" + @@ -310,6 +357,11 @@ public void testMapObj() throws ClassNotFoundException { " res_stringkeymapvalue_value_0builder.setInt64Value(42L);\n" + " res_stringkeymapvalue_value_0builder.setFloatvalue(0.42f);\n" + " res_stringkeymapvalue_value_0builder.setBoolvalue(false);\n" + + " com.google.protobuf.ByteString res_stringkeymapvalue_value_0_bytesvalue = null;\n" + + " {\n" + + " res_stringkeymapvalue_value_0_bytesvalue = com.google.protobuf.ByteString.copyFromUtf8(\"helloworld\");\n" + + " }\n" + + " res_stringkeymapvalue_value_0builder.setBytesvalue(res_stringkeymapvalue_value_0_bytesvalue);\n" + " res_stringkeymapvalue_value_0builder.setEnumvalue(((io.grpc.examples.evotests.datatypes.SimpleEnum)(io.grpc.examples.evotests.datatypes.SimpleEnum.TWO)));\n" + " res_stringkeymapvalue_value_0 = res_stringkeymapvalue_value_0builder.build();\n" + " }\n" + @@ -324,6 +376,60 @@ public void testMapObj() throws ClassNotFoundException { for (int i = 0; i < param1InstanceJava.size(); i++) assertEquals(expectedContents[i], param1InstanceJava.get(i)); + io.grpc.examples.evotests.datatypes.MapObj res = null; + { + io.grpc.examples.evotests.datatypes.MapObj.Builder resbuilder = io.grpc.examples.evotests.datatypes.MapObj.newBuilder(); + resbuilder.setName("bar"); + java.util.Map res_intkeymapvalue = null; + { + res_intkeymapvalue = new java.util.HashMap<>(); + java.lang.Integer res_intkeymapvalue_key_0 = 1; + io.grpc.examples.evotests.datatypes.SimpleObj res_intkeymapvalue_value_0 = null; + { + io.grpc.examples.evotests.datatypes.SimpleObj.Builder res_intkeymapvalue_value_0builder = io.grpc.examples.evotests.datatypes.SimpleObj.newBuilder(); + res_intkeymapvalue_value_0builder.setName("int1Foo"); + res_intkeymapvalue_value_0builder.setDoublevalue(0.42); + res_intkeymapvalue_value_0builder.setInt32Value(42); + res_intkeymapvalue_value_0builder.setInt64Value(42L); + res_intkeymapvalue_value_0builder.setFloatvalue(0.42f); + res_intkeymapvalue_value_0builder.setBoolvalue(false); + com.google.protobuf.ByteString res_intkeymapvalue_value_0_bytesvalue = null; + { + res_intkeymapvalue_value_0_bytesvalue = com.google.protobuf.ByteString.copyFromUtf8("helloworld"); + } + res_intkeymapvalue_value_0builder.setBytesvalue(res_intkeymapvalue_value_0_bytesvalue); + res_intkeymapvalue_value_0builder.setEnumvalue(((io.grpc.examples.evotests.datatypes.SimpleEnum)(io.grpc.examples.evotests.datatypes.SimpleEnum.ONE))); + res_intkeymapvalue_value_0 = res_intkeymapvalue_value_0builder.build(); + } + res_intkeymapvalue.put(res_intkeymapvalue_key_0,res_intkeymapvalue_value_0); + } + resbuilder.putAllIntkeymapvalue(res_intkeymapvalue); + java.util.Map res_stringkeymapvalue = null; + { + res_stringkeymapvalue = new java.util.HashMap<>(); + java.lang.String res_stringkeymapvalue_key_0 = "strbar"; + io.grpc.examples.evotests.datatypes.SimpleObj res_stringkeymapvalue_value_0 = null; + { + io.grpc.examples.evotests.datatypes.SimpleObj.Builder res_stringkeymapvalue_value_0builder = io.grpc.examples.evotests.datatypes.SimpleObj.newBuilder(); + res_stringkeymapvalue_value_0builder.setName("strbarFoo"); + res_stringkeymapvalue_value_0builder.setDoublevalue(0.42); + res_stringkeymapvalue_value_0builder.setInt32Value(42); + res_stringkeymapvalue_value_0builder.setInt64Value(42L); + res_stringkeymapvalue_value_0builder.setFloatvalue(0.42f); + res_stringkeymapvalue_value_0builder.setBoolvalue(false); + com.google.protobuf.ByteString res_stringkeymapvalue_value_0_bytesvalue = null; + { + res_stringkeymapvalue_value_0_bytesvalue = com.google.protobuf.ByteString.copyFromUtf8("helloworld"); + } + res_stringkeymapvalue_value_0builder.setBytesvalue(res_stringkeymapvalue_value_0_bytesvalue); + res_stringkeymapvalue_value_0builder.setEnumvalue(((io.grpc.examples.evotests.datatypes.SimpleEnum)(io.grpc.examples.evotests.datatypes.SimpleEnum.TWO))); + res_stringkeymapvalue_value_0 = res_stringkeymapvalue_value_0builder.build(); + } + res_stringkeymapvalue.put(res_stringkeymapvalue_key_0,res_stringkeymapvalue_value_0); + } + resbuilder.putAllStringkeymapvalue(res_stringkeymapvalue); + res = resbuilder.build(); + } } @@ -351,6 +457,7 @@ public void testListObj() throws ClassNotFoundException { .setInt64Value(42L) .setFloatvalue(0.42f) .setBoolvalue(false) + .setBytesvalue(ByteString.copyFromUtf8("helloworld-foo")) .setEnumvalue(SimpleEnum.ONE) .build(); @@ -362,6 +469,7 @@ public void testListObj() throws ClassNotFoundException { .setInt64Value(42L) .setFloatvalue(0.42f) .setBoolvalue(false) + .setBytesvalue(ByteString.copyFromUtf8("helloworld-bar")) .setEnumvalue(SimpleEnum.TWO) .build(); @@ -420,6 +528,11 @@ public void testListObj() throws ClassNotFoundException { " res_objlistvalue_e_0builder.setInt64Value(42L);\n" + " res_objlistvalue_e_0builder.setFloatvalue(0.42f);\n" + " res_objlistvalue_e_0builder.setBoolvalue(false);\n" + + " com.google.protobuf.ByteString res_objlistvalue_e_0_bytesvalue = null;\n" + + " {\n" + + " res_objlistvalue_e_0_bytesvalue = com.google.protobuf.ByteString.copyFromUtf8(\"helloworld-foo\");\n" + + " }\n" + + " res_objlistvalue_e_0builder.setBytesvalue(res_objlistvalue_e_0_bytesvalue);\n" + " res_objlistvalue_e_0builder.setEnumvalue(((io.grpc.examples.evotests.datatypes.SimpleEnum)(io.grpc.examples.evotests.datatypes.SimpleEnum.ONE)));\n" + " res_objlistvalue_e_0 = res_objlistvalue_e_0builder.build();\n" + " }\n" + @@ -433,6 +546,11 @@ public void testListObj() throws ClassNotFoundException { " res_objlistvalue_e_1builder.setInt64Value(42L);\n" + " res_objlistvalue_e_1builder.setFloatvalue(0.42f);\n" + " res_objlistvalue_e_1builder.setBoolvalue(false);\n" + + " com.google.protobuf.ByteString res_objlistvalue_e_1_bytesvalue = null;\n" + + " {\n" + + " res_objlistvalue_e_1_bytesvalue = com.google.protobuf.ByteString.copyFromUtf8(\"helloworld-bar\");\n" + + " }\n" + + " res_objlistvalue_e_1builder.setBytesvalue(res_objlistvalue_e_1_bytesvalue);\n" + " res_objlistvalue_e_1builder.setEnumvalue(((io.grpc.examples.evotests.datatypes.SimpleEnum)(io.grpc.examples.evotests.datatypes.SimpleEnum.TWO)));\n" + " res_objlistvalue_e_1 = res_objlistvalue_e_1builder.build();\n" + " }\n" + @@ -448,6 +566,69 @@ public void testListObj() throws ClassNotFoundException { assertEquals(expectedContents[i], param1InstanceJava.get(i)); + io.grpc.examples.evotests.datatypes.ListObj res = null; + { + io.grpc.examples.evotests.datatypes.ListObj.Builder resbuilder = io.grpc.examples.evotests.datatypes.ListObj.newBuilder(); + resbuilder.setName("bar"); + java.util.List res_intlistvalue = null; + { + res_intlistvalue = new java.util.ArrayList<>(); + java.lang.Integer res_intlistvalue_e_0 = 42; + res_intlistvalue.add(res_intlistvalue_e_0); + } + resbuilder.addAllIntlistvalue(res_intlistvalue); + java.util.List res_stringlistvalue = null; + { + res_stringlistvalue = new java.util.ArrayList<>(); + java.lang.String res_stringlistvalue_e_0 = "barInList"; + res_stringlistvalue.add(res_stringlistvalue_e_0); + java.lang.String res_stringlistvalue_e_1 = "fooInList"; + res_stringlistvalue.add(res_stringlistvalue_e_1); + } + resbuilder.addAllStringlistvalue(res_stringlistvalue); + java.util.List res_objlistvalue = null; + { + res_objlistvalue = new java.util.ArrayList<>(); + io.grpc.examples.evotests.datatypes.SimpleObj res_objlistvalue_e_0 = null; + { + io.grpc.examples.evotests.datatypes.SimpleObj.Builder res_objlistvalue_e_0builder = io.grpc.examples.evotests.datatypes.SimpleObj.newBuilder(); + res_objlistvalue_e_0builder.setName("int1Foo"); + res_objlistvalue_e_0builder.setDoublevalue(0.42); + res_objlistvalue_e_0builder.setInt32Value(42); + res_objlistvalue_e_0builder.setInt64Value(42L); + res_objlistvalue_e_0builder.setFloatvalue(0.42f); + res_objlistvalue_e_0builder.setBoolvalue(false); + com.google.protobuf.ByteString res_objlistvalue_e_0_bytesvalue = null; + { + res_objlistvalue_e_0_bytesvalue = com.google.protobuf.ByteString.copyFromUtf8("helloworld-foo"); + } + res_objlistvalue_e_0builder.setBytesvalue(res_objlistvalue_e_0_bytesvalue); + res_objlistvalue_e_0builder.setEnumvalue(((io.grpc.examples.evotests.datatypes.SimpleEnum)(io.grpc.examples.evotests.datatypes.SimpleEnum.ONE))); + res_objlistvalue_e_0 = res_objlistvalue_e_0builder.build(); + } + res_objlistvalue.add(res_objlistvalue_e_0); + io.grpc.examples.evotests.datatypes.SimpleObj res_objlistvalue_e_1 = null; + { + io.grpc.examples.evotests.datatypes.SimpleObj.Builder res_objlistvalue_e_1builder = io.grpc.examples.evotests.datatypes.SimpleObj.newBuilder(); + res_objlistvalue_e_1builder.setName("strbarFoo"); + res_objlistvalue_e_1builder.setDoublevalue(0.42); + res_objlistvalue_e_1builder.setInt32Value(42); + res_objlistvalue_e_1builder.setInt64Value(42L); + res_objlistvalue_e_1builder.setFloatvalue(0.42f); + res_objlistvalue_e_1builder.setBoolvalue(false); + com.google.protobuf.ByteString res_objlistvalue_e_1_bytesvalue = null; + { + res_objlistvalue_e_1_bytesvalue = com.google.protobuf.ByteString.copyFromUtf8("helloworld-bar"); + } + res_objlistvalue_e_1builder.setBytesvalue(res_objlistvalue_e_1_bytesvalue); + res_objlistvalue_e_1builder.setEnumvalue(((io.grpc.examples.evotests.datatypes.SimpleEnum)(io.grpc.examples.evotests.datatypes.SimpleEnum.TWO))); + res_objlistvalue_e_1 = res_objlistvalue_e_1builder.build(); + } + res_objlistvalue.add(res_objlistvalue_e_1); + } + resbuilder.addAllObjlistvalue(res_objlistvalue); + res = resbuilder.build(); + } } } From e9709617bdaa91913e8e99134aaf57192ca621f9 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 10 Jul 2023 19:16:04 +0200 Subject: [PATCH 130/345] remove mis import --- .../client/java/controller/problem/rpc/RPCEndpointsBuilder.java | 1 - 1 file changed, 1 deletion(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java index abea9dde97..8aad5d9b71 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java @@ -3,7 +3,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.sun.xml.internal.bind.v2.TODO; import org.evomaster.client.java.controller.api.dto.AuthenticationDto; import org.evomaster.client.java.controller.api.dto.CustomizedRequestValueDto; import org.evomaster.client.java.controller.api.dto.JsonAuthRPCEndpointDto; From af7653c03ef0e96d662b040b170895fe1f2de30c Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Mon, 10 Jul 2023 19:29:33 +0200 Subject: [PATCH 131/345] minor fix --- .../problem/rpc/RPCEndpointsBuilder.java | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java index 8aad5d9b71..495bde22fa 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/RPCEndpointsBuilder.java @@ -206,18 +206,20 @@ public static InterfaceSchema build(String interfaceName, RPCType rpcType, Objec InterfaceSchema schema = new InterfaceSchema(interfaceName, endpoints, getClientClass(client) , rpcType, skippedEndpoints, authEndpoints, endpointsForAuth); for (Method m : interfaze.getDeclaredMethods()) { - if (filterRPCFunctionMethod(m, skipEndpointsByName, skipEndpointsByAnnotation, involveEndpointsByName, involveEndpointsByAnnotation)){ - try{ - EndpointSchema endpointSchema = build(schema, m, rpcType, authenticationDtoList, customizedRequestValueDtos, notNullAnnotations); - endpoints.add(endpointSchema); - }catch (RuntimeException exception){ + if (filterRPCFunctionMethod(m)){ + if (filterRPCFunctionMethodBasedOnSpecified(m, skipEndpointsByName, skipEndpointsByAnnotation, involveEndpointsByName, involveEndpointsByAnnotation)){ + try{ + EndpointSchema endpointSchema = build(schema, m, rpcType, authenticationDtoList, customizedRequestValueDtos, notNullAnnotations); + endpoints.add(endpointSchema); + }catch (RuntimeException exception){ /* TODO might send such log to core in order to better identify problems which is not handled yet */ - SimpleLogger.recordErrorMessage("EM Driver Error: fail to handle the endpoint schema "+m.getName()+" with the error msg:"+exception.getMessage()); + SimpleLogger.recordErrorMessage("EM Driver Error: fail to handle the endpoint schema "+m.getName()+" with the error msg:"+exception.getMessage()); + } + }else { + skippedEndpoints.add(m.getName()); } - } else { - skippedEndpoints.add(m.getName()); } List auths = getAuthEndpointInInterface(authenticationDtoList, interfaceName, m); @@ -337,19 +339,8 @@ private static List getAuthEndpointInInterface(List skipEndpointsByName, List skipEndpointsByAnnotation, - List involveEndpointsByName, List involveEndpointsByAnnotation){ - if (skipEndpointsByName != null && involveEndpointsByName != null) - throw new IllegalArgumentException("Driver Config Error: skipEndpointsByName and involveEndpointsByName should not be specified at same time."); - if (skipEndpointsByAnnotation != null && involveEndpointsByAnnotation != null) - throw new IllegalArgumentException("Driver Config Error: skipEndpointsByAnnotation and involveEndpointsByAnnotation should not be specified at same time."); - - if (skipEndpointsByName != null || skipEndpointsByAnnotation != null) - return !anyMatchByNameAndAnnotation(endpoint, skipEndpointsByName, skipEndpointsByAnnotation); + private static boolean filterRPCFunctionMethod(Method endpoint){ - if (involveEndpointsByName != null || involveEndpointsByAnnotation != null) - return anyMatchByNameAndAnnotation(endpoint, involveEndpointsByName, involveEndpointsByAnnotation); /* filter streaming API @@ -370,6 +361,23 @@ private static boolean filterRPCFunctionMethod(Method endpoint, return Modifier.isPublic(endpoint.getModifiers()); } + private static boolean filterRPCFunctionMethodBasedOnSpecified(Method endpoint, + List skipEndpointsByName, List skipEndpointsByAnnotation, + List involveEndpointsByName, List involveEndpointsByAnnotation){ + if (skipEndpointsByName != null && involveEndpointsByName != null) + throw new IllegalArgumentException("Driver Config Error: skipEndpointsByName and involveEndpointsByName should not be specified at same time."); + if (skipEndpointsByAnnotation != null && involveEndpointsByAnnotation != null) + throw new IllegalArgumentException("Driver Config Error: skipEndpointsByAnnotation and involveEndpointsByAnnotation should not be specified at same time."); + + if (skipEndpointsByName != null || skipEndpointsByAnnotation != null) + return !anyMatchByNameAndAnnotation(endpoint, skipEndpointsByName, skipEndpointsByAnnotation); + + if (involveEndpointsByName != null || involveEndpointsByAnnotation != null) + return anyMatchByNameAndAnnotation(endpoint, involveEndpointsByName, involveEndpointsByAnnotation); + + return true; + } + private static boolean isPotentialStreamingAPI(Method method){ Class returnType = method.getReturnType(); if (returnType.equals(Iterator.class)) From 074b90f19a24fbe6106e8dc1639f2da203828062 Mon Sep 17 00:00:00 2001 From: hghianni Date: Tue, 11 Jul 2023 16:59:28 -0300 Subject: [PATCH 132/345] Adapt to work with External controllers - Converting document's type to OpenApi instead of storing it as Java class. - Extract required info from collection in replacement class as MongoCollection is not serializable --- .../database/execution/MongoFailedQuery.java | 7 +- .../controller/internal/db/MongoHandler.java | 60 ++-------- .../mongo/MongoHeuristicsCalculator.java | 9 +- .../java/controller/mongo/MongoOperation.java | 51 ++++---- .../mongo/selectors/ImplicitSelector.java | 2 +- .../mongo/selectors/NotSelector.java | 2 +- .../controller/mongo/utils/BsonHelper.java | 43 +------ .../mongo/MongoHeuristicCalculatorTest.java | 90 +++++++------- .../mongo/MongoScriptRunnerTest.java | 1 - .../InstrumentationController.java | 4 + .../instrumentation/MongoCollectionInfo.java | 8 +- .../java/instrumentation/MongoInfo.java | 46 ++++++- ...ongoEntityInformationClassReplacement.java | 4 +- .../MongoCollectionClassReplacement.java | 61 +++++++++- .../external/AgentController.java | 14 +++ .../core/mongo/MongoActionGeneBuilder.kt | 113 ------------------ .../org/evomaster/core/mongo/MongoDbAction.kt | 36 ++---- .../core/mongo/MongoInsertBuilder.kt | 2 +- .../enterprise/service/EnterpriseSampler.kt | 2 +- .../core/mongo/MongoActionGeneBuilderTest.kt | 82 ------------- .../evomaster/core/mongo/MongoActionTest.kt | 27 ++--- .../mongo/MongoDbActionTransformerTest.kt | 7 +- .../core/mongo/MongoInsertBuilderTest.kt | 5 +- 23 files changed, 252 insertions(+), 424 deletions(-) delete mode 100644 core/src/main/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilder.kt delete mode 100644 core/src/test/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilderTest.kt diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/MongoFailedQuery.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/MongoFailedQuery.java index be68a8ea3d..340a935219 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/MongoFailedQuery.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/database/execution/MongoFailedQuery.java @@ -13,9 +13,9 @@ public class MongoFailedQuery { /** * The type of the new document. Should map the type of the documents of the collection. */ - private Class documentsType; + private String documentsType; - public MongoFailedQuery(String database, String collection, Class documentsType) { + public MongoFailedQuery(String database, String collection, String documentsType) { this.database = database; this.collection = collection; this.documentsType = documentsType; @@ -24,11 +24,12 @@ public MongoFailedQuery(String database, String collection, Class documentsTy public MongoFailedQuery(){ this.database = ""; this.collection = ""; + this.documentsType = ""; } public String getDatabase() {return database;} public String getCollection() {return collection;} - public Class getDocumentsType() { + public String getDocumentsType() { return documentsType; } } diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/MongoHandler.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/MongoHandler.java index d133d99438..8178a9c6b5 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/MongoHandler.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/MongoHandler.java @@ -7,7 +7,6 @@ import org.evomaster.client.java.instrumentation.MongoCollectionInfo; import org.evomaster.client.java.instrumentation.MongoInfo; -import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -45,9 +44,10 @@ public class MongoHandler { private final List failedQueries; /** - * Info about types of the documents of collections + * Info about types of the documents of the repository extracted from Spring framework. + * Documents of the collection will be mapped to the Repository type */ - private final Map> collectionInfo; + private final Map collectionInfo; public MongoHandler() { distances = new ArrayList<>(); @@ -112,11 +112,11 @@ public MongoExecutionDto getExecutionDto() { } private double computeDistance(MongoInfo info) { - Object collection = info.getCollection(); - Iterable documents = getDocuments(collection); + Iterable documents = info.getDocuments(); boolean collectionIsEmpty = !documents.iterator().hasNext(); - if (collectionIsEmpty) failedQueries.add(new MongoOperation(collection, info.getQuery())); + if (collectionIsEmpty) + failedQueries.add(new MongoOperation(info.getCollectionName(), info.getQuery(), info.getDatabaseName(), info.getDocumentsType())); MongoHeuristicsCalculator calculator = new MongoHeuristicsCalculator(); @@ -130,53 +130,19 @@ private double computeDistance(MongoInfo info) { return min; } - private static Iterable getDocuments(Object collection) { - try { - Class collectionClass = collection.getClass().getClassLoader().loadClass("com.mongodb.client.MongoCollection"); - return (Iterable) collectionClass.getMethod("find").invoke(collection); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | - ClassNotFoundException e) { - throw new RuntimeException("Failed to retrieve all documents from a mongo collection", e); - } - } - private MongoFailedQuery extractRelevantInfo(MongoOperation operation) { - Object collection = operation.getCollection(); - - String databaseName; - String collectionName; - Class documentsType; - - try { - Class collectionClass = collection.getClass().getClassLoader().loadClass("com.mongodb.client.MongoCollection"); - Object namespace = collectionClass.getMethod("getNamespace").invoke(collection); - databaseName = (String) namespace.getClass().getMethod("getDatabaseName").invoke(namespace); - collectionName = (String) namespace.getClass().getMethod("getCollectionName").invoke(namespace); - } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException | - NoSuchMethodException e) { - throw new RuntimeException("Failed to retrieve collection name or database name", e); - } - - if (collectionTypeIsRegistered(collectionName)) { - documentsType = collectionInfo.get(collectionName); + String documentsType; + if (collectionTypeIsRegistered(operation.getCollectionName())) { + // We have to which class the documents of the collection will be mapped to + documentsType = collectionInfo.get(operation.getCollectionName()); } else { - documentsType = extractDocumentsType(collection); + // Just using the documents type provided by the MongoCollection method + documentsType = operation.getDocumentsType(); } - - return new MongoFailedQuery(databaseName, collectionName, documentsType); + return new MongoFailedQuery(operation.getDatabaseName(), operation.getCollectionName(), documentsType); } private boolean collectionTypeIsRegistered(String collectionName) { return collectionInfo.containsKey(collectionName); } - - private static Class extractDocumentsType(Object collection) { - try { - Class collectionClass = collection.getClass().getClassLoader().loadClass("com.mongodb.client.MongoCollection"); - return (Class) collectionClass.getMethod("getDocumentClass").invoke(collection); - } catch (NoSuchMethodException | ClassNotFoundException | InvocationTargetException | - IllegalAccessException e) { - throw new RuntimeException("Failed to retrieve document's type from collection", e); - } - } } diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoHeuristicsCalculator.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoHeuristicsCalculator.java index 18c0ede42f..cbfbf38960 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoHeuristicsCalculator.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoHeuristicsCalculator.java @@ -28,8 +28,7 @@ public double computeExpression(Object query, Object doc) { } private QueryOperation getOperation(Object query) { - Object queryDocument = convertToQueryDocument(query); - return new QueryParser().parse(queryDocument); + return new QueryParser().parse(query); } private double calculateDistance(QueryOperation operation, Object doc) { @@ -199,7 +198,7 @@ private double calculateDistanceForElemMatch(ElemMatchOperation operation, Objec List val = (List) actualValue; return val.stream() .mapToDouble(elem -> { - Object newDoc = newDocument(); + Object newDoc = newDocument(doc); appendToDocument(newDoc, operation.getFieldName(), elem); return calculateDistance(operation.getCondition(), newDoc); }) @@ -383,6 +382,10 @@ private double compareValues(Object val1, Object val2) { return (double) DistanceHelper.getLeftAlignmentDistance((String) val1, (String) val2); } + if (val1 instanceof Boolean && val2 instanceof Boolean) { + return val1 == val2 ? 0d : 1d; + } + if (val1 instanceof List && val2 instanceof List) { // Modify return Double.MAX_VALUE; diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoOperation.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoOperation.java index b9bc4ab4cf..9b9415fa5a 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoOperation.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/MongoOperation.java @@ -2,49 +2,56 @@ public class MongoOperation { /** + * Executed FIND query * Should be an implementation of class {@link MongoOperation#queryClass} */ private final Object query; + + /** + * Name of the collection that the operation was applied to + */ + private final String collectionName; + /** - * Should be an implementation of class {@link MongoOperation#collectionClass} + * Name of the database that the operation was applied to */ - private final Object collection; + private final String databaseName; - private final String collectionClass = "com.mongodb.client.MongoCollection"; + /** + * Type of the documents of the collection + */ + private final String documentsType; private final String queryClass = "org.bson.conversions.Bson"; - public MongoOperation(Object collection, Object query) { - if (!isImplementationOfMongoCollection(collection)) - throw new java.lang.IllegalArgumentException("collection must be of type " + collectionClass); - if (!isImplementationOfBson(query)) - throw new java.lang.IllegalArgumentException("query must be of type " + queryClass); - this.collection = collection; + public MongoOperation(String collectionName, Object query, String databaseName, String documentsType) { + if (!isImplementationOfBson(query)) { + throw new IllegalArgumentException("query must be of type " + queryClass); + } + this.collectionName = collectionName; + this.databaseName = databaseName; + this.documentsType = documentsType; this.query = query; } - public Object getCollection() { - return collection; + public String getCollectionName() { + return collectionName; } - public Object getQuery() { - return query; + public String getDatabaseName() { + return databaseName; } - private boolean isImplementationOfMongoCollection(Object collection) { - return implementsInterface(collection, collectionClass); + public Object getQuery() { + return query; } - private boolean isImplementationOfBson(Object query) { - return implementsInterface(query, queryClass); - } + public String getDocumentsType() {return documentsType;} - private boolean implementsInterface(Object obj, String interfaceName) { + private boolean isImplementationOfBson(Object obj) { Class[] interfaces = obj.getClass().getInterfaces(); for (Class intf : interfaces) { - if (intf.getName().equals(interfaceName)) { - return true; - } + if (intf.getName().equals(queryClass)) return true; } return false; } diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/selectors/ImplicitSelector.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/selectors/ImplicitSelector.java index fade591a04..f04f82bdca 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/selectors/ImplicitSelector.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/selectors/ImplicitSelector.java @@ -31,7 +31,7 @@ protected List parseConditions(Object query) { Set fields = keySet(query); ArrayList conditions = new ArrayList<>(); fields.forEach(fieldName -> { - Object newQuery = newDocument(); + Object newQuery = newDocument(query); appendToDocument(newQuery, fieldName, getValue(query, fieldName)); conditions.add(new QueryParser().parse(newQuery)); }); diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/selectors/NotSelector.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/selectors/NotSelector.java index ea5822f482..ad2a79c739 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/selectors/NotSelector.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/selectors/NotSelector.java @@ -14,7 +14,7 @@ protected QueryOperation parseValue(String fieldName, Object value) { if (isDocument(value)) { // This is necessary for query parser to work correctly as the syntax for not is different // The field is at the beginning instead - Object docWithRemovedNot = newDocument(); + Object docWithRemovedNot = newDocument(value); appendToDocument(docWithRemovedNot, fieldName, value); QueryOperation condition = new QueryParser().parse(docWithRemovedNot); return new NotOperation(fieldName, condition); diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/utils/BsonHelper.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/utils/BsonHelper.java index 64c79fa57b..85ae9bb44c 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/utils/BsonHelper.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/mongo/utils/BsonHelper.java @@ -5,9 +5,9 @@ import java.util.Set; public class BsonHelper { - public static Object newDocument() { + public static Object newDocument(Object document) { try { - return documentClass.getConstructor().newInstance(); + return document.getClass().getConstructor().newInstance(); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { throw new RuntimeException(e); @@ -56,26 +56,7 @@ public static Set documentKeys(Object document) { } public static Boolean isDocument(Object value) { - return documentClass.isInstance(value); - } - - public static Object convertToQueryDocument(Object query) { - try { - Method toBsonDocument = query.getClass().getMethod("toBsonDocument"); - Object bsonDocument = toBsonDocument.invoke(query); - - Object documentCodec = documentCodecClass.getDeclaredConstructor().newInstance(); - Method asBsonReader = bsonDocumentClass.getMethod("asBsonReader"); - Method builder = decoderContextClass.getMethod("builder"); - Object builderInstance = builder.invoke(null); - Method build = builderInstance.getClass().getMethod("build"); - - Method decode = documentCodecClass.getMethod("decode", bsonReaderClass, decoderContextClass); - return decode.invoke(documentCodec, asBsonReader.invoke(bsonDocument), build.invoke(builderInstance)); - } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | - InvocationTargetException e) { - throw new RuntimeException(e); - } + return value.getClass().getName().equals("org.bson.Document"); } public static String getType(Object bsonType) { @@ -116,22 +97,4 @@ public static Object getTypeFromAlias(String alias) { throw new RuntimeException(e); } } - - private static final Class documentClass = getClass("org.bson.Document"); - - private static final Class bsonDocumentClass = getClass("org.bson.BsonDocument"); - - private static final Class documentCodecClass = getClass("org.bson.codecs.DocumentCodec"); - - private static final Class decoderContextClass = getClass("org.bson.codecs.DecoderContext"); - - private static final Class bsonReaderClass = getClass("org.bson.BsonReader"); - - private static Class getClass(String className) { - try { - return Class.forName(className); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } - } } diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/mongo/MongoHeuristicCalculatorTest.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/mongo/MongoHeuristicCalculatorTest.java index 12881ba504..0336f0d10e 100644 --- a/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/mongo/MongoHeuristicCalculatorTest.java +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/mongo/MongoHeuristicCalculatorTest.java @@ -4,6 +4,8 @@ import org.bson.BsonDocument; import org.bson.BsonType; import org.bson.Document; +import org.bson.codecs.DecoderContext; +import org.bson.codecs.DocumentCodec; import org.bson.conversions.Bson; import org.evomaster.client.java.controller.mongo.MongoHeuristicsCalculator; import org.junit.jupiter.api.Test; @@ -21,8 +23,8 @@ public void testEquals() { Document doc = new Document().append("age", 10); Bson bsonTrue = Filters.eq("age", 10); Bson bsonFalse = Filters.eq("age", 26); - Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(bsonTrue, doc); - Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(bsonFalse, doc); + Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue), doc); + Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonFalse), doc); assertEquals(0.0, distanceMatch); assertEquals(16.0, distanceNotMatch); } @@ -33,9 +35,9 @@ public void testNotEquals() { Bson bsonTrue1 = Filters.ne("age", 26); Bson bsonTrue2 = Filters.ne("some-field", 26); Bson bsonFalse = Filters.ne("age", 10); - Double distanceMatch1 = new MongoHeuristicsCalculator().computeExpression(bsonTrue1, doc); - Double distanceMatch2 = new MongoHeuristicsCalculator().computeExpression(bsonTrue2, doc); - Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(bsonFalse, doc); + Double distanceMatch1 = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue1), doc); + Double distanceMatch2 = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue2), doc); + Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonFalse), doc); assertEquals(0.0, distanceMatch1); assertEquals(0.0, distanceMatch2); assertEquals(1.0, distanceNotMatch); @@ -46,8 +48,8 @@ public void testGreaterThan() { Document doc = new Document().append("age", 10); Bson bsonTrue = Filters.gt("age", 5); Bson bsonFalse = Filters.gt("age", 13); - Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(bsonTrue, doc); - Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(bsonFalse, doc); + Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue), doc); + Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonFalse), doc); assertEquals(0.0, distanceMatch); assertEquals(4.0, distanceNotMatch); } @@ -57,8 +59,8 @@ public void testGreaterThanEquals() { Document doc = new Document().append("age", 10); Bson bsonTrue = Filters.gte("age", 5); Bson bsonFalse = Filters.gte("age", 13); - Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(bsonTrue, doc); - Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(bsonFalse, doc); + Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue), doc); + Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonFalse), doc); assertEquals(0.0, distanceMatch); assertEquals(3.0, distanceNotMatch); } @@ -68,8 +70,8 @@ public void testLessThan() { Document doc = new Document().append("age", 10); Bson bsonTrue = Filters.lt("age", 11); Bson bsonFalse = Filters.lt("age", 7); - Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(bsonTrue, doc); - Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(bsonFalse, doc); + Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue), doc); + Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonFalse), doc); assertEquals(0.0, distanceMatch); assertEquals(4.0, distanceNotMatch); } @@ -79,8 +81,8 @@ public void testLessThanEquals() { Document doc = new Document().append("age", 10); Bson bsonTrue = Filters.lte("age", 11); Bson bsonFalse = Filters.lte("age", 7); - Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(bsonTrue, doc); - Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(bsonFalse, doc); + Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue), doc); + Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonFalse), doc); assertEquals(0.0, distanceMatch); assertEquals(3.0, distanceNotMatch); } @@ -90,8 +92,8 @@ public void testOr() { Document doc = new Document().append("age", 10); Bson bsonTrue = Filters.or(Filters.gt("age", 9), Filters.lt("age", 20)); Bson bsonFalse = Filters.or(Filters.gt("age", 17), Filters.lt("age", 8)); - Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(bsonTrue, doc); - Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(bsonFalse, doc); + Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue), doc); + Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonFalse), doc); assertEquals(0.0, distanceMatch); assertEquals(3.0, distanceNotMatch); } @@ -101,8 +103,8 @@ public void testAnd() { Document doc = new Document().append("age", 10); Bson bsonTrue = Filters.and(Filters.gt("age", 9), Filters.lt("age", 20)); Bson bsonFalse = Filters.and(Filters.gt("age", 10), Filters.lt("age", 8)); - Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(bsonTrue, doc); - Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(bsonFalse, doc); + Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue), doc); + Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonFalse), doc); assertEquals(0.0, distanceMatch); assertEquals(4.0, distanceNotMatch); } @@ -111,8 +113,8 @@ public void testImplicitAnd() { Document doc = new Document().append("age", 10).append("kg", 50); Bson bsonTrue = BsonDocument.parse("{age: 10, kg: {$gt: 40}}"); Bson bsonFalse = BsonDocument.parse("{age: 9, kg: {$gt: 40}}"); - Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(bsonTrue, doc); - Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(bsonFalse, doc); + Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue), doc); + Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonFalse), doc); assertEquals(0.0, distanceMatch); assertEquals(1.0, distanceNotMatch); } @@ -122,8 +124,8 @@ public void testIn() { Document doc = new Document().append("age", 10); Bson bsonTrue = Filters.in("age", new ArrayList<>(Arrays.asList(1, 10, 8))); Bson bsonFalse = Filters.in("age", new ArrayList<>(Arrays.asList(1, 15))); - Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(bsonTrue, doc); - Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(bsonFalse, doc); + Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue), doc); + Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonFalse), doc); assertEquals(0.0, distanceMatch); assertEquals(5.0, distanceNotMatch); } @@ -133,8 +135,8 @@ public void testNotIn() { Document doc = new Document().append("age", 10); Bson bsonTrue = Filters.nin("age", new ArrayList<>(Arrays.asList(1, 8))); Bson bsonFalse = Filters.nin("age", new ArrayList<>(Arrays.asList(1, 10))); - Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(bsonTrue, doc); - Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(bsonFalse, doc); + Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue), doc); + Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonFalse), doc); assertEquals(0.0, distanceMatch); assertEquals(1.0, distanceNotMatch); } @@ -144,8 +146,8 @@ public void testAll() { Document doc = new Document().append("employees", new ArrayList<>(Arrays.asList(1, 5, 6))); Bson bsonTrue = Filters.all("employees", new ArrayList<>(Arrays.asList(1, 5, 6))); Bson bsonFalse = Filters.all("employees", new ArrayList<>(Arrays.asList(1, 7, 8))); - Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(bsonTrue, doc); - Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(bsonFalse, doc); + Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue), doc); + Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonFalse), doc); assertEquals(0.0, distanceMatch); assertEquals(3.0, distanceNotMatch); } @@ -155,8 +157,8 @@ public void testSize() { Document doc = new Document().append("employees", new ArrayList<>(Arrays.asList(1, 5, 6))); Bson bsonTrue = Filters.size("employees", 3); Bson bsonFalse = Filters.size("employees", 5); - Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(bsonTrue, doc); - Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(bsonFalse, doc); + Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue), doc); + Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonFalse), doc); assertEquals(0.0, distanceMatch); assertEquals(2.0, distanceNotMatch); } @@ -166,8 +168,8 @@ public void testMod() { Document doc = new Document().append("age", 20); Bson bsonTrue = Filters.mod("age", 3, 2); Bson bsonFalse = Filters.mod("age", 3, 0); - Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(bsonTrue, doc); - Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(bsonFalse, doc); + Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue), doc); + Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonFalse), doc); assertEquals(0.0, distanceMatch); assertEquals(2.0, distanceNotMatch); } @@ -177,8 +179,8 @@ public void testNot() { Document doc = new Document().append("age", 20); Bson bsonTrue = Filters.not(Filters.gt("age", 30)); Bson bsonFalse = Filters.not(Filters.gt("age", 10)); - Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(bsonTrue, doc); - Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(bsonFalse, doc); + Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue), doc); + Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonFalse), doc); assertEquals(0.0, distanceMatch); assertEquals(10.0, distanceNotMatch); } @@ -188,8 +190,8 @@ public void testExistsTrueVersion() { Document doc = new Document().append("age", 20); Bson bsonTrue = Filters.exists("age", true); Bson bsonFalse = Filters.exists("name", true); - Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(bsonTrue, doc); - Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(bsonFalse, doc); + Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue), doc); + Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonFalse), doc); assertEquals(0.0, distanceMatch); assertEquals(65563.0, distanceNotMatch); } @@ -199,8 +201,8 @@ public void testExistsFalseVersion() { Document doc = new Document().append("age", 20); Bson bsonTrue = Filters.exists("name", false); Bson bsonFalse = Filters.exists("age", false); - Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(bsonTrue, doc); - Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(bsonFalse, doc); + Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue), doc); + Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonFalse), doc); assertEquals(0.0, distanceMatch); assertEquals(1.0, distanceNotMatch); } @@ -210,8 +212,8 @@ public void testTypeExplicitVersion() { Document doc = new Document().append("age", 20); Bson bsonTrue = Filters.type("age", BsonType.INT32); Bson bsonFalse = Filters.type("age", BsonType.DOUBLE); - Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(bsonTrue, doc); - Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(bsonFalse, doc); + Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue), doc); + Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonFalse), doc); assertEquals(0.0, distanceMatch); assertEquals(65551.0, distanceNotMatch); } @@ -222,8 +224,8 @@ public void testTypeAliasVersion() { Document doc = new Document().append("age", 20); Bson bsonTrue = Filters.type("age", BsonType.INT32.name()); Bson bsonFalse = Filters.type("age", BsonType.DOUBLE.name()); - Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(bsonTrue, doc); - Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(bsonFalse, doc); + Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue), doc); + Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonFalse), doc); assertEquals(0.0, distanceMatch); assertEquals(65551.0, distanceNotMatch); } @@ -233,9 +235,15 @@ public void testElemMatch() { Document doc = new Document().append("years", new ArrayList<>(Arrays.asList(2002, 2010))); Bson bsonTrue = Filters.elemMatch("years", Filters.gt("years", 2009)); Bson bsonFalse = Filters.elemMatch("years", Filters.lt("years", 2001)); - Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(bsonTrue, doc); - Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(bsonFalse, doc); + Double distanceMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonTrue), doc); + Double distanceNotMatch = new MongoHeuristicsCalculator().computeExpression(convertToDocument(bsonFalse), doc); assertEquals(0.0, distanceMatch); assertEquals(2.0, distanceNotMatch); } + + private static Document convertToDocument(Bson filter){ + BsonDocument bsonDocument = filter.toBsonDocument(); + DocumentCodec documentCodec = new DocumentCodec(); + return documentCodec.decode(bsonDocument.asBsonReader(), DecoderContext.builder().build()); + } } \ No newline at end of file diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/mongo/MongoScriptRunnerTest.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/mongo/MongoScriptRunnerTest.java index e06163fa25..22c23df227 100644 --- a/client-java/controller/src/test/java/org/evomaster/client/java/controller/mongo/MongoScriptRunnerTest.java +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/mongo/MongoScriptRunnerTest.java @@ -12,7 +12,6 @@ import static org.junit.jupiter.api.Assertions.*; -// Is this test possible? Docker should be running I think public class MongoScriptRunnerTest { private static MongoClient connection; diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/InstrumentationController.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/InstrumentationController.java index 719a1189b4..db91e2232e 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/InstrumentationController.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/InstrumentationController.java @@ -47,6 +47,10 @@ public static void setExecutingInitSql(boolean executingInitSql){ ExecutionTracer.setExecutingInitSql(executingInitSql); } + public static void setExecutingInitMongo(boolean executingInitMongo){ + ExecutionTracer.setExecutingInitMongo(executingInitMongo); + } + public static void setExecutingAction(boolean executingAction){ ExecutionTracer.setExecutingAction(executingAction); } diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/MongoCollectionInfo.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/MongoCollectionInfo.java index 63f72f750c..7c5a786c76 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/MongoCollectionInfo.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/MongoCollectionInfo.java @@ -7,14 +7,14 @@ */ public class MongoCollectionInfo implements Serializable { private final String collectionName; - private final Class documentsType; + private final String documentsType; - public MongoCollectionInfo(String collectionName, Class collectionType) { + public MongoCollectionInfo(String collectionName, String documentsType) { this.collectionName = collectionName; - this.documentsType = collectionType; + this.documentsType = documentsType; } public String getCollectionName() {return collectionName;} - public Class getDocumentsType() {return documentsType;} + public String getDocumentsType() {return documentsType;} } diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/MongoInfo.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/MongoInfo.java index c4e5a2fece..a6239f6b4b 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/MongoInfo.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/MongoInfo.java @@ -6,13 +6,38 @@ * Info related to MONGO command execution. */ public class MongoInfo implements Serializable { - private final Object collection; + /** + * Name of the collection that the operation was applied to + */ + private final String collectionName; + + /** + * Name of the database that the operation was applied to + */ + private final String databaseName; + + /** + * Type of the documents of the collection + */ + private final String documentsType; + + /** + * Documents in the collection at the moment of the operation + */ + private final Iterable documents; + + /** + * Executed FIND query + */ private final Object bson; private final boolean successfullyExecuted; private final long executionTime; - public MongoInfo(Object collection, Object bson, boolean successfullyExecuted, long executionTime) { - this.collection = collection; + public MongoInfo(String collectionName, String databaseName, String documentsType, Iterable documents, Object bson, boolean successfullyExecuted, long executionTime) { + this.collectionName = collectionName; + this.databaseName = databaseName; + this.documentsType = documentsType; + this.documents = documents; this.bson = bson; this.successfullyExecuted = successfullyExecuted; this.executionTime = executionTime; @@ -21,7 +46,18 @@ public MongoInfo(Object collection, Object bson, boolean successfullyExecuted, l public Object getQuery() { return bson; } - public Object getCollection() { - return collection; + + public String getCollectionName() { + return collectionName; + } + + public Iterable getDocuments() { + return documents; + } + + public String getDocumentsType() {return documentsType;} + + public String getDatabaseName() { + return databaseName; } } diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MappingMongoEntityInformationClassReplacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MappingMongoEntityInformationClassReplacement.java index 5ddc9d4f8c..556dc69fef 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MappingMongoEntityInformationClassReplacement.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MappingMongoEntityInformationClassReplacement.java @@ -4,6 +4,7 @@ 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.ThirdPartyMethodReplacementClass; +import org.evomaster.client.java.instrumentation.object.ClassToSchema; import org.evomaster.client.java.instrumentation.shared.ReplacementCategory; import org.evomaster.client.java.instrumentation.shared.ReplacementType; import org.evomaster.client.java.instrumentation.staticstate.ExecutionTracer; @@ -88,7 +89,8 @@ private static void handleMappingMongoEntityInformationConstructor(String id, Li addInstance(mappingMongoEntityInformation); String collectionName = (String) mappingMongoEntityInformation.getClass().getMethod("getCollectionName").invoke(mappingMongoEntityInformation); Class repositoryType = (Class) mappingMongoEntityInformation.getClass().getMethod("getJavaType").invoke(mappingMongoEntityInformation); - ExecutionTracer.addMongoCollectionInfo(new MongoCollectionInfo(collectionName, repositoryType)); + String schema = ClassToSchema.getOrDeriveSchemaWithItsRef(repositoryType); + ExecutionTracer.addMongoCollectionInfo(new MongoCollectionInfo(collectionName, schema)); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { throw new RuntimeException(e); } diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacement.java index 169880915e..e0dc82fe40 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacement.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacement.java @@ -5,12 +5,14 @@ import org.evomaster.client.java.instrumentation.coverage.methodreplacement.ThirdPartyCast; import org.evomaster.client.java.instrumentation.coverage.methodreplacement.ThirdPartyMethodReplacementClass; import org.evomaster.client.java.instrumentation.coverage.methodreplacement.UsageFilter; +import org.evomaster.client.java.instrumentation.object.ClassToSchema; import org.evomaster.client.java.instrumentation.shared.ReplacementCategory; import org.evomaster.client.java.instrumentation.shared.ReplacementType; import org.evomaster.client.java.instrumentation.staticstate.ExecutionTracer; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -62,9 +64,9 @@ private static Object handleFind(String id, Object mongoCollection, List long end = System.currentTimeMillis(); handleMongo(mongoCollection, query, true, end - start); return result; - } catch (IllegalAccessException e){ + } catch (IllegalAccessException e) { throw new RuntimeException(e); - } catch (InvocationTargetException e){ + } catch (InvocationTargetException e) { throw (RuntimeException) e.getCause(); } } @@ -74,7 +76,60 @@ private static Method retrieveFindMethod(String id, Object mongoCollection) { } private static void handleMongo(Object mongoCollection, Object bson, boolean successfullyExecuted, long executionTime) { - MongoInfo info = new MongoInfo(mongoCollection, bson, successfullyExecuted, executionTime); + String schema = ClassToSchema.getOrDeriveSchemaWithItsRef(extractDocumentsType(mongoCollection)); + MongoInfo info = new MongoInfo(getCollectionName(mongoCollection), getDatabaseName(mongoCollection), schema, getDocuments(mongoCollection), bson, successfullyExecuted, executionTime); ExecutionTracer.addMongoInfo(info); } + + + private static Iterable getDocuments(Object collection) { + // Need to convert result of getDocuments which a FindIterable instance as it is not Serializable + List documentsAsList = new ArrayList<>(); + try { + Class collectionClass = getCollectionClass(collection); + Iterable findIterable = (Iterable) collectionClass.getMethod("find").invoke(collection); + findIterable.forEach(documentsAsList::add); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | + ClassNotFoundException e) { + throw new RuntimeException("Failed to retrieve all documents from a mongo collection", e); + } + return documentsAsList; + } + + private static Class extractDocumentsType(Object collection) { + try { + Class collectionClass = getCollectionClass(collection); + return (Class) collectionClass.getMethod("getDocumentClass").invoke(collection); + } catch (NoSuchMethodException | ClassNotFoundException | InvocationTargetException | + IllegalAccessException e) { + throw new RuntimeException("Failed to retrieve document's type from collection", e); + } + } + + private static String getDatabaseName(Object collection) { + try { + Class collectionClass = getCollectionClass(collection); + Object namespace = collectionClass.getMethod("getNamespace").invoke(collection); + return (String) namespace.getClass().getMethod("getDatabaseName").invoke(namespace); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | + ClassNotFoundException e) { + throw new RuntimeException("Failed to retrieve name of the database in which collection is", e); + } + } + + private static String getCollectionName(Object collection) { + try { + Class collectionClass = getCollectionClass(collection); + Object namespace = collectionClass.getMethod("getNamespace").invoke(collection); + return (String) namespace.getClass().getMethod("getCollectionName").invoke(namespace); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | + ClassNotFoundException e) { + throw new RuntimeException("Failed to retrieve collection name", e); + } + } + + private static Class getCollectionClass(Object collection) throws ClassNotFoundException { + // collection is an implementation of interface MongoCollection + return collection.getClass().getInterfaces()[0]; + } } diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/AgentController.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/AgentController.java index aeaaccbfe3..9f6f9acebc 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/AgentController.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/external/AgentController.java @@ -93,6 +93,10 @@ public static void start(int port){ handleExecutingInitSql(); sendCommand(Command.ACK); break; + case EXECUTING_INIT_MONGO: + handleExecutingInitMongo(); + sendCommand(Command.ACK); + break; case EXECUTING_ACTION: handleExecutingAction(); sendCommand(Command.ACK); @@ -165,6 +169,16 @@ private static void handleExecutingInitSql() { } } + private static void handleExecutingInitMongo() { + try { + Object msg = in.readObject(); + Boolean executingInitMongo = (Boolean) msg; + InstrumentationController.setExecutingInitMongo(executingInitMongo); + } catch (Exception e){ + SimpleLogger.error("Failure in handling executing-init-mongo: "+e.getMessage()); + } + } + private static void handleExecutingAction() { try { Object msg = in.readObject(); diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilder.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilder.kt deleted file mode 100644 index ddffb67343..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilder.kt +++ /dev/null @@ -1,113 +0,0 @@ -package org.evomaster.core.mongo - -import org.evomaster.core.search.gene.* -import org.evomaster.core.search.gene.collection.ArrayGene -import org.evomaster.core.search.gene.datetime.DateGene -import org.evomaster.core.search.gene.numeric.* -import org.evomaster.core.search.gene.string.StringGene -import org.slf4j.Logger -import org.slf4j.LoggerFactory - -// Default Mapping of Java Classes to BSON types: -// https://mongodb.github.io/mongo-java-driver/3.6/javadoc/?org/bson/codecs/BsonTypeClassMap.html -class MongoActionGeneBuilder { - private val log: Logger = LoggerFactory.getLogger(MongoActionGeneBuilder::class.java) - - fun buildGene(fieldName: String, valueType: Class): Gene? { - val typeName = valueType.name; - - if(typeName == "java.lang.String"){ - return StringGene(name = fieldName) - } - - if(typeName == "int" || typeName == "java.lang.Integer"){ - return IntegerGene(name = fieldName, min = Int.MIN_VALUE, max = Int.MAX_VALUE) - } - - if(typeName == "long" || typeName == "java.lang.Long"){ - return LongGene(name = fieldName, min = Long.MIN_VALUE, max = Long.MAX_VALUE) - } - - if(typeName == "double" || typeName == "java.lang.Double"){ - return DoubleGene(name = fieldName, min = Double.MIN_VALUE, max = Double.MAX_VALUE) - } - - if(typeName == "boolean" || typeName == "java.lang.Boolean") { - return BooleanGene(name = fieldName) - } - - if(typeName == "java.util.Date") { - return DateGene(name = fieldName, onlyValidDates = true) - } - - if(isAListImplementation(valueType)){ - val elementsGene = buildGene("", valueType.componentType) - return if(elementsGene != null) ArrayGene(name = fieldName, template = elementsGene) else null - } - - if(typeName == "org.bson.types.Decimal128") { - return unhandledValueType(fieldName) - } - - if(typeName == "org.bson.types.Binary") { - return unhandledValueType(fieldName) - } - - if(typeName == "org.bson.types.ObjectId") { - return unhandledValueType(fieldName) - } - - if(typeName == "org.bson.types.RegularExpression") { - return unhandledValueType(fieldName) - } - - // Deprecated - if(typeName == "org.bson.types.Symbol") { - return unhandledValueType(fieldName) - } - - // Deprecated - if(typeName == "org.bson.types.DBPointer") { - return unhandledValueType(fieldName) - } - - if(typeName == "org.bson.types.MaxKey") { - return unhandledValueType(fieldName) - } - - if(typeName == "org.bson.types.MinKey") { - return unhandledValueType(fieldName) - } - - if(typeName == "org.bson.types.Code") { - return unhandledValueType(fieldName) - } - - // Deprecated - if(typeName == "org.bson.types.CodeWithScope") { - return unhandledValueType(fieldName) - } - - if(typeName == "org.bson.types.BSONTimestamp") { - return unhandledValueType(fieldName) - } - - // Deprecated - if(typeName == "org.bson.types.Undefined") { - return unhandledValueType(fieldName) - } - - if(typeName == "org.bson.Document") { - return ObjectGene(fieldName, listOf()) - } - - return ObjectGene(fieldName, valueType.fields.mapNotNull { field -> buildGene(field.name, field.type) }) - } - - private fun unhandledValueType(fieldName: String): Gene? { - log.warn(("Cannot convert field: $fieldName to gene")) - return null - } - - private fun isAListImplementation(valueType: Class) = valueType.interfaces.any { i -> i.typeName == "java.util.List" } -} diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt index 036f87b5e4..7a67f2cc88 100644 --- a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt @@ -1,9 +1,8 @@ package org.evomaster.core.mongo +import org.evomaster.core.problem.rest.RestActionBuilderV3.createObjectGenesForDTOs import org.evomaster.core.search.Action import org.evomaster.core.search.gene.Gene -import org.evomaster.core.search.gene.ObjectGene -import java.lang.reflect.Modifier import java.util.* class MongoDbAction( @@ -18,38 +17,23 @@ class MongoDbAction( /** * The type of the new document. Should map the type of the documents of the collection */ - val documentsType: Class<*>, + val documentsType: String, computedGenes: List? = null ) : Action(listOf()) { private val genes: List = (computedGenes ?: computeGenes()).also { addChildren(it) } private fun computeGenes(): List { - val genes = - if (documentsType.name == "org.bson.Document") { - /* There are two different scenarios here: - 1) The collection has no type restriction. - 2) For some reason, it was not possible to determine the document's type. - - In case 1) any insertion to the collection would be valid. - In case 2) we don't have enough information. - - In both cases, we don't have fields from which to build genes. - One possibility would be to extract the fields used in the query, - but there is no guarantee that it would work in case 2) - (the fields used in the query may be different or a subset of the actual type of the collection). - */ - listOf() - } else { - getFieldsFromType().mapNotNull { MongoActionGeneBuilder().buildGene(it.name, it.type) } - } - return Collections.singletonList(ObjectGene("BSON", genes)) + val documentsTypeName = documentsType.substringBefore(":").drop(1).dropLast(1) + return Collections.singletonList( + createObjectGenesForDTOs( + documentsTypeName, documentsType, false + ) + ) } override fun getName(): String { - return "MONGO_Insert_${database}_${collection}_${ - getFieldsFromType().map { it.name }.sorted().joinToString("_") - }" + return "MONGO_Insert_${database}_${collection}_${documentsType}" } override fun seeTopGenes(): List { @@ -63,6 +47,4 @@ class MongoDbAction( override fun copyContent(): Action { return MongoDbAction(database, collection, documentsType, genes.map(Gene::copy)) } - - private fun getFieldsFromType() = documentsType.declaredFields.filter { Modifier.isPublic(it.modifiers) } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoInsertBuilder.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoInsertBuilder.kt index fc916af983..1bc4fecb4a 100644 --- a/core/src/main/kotlin/org/evomaster/core/mongo/MongoInsertBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoInsertBuilder.kt @@ -1,7 +1,7 @@ package org.evomaster.core.mongo class MongoInsertBuilder { - fun createMongoInsertionAction(database: String, collection: String, documentsType: Class<*>): MongoDbAction{ + fun createMongoInsertionAction(database: String, collection: String, documentsType: String): MongoDbAction{ return MongoDbAction(database, collection, documentsType) } } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt index 648d976620..fb5e2a7aa0 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt @@ -78,7 +78,7 @@ abstract class EnterpriseSampler : Sampler() where T : Individual { return actions } - fun sampleMongoInsertion(database: String, collection: String, documentsType: Class<*>): MongoDbAction { + fun sampleMongoInsertion(database: String, collection: String, documentsType: String): MongoDbAction { val action = MongoInsertBuilder().createMongoInsertionAction(database, collection, documentsType) action.seeTopGenes().forEach{it.doInitialize(randomness)} return action diff --git a/core/src/test/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilderTest.kt b/core/src/test/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilderTest.kt deleted file mode 100644 index 264c0c00c3..0000000000 --- a/core/src/test/kotlin/org/evomaster/core/mongo/MongoActionGeneBuilderTest.kt +++ /dev/null @@ -1,82 +0,0 @@ -package org.evomaster.core.mongo - -import org.evomaster.core.search.gene.BooleanGene -import org.evomaster.core.search.gene.ObjectGene -import org.evomaster.core.search.gene.collection.ArrayGene -import org.evomaster.core.search.gene.datetime.DateGene -import org.evomaster.core.search.gene.numeric.DoubleGene -import org.evomaster.core.search.gene.numeric.IntegerGene -import org.evomaster.core.search.gene.numeric.LongGene -import org.evomaster.core.search.gene.string.StringGene -import org.junit.jupiter.api.Test -import java.util.* - -class MongoActionGeneBuilderTest { - - inner class Object { - var someField: Int = 0 - } - - @Test - fun testStringField() { - val gene = MongoActionGeneBuilder().buildGene("someField", String::class.java) - assert(StringGene::class.isInstance(gene)) - } - - @Test - fun testIntegerField() { - val gene = MongoActionGeneBuilder().buildGene("someField", Integer::class.java) - assert(IntegerGene::class.isInstance(gene)) - } - - @Test - fun testLongField() { - val gene = MongoActionGeneBuilder().buildGene("someField", Long::class.java) - assert(LongGene::class.isInstance(gene)) - } - - @Test - fun testDoubleField() { - val gene = MongoActionGeneBuilder().buildGene("someField", Double::class.java) - assert(DoubleGene::class.isInstance(gene)) - } - - @Test - fun testDateField() { - val gene = MongoActionGeneBuilder().buildGene("someField", Date::class.java) - assert(DateGene::class.isInstance(gene)) - } - - @Test - fun testBooleanField() { - val gene = MongoActionGeneBuilder().buildGene("someField", Boolean::class.java) - assert(BooleanGene::class.isInstance(gene)) - } - - @Test - fun testObjectField() { - val gene = MongoActionGeneBuilder().buildGene("someField", Object::class.java) - assert(ObjectGene::class.isInstance(gene)) - } - - @Test - fun testUnhandledTypeField() { - val gene = MongoActionGeneBuilder().buildGene("someField", Class.forName("org.bson.types.Decimal128")) - assert(gene == null) - } - - @Test - fun testDocumentField() { - val gene = MongoActionGeneBuilder().buildGene("someField", Class.forName("org.bson.Document")) - assert(ObjectGene::class.isInstance(gene)) - } - - /* - @Test - fun testListField() { - val gene = MongoActionGeneBuilder().buildGene("someField", ArrayList()::class.java) - assert(ArrayGene::class.isInstance(gene)) - } - - */ -} \ No newline at end of file diff --git a/core/src/test/kotlin/org/evomaster/core/mongo/MongoActionTest.kt b/core/src/test/kotlin/org/evomaster/core/mongo/MongoActionTest.kt index 07752dc6cc..1b3c8ebe82 100644 --- a/core/src/test/kotlin/org/evomaster/core/mongo/MongoActionTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/mongo/MongoActionTest.kt @@ -1,31 +1,20 @@ package org.evomaster.core.mongo import org.evomaster.core.search.gene.ObjectGene -import org.evomaster.core.search.gene.numeric.IntegerGene +import org.evomaster.core.search.gene.optional.OptionalGene import org.junit.jupiter.api.Test class MongoActionTest { - - inner class Object { - @JvmField - var someField: Int = 0 - } - - @Test - fun testGenesWhenDocument() { - val action = MongoDbAction("someDatabase", "someCollection", Class.forName("org.bson.Document")) - val gene = action.seeTopGenes().first() - assert(ObjectGene::class.isInstance(gene)) - gene as ObjectGene - assert(gene.fields.isEmpty()) - } - @Test - fun testGenesWhenNotDocument() { - val action = MongoDbAction("someDatabase", "someCollection", Object::class.java) + fun testGenes() { + val action = MongoDbAction( + "someDatabase", + "someCollection", + "\"CustomType\":{\"CustomType\":{\"type\":\"object\", \"properties\": {\"aField\":{\"type\":\"integer\"}}}}" + ) val gene = action.seeTopGenes().first() assert(ObjectGene::class.isInstance(gene)) gene as ObjectGene - assert(IntegerGene::class.isInstance(gene.fields.first())) + assert(OptionalGene::class.isInstance(gene.fields.first())) } } \ No newline at end of file diff --git a/core/src/test/kotlin/org/evomaster/core/mongo/MongoDbActionTransformerTest.kt b/core/src/test/kotlin/org/evomaster/core/mongo/MongoDbActionTransformerTest.kt index e8b8537ef6..a003009d83 100644 --- a/core/src/test/kotlin/org/evomaster/core/mongo/MongoDbActionTransformerTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/mongo/MongoDbActionTransformerTest.kt @@ -6,10 +6,6 @@ import org.junit.jupiter.api.Test class MongoDbActionTransformerTest { - class CustomType(field: Int) { - val aField = field - } - @Test fun testEmpty() { val actions = listOf() @@ -21,7 +17,8 @@ class MongoDbActionTransformerTest { fun testNotEmpty() { val database = "aDatabase" val collection = "aCollection" - val action = MongoDbAction(database, collection, CustomType::class.java) + val documentsType = "\"CustomType\":{\"CustomType\":{\"type\":\"object\", \"properties\": {\"aField\":{\"type\":\"string\"}}}}" + val action = MongoDbAction(database, collection, documentsType) val actions = listOf(action) val dto = MongoDbActionTransformer.transform(actions) assertFalse(dto.insertions.isEmpty()) diff --git a/core/src/test/kotlin/org/evomaster/core/mongo/MongoInsertBuilderTest.kt b/core/src/test/kotlin/org/evomaster/core/mongo/MongoInsertBuilderTest.kt index 5795bfb4a8..035684b0ed 100644 --- a/core/src/test/kotlin/org/evomaster/core/mongo/MongoInsertBuilderTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/mongo/MongoInsertBuilderTest.kt @@ -3,14 +3,11 @@ package org.evomaster.core.mongo import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test class MongoInsertBuilderTest { - class CustomType(field: Int) { - val aField = field - } @Test fun testInsert() { val database = "aDatabase" val collection = "aCollection" - val documentsType = CustomType::class.java + val documentsType ="\"CustomType\":{\"CustomType\":{\"type\":\"object\", \"properties\": {\"aField\":{\"type\":\"string\"}}}}" val builder = MongoInsertBuilder() val action = builder.createMongoInsertionAction(database, collection, documentsType) From 699ff4a3df27af2bb2f98c99caeec469b7f98413 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Thu, 13 Jul 2023 00:36:17 +0200 Subject: [PATCH 133/345] address comments --- .../controller/problem/rpc/schema/params/ObjectParam.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java index 5d71b80655..aaaac50eb2 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/ObjectParam.java @@ -256,13 +256,13 @@ public List newInstanceWithJava(boolean isDeclaration, boolean doesInclu if (f.accessibleSchema != null && f.accessibleSchema.setterMethodName != null){ String fName = ownVarName; boolean fdeclar = false; - if (f instanceof ObjectParam || f instanceof MapParam || f instanceof CollectionParam || f instanceof DateParam || f instanceof BigDecimalParam || f instanceof BigIntegerParam || f instanceof Protobuf3ByteStringParam){ + if (needRenameField(f)){ fName = varName+"_"+f.getName(); fdeclar = true; } codes.addAll(f.newInstanceWithJava(fdeclar, true, fName, indent+1)); - if (f instanceof ObjectParam || f instanceof MapParam || f instanceof CollectionParam || f instanceof DateParam || f instanceof BigDecimalParam || f instanceof BigIntegerParam || f instanceof Protobuf3ByteStringParam){ + if (needRenameField(f)){ CodeJavaGenerator.addCode(codes, CodeJavaGenerator.methodInvocation(ownVarName, f.accessibleSchema.setterMethodName, fName)+CodeJavaGenerator.appendLast(),indent+1); } }else { @@ -279,6 +279,10 @@ public List newInstanceWithJava(boolean isDeclaration, boolean doesInclu return codes; } + private boolean needRenameField(NamedTypedValue f){ + return f instanceof ObjectParam || f instanceof MapParam || f instanceof CollectionParam || f instanceof DateParam || f instanceof BigDecimalParam || f instanceof BigIntegerParam || f instanceof Protobuf3ByteStringParam; + } + @Override public List newAssertionWithJava(int indent, String responseVarName, int maxAssertionForDataInCollection) { List codes = new ArrayList<>(); From 82d745fe9bcbcc7a45ba31ed290f422fc18f0998 Mon Sep 17 00:00:00 2001 From: hghianni Date: Fri, 14 Jul 2023 13:50:35 -0300 Subject: [PATCH 134/345] Force fields of type of collection to be not optional --- ...gMongoEntityInformationClassReplacement.java | 2 ++ .../MongoCollectionClassReplacement.java | 2 ++ .../instrumentation/object/ClassToSchema.java | 17 ++++++++++++++--- .../org/evomaster/core/mongo/MongoActionTest.kt | 5 +++-- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MappingMongoEntityInformationClassReplacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MappingMongoEntityInformationClassReplacement.java index 556dc69fef..22fe79c0dd 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MappingMongoEntityInformationClassReplacement.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MappingMongoEntityInformationClassReplacement.java @@ -89,7 +89,9 @@ private static void handleMappingMongoEntityInformationConstructor(String id, Li addInstance(mappingMongoEntityInformation); String collectionName = (String) mappingMongoEntityInformation.getClass().getMethod("getCollectionName").invoke(mappingMongoEntityInformation); Class repositoryType = (Class) mappingMongoEntityInformation.getClass().getMethod("getJavaType").invoke(mappingMongoEntityInformation); + ClassToSchema.setObjectFieldsRequired(true); String schema = ClassToSchema.getOrDeriveSchemaWithItsRef(repositoryType); + ClassToSchema.setObjectFieldsRequired(false); ExecutionTracer.addMongoCollectionInfo(new MongoCollectionInfo(collectionName, schema)); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { throw new RuntimeException(e); diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacement.java index e0dc82fe40..3a021b317d 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacement.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacement.java @@ -76,7 +76,9 @@ private static Method retrieveFindMethod(String id, Object mongoCollection) { } private static void handleMongo(Object mongoCollection, Object bson, boolean successfullyExecuted, long executionTime) { + ClassToSchema.setObjectFieldsRequired(true); String schema = ClassToSchema.getOrDeriveSchemaWithItsRef(extractDocumentsType(mongoCollection)); + ClassToSchema.setObjectFieldsRequired(false); MongoInfo info = new MongoInfo(getCollectionName(mongoCollection), getDatabaseName(mongoCollection), schema, getDocuments(mongoCollection), bson, successfullyExecuted, executionTime); ExecutionTracer.addMongoInfo(info); } diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/object/ClassToSchema.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/object/ClassToSchema.java index 419749ed5e..5cdfb4516a 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/object/ClassToSchema.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/object/ClassToSchema.java @@ -67,6 +67,8 @@ public class ClassToSchema { private static final String fieldRefPostfix = "\"}"; + private static boolean objectFieldsRequired = false; + public static void registerSchemaIfNeeded(Class valueType) { if (valueType == null) { @@ -305,6 +307,7 @@ private static String getSchema(Type type, Boolean useRefObject, List> List properties = new ArrayList<>(); + List propertiesNames = new ArrayList<>(); //general object, let's look at its fields Class target = klass; @@ -320,11 +323,12 @@ private static String getSchema(Type type, Boolean useRefObject, List> }else fieldSchema = getOrDeriveSchema(fieldName, f.getGenericType(), true, nested); properties.add(fieldSchema); + propertiesNames.add("\"" + fieldName + "\""); } target = target.getSuperclass(); } - return fieldObjectSchema(properties); + return fieldObjectSchema(properties, propertiesNames); } private static boolean shouldAddToSchema(Field field) { @@ -420,9 +424,12 @@ private static String fieldStringKeyMapSchema(Class klass, ParameterizedType return "{\"type\":\"object\", \"additionalProperties\":" + value + "}"; } - private static String fieldObjectSchema(List properties) { + private static String fieldObjectSchema(List properties, List propertiesNames) { String p = properties.stream().collect(Collectors.joining(",")); - + String r = propertiesNames.stream().collect(Collectors.joining(",")); + if(objectFieldsRequired){ + return "{\"type\":\"object\", \"properties\": {" + p + "}, \"required\": [" + r + "]}"; + } return "{\"type\":\"object\", \"properties\": {" + p + "}}"; } @@ -455,4 +462,8 @@ private static String getNameEnumConstant(Object object) { return object.toString(); } } + + public static void setObjectFieldsRequired(boolean objectFieldsRequired){ + ClassToSchema.objectFieldsRequired = objectFieldsRequired; + } } diff --git a/core/src/test/kotlin/org/evomaster/core/mongo/MongoActionTest.kt b/core/src/test/kotlin/org/evomaster/core/mongo/MongoActionTest.kt index 1b3c8ebe82..2d2e18e810 100644 --- a/core/src/test/kotlin/org/evomaster/core/mongo/MongoActionTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/mongo/MongoActionTest.kt @@ -1,6 +1,7 @@ package org.evomaster.core.mongo import org.evomaster.core.search.gene.ObjectGene +import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.optional.OptionalGene import org.junit.jupiter.api.Test @@ -10,11 +11,11 @@ class MongoActionTest { val action = MongoDbAction( "someDatabase", "someCollection", - "\"CustomType\":{\"CustomType\":{\"type\":\"object\", \"properties\": {\"aField\":{\"type\":\"integer\"}}}}" + "\"CustomType\":{\"CustomType\":{\"type\":\"object\", \"properties\": {\"aField\":{\"type\":\"integer\"}}, \"required\": [\"aField\"]}}" ) val gene = action.seeTopGenes().first() assert(ObjectGene::class.isInstance(gene)) gene as ObjectGene - assert(OptionalGene::class.isInstance(gene.fields.first())) + assert(IntegerGene::class.isInstance(gene.fields.first())) } } \ No newline at end of file From 6ff1c8185b522122143a58fa54ba9821895cf58f Mon Sep 17 00:00:00 2001 From: hghianni Date: Fri, 14 Jul 2023 13:54:12 -0300 Subject: [PATCH 135/345] Handle Spring field annotation --- .../client/java/instrumentation/object/ClassToSchema.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/object/ClassToSchema.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/object/ClassToSchema.java index 5cdfb4516a..7d0a858b19 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/object/ClassToSchema.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/object/ClassToSchema.java @@ -371,7 +371,8 @@ private static String getName(Field field) { for (Annotation a : field.getAnnotations()) { String name = a.annotationType().getName(); if (name.equals("com.fasterxml.jackson.annotation.JsonProperty") - || name.equals("com.google.gson.annotations.SerializedName")) { + || name.equals("com.google.gson.annotations.SerializedName") + || name.equals("org.springframework.data.mongodb.core.mapping.Field")) { try { Method m = a.annotationType().getMethod("value"); String value = (String) m.invoke(a); From 06895fb76c3a12d662a09cfa9d090f7a731ee790 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 24 Jul 2023 21:21:41 +0200 Subject: [PATCH 136/345] TOSEM survey on REST --- docs/publications.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/publications.md b/docs/publications.md index 39a04fe809..4ce9fd4da4 100644 --- a/docs/publications.md +++ b/docs/publications.md @@ -14,10 +14,6 @@ Also, some of these papers provides full replication packages, which are linked ## Recent arXiv Technical Reports, not Peer-Reviewed (Yet) -* A. Golmohammadi, M. Zhang, A. Arcuri. - *Testing RESTful APIs: A Survey*. - [[arxiv](https://arxiv.org/abs/2212.14604)] - * M. Zhang, A. Arcuri, Y. Li, K Xue, Z Wang, J. Huo, W Huang. *Fuzzing Microservices In Industry: Experience of Applying EvoMaster at Meituan*. @@ -31,6 +27,13 @@ Also, some of these papers provides full replication packages, which are linked ### 2023 +* A. Golmohammadi, M. Zhang, A. Arcuri. + *Testing RESTful APIs: A Survey*. + ACM Transactions on Software Engineering and Methodology (TOSEM). (to appear) + [[arxiv](https://arxiv.org/abs/2212.14604)] + + + * A. Golmohammadi, M. Zhang, A. Arcuri *.NET/C# Instrumentation for Search-Based Software Testing*. Software Quality Journal (SQJ). (to appear) From 60ac39fb6b8ca0135ac2284bd6b31050e4daaf64 Mon Sep 17 00:00:00 2001 From: hghianni Date: Sun, 30 Jul 2023 20:22:24 -0300 Subject: [PATCH 137/345] Address comments --- .../java/instrumentation/ExtractJvmClass.java | 2 +- ...ongoEntityInformationClassReplacement.java | 4 +- .../MongoCollectionClassReplacement.java | 4 +- .../instrumentation/object/ClassToSchema.java | 126 +++++++++--------- 4 files changed, 69 insertions(+), 67 deletions(-) diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/ExtractJvmClass.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/ExtractJvmClass.java index b4df58457d..96f991e2bd 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/ExtractJvmClass.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/ExtractJvmClass.java @@ -23,7 +23,7 @@ public static Map extractAsSchema(List jvmDtoNames) { continue; } clazz = Class.forName(dtoName); - schemas.putAll(ClassToSchema.getOrDeriveSchemaAndNestedClasses(clazz)); + schemas.putAll(ClassToSchema.getOrDeriveSchemaAndNestedClasses(clazz, false)); } catch (ClassNotFoundException e) { SimpleLogger.uniqueWarn("Fail to extract Jvm DTO as schema:"+e.getMessage()); } diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MappingMongoEntityInformationClassReplacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MappingMongoEntityInformationClassReplacement.java index 22fe79c0dd..58bf00e7ee 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MappingMongoEntityInformationClassReplacement.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MappingMongoEntityInformationClassReplacement.java @@ -89,9 +89,7 @@ private static void handleMappingMongoEntityInformationConstructor(String id, Li addInstance(mappingMongoEntityInformation); String collectionName = (String) mappingMongoEntityInformation.getClass().getMethod("getCollectionName").invoke(mappingMongoEntityInformation); Class repositoryType = (Class) mappingMongoEntityInformation.getClass().getMethod("getJavaType").invoke(mappingMongoEntityInformation); - ClassToSchema.setObjectFieldsRequired(true); - String schema = ClassToSchema.getOrDeriveSchemaWithItsRef(repositoryType); - ClassToSchema.setObjectFieldsRequired(false); + String schema = ClassToSchema.getOrDeriveSchemaWithItsRef(repositoryType, true); ExecutionTracer.addMongoCollectionInfo(new MongoCollectionInfo(collectionName, schema)); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { throw new RuntimeException(e); diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacement.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacement.java index 3a021b317d..d23cc62fe0 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacement.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/coverage/methodreplacement/thirdpartyclasses/MongoCollectionClassReplacement.java @@ -76,9 +76,7 @@ private static Method retrieveFindMethod(String id, Object mongoCollection) { } private static void handleMongo(Object mongoCollection, Object bson, boolean successfullyExecuted, long executionTime) { - ClassToSchema.setObjectFieldsRequired(true); - String schema = ClassToSchema.getOrDeriveSchemaWithItsRef(extractDocumentsType(mongoCollection)); - ClassToSchema.setObjectFieldsRequired(false); + String schema = ClassToSchema.getOrDeriveSchemaWithItsRef(extractDocumentsType(mongoCollection), true); MongoInfo info = new MongoInfo(getCollectionName(mongoCollection), getDatabaseName(mongoCollection), schema, getDocuments(mongoCollection), bson, successfullyExecuted, executionTime); ExecutionTracer.addMongoInfo(info); } diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/object/ClassToSchema.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/object/ClassToSchema.java index 7d0a858b19..ce3e238517 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/object/ClassToSchema.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/object/ClassToSchema.java @@ -67,23 +67,21 @@ public class ClassToSchema { private static final String fieldRefPostfix = "\"}"; - private static boolean objectFieldsRequired = false; - public static void registerSchemaIfNeeded(Class valueType) { if (valueType == null) { return; } - if(valueType.getName().startsWith("io.swagger.")){ + if (valueType.getName().startsWith("io.swagger.")) { //no point in dealing with this. //also it happens in E2E, where it leads to a infinite recursion return; } - try{ + try { String name = valueType.getName(); - if (!UnitsInfoRecorder.isDtoSchemaRegister(name)){ + if (!UnitsInfoRecorder.isDtoSchemaRegister(name)) { List> embedded = new ArrayList<>(); String schema = ClassToSchema.getOrDeriveSchema(valueType, embedded); UnitsInfoRecorder.registerNewParsedDto(name, schema); @@ -93,8 +91,8 @@ public static void registerSchemaIfNeeded(Class valueType) { } } - }catch (Exception e){ - SimpleLogger.warn("Fail to get schema for Class:"+valueType.getName(), e); + } catch (Exception e) { + SimpleLogger.warn("Fail to get schema for Class:" + valueType.getName(), e); /* fail with tests */ @@ -126,13 +124,18 @@ public static void registerSchemaIfNeeded(Class valueType) { * "org.evomaster.client.java.instrumentation.object.dtos.CycleDtoB":{"type":"object", "properties": {"cycleBId":{"type":"string"},"cycleDtoA":{"$ref":"#/components/schemas/org.evomaster.client.java.instrumentation.object.dtos.CycleDtoA"}}}} * */ - public static String getOrDeriveSchemaWithItsRef(Class klass){ - if (!cacheSchemaWithItsRef.containsKey(klass)){ + + public static String getOrDeriveSchemaWithItsRef(Class klass) { + return getOrDeriveSchemaWithItsRef(klass, false); + } + + public static String getOrDeriveSchemaWithItsRef(Class klass, boolean objectFieldsRequired) { + if (!cacheSchemaWithItsRef.containsKey(klass)) { StringBuilder sb = new StringBuilder(); - Map map = getOrDeriveSchemaAndNestedClasses(klass); + Map map = getOrDeriveSchemaAndNestedClasses(klass, objectFieldsRequired); sb.append("{"); sb.append(map.get(klass.getName())); - map.keySet().stream().filter(s-> !s.equals(klass.getName())).forEach(s-> + map.keySet().stream().filter(s -> !s.equals(klass.getName())).forEach(s -> sb.append(",").append(map.get(s))); sb.append("}"); @@ -142,12 +145,15 @@ public static String getOrDeriveSchemaWithItsRef(Class klass){ return cacheSchemaWithItsRef.get(klass); } + public static String getOrDeriveNonNestedSchema(Class klass, boolean objectFieldsRequired) { + return getOrDeriveSchema(klass, Collections.emptyList(), objectFieldsRequired); + } + /** - * * @return a schema representation of the class in the form "name: {...}" */ public static String getOrDeriveNonNestedSchema(Class klass) { - return getOrDeriveSchema(klass, Collections.emptyList()); + return getOrDeriveNonNestedSchema(klass, false); } @@ -157,14 +163,18 @@ public static String getOrDeriveNonNestedSchema(Class klass) { * like a field entry in an OpenAPI object definition */ public static String getOrDeriveSchema(Class klass, List> nested) { - if (!cacheSchema.containsKey(klass)){ - cacheSchema.put(klass, getOrDeriveSchema(klass.getName(), klass, false, nested)); + return getOrDeriveSchema(klass, nested, false); + } + + public static String getOrDeriveSchema(Class klass, List> nested, boolean objectFieldsRequired) { + if (!cacheSchema.containsKey(klass)) { + cacheSchema.put(klass, getOrDeriveSchema(klass.getName(), klass, false, nested, objectFieldsRequired)); } return cacheSchema.get(klass); } - private static String getOrDeriveSchema(String name, Type type, Boolean useRefObject, List> nested) { + private static String getOrDeriveSchema(String name, Type type, Boolean useRefObject, List> nested, boolean objectFieldsRequired) { // TODO might handle collection and map in the cache later if (cacheSchema.containsKey(type) && !useRefObject && !isCollectionOrMap(type)) { @@ -172,7 +182,7 @@ private static String getOrDeriveSchema(String name, Type type, Boolean useRefOb } - String schema = getSchema(type, useRefObject, nested, false); + String schema = getSchema(type, useRefObject, nested, false, objectFieldsRequired); String namedSchema = named(name, schema); @@ -185,21 +195,21 @@ private static String getOrDeriveSchema(String name, Type type, Boolean useRefOb return namedSchema; } - private static boolean isCollectionOrMap(Type type){ + private static boolean isCollectionOrMap(Type type) { if (!(type instanceof Class)) return false; Class kclazz = (Class) type; return kclazz.isArray() || List.class.isAssignableFrom(kclazz) || Set.class.isAssignableFrom(kclazz) || Map.class.isAssignableFrom(kclazz); } - public static Map getOrDeriveSchemaAndNestedClasses(Class klass) { - if (!cacheMapOfDtoAndItsRefToSchemas.containsKey(klass)){ + public static Map getOrDeriveSchemaAndNestedClasses(Class klass, boolean objectFieldsRequired) { + if (!cacheMapOfDtoAndItsRefToSchemas.containsKey(klass)) { List> nested = new ArrayList<>(); registerSchemaIfNeeded(klass); - findAllNestedClassAndRegisterThemIfNeeded(klass, nested); + findAllNestedClassAndRegisterThemIfNeeded(klass, nested, objectFieldsRequired); Map map = new LinkedHashMap<>(); - for (Class nkclass : nested){ - map.putIfAbsent(nkclass.getName(), getOrDeriveNonNestedSchema(nkclass)); + for (Class nkclass : nested) { + map.putIfAbsent(nkclass.getName(), getOrDeriveNonNestedSchema(nkclass, objectFieldsRequired)); } cacheMapOfDtoAndItsRefToSchemas.put(klass, map); @@ -207,14 +217,14 @@ public static Map getOrDeriveSchemaAndNestedClasses(Class kla return cacheMapOfDtoAndItsRefToSchemas.get(klass); } - private static void findAllNestedClassAndRegisterThemIfNeeded(Class klass, List> nested){ - if (!nested.contains(klass)){ + private static void findAllNestedClassAndRegisterThemIfNeeded(Class klass, List> nested, boolean objectFieldsRequired) { + if (!nested.contains(klass)) { List> innerNested = new ArrayList<>(); - getSchema(klass, false, innerNested, true); + getSchema(klass, false, innerNested, true, objectFieldsRequired); nested.add(klass); - List> toAdd = innerNested.stream().filter(s-> !nested.contains(s)).collect(Collectors.toList()); + List> toAdd = innerNested.stream().filter(s -> !nested.contains(s)).collect(Collectors.toList()); if (toAdd.isEmpty()) return; - toAdd.forEach(a-> findAllNestedClassAndRegisterThemIfNeeded(a, nested)); + toAdd.forEach(a -> findAllNestedClassAndRegisterThemIfNeeded(a, nested, objectFieldsRequired)); } } @@ -224,12 +234,12 @@ private static String named(String name, String jsonObject) { /** - * * @param useRefObject represents whether to represent the object with ref - * @param nested is a list of nested classes - * @param allNested represents whether to add all nested into [nested] + * @param nested is a list of nested classes + * @param allNested represents whether to add all nested into [nested] + * @param objectFieldsRequired represents whether set fields of objects as required */ - private static String getSchema(Type type, Boolean useRefObject, List> nested, boolean allNested) { + private static String getSchema(Type type, Boolean useRefObject, List> nested, boolean allNested, boolean objectFieldsRequired) { Class klass = null; if (type instanceof Class) { @@ -241,8 +251,8 @@ private static String getSchema(Type type, Boolean useRefObject, List> } if (klass != null) { - if (klass.isEnum()){ - String [] items = Arrays.stream(klass.getEnumConstants()).map(e-> getNameEnumConstant(e)).toArray(String[]::new); + if (klass.isEnum()) { + String[] items = Arrays.stream(klass.getEnumConstants()).map(e -> getNameEnumConstant(e)).toArray(String[]::new); return fieldEnumSchema(items); } @@ -282,24 +292,24 @@ private static String getSchema(Type type, Boolean useRefObject, List> if ((klass != null && (klass.isArray() || List.class.isAssignableFrom(klass) || Set.class.isAssignableFrom(klass))) || (pType != null && (List.class.isAssignableFrom((Class) pType.getRawType()) || Set.class.isAssignableFrom((Class) pType.getRawType())))) { - return fieldArraySchema(klass, pType, nested, allNested); + return fieldArraySchema(klass, pType, nested, allNested, objectFieldsRequired); } //TOOD Map - if ((klass != null && Map.class.isAssignableFrom(klass))|| pType!=null && Map.class.isAssignableFrom((Class) pType.getRawType())){ - if (pType!=null && pType.getActualTypeArguments().length > 0){ + if ((klass != null && Map.class.isAssignableFrom(klass)) || pType != null && Map.class.isAssignableFrom((Class) pType.getRawType())) { + if (pType != null && pType.getActualTypeArguments().length > 0) { Type keyType = pType.getActualTypeArguments()[0]; - if (keyType != String.class){ + if (keyType != String.class) { throw new IllegalStateException("only support Map with String key"); } } - return fieldStringKeyMapSchema(klass, pType, nested, allNested); + return fieldStringKeyMapSchema(klass, pType, nested, allNested, objectFieldsRequired); } - if (useRefObject){ + if (useRefObject) { // register this class - if ((allNested || !UnitsInfoRecorder.isDtoSchemaRegister(klass.getName())) && !nested.contains(klass)){ + if ((allNested || !UnitsInfoRecorder.isDtoSchemaRegister(klass.getName())) && !nested.contains(klass)) { nested.add(klass); } return fieldObjectRefSchema(klass.getName()); @@ -318,17 +328,17 @@ private static String getSchema(Type type, Boolean useRefObject, List> } String fieldName = getName(f); String fieldSchema = null; - if (allNested){ - fieldSchema = named(fieldName, getSchema(f.getGenericType(), true, nested, true)); - }else - fieldSchema = getOrDeriveSchema(fieldName, f.getGenericType(), true, nested); + if (allNested) { + fieldSchema = named(fieldName, getSchema(f.getGenericType(), true, nested, true, objectFieldsRequired)); + } else + fieldSchema = getOrDeriveSchema(fieldName, f.getGenericType(), true, nested, objectFieldsRequired); properties.add(fieldSchema); propertiesNames.add("\"" + fieldName + "\""); } target = target.getSuperclass(); } - return fieldObjectSchema(properties, propertiesNames); + return fieldObjectSchema(properties, propertiesNames, objectFieldsRequired); } private static boolean shouldAddToSchema(Field field) { @@ -388,47 +398,47 @@ private static String getName(Field field) { return field.getName(); } - private static String fieldArraySchema(Class klass, ParameterizedType pType, List> embedded, boolean allEmbedded) { + private static String fieldArraySchema(Class klass, ParameterizedType pType, List> embedded, boolean allEmbedded, boolean objectFieldsRequired) { String item; if (klass != null) { if (klass.isArray()) { - item = getSchema(klass.getComponentType(), true, embedded, allEmbedded); + item = getSchema(klass.getComponentType(), true, embedded, allEmbedded, objectFieldsRequired); } else { /* This would happen if we have non-generic List or Set? What to do? I guess can just use String */ - item = getSchema(String.class,true, embedded, allEmbedded); + item = getSchema(String.class, true, embedded, allEmbedded, objectFieldsRequired); } } else { //either List<> or Set<> Type generic = pType.getActualTypeArguments()[0]; - item = getSchema(generic,true, embedded, allEmbedded); + item = getSchema(generic, true, embedded, allEmbedded, objectFieldsRequired); } return "{\"type\":\"array\", \"items\":" + item + "}"; } - private static String fieldStringKeyMapSchema(Class klass, ParameterizedType pType, List> embedded, boolean allEmbedded) { + private static String fieldStringKeyMapSchema(Class klass, ParameterizedType pType, List> embedded, boolean allEmbedded, boolean objectFieldsRequired) { String value; if (klass != null) { - value = getSchema(String.class,true, embedded, allEmbedded); + value = getSchema(String.class, true, embedded, allEmbedded, objectFieldsRequired); } else { Type generic = pType.getActualTypeArguments()[1]; - value = getSchema(generic,true, embedded, allEmbedded); + value = getSchema(generic, true, embedded, allEmbedded, objectFieldsRequired); } return "{\"type\":\"object\", \"additionalProperties\":" + value + "}"; } - private static String fieldObjectSchema(List properties, List propertiesNames) { + private static String fieldObjectSchema(List properties, List propertiesNames, boolean objectFieldsRequired) { String p = properties.stream().collect(Collectors.joining(",")); String r = propertiesNames.stream().collect(Collectors.joining(",")); - if(objectFieldsRequired){ + if (objectFieldsRequired) { return "{\"type\":\"object\", \"properties\": {" + p + "}, \"required\": [" + r + "]}"; } return "{\"type\":\"object\", \"properties\": {" + p + "}}"; @@ -447,7 +457,7 @@ private static String fieldSchema(String type, String format) { } private static String fieldEnumSchema(String[] items) { - return "{\"type\":\"string\", \"enum\":["+ Arrays.stream(items).map(s-> "\""+s+"\"").collect(Collectors.joining(",")) +"]}"; + return "{\"type\":\"string\", \"enum\":[" + Arrays.stream(items).map(s -> "\"" + s + "\"").collect(Collectors.joining(",")) + "]}"; } /* @@ -463,8 +473,4 @@ private static String getNameEnumConstant(Object object) { return object.toString(); } } - - public static void setObjectFieldsRequired(boolean objectFieldsRequired){ - ClassToSchema.objectFieldsRequired = objectFieldsRequired; - } } From 803d808329d4e3372a2170c9b3a67c0de946d110 Mon Sep 17 00:00:00 2001 From: hghianni Date: Sun, 30 Jul 2023 22:02:10 -0300 Subject: [PATCH 138/345] Add test & fix --- .../java/instrumentation/object/ClassToSchema.java | 9 +++++++-- .../instrumentation/object/ClassToSchemaTest.java | 13 +++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/object/ClassToSchema.java b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/object/ClassToSchema.java index ce3e238517..53978a5cbc 100644 --- a/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/object/ClassToSchema.java +++ b/client-java/instrumentation/src/main/java/org/evomaster/client/java/instrumentation/object/ClassToSchema.java @@ -67,7 +67,12 @@ public class ClassToSchema { private static final String fieldRefPostfix = "\"}"; + public static void registerSchemaIfNeeded(Class valueType) { + registerSchemaIfNeeded(valueType, false); + } + + public static void registerSchemaIfNeeded(Class valueType, boolean objectFieldsRequired) { if (valueType == null) { return; @@ -83,7 +88,7 @@ public static void registerSchemaIfNeeded(Class valueType) { String name = valueType.getName(); if (!UnitsInfoRecorder.isDtoSchemaRegister(name)) { List> embedded = new ArrayList<>(); - String schema = ClassToSchema.getOrDeriveSchema(valueType, embedded); + String schema = ClassToSchema.getOrDeriveSchema(valueType, embedded, objectFieldsRequired); UnitsInfoRecorder.registerNewParsedDto(name, schema); ExecutionTracer.addParsedDtoName(name); if (!embedded.isEmpty()){ @@ -205,7 +210,7 @@ private static boolean isCollectionOrMap(Type type) { public static Map getOrDeriveSchemaAndNestedClasses(Class klass, boolean objectFieldsRequired) { if (!cacheMapOfDtoAndItsRefToSchemas.containsKey(klass)) { List> nested = new ArrayList<>(); - registerSchemaIfNeeded(klass); + registerSchemaIfNeeded(klass, objectFieldsRequired); findAllNestedClassAndRegisterThemIfNeeded(klass, nested, objectFieldsRequired); Map map = new LinkedHashMap<>(); for (Class nkclass : nested) { diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/object/ClassToSchemaTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/object/ClassToSchemaTest.java index 60948c3bc4..b5439e77bc 100644 --- a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/object/ClassToSchemaTest.java +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/object/ClassToSchemaTest.java @@ -208,6 +208,19 @@ public void testDtoEnum(){ verifyEnumOfFieldInProperties(obj, "bar", new String[]{"ONE", "TWO", "THREE"}); } + @Test + public void testObjectRequiredFields(){ + + String schema = ClassToSchema.getOrDeriveNonNestedSchema(DtoBase.class, true); + JsonObject json = parse(schema); + + JsonObject obj = json.get(DtoBase.class.getName()).getAsJsonObject(); + assertNotNull(obj); + assertNotNull(obj.get("required")); + assertEquals(1, obj.get("required").getAsJsonArray().size()); + assertEquals("foo", obj.get("required").getAsJsonArray().get(0).getAsString()); + } + private void checkDtoArray(JsonObject obj){ assertEquals(5, obj.get("properties").getAsJsonObject().entrySet().size()); From d05664fa940e3d8e059f087f46bb057497b27547 Mon Sep 17 00:00:00 2001 From: hghianni Date: Sun, 30 Jul 2023 22:13:42 -0300 Subject: [PATCH 139/345] Use new dto in test --- .../instrumentation/object/ClassToSchemaTest.java | 4 ++-- .../java/instrumentation/object/dtos/DtoObj.java | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/object/dtos/DtoObj.java diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/object/ClassToSchemaTest.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/object/ClassToSchemaTest.java index b5439e77bc..390a756774 100644 --- a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/object/ClassToSchemaTest.java +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/object/ClassToSchemaTest.java @@ -211,10 +211,10 @@ public void testDtoEnum(){ @Test public void testObjectRequiredFields(){ - String schema = ClassToSchema.getOrDeriveNonNestedSchema(DtoBase.class, true); + String schema = ClassToSchema.getOrDeriveNonNestedSchema(DtoObj.class, true); JsonObject json = parse(schema); - JsonObject obj = json.get(DtoBase.class.getName()).getAsJsonObject(); + JsonObject obj = json.get(DtoObj.class.getName()).getAsJsonObject(); assertNotNull(obj); assertNotNull(obj.get("required")); assertEquals(1, obj.get("required").getAsJsonArray().size()); diff --git a/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/object/dtos/DtoObj.java b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/object/dtos/DtoObj.java new file mode 100644 index 0000000000..8b645efefb --- /dev/null +++ b/client-java/instrumentation/src/test/java/org/evomaster/client/java/instrumentation/object/dtos/DtoObj.java @@ -0,0 +1,14 @@ +package org.evomaster.client.java.instrumentation.object.dtos; + +public class DtoObj { + + private String foo; + + public String getFoo() { + return foo; + } + + public void setFoo(String foo) { + this.foo = foo; + } +} From 201f35366912942a741582ff6ff9d41aac5f1451 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Mon, 31 Jul 2023 09:42:30 +0200 Subject: [PATCH 140/345] info for RPC support --- README.md | 6 ++++-- docs/write_driver.md | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 85f5138547..ed39c8b28b 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ building on decades of research in the field of [Search-Based Software Testing]( __Key features__: -* _Web APIs_: At the moment, _EvoMaster_ can generate test cases for __REST__ and __GraphQL__ APIs. +* _Web APIs_: At the moment, _EvoMaster_ can generate test cases for __REST__, __GraphQL__ and __RPC__ (e.g., __gRPC__ and __Thrift__) APIs. * _Blackbox_ testing mode: can run on any API (regardless of its programming language, e.g., Python and Go). However, results for blackbox testing will be worse than whitebox testing (e.g., due to lack of code analysis). @@ -41,7 +41,7 @@ __Key features__: JVM (e.g., Java and Kotlin). _EvoMaster_ analyses the bytecode of the tested applications, and uses several heuristics such as _testability transformations_ and _taint analysis_ to be able to generate more effective test cases. We support JDK __8__ and the major LTS versions after that (currently JDK __17__). Might work on other JVM versions, but we provide __NO__ support for it. - Note: there is initial support for other languages as well, like for example JavaScript/TypeScript, but they are not in a stable, feature-complete state yet. + Note: there is initial support for other languages as well, like for example JavaScript/TypeScript and C#, but they are not in a stable, feature-complete state yet. * _Installation_: we provide installers for the main operating systems: Windows (`.msi`), OSX (`.dmg`) and Linux (`.deb`). We also provide an uber-fat JAR file. @@ -79,6 +79,8 @@ __Known limitations__: We recommend to first try the search for 10 minutes, just to get an idea of what type of tests can be generated. But, then, you should run _EvoMaster_ for something like between 1 and 24 hours (the longer the better, but it is unlikely to get better results after 24 hours). + +* _RPC APIs_: for the moment, we do not directly support RPC schema definitions. Fuzzing RPC APIs requires to write a driver, using the client library of the API to make the calls. * _External services_: (e.g., other RESTful APIs) currently there is no support for them (e.g., to automatically mock them). It is work in progress. diff --git a/docs/write_driver.md b/docs/write_driver.md index 9a647f2ff3..8a01dbec0d 100644 --- a/docs/write_driver.md +++ b/docs/write_driver.md @@ -324,6 +324,21 @@ To test a GraphQL API, in the the `getProblemInfo()`, you need to return an inst Here, you need to specify the endpoint of where the GraphQL API can be accessed. Default value is `/graphql`. Note: must be able to do an _introspective query_ on such API to fetch its schema. If this is disabled for security reasons, _EvoMaster_ will fail. +## RPC APIs + +To test RPC APIs, in the the `getProblemInfo()`, you need to return an instance of the +`RPCProblem` class. +Fuzzing RPC APIs requires a driver, and importing a JVM version of the API client library used to communicate with it. +There is no direct support for file formats such as `.proto` and `.thrift`. +Schema definitions are derived from the Java/Kotlin interfaces of the API client library. +On the one hand, this means it is possible to fuzz any type of RPC API (and not just gRPC and Thrift), as long as there is a JVM client library. +On the other hand, _black-box_ testing would require to write a driver. + +Here, in `RPCProblem` there are 3 main things you need to specify: +- the interface for the client library, defining the operations available in the API. +- implementation for the interface, with actual client code doing the calls to the API. +- specify the RPC type (e.g., gRPC or Thrift). + ## Security The SUT might require authenticated requests (e.g., when _Spring Security_ is used). From adfeb81b6d04895081ea003df84be677d3be7ea0 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Tue, 1 Aug 2023 13:19:42 +0200 Subject: [PATCH 141/345] documentation update --- docs/write_driver.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/write_driver.md b/docs/write_driver.md index 8a01dbec0d..94a06ac69a 100644 --- a/docs/write_driver.md +++ b/docs/write_driver.md @@ -329,14 +329,16 @@ Note: must be able to do an _introspective query_ on such API to fetch its schem To test RPC APIs, in the the `getProblemInfo()`, you need to return an instance of the `RPCProblem` class. Fuzzing RPC APIs requires a driver, and importing a JVM version of the API client library used to communicate with it. -There is no direct support for file formats such as `.proto` and `.thrift`. +Note that such API client library can be generated based on the schema definition file, e.g., `.proto` and `.thrift`, but this is tool and framework dependent. +Preparing such client library for the JVM needs to be setup by the user. +Currently, there is no direct support in _EvoMaster_ for file formats such as `.proto` and `.thrift`. Schema definitions are derived from the Java/Kotlin interfaces of the API client library. On the one hand, this means it is possible to fuzz any type of RPC API (and not just gRPC and Thrift), as long as there is a JVM client library. On the other hand, _black-box_ testing would require to write a driver. Here, in `RPCProblem` there are 3 main things you need to specify: - the interface for the client library, defining the operations available in the API. -- implementation for the interface, with actual client code doing the calls to the API. +- implementation for the interface, initializing a client stub to make calls to the API for each specified interface. - specify the RPC type (e.g., gRPC or Thrift). ## Security From 939780f43b2557f2157ee2d1f03744e4f60b08d1 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Wed, 2 Aug 2023 08:22:37 +0200 Subject: [PATCH 142/345] refactoring package for Action --- .../base/BaseGraphQLApplicationTest.kt | 2 +- .../rest/service/resource/ResourceTestBase.kt | 14 +++++-- .../org/evomaster/core/database/DbAction.kt | 2 +- .../evomaster/core/database/DbActionResult.kt | 4 +- .../evomaster/core/database/DbActionUtils.kt | 6 +-- .../org/evomaster/core/mongo/MongoDbAction.kt | 2 +- .../core/mongo/MongoDbActionResult.kt | 4 +- .../output/service/GraphQLTestCaseWriter.kt | 4 +- .../output/service/HttpWsTestCaseWriter.kt | 2 +- .../core/output/service/NoTestCaseWriter.kt | 4 +- .../core/output/service/RPCTestCaseWriter.kt | 4 +- .../core/output/service/RestTestCaseWriter.kt | 4 +- .../core/output/service/TestCaseWriter.kt | 18 ++++----- .../core/output/service/WebTestCaseWriter.kt | 4 +- .../evomaster/core/problem/api/ApiWsAction.kt | 2 +- .../core/problem/api/ApiWsIndividual.kt | 2 +- .../core/problem/api/service/ApiWsFitness.kt | 4 +- .../api/service/ApiWsStructureMutator.kt | 2 +- .../enterprise/EnterpriseActionGroup.kt | 2 + .../enterprise/EnterpriseIndividual.kt | 3 ++ .../enterprise/service/EnterpriseFitness.kt | 4 +- .../ApiExternalServiceAction.kt | 2 +- .../core/problem/graphql/GraphQLAction.kt | 2 +- .../core/problem/graphql/GraphQLIndividual.kt | 3 ++ .../core/problem/graphql/GraphQlCallResult.kt | 4 +- .../graphql/builder/GraphQLActionBuilder.kt | 4 +- .../graphql/service/GraphQLBlackBoxFitness.kt | 2 +- .../problem/graphql/service/GraphQLFitness.kt | 2 +- .../evomaster/core/problem/gui/GuiAction.kt | 2 +- .../core/problem/gui/GuiIndividual.kt | 2 +- .../core/problem/httpws/HttpWsCallResult.kt | 2 +- .../httpws/auth/HttpWsAuthenticationInfo.kt | 2 +- .../problem/httpws/service/HttpWsFitness.kt | 2 +- .../core/problem/rest/RestActionBuilderV3.kt | 18 ++++----- .../core/problem/rest/RestCallAction.kt | 2 +- .../core/problem/rest/RestCallResult.kt | 4 +- .../core/problem/rest/RestIndividual.kt | 5 ++- .../problem/rest/resource/ResourceCluster.kt | 2 +- .../rest/resource/RestResourceCalls.kt | 4 ++ .../problem/rest/resource/RestResourceNode.kt | 7 ++-- .../rest/service/AbstractRestFitness.kt | 4 +- .../rest/service/AbstractRestSampler.kt | 2 +- .../rest/service/BlackBoxRestFitness.kt | 2 +- .../rest/service/ResourceDepManageService.kt | 4 +- .../rest/service/ResourceManageService.kt | 4 +- .../problem/rest/service/ResourceSampler.kt | 2 +- .../core/problem/rest/service/RestFitness.kt | 4 +- .../rest/service/RestResourceFitness.kt | 4 +- .../service/RestResourceStructureMutator.kt | 2 +- .../rest/service/RestStructureMutator.kt | 2 +- .../core/problem/rpc/RPCCallResult.kt | 4 +- .../core/problem/rpc/RPCIndividual.kt | 3 ++ .../problem/rpc/auth/RPCAuthenticationInfo.kt | 2 +- .../rpc/service/RPCEndpointsHandler.kt | 4 +- .../core/problem/rpc/service/RPCFitness.kt | 2 +- .../core/problem/rpc/service/RPCSampler.kt | 2 +- .../util/RestResourceTemplateHandler.kt | 2 +- .../problem/util/inference/SimpleDeriveR2T.kt | 2 +- .../core/problem/webfrontend/WebIndividual.kt | 2 +- .../core/problem/webfrontend/WebResult.kt | 4 +- .../problem/webfrontend/service/WebFitness.kt | 2 +- .../evomaster/core/search/EvaluatedAction.kt | 2 + .../core/search/EvaluatedIndividual.kt | 4 +- .../org/evomaster/core/search/Individual.kt | 4 ++ .../core/search/StructuralElement.kt | 1 + .../core/search/{ => action}/Action.kt | 4 +- .../search/{ => action}/ActionComponent.kt | 10 +++-- .../{ => action}/ActionDependentGroup.kt | 5 ++- .../core/search/{ => action}/ActionFilter.kt | 2 +- .../core/search/{ => action}/ActionResult.kt | 5 +-- .../core/search/{ => action}/ActionTree.kt | 9 +++-- .../impactinfocollection/ImpactOfAction.kt | 2 +- .../impactinfocollection/ImpactUtils.kt | 4 +- .../ImpactsOfIndividual.kt | 4 +- .../InitializationActionImpacts.kt | 8 ++-- .../search/service/ExecutionInfoReporter.kt | 2 +- .../evomaster/core/search/service/Sampler.kt | 2 +- .../service/monitor/InterfaceAdapter.kt | 2 +- .../mutator/MutatedGeneSpecification.kt | 20 +++++----- .../search/service/mutator/StandardMutator.kt | 2 +- .../genemutation/ArchiveGeneMutator.kt | 4 +- .../genemutation/ArchiveMutationUtils.kt | 4 +- .../org/evomaster/core/taint/TaintAnalysis.kt | 2 +- .../core/output/EvaluatedIndividualBuilder.kt | 2 + .../problem/graphql/PetClinicCheckMain.kt | 2 +- .../builder/GraphQLActionBuilderTest.kt | 2 +- .../problem/rest/RestActionBuilderV3Test.kt | 2 +- .../core/problem/rest/SameObjectNameTest.kt | 2 +- .../individual/RestIndividualResourceTest.kt | 2 +- .../RestResourceIndividualDisabledHMTest.kt | 2 +- .../resource/ResourceNodeCreationChainTest.kt | 4 +- .../problem/rest/resource/ResourceNodeTest.kt | 4 +- .../rest/resource/ResourceNodeWithDbTest.kt | 40 +++++++++---------- .../rest/seeding/postman/PostmanParserTest.kt | 2 +- .../algorithms/constant/ConstantAction.kt | 2 +- .../search/algorithms/onemax/OneMaxAction.kt | 2 +- .../core/search/gene/GeneUtilsTest.kt | 2 +- .../core/search/gene/binding/BindingAction.kt | 2 +- .../search/gene/binding/BindingIndividual.kt | 2 +- .../individual/IndividualGeneImpactTest.kt | 1 + .../matchproblem/PrimitiveTypeMatchAction.kt | 2 +- .../mutationweight/GeneWeightTestSchema.kt | 2 +- .../action/GraphQLActionStructureTest.kt | 2 +- .../action/RestCallStructureTest.kt | 2 +- .../RestResourceCallStructureTest.kt | 4 +- .../core/utils/BindingBuilderTest.kt | 2 +- .../e2etests/utils/RestTestBase.java | 2 + .../resource/ResourceMIOHWTestBase.java | 4 +- .../examples/resource/ResourceTestBase.java | 2 +- .../ResourceDbMIOAndAdaptiveHMBasicTest.java | 2 +- .../resource/db/ResourceDbMIOBasicTest.java | 3 +- ...esourceDbMIOAndHypermutationBasicTest.java | 2 +- .../resource/ResourceMIOHWTestBase.java | 4 +- .../examples/resource/ResourceTestBase.java | 2 +- .../ResourceDbMIOAndAdaptiveHMBasicTest.java | 2 +- .../resource/db/ResourceDbMIOBasicTest.java | 2 +- ...esourceDbMIOAndHypermutationBasicTest.java | 2 +- .../ExternalServiceMockingFlakyEMTest.java | 4 +- .../numericstring/RPCSchemaHandlerTest.java | 2 +- 119 files changed, 246 insertions(+), 201 deletions(-) rename core/src/main/kotlin/org/evomaster/core/search/{ => action}/Action.kt (95%) rename core/src/main/kotlin/org/evomaster/core/search/{ => action}/ActionComponent.kt (61%) rename core/src/main/kotlin/org/evomaster/core/search/{ => action}/ActionDependentGroup.kt (84%) rename core/src/main/kotlin/org/evomaster/core/search/{ => action}/ActionFilter.kt (94%) rename core/src/main/kotlin/org/evomaster/core/search/{ => action}/ActionResult.kt (92%) rename core/src/main/kotlin/org/evomaster/core/search/{ => action}/ActionTree.kt (65%) diff --git a/core-graphql-it/src/test/kotlin/com/foo/graphql/base/BaseGraphQLApplicationTest.kt b/core-graphql-it/src/test/kotlin/com/foo/graphql/base/BaseGraphQLApplicationTest.kt index 8cda3ab2ea..322d9e53f5 100644 --- a/core-graphql-it/src/test/kotlin/com/foo/graphql/base/BaseGraphQLApplicationTest.kt +++ b/core-graphql-it/src/test/kotlin/com/foo/graphql/base/BaseGraphQLApplicationTest.kt @@ -4,7 +4,7 @@ import org.evomaster.client.java.controller.problem.GraphQlProblem import org.evomaster.core.problem.graphql.GraphQLAction import org.evomaster.core.problem.graphql.builder.GraphQLActionBuilder import org.evomaster.core.problem.graphql.IntrospectiveQuery -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.* import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test diff --git a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/ResourceTestBase.kt b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/ResourceTestBase.kt index a14f36241e..eb1dcd8bc2 100644 --- a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/ResourceTestBase.kt +++ b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/ResourceTestBase.kt @@ -9,6 +9,8 @@ import org.evomaster.client.java.controller.internal.db.SchemaExtractor import org.evomaster.core.BaseModule import org.evomaster.core.EMConfig import org.evomaster.core.TestUtils +import org.evomaster.core.search.action.ActionFilter +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.database.DatabaseExecutor import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionResult @@ -157,7 +159,8 @@ abstract class ResourceTestBase : ExtractTestBaseH2(), ResourceBasedTestInterfac assertEquals(1, this!!.getResourceCalls().size) getResourceCalls().first().apply { assertTrue(!template!!.independent || seeActions(ActionFilter.ONLY_SQL).isNotEmpty()){ - "the first call with $method should not be independent, but ${template!!.template} with ${seeActionSize(ActionFilter.ONLY_SQL)} dbActions" + "the first call with $method should not be independent, but ${template!!.template} with ${seeActionSize( + ActionFilter.ONLY_SQL)} dbActions" } } @@ -166,7 +169,8 @@ abstract class ResourceTestBase : ExtractTestBaseH2(), ResourceBasedTestInterfac assertEquals(2, this!!.getResourceCalls().size) getResourceCalls().first().apply { assertTrue(!template!!.independent || seeActions(ActionFilter.ONLY_SQL).isNotEmpty()){ - "the first call with $method should not be independent, but ${template!!.template} with ${seeActionSize(ActionFilter.ONLY_SQL)} dbActions" + "the first call with $method should not be independent, but ${template!!.template} with ${seeActionSize( + ActionFilter.ONLY_SQL)} dbActions" } } @@ -175,7 +179,8 @@ abstract class ResourceTestBase : ExtractTestBaseH2(), ResourceBasedTestInterfac assertTrue(2 <= this!!.getResourceCalls().size) getResourceCalls().first().apply { assertTrue(!template!!.independent || seeActions(ActionFilter.ONLY_SQL).isNotEmpty()){ - "the first call with $method should not be independent, but ${template!!.template} with ${seeActionSize(ActionFilter.ONLY_SQL)} dbActions" + "the first call with $method should not be independent, but ${template!!.template} with ${seeActionSize( + ActionFilter.ONLY_SQL)} dbActions" } } } @@ -445,7 +450,8 @@ abstract class ResourceTestBase : ExtractTestBaseH2(), ResourceBasedTestInterfac } - private fun generateIndividualResults(individual: Individual) : List = individual.seeActions(ActionFilter.ALL).map { + private fun generateIndividualResults(individual: Individual) : List = individual.seeActions( + ActionFilter.ALL).map { if (it is DbAction) DbActionResult().also { it.setInsertExecutionResult(true) } else ActionResult() } diff --git a/core/src/main/kotlin/org/evomaster/core/database/DbAction.kt b/core/src/main/kotlin/org/evomaster/core/database/DbAction.kt index 3ac7f5ccf6..8df175946b 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/DbAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/DbAction.kt @@ -2,7 +2,7 @@ package org.evomaster.core.database import org.evomaster.core.database.schema.Column import org.evomaster.core.database.schema.Table -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.Gene import org.evomaster.core.search.gene.placeholder.ImmutableDataHolderGene import org.evomaster.core.search.gene.string.StringGene diff --git a/core/src/main/kotlin/org/evomaster/core/database/DbActionResult.kt b/core/src/main/kotlin/org/evomaster/core/database/DbActionResult.kt index 9b2fe746fb..d9b5fc1cb8 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/DbActionResult.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/DbActionResult.kt @@ -1,6 +1,6 @@ package org.evomaster.core.database -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionResult /** * sql insert action execution result diff --git a/core/src/main/kotlin/org/evomaster/core/database/DbActionUtils.kt b/core/src/main/kotlin/org/evomaster/core/database/DbActionUtils.kt index 4f030c6f1f..5944586ea4 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/DbActionUtils.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/DbActionUtils.kt @@ -1,7 +1,7 @@ package org.evomaster.core.database import org.evomaster.core.Lazy -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.Gene import org.evomaster.core.search.gene.sql.SqlForeignKeyGene import org.evomaster.core.search.gene.sql.SqlPrimaryKeyGene @@ -230,8 +230,8 @@ object DbActionUtils { * If no such gene is found, the function returns the tuple (-1,null). */ private fun findFirstOffendingGeneWithIndex( - actions: List, - randomness: Randomness? = null + actions: List, + randomness: Randomness? = null ): Pair { /* diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt index 7a67f2cc88..b81214f428 100644 --- a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt @@ -1,7 +1,7 @@ package org.evomaster.core.mongo import org.evomaster.core.problem.rest.RestActionBuilderV3.createObjectGenesForDTOs -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.Gene import java.util.* diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionResult.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionResult.kt index a2a31848e3..b8545019df 100644 --- a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionResult.kt +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbActionResult.kt @@ -1,7 +1,7 @@ package org.evomaster.core.mongo -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionResult /** * sql insert action execution result diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/GraphQLTestCaseWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/GraphQLTestCaseWriter.kt index 39fd4e4eb4..228ed01d42 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/GraphQLTestCaseWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/GraphQLTestCaseWriter.kt @@ -9,8 +9,8 @@ import org.evomaster.core.problem.graphql.GraphQlCallResult import org.evomaster.core.problem.graphql.service.GraphQLFitness import org.evomaster.core.problem.httpws.HttpWsAction import org.evomaster.core.problem.httpws.HttpWsCallResult -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.gene.utils.GeneUtils import org.slf4j.LoggerFactory diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/HttpWsTestCaseWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/HttpWsTestCaseWriter.kt index d9ab137a26..bc2fda85b7 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/HttpWsTestCaseWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/HttpWsTestCaseWriter.kt @@ -12,7 +12,7 @@ import org.evomaster.core.problem.httpws.HttpWsAction import org.evomaster.core.problem.httpws.HttpWsCallResult import org.evomaster.core.problem.rest.param.BodyParam import org.evomaster.core.problem.rest.param.HeaderParam -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.search.EvaluatedAction import org.evomaster.core.search.FitnessValue import org.evomaster.core.search.gene.utils.GeneUtils diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/NoTestCaseWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/NoTestCaseWriter.kt index faac5ec7c7..0008350e0d 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/NoTestCaseWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/NoTestCaseWriter.kt @@ -1,8 +1,8 @@ package org.evomaster.core.output.service import org.evomaster.core.output.Lines -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.search.EvaluatedIndividual import java.nio.file.Path diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/RPCTestCaseWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/RPCTestCaseWriter.kt index 2e63abedc4..44d296146b 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/RPCTestCaseWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/RPCTestCaseWriter.kt @@ -11,8 +11,8 @@ import org.evomaster.core.problem.rpc.RPCCallAction import org.evomaster.core.problem.rpc.RPCCallResult import org.evomaster.core.problem.rpc.RPCIndividual import org.evomaster.core.problem.rpc.service.RPCEndpointsHandler -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.gene.utils.GeneUtils import org.slf4j.Logger diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/RestTestCaseWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/RestTestCaseWriter.kt index 43fbeac259..a10fffc6f2 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/RestTestCaseWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/RestTestCaseWriter.kt @@ -9,8 +9,8 @@ import org.evomaster.core.problem.httpws.HttpWsCallResult import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestCallResult import org.evomaster.core.problem.rest.RestIndividual -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.Individual import org.evomaster.core.search.gene.utils.GeneUtils diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/TestCaseWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/TestCaseWriter.kt index eab99856a4..e3296ea326 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/TestCaseWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/TestCaseWriter.kt @@ -9,8 +9,8 @@ import org.evomaster.core.output.service.TestWriterUtils.Companion.getWireMockVa import org.evomaster.core.problem.externalservice.httpws.HttpWsExternalService import org.evomaster.core.problem.externalservice.httpws.HttpExternalServiceAction import org.evomaster.core.problem.externalservice.httpws.param.HttpWsResponseParam -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.search.EvaluatedIndividual import org.slf4j.LoggerFactory import java.nio.file.Files @@ -225,13 +225,13 @@ abstract class TestCaseWriter { open fun addExtraInitStatement(lines: Lines) {} protected fun addActionInTryCatch( - call: Action, - index: Int, - testCaseName: String, - lines: Lines, - res: ActionResult, - testSuitePath: Path?, - baseUrlOfSut: String + call: Action, + index: Int, + testCaseName: String, + lines: Lines, + res: ActionResult, + testSuitePath: Path?, + baseUrlOfSut: String ) { when { /* diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/WebTestCaseWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/WebTestCaseWriter.kt index f43b19bf7b..c8943926a9 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/WebTestCaseWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/WebTestCaseWriter.kt @@ -2,8 +2,8 @@ package org.evomaster.core.output.service import org.evomaster.core.output.Lines import org.evomaster.core.problem.webfrontend.* -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.search.EvaluatedIndividual import java.lang.IllegalStateException import java.nio.file.Path diff --git a/core/src/main/kotlin/org/evomaster/core/problem/api/ApiWsAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/api/ApiWsAction.kt index bb3b3cbf83..a21dc9bffc 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/api/ApiWsAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/api/ApiWsAction.kt @@ -2,7 +2,7 @@ package org.evomaster.core.problem.api import org.evomaster.core.problem.api.auth.AuthenticationInfo import org.evomaster.core.problem.api.param.Param -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action /** * an action for handling API diff --git a/core/src/main/kotlin/org/evomaster/core/problem/api/ApiWsIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/api/ApiWsIndividual.kt index d2b3fa86d7..70326a3ecd 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/api/ApiWsIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/api/ApiWsIndividual.kt @@ -2,7 +2,7 @@ package org.evomaster.core.problem.api import org.evomaster.core.problem.enterprise.EnterpriseIndividual import org.evomaster.core.problem.enterprise.SampleType -import org.evomaster.core.search.ActionComponent +import org.evomaster.core.search.action.ActionComponent import org.evomaster.core.search.GroupsOfChildren import org.evomaster.core.search.StructuralElement import org.evomaster.core.search.tracer.TrackOperator diff --git a/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsFitness.kt index ef82e6d50e..63b3018f58 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsFitness.kt @@ -15,8 +15,8 @@ import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.output.service.TestSuiteWriter import org.evomaster.core.problem.enterprise.service.EnterpriseFitness import org.evomaster.core.remote.service.RemoteController -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.search.FitnessValue import org.evomaster.core.search.Individual import org.evomaster.core.search.gene.string.StringGene diff --git a/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt b/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt index e28ae46ec0..4c44df83ed 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt @@ -14,7 +14,7 @@ import org.evomaster.core.problem.externalservice.httpws.service.HarvestActualHt import org.evomaster.core.problem.externalservice.httpws.service.HttpWsExternalServiceHandler import org.evomaster.core.problem.externalservice.httpws.HttpExternalServiceAction import org.evomaster.core.problem.externalservice.httpws.param.HttpWsResponseParam -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.GroupsOfChildren import org.evomaster.core.search.Individual diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseActionGroup.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseActionGroup.kt index fdce4d8516..585c41a26f 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseActionGroup.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseActionGroup.kt @@ -1,5 +1,7 @@ package org.evomaster.core.problem.enterprise +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionDependentGroup import org.evomaster.core.problem.externalservice.ApiExternalServiceAction import org.evomaster.core.search.* diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt index 2285f28459..eb87d3cf31 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt @@ -1,6 +1,9 @@ package org.evomaster.core.problem.enterprise import org.evomaster.core.Lazy +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionComponent +import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionUtils import org.evomaster.core.mongo.MongoDbAction diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt index 3317a1436a..b8c13c59af 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt @@ -16,8 +16,8 @@ import org.evomaster.core.mongo.MongoDbActionResult import org.evomaster.core.mongo.MongoDbActionTransformer import org.evomaster.core.mongo.MongoExecution import org.evomaster.core.remote.service.RemoteController -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.search.FitnessValue import org.evomaster.core.search.Individual import org.evomaster.core.search.gene.sql.SqlAutoIncrementGene diff --git a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/ApiExternalServiceAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/ApiExternalServiceAction.kt index 9297629933..58cae726f9 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/ApiExternalServiceAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/ApiExternalServiceAction.kt @@ -1,7 +1,7 @@ package org.evomaster.core.problem.externalservice import org.evomaster.core.problem.externalservice.param.ResponseParam -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action abstract class ApiExternalServiceAction( /** diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLAction.kt index 883107712a..1d12acfd95 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLAction.kt @@ -4,7 +4,7 @@ import org.evomaster.core.problem.httpws.HttpWsAction import org.evomaster.core.problem.httpws.auth.HttpWsAuthenticationInfo import org.evomaster.core.problem.httpws.auth.NoAuth import org.evomaster.core.problem.api.param.Param -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.Gene diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLIndividual.kt index b34b6c91f8..b16f5cd19e 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLIndividual.kt @@ -1,5 +1,8 @@ package org.evomaster.core.problem.graphql +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionComponent +import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionUtils import org.evomaster.core.mongo.MongoDbAction diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQlCallResult.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQlCallResult.kt index e43a7eb649..e548f1e94f 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQlCallResult.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQlCallResult.kt @@ -2,8 +2,8 @@ package org.evomaster.core.problem.graphql import com.google.common.annotations.VisibleForTesting import org.evomaster.core.problem.httpws.HttpWsCallResult -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionResult class GraphQlCallResult : HttpWsCallResult { diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/builder/GraphQLActionBuilder.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/builder/GraphQLActionBuilder.kt index 977e7c2d34..e12b657cb8 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/builder/GraphQLActionBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/builder/GraphQLActionBuilder.kt @@ -11,7 +11,7 @@ import org.evomaster.core.problem.graphql.param.GQReturnParam import org.evomaster.core.problem.graphql.schema.* import org.evomaster.core.problem.graphql.schema.__TypeKind.* import org.evomaster.core.remote.SutProblemException -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.collection.ArrayGene import org.evomaster.core.search.gene.collection.EnumGene @@ -156,7 +156,7 @@ object GraphQLActionBuilder { actionCluster: MutableMap, state: TempState, - ) { + ) { val action:GraphQLAction = if (state.inputTypeName[element.fieldName]?.isNotEmpty() == true) GraphQLAction(actionId, state.inputTypeName[element.fieldName].toString(), type, params) else diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLBlackBoxFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLBlackBoxFitness.kt index af4adbc9fe..e3a490ad32 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLBlackBoxFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLBlackBoxFitness.kt @@ -5,7 +5,7 @@ import org.evomaster.core.problem.graphql.GraphQLAction import org.evomaster.core.problem.graphql.GraphQLIndividual import org.evomaster.core.problem.graphql.GraphQlCallResult import org.evomaster.core.problem.httpws.HttpWsCallResult -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.FitnessValue import org.slf4j.Logger diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLFitness.kt index 2a72c4a35e..0dc8140376 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLFitness.kt @@ -11,7 +11,7 @@ import org.evomaster.core.problem.graphql.* import org.evomaster.core.problem.httpws.service.HttpWsFitness import org.evomaster.core.problem.httpws.auth.NoAuth import org.evomaster.core.remote.TcpUtils -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.FitnessValue import org.evomaster.core.search.gene.utils.GeneUtils diff --git a/core/src/main/kotlin/org/evomaster/core/problem/gui/GuiAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/gui/GuiAction.kt index ee562bef4f..f811659936 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/gui/GuiAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/gui/GuiAction.kt @@ -1,6 +1,6 @@ package org.evomaster.core.problem.gui -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.StructuralElement diff --git a/core/src/main/kotlin/org/evomaster/core/problem/gui/GuiIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/gui/GuiIndividual.kt index 06ec8f0e06..67b3a512a1 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/gui/GuiIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/gui/GuiIndividual.kt @@ -2,7 +2,7 @@ package org.evomaster.core.problem.gui import org.evomaster.core.problem.enterprise.EnterpriseIndividual import org.evomaster.core.problem.enterprise.SampleType -import org.evomaster.core.search.ActionComponent +import org.evomaster.core.search.action.ActionComponent import org.evomaster.core.search.GroupsOfChildren import org.evomaster.core.search.StructuralElement import org.evomaster.core.search.tracer.TrackOperator diff --git a/core/src/main/kotlin/org/evomaster/core/problem/httpws/HttpWsCallResult.kt b/core/src/main/kotlin/org/evomaster/core/problem/httpws/HttpWsCallResult.kt index ef1ea3dea5..e3f4adc5af 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/httpws/HttpWsCallResult.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/httpws/HttpWsCallResult.kt @@ -3,7 +3,7 @@ package org.evomaster.core.problem.httpws import com.google.common.annotations.VisibleForTesting import com.google.gson.Gson import com.google.gson.JsonSyntaxException -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.ActionResult import javax.ws.rs.core.MediaType abstract class HttpWsCallResult : ActionResult { diff --git a/core/src/main/kotlin/org/evomaster/core/problem/httpws/auth/HttpWsAuthenticationInfo.kt b/core/src/main/kotlin/org/evomaster/core/problem/httpws/auth/HttpWsAuthenticationInfo.kt index 32e1855ba4..ef2a82ae0d 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/httpws/auth/HttpWsAuthenticationInfo.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/httpws/auth/HttpWsAuthenticationInfo.kt @@ -2,7 +2,7 @@ package org.evomaster.core.problem.httpws.auth import org.evomaster.core.problem.api.auth.AuthenticationInfo import org.evomaster.core.problem.rest.RestCallAction -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action /** * should be immutable diff --git a/core/src/main/kotlin/org/evomaster/core/problem/httpws/service/HttpWsFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/httpws/service/HttpWsFitness.kt index 8fb3aae4e4..257c9bef72 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/httpws/service/HttpWsFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/httpws/service/HttpWsFitness.kt @@ -15,7 +15,7 @@ import org.evomaster.core.problem.httpws.HttpWsCallResult import org.evomaster.core.problem.rest.* import org.evomaster.core.problem.rest.param.HeaderParam import org.evomaster.core.remote.SutProblemException -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.Individual import org.glassfish.jersey.client.ClientConfig import org.glassfish.jersey.client.ClientProperties diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3.kt index 56d4f13dd0..839c367ad7 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3.kt @@ -19,7 +19,7 @@ import org.evomaster.core.parser.RegexHandler import org.evomaster.core.problem.api.param.Param import org.evomaster.core.problem.rest.param.* import org.evomaster.core.remote.SutProblemException -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.collection.ArrayGene import org.evomaster.core.search.gene.collection.EnumGene @@ -260,14 +260,14 @@ object RestActionBuilderV3 { private fun handleOperation( - actionCluster: MutableMap, - verb: HttpVerb, - restPath: RestPath, - operation: Operation, - swagger: OpenAPI, - doParseDescription: Boolean, - enableConstraintHandling: Boolean, - errorEndpoints : MutableList = mutableListOf() + actionCluster: MutableMap, + verb: HttpVerb, + restPath: RestPath, + operation: Operation, + swagger: OpenAPI, + doParseDescription: Boolean, + enableConstraintHandling: Boolean, + errorEndpoints : MutableList = mutableListOf() ) { try{ diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestCallAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestCallAction.kt index 197d790376..6709b25341 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestCallAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestCallAction.kt @@ -11,7 +11,7 @@ import org.evomaster.core.problem.rest.resource.ActionRToken import org.evomaster.core.problem.util.ParamUtil import org.evomaster.core.problem.rest.util.ParserUtil import org.evomaster.core.problem.util.BindingBuilder -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.Gene import org.evomaster.core.search.gene.optional.OptionalGene import org.evomaster.core.search.service.Randomness diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestCallResult.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestCallResult.kt index d18b02b407..2d48ec715c 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestCallResult.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestCallResult.kt @@ -4,8 +4,8 @@ import com.google.common.annotations.VisibleForTesting import com.google.gson.Gson import com.google.gson.JsonObject import org.evomaster.core.problem.httpws.HttpWsCallResult -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionResult import javax.ws.rs.core.MediaType diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt index 8e00d1187b..28fed86bb3 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt @@ -1,5 +1,8 @@ package org.evomaster.core.problem.rest +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionComponent +import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionUtils import org.evomaster.core.mongo.MongoDbAction @@ -9,7 +12,7 @@ import org.evomaster.core.problem.externalservice.ApiExternalServiceAction import org.evomaster.core.problem.rest.resource.RestResourceCalls import org.evomaster.core.problem.rest.resource.SamplerSpecification import org.evomaster.core.search.* -import org.evomaster.core.search.ActionFilter.* +import org.evomaster.core.search.action.ActionFilter.* import org.evomaster.core.search.gene.* import org.evomaster.core.search.tracer.Traceable import org.evomaster.core.search.tracer.TraceableElementCopyFilter diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/ResourceCluster.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/ResourceCluster.kt index ffbd85640e..45aa4e90a2 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/ResourceCluster.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/ResourceCluster.kt @@ -10,7 +10,7 @@ import org.evomaster.core.database.schema.Table import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.util.inference.SimpleDeriveResourceBinding -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.service.Randomness /** diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceCalls.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceCalls.kt index 7e5329b07c..70e9dc7c18 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceCalls.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceCalls.kt @@ -1,6 +1,10 @@ package org.evomaster.core.problem.rest.resource import org.evomaster.core.Lazy +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionComponent +import org.evomaster.core.search.action.ActionFilter +import org.evomaster.core.search.action.ActionTree import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionUtils import org.evomaster.core.mongo.MongoDbAction diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceNode.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceNode.kt index aa34b4b62c..257bd34345 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceNode.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceNode.kt @@ -12,8 +12,8 @@ import org.evomaster.core.problem.rest.resource.dependency.* import org.evomaster.core.problem.util.ParamUtil import org.evomaster.core.problem.rest.util.ParserUtil import org.evomaster.core.problem.util.RestResourceTemplateHandler -import org.evomaster.core.search.ActionFilter -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.ActionFilter +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.search.gene.Gene import org.evomaster.core.search.gene.ObjectGene import org.evomaster.core.search.gene.sql.SqlForeignKeyGene @@ -327,7 +327,8 @@ open class RestResourceNode( * generated another resource calls which differs from [calls] */ fun generateAnother(calls : RestResourceCalls, randomness: Randomness, maxTestSize: Int) : RestResourceCalls?{ - val current = calls.template?.template?: RestResourceTemplateHandler.getStringTemplateByActions(calls.seeActions(ActionFilter.NO_SQL).filterIsInstance()) + val current = calls.template?.template?: RestResourceTemplateHandler.getStringTemplateByActions(calls.seeActions( + ActionFilter.NO_SQL).filterIsInstance()) val rest = templates.filter { it.value.template != current} if(rest.isEmpty()) return null val selected = randomness.choose(rest.keys) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestFitness.kt index e975911d0c..b705829ae3 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestFitness.kt @@ -21,8 +21,8 @@ import org.evomaster.core.problem.rest.param.UpdateForBodyParam import org.evomaster.core.problem.util.ParserDtoUtil import org.evomaster.core.remote.SutProblemException import org.evomaster.core.remote.TcpUtils -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.search.FitnessValue import org.evomaster.core.search.Individual import org.evomaster.core.search.gene.* diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt index 627ba9daef..7aec725dc6 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/AbstractRestSampler.kt @@ -19,7 +19,7 @@ import org.evomaster.core.problem.rest.param.QueryParam import org.evomaster.core.problem.rest.seeding.Parser import org.evomaster.core.problem.rest.seeding.postman.PostmanParser import org.evomaster.core.remote.SutProblemException -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.optional.CustomMutationRateGene import org.evomaster.core.search.gene.optional.OptionalGene import org.evomaster.core.search.gene.string.StringGene diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/BlackBoxRestFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/BlackBoxRestFitness.kt index 22730b3d99..d78e48c4b6 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/BlackBoxRestFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/BlackBoxRestFitness.kt @@ -4,7 +4,7 @@ import org.evomaster.client.java.controller.api.dto.AdditionalInfoDto import org.evomaster.core.problem.httpws.HttpWsCallResult import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestIndividual -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.FitnessValue import org.slf4j.Logger diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceDepManageService.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceDepManageService.kt index 865b9c9fa7..5860e245ee 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceDepManageService.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceDepManageService.kt @@ -22,8 +22,8 @@ import org.evomaster.core.problem.rest.resource.dependency.SelfResourcesRelation import org.evomaster.core.problem.util.inference.SimpleDeriveResourceBinding import org.evomaster.core.problem.util.inference.model.ParamGeneBindMap import org.evomaster.core.problem.util.StringSimilarityComparator -import org.evomaster.core.search.ActionFilter -import org.evomaster.core.search.ActionFilter.* +import org.evomaster.core.search.action.ActionFilter +import org.evomaster.core.search.action.ActionFilter.* import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.service.AdaptiveParameterControl import org.evomaster.core.search.service.Randomness diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceManageService.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceManageService.kt index 0166441467..c079098c15 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceManageService.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceManageService.kt @@ -11,8 +11,8 @@ import org.evomaster.core.problem.rest.* import org.evomaster.core.problem.httpws.auth.HttpWsAuthenticationInfo import org.evomaster.core.problem.rest.resource.* import org.evomaster.core.problem.util.RestResourceTemplateHandler -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionFilter +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.gene.utils.GeneUtils import org.evomaster.core.search.service.AdaptiveParameterControl import org.evomaster.core.search.service.Randomness diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampler.kt index ffaf6b797f..08ae3e9a4d 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampler.kt @@ -8,7 +8,7 @@ import org.evomaster.core.problem.rest.* import org.evomaster.core.problem.httpws.auth.NoAuth import org.evomaster.core.problem.rest.resource.RestResourceCalls import org.evomaster.core.problem.rest.resource.SamplerSpecification -import org.evomaster.core.search.ActionFilter +import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.service.Randomness import org.evomaster.core.search.tracer.Traceable diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt index 99fd953243..84ebaad959 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt @@ -5,8 +5,8 @@ import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestCallResult import org.evomaster.core.problem.rest.RestIndividual -import org.evomaster.core.search.ActionFilter -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.ActionFilter +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.FitnessValue import org.slf4j.Logger diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt index 58fde45df2..9ed447c594 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt @@ -10,8 +10,8 @@ import org.evomaster.core.problem.externalservice.httpws.HttpExternalServiceRequ import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestCallResult import org.evomaster.core.problem.rest.RestIndividual -import org.evomaster.core.search.ActionFilter -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.ActionFilter +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.FitnessValue import org.slf4j.Logger diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceStructureMutator.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceStructureMutator.kt index 5ea231e82d..c0b2a718ff 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceStructureMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceStructureMutator.kt @@ -12,7 +12,7 @@ import org.evomaster.core.problem.rest.resource.ResourceImpactOfIndividual import org.evomaster.core.problem.rest.resource.RestResourceCalls import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.Individual -import org.evomaster.core.search.ActionFilter.* +import org.evomaster.core.search.action.ActionFilter.* import org.evomaster.core.search.impact.impactinfocollection.value.numeric.IntegerGeneImpact import org.evomaster.core.search.service.mutator.MutatedGeneSpecification import org.evomaster.core.search.service.mutator.MutationWeightControl diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestStructureMutator.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestStructureMutator.kt index ad52952429..de2753a608 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestStructureMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestStructureMutator.kt @@ -7,7 +7,7 @@ import org.evomaster.core.problem.api.service.ApiWsStructureMutator import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.rest.* import org.evomaster.core.problem.rest.resource.RestResourceCalls -import org.evomaster.core.search.ActionFilter +import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.Individual import org.evomaster.core.search.service.mutator.MutatedGeneSpecification diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCCallResult.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCCallResult.kt index cbe4e57f65..6689f4d38d 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCCallResult.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCCallResult.kt @@ -6,8 +6,8 @@ import org.evomaster.client.java.controller.api.dto.problem.rpc.RPCExceptionInfo import org.evomaster.client.java.controller.api.dto.problem.rpc.exception.RPCExceptionCategory import org.evomaster.client.java.controller.api.dto.problem.rpc.exception.RPCExceptionType import org.evomaster.core.Lazy -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionResult /** * define RPC call result with various situations, diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt index a60a734685..02cb11ec2d 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt @@ -1,6 +1,9 @@ package org.evomaster.core.problem.rpc import org.evomaster.core.Lazy +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionComponent +import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionUtils import org.evomaster.core.mongo.MongoDbAction diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/auth/RPCAuthenticationInfo.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/auth/RPCAuthenticationInfo.kt index 5231811fff..21fe3a7ce8 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/auth/RPCAuthenticationInfo.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/auth/RPCAuthenticationInfo.kt @@ -1,7 +1,7 @@ package org.evomaster.core.problem.rpc.auth import org.evomaster.core.problem.api.auth.AuthenticationInfo -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action open class RPCAuthenticationInfo( name: String, diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt index 6eda696ef3..0edfdfa751 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCEndpointsHandler.kt @@ -31,8 +31,8 @@ import org.evomaster.core.problem.util.ParserDtoUtil.parseJsonNodeAsGene import org.evomaster.core.problem.util.ParserDtoUtil.setGeneBasedOnString import org.evomaster.core.problem.util.ParserDtoUtil.wrapWithOptionalGene import org.evomaster.core.remote.service.RemoteController -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionComponent +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionComponent import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.GroupsOfChildren import org.evomaster.core.search.gene.* diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt index 0049b4b250..90def82f78 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt @@ -16,7 +16,7 @@ import org.evomaster.core.problem.rpc.RPCIndividual import org.evomaster.core.problem.rpc.param.RPCParam import org.evomaster.core.problem.util.ParamUtil import org.evomaster.core.problem.util.ParserDtoUtil.wrapWithOptionalGene -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.FitnessValue import org.evomaster.core.search.gene.interfaces.CollectionGene diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCSampler.kt index f3f25987ca..27915083ef 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCSampler.kt @@ -9,7 +9,7 @@ import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.rpc.RPCCallAction import org.evomaster.core.problem.rpc.RPCIndividual import org.evomaster.core.remote.SutProblemException -import org.evomaster.core.search.ActionComponent +import org.evomaster.core.search.action.ActionComponent import org.slf4j.Logger import org.slf4j.LoggerFactory import javax.annotation.PostConstruct diff --git a/core/src/main/kotlin/org/evomaster/core/problem/util/RestResourceTemplateHandler.kt b/core/src/main/kotlin/org/evomaster/core/problem/util/RestResourceTemplateHandler.kt index da1df15ca4..ff15cac4f6 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/util/RestResourceTemplateHandler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/util/RestResourceTemplateHandler.kt @@ -4,7 +4,7 @@ import org.evomaster.core.problem.rest.HttpVerb import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.resource.CallsTemplate import org.evomaster.core.problem.rest.resource.RestResourceCalls -import org.evomaster.core.search.ActionFilter +import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.service.Randomness diff --git a/core/src/main/kotlin/org/evomaster/core/problem/util/inference/SimpleDeriveR2T.kt b/core/src/main/kotlin/org/evomaster/core/problem/util/inference/SimpleDeriveR2T.kt index 90b50af496..b4e6bbfe7c 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/util/inference/SimpleDeriveR2T.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/util/inference/SimpleDeriveR2T.kt @@ -13,7 +13,7 @@ import org.evomaster.core.problem.util.ParamUtil import org.evomaster.core.problem.util.inference.model.MatchedInfo import org.evomaster.core.problem.util.inference.model.ParamGeneBindMap import org.evomaster.core.problem.util.StringSimilarityComparator -import org.evomaster.core.search.ActionFilter +import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.gene.ObjectGene /** diff --git a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/WebIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/WebIndividual.kt index 19bd0c2312..f2c2fa8f47 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/WebIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/WebIndividual.kt @@ -6,7 +6,7 @@ import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.graphql.GraphQLIndividual import org.evomaster.core.problem.gui.GuiIndividual import org.evomaster.core.problem.rest.RestCallAction -import org.evomaster.core.search.ActionComponent +import org.evomaster.core.search.action.ActionComponent import org.evomaster.core.search.GroupsOfChildren import org.evomaster.core.search.Individual import org.evomaster.core.search.StructuralElement diff --git a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/WebResult.kt b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/WebResult.kt index d3211e2358..3e986789db 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/WebResult.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/WebResult.kt @@ -1,7 +1,7 @@ package org.evomaster.core.problem.webfrontend -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionResult import java.net.URL class WebResult : ActionResult { diff --git a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebFitness.kt index d2c559e1f5..aae1b54141 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebFitness.kt @@ -5,7 +5,7 @@ import org.evomaster.core.Lazy import org.evomaster.core.database.DbAction import org.evomaster.core.problem.enterprise.service.EnterpriseFitness import org.evomaster.core.problem.webfrontend.* -import org.evomaster.core.search.ActionResult +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.FitnessValue import org.evomaster.core.taint.TaintAnalysis diff --git a/core/src/main/kotlin/org/evomaster/core/search/EvaluatedAction.kt b/core/src/main/kotlin/org/evomaster/core/search/EvaluatedAction.kt index f3253c5b09..2bff428095 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/EvaluatedAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/EvaluatedAction.kt @@ -1,5 +1,7 @@ package org.evomaster.core.search +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionResult import org.evomaster.core.mongo.MongoDbAction diff --git a/core/src/main/kotlin/org/evomaster/core/search/EvaluatedIndividual.kt b/core/src/main/kotlin/org/evomaster/core/search/EvaluatedIndividual.kt index f4493f57a0..2d0ff1728e 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/EvaluatedIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/EvaluatedIndividual.kt @@ -8,6 +8,7 @@ import org.evomaster.core.search.tracer.Traceable import org.evomaster.core.search.tracer.TraceableElementCopyFilter import org.evomaster.core.search.tracer.TrackOperator import org.evomaster.core.Lazy +import org.evomaster.core.search.action.Action import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionResult import org.evomaster.core.logging.LoggingUtil @@ -18,7 +19,8 @@ import org.evomaster.core.problem.rest.RestCallResult import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.rest.resource.ResourceImpactOfIndividual import org.evomaster.core.search.Individual.GeneFilter -import org.evomaster.core.search.ActionFilter.* +import org.evomaster.core.search.action.ActionFilter.* +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.search.service.mutator.EvaluatedMutation import org.evomaster.core.search.tracer.TrackingHistory import org.slf4j.Logger diff --git a/core/src/main/kotlin/org/evomaster/core/search/Individual.kt b/core/src/main/kotlin/org/evomaster/core/search/Individual.kt index 1b1ab1e710..d86443967a 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/Individual.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/Individual.kt @@ -1,6 +1,10 @@ package org.evomaster.core.search import org.evomaster.core.EMConfig +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionComponent +import org.evomaster.core.search.action.ActionFilter +import org.evomaster.core.search.action.ActionTree import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionUtils import org.evomaster.core.logging.LoggingUtil diff --git a/core/src/main/kotlin/org/evomaster/core/search/StructuralElement.kt b/core/src/main/kotlin/org/evomaster/core/search/StructuralElement.kt index 2eedab6279..b5152d6f0c 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/StructuralElement.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/StructuralElement.kt @@ -1,6 +1,7 @@ package org.evomaster.core.search import org.evomaster.core.Lazy +import org.evomaster.core.search.action.ActionComponent import org.evomaster.core.problem.api.param.Param import org.evomaster.core.search.gene.Gene import org.evomaster.core.search.gene.root.CompositeGene diff --git a/core/src/main/kotlin/org/evomaster/core/search/Action.kt b/core/src/main/kotlin/org/evomaster/core/search/action/Action.kt similarity index 95% rename from core/src/main/kotlin/org/evomaster/core/search/Action.kt rename to core/src/main/kotlin/org/evomaster/core/search/action/Action.kt index e03d86f806..d8930431ff 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/Action.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/action/Action.kt @@ -1,5 +1,7 @@ -package org.evomaster.core.search +package org.evomaster.core.search.action +import org.evomaster.core.search.Individual +import org.evomaster.core.search.StructuralElement import org.evomaster.core.search.gene.Gene import org.evomaster.core.search.service.Randomness import org.slf4j.Logger diff --git a/core/src/main/kotlin/org/evomaster/core/search/ActionComponent.kt b/core/src/main/kotlin/org/evomaster/core/search/action/ActionComponent.kt similarity index 61% rename from core/src/main/kotlin/org/evomaster/core/search/ActionComponent.kt rename to core/src/main/kotlin/org/evomaster/core/search/action/ActionComponent.kt index 817b244f63..b037966b4d 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/ActionComponent.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/action/ActionComponent.kt @@ -1,13 +1,15 @@ -package org.evomaster.core.search +package org.evomaster.core.search.action +import org.evomaster.core.search.GroupsOfChildren +import org.evomaster.core.search.StructuralElement /** * Shared superclass to represent single [Action] and groups [ActionTree] */ abstract class ActionComponent( - children: MutableList, - childTypeVerifier: (Class<*>) -> Boolean, - groups : GroupsOfChildren? = null + children: MutableList, + childTypeVerifier: (Class<*>) -> Boolean, + groups : GroupsOfChildren? = null ) : StructuralElement(children, childTypeVerifier, groups as GroupsOfChildren?){ diff --git a/core/src/main/kotlin/org/evomaster/core/search/ActionDependentGroup.kt b/core/src/main/kotlin/org/evomaster/core/search/action/ActionDependentGroup.kt similarity index 84% rename from core/src/main/kotlin/org/evomaster/core/search/ActionDependentGroup.kt rename to core/src/main/kotlin/org/evomaster/core/search/action/ActionDependentGroup.kt index ed3669636e..7289d0f939 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/ActionDependentGroup.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/action/ActionDependentGroup.kt @@ -1,4 +1,7 @@ -package org.evomaster.core.search +package org.evomaster.core.search.action + +import org.evomaster.core.search.GroupsOfChildren +import org.evomaster.core.search.StructuralElement open class ActionDependentGroup( children: MutableList, diff --git a/core/src/main/kotlin/org/evomaster/core/search/ActionFilter.kt b/core/src/main/kotlin/org/evomaster/core/search/action/ActionFilter.kt similarity index 94% rename from core/src/main/kotlin/org/evomaster/core/search/ActionFilter.kt rename to core/src/main/kotlin/org/evomaster/core/search/action/ActionFilter.kt index bf97763a7c..f92f0804b0 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/ActionFilter.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/action/ActionFilter.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.search +package org.evomaster.core.search.action enum class ActionFilter { /** diff --git a/core/src/main/kotlin/org/evomaster/core/search/ActionResult.kt b/core/src/main/kotlin/org/evomaster/core/search/action/ActionResult.kt similarity index 92% rename from core/src/main/kotlin/org/evomaster/core/search/ActionResult.kt rename to core/src/main/kotlin/org/evomaster/core/search/action/ActionResult.kt index af6d12816a..265a85be2f 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/ActionResult.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/action/ActionResult.kt @@ -1,5 +1,4 @@ -package org.evomaster.core.search - +package org.evomaster.core.search.action open class ActionResult constructor( /** Specify whether the result of this action led to stop the evaluation @@ -17,7 +16,7 @@ open class ActionResult constructor( } - open fun copy() : ActionResult{ + open fun copy() : ActionResult { return ActionResult(this) } diff --git a/core/src/main/kotlin/org/evomaster/core/search/ActionTree.kt b/core/src/main/kotlin/org/evomaster/core/search/action/ActionTree.kt similarity index 65% rename from core/src/main/kotlin/org/evomaster/core/search/ActionTree.kt rename to core/src/main/kotlin/org/evomaster/core/search/action/ActionTree.kt index e1b3043feb..4d5d0bc60c 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/ActionTree.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/action/ActionTree.kt @@ -1,5 +1,6 @@ -package org.evomaster.core.search +package org.evomaster.core.search.action +import org.evomaster.core.search.GroupsOfChildren /** * Tree/group of actions that are strongly related. @@ -8,9 +9,9 @@ package org.evomaster.core.search * When executing a test case, such tree-structure groups need to be flattened */ abstract class ActionTree( - children: MutableList, - childTypeVerifier: (Class<*>) -> Boolean = {k -> ActionComponent::class.java.isAssignableFrom(k)}, - groups : GroupsOfChildren? = null + children: MutableList, + childTypeVerifier: (Class<*>) -> Boolean = {k -> ActionComponent::class.java.isAssignableFrom(k)}, + groups : GroupsOfChildren? = null ) : ActionComponent( children, childTypeVerifier, diff --git a/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactOfAction.kt b/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactOfAction.kt index d20e6ad384..550792910e 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactOfAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactOfAction.kt @@ -1,6 +1,6 @@ package org.evomaster.core.search.impact.impactinfocollection -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action /** * @property localId represent local id of the action diff --git a/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactUtils.kt b/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactUtils.kt index 77e3537f25..5bfe76ce59 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactUtils.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactUtils.kt @@ -1,8 +1,8 @@ package org.evomaster.core.search.impact.impactinfocollection -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.Individual -import org.evomaster.core.search.ActionFilter +import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.sql.* import org.evomaster.core.search.impact.impactinfocollection.sql.* diff --git a/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactsOfIndividual.kt b/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactsOfIndividual.kt index 19b2dc8c94..437f103ce3 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactsOfIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactsOfIndividual.kt @@ -2,10 +2,10 @@ package org.evomaster.core.search.impact.impactinfocollection import org.evomaster.core.database.DbAction import org.evomaster.core.mongo.MongoDbAction -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.FitnessValue import org.evomaster.core.search.Individual -import org.evomaster.core.search.ActionFilter +import org.evomaster.core.search.action.ActionFilter import org.slf4j.Logger import org.slf4j.LoggerFactory diff --git a/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/InitializationActionImpacts.kt b/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/InitializationActionImpacts.kt index 78c3ddcf2a..9441470ea6 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/InitializationActionImpacts.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/InitializationActionImpacts.kt @@ -3,7 +3,7 @@ package org.evomaster.core.search.impact.impactinfocollection import org.evomaster.core.Lazy import org.evomaster.core.database.DbAction import org.evomaster.core.mongo.MongoDbAction -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -147,9 +147,9 @@ class InitializationActionImpacts(val abstract: Boolean, val enableImpactOnDupli } private fun addedInitialization( - insertions: List, - completeSequence : MutableList, - indexMap: MutableList> + insertions: List, + completeSequence : MutableList, + indexMap: MutableList> ){ val group = insertions.map { a-> ImpactsOfAction(a) } val key = generateTemplateKey(group.map { i-> i.actionName!! }) diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/ExecutionInfoReporter.kt b/core/src/main/kotlin/org/evomaster/core/search/service/ExecutionInfoReporter.kt index 6410ce394c..247d90b1f4 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/ExecutionInfoReporter.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/ExecutionInfoReporter.kt @@ -3,7 +3,7 @@ package org.evomaster.core.search.service import com.google.inject.Inject import org.evomaster.core.EMConfig import org.evomaster.core.database.DatabaseExecution -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.Individual import org.evomaster.core.utils.ReportWriter.wrapWithQuotation import org.evomaster.core.utils.ReportWriter.writeByChannel diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt b/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt index e395857cee..1bd72485c6 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/Sampler.kt @@ -3,7 +3,7 @@ package org.evomaster.core.search.service import com.google.inject.Inject import org.evomaster.client.java.controller.api.dto.SutInfoDto import org.evomaster.core.EMConfig -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.Individual import org.evomaster.core.search.gene.optional.OptionalGene diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/monitor/InterfaceAdapter.kt b/core/src/main/kotlin/org/evomaster/core/search/service/monitor/InterfaceAdapter.kt index 7db36fa01d..317ab6f23e 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/monitor/InterfaceAdapter.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/monitor/InterfaceAdapter.kt @@ -2,7 +2,7 @@ package org.evomaster.core.search.service.monitor import com.google.gson.* import org.evomaster.core.problem.rest.RestCallAction -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.Gene import java.lang.reflect.Constructor import java.lang.reflect.Parameter diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/MutatedGeneSpecification.kt b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/MutatedGeneSpecification.kt index dcbd23bd49..77db5b6b7c 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/MutatedGeneSpecification.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/MutatedGeneSpecification.kt @@ -1,7 +1,7 @@ package org.evomaster.core.search.service.mutator import org.evomaster.core.database.DbAction -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.Individual import org.evomaster.core.search.gene.Gene @@ -18,21 +18,21 @@ import org.evomaster.core.search.gene.Gene * @property mutatedDbActionPosition records where mutated/added/removed dbgenes are located. */ data class MutatedGeneSpecification ( - val mutatedGenes : MutableList = mutableListOf(), - val mutatedDbGenes : MutableList = mutableListOf(), - val mutatedInitGenes : MutableList = mutableListOf(), + val mutatedGenes : MutableList = mutableListOf(), + val mutatedDbGenes : MutableList = mutableListOf(), + val mutatedInitGenes : MutableList = mutableListOf(), //SQL handling - val addedInitializationGenes : MutableList = mutableListOf(), - val addedExistingDataInitialization: MutableList = mutableListOf(), - val addedInitializationGroup: MutableList> = mutableListOf(), + val addedInitializationGenes : MutableList = mutableListOf(), + val addedExistingDataInitialization: MutableList = mutableListOf(), + val addedInitializationGroup: MutableList> = mutableListOf(), //SQL resource handling - val addedDbActions : MutableList> = mutableListOf(), - val removedDbActions : MutableList> = mutableListOf(), + val addedDbActions : MutableList> = mutableListOf(), + val removedDbActions : MutableList> = mutableListOf(), // external service actions - val addedExternalServiceActions : MutableList = mutableListOf() + val addedExternalServiceActions : MutableList = mutableListOf() ){ var mutatedIndividual: Individual? = null diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/StandardMutator.kt b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/StandardMutator.kt index 84fd2fb3ec..c5fb508a33 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/StandardMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/StandardMutator.kt @@ -18,7 +18,7 @@ import org.evomaster.core.problem.rest.param.UpdateForBodyParam import org.evomaster.core.problem.rest.resource.ResourceImpactOfIndividual import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.Individual -import org.evomaster.core.search.ActionFilter +import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.Individual.GeneFilter.ALL import org.evomaster.core.search.Individual.GeneFilter.NO_SQL import org.evomaster.core.search.gene.* diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/genemutation/ArchiveGeneMutator.kt b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/genemutation/ArchiveGeneMutator.kt index f15480f8b5..c416fae56f 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/genemutation/ArchiveGeneMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/genemutation/ArchiveGeneMutator.kt @@ -4,7 +4,7 @@ import com.google.inject.Inject import org.evomaster.client.java.instrumentation.shared.TaintInputName import org.evomaster.core.EMConfig import org.evomaster.core.Lazy -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.Individual import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.numeric.* @@ -447,7 +447,7 @@ class ArchiveGeneMutator{ * extract mutated info only for standard mutation */ private fun mutatedGenePairForIndividualWithActions( - originalActions: List, mutatedActions : List, mutatedGenes: List, genesAtActionIndex: List + originalActions: List, mutatedActions : List, mutatedGenes: List, genesAtActionIndex: List ) : MutableList>{ Lazy.assert { mutatedActions.isEmpty() || mutatedActions.size > genesAtActionIndex.maxOrNull()!! diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/genemutation/ArchiveMutationUtils.kt b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/genemutation/ArchiveMutationUtils.kt index 5aa7c46a66..1d51a95a9a 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/genemutation/ArchiveMutationUtils.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/genemutation/ArchiveMutationUtils.kt @@ -3,9 +3,9 @@ package org.evomaster.core.search.service.mutator.genemutation import org.evomaster.core.EMConfig import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.util.ParamUtil -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.Individual -import org.evomaster.core.search.ActionFilter +import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.gene.Gene import org.evomaster.core.search.service.mutator.EvaluatedMutation import org.evomaster.core.search.service.mutator.MutatedGeneSpecification diff --git a/core/src/main/kotlin/org/evomaster/core/taint/TaintAnalysis.kt b/core/src/main/kotlin/org/evomaster/core/taint/TaintAnalysis.kt index 573300df67..9cd8013d0b 100644 --- a/core/src/main/kotlin/org/evomaster/core/taint/TaintAnalysis.kt +++ b/core/src/main/kotlin/org/evomaster/core/taint/TaintAnalysis.kt @@ -9,7 +9,7 @@ import org.evomaster.core.database.DbAction import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.problem.rest.RestActionBuilderV3 import org.evomaster.core.problem.rest.RestCallAction -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.Individual import org.evomaster.core.search.gene.collection.* import org.evomaster.core.search.gene.interfaces.TaintableGene diff --git a/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt b/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt index 0a6763988e..0b9d88c1c0 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt @@ -1,6 +1,8 @@ package org.evomaster.core.output import org.evomaster.core.TestUtils +import org.evomaster.core.search.action.ActionFilter +import org.evomaster.core.search.action.ActionResult import org.evomaster.core.database.DbAction import org.evomaster.core.database.DbActionResult import org.evomaster.core.problem.enterprise.SampleType diff --git a/core/src/test/kotlin/org/evomaster/core/problem/graphql/PetClinicCheckMain.kt b/core/src/test/kotlin/org/evomaster/core/problem/graphql/PetClinicCheckMain.kt index 1e4385543f..77bf5ed0c5 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/graphql/PetClinicCheckMain.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/graphql/PetClinicCheckMain.kt @@ -4,7 +4,7 @@ package org.evomaster.core.problem.graphql import com.google.gson.Gson import org.evomaster.core.problem.graphql.builder.GraphQLActionBuilder import org.evomaster.core.problem.graphql.schema.SchemaObj -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action class PetClinicCheckMain { diff --git a/core/src/test/kotlin/org/evomaster/core/problem/graphql/builder/GraphQLActionBuilderTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/graphql/builder/GraphQLActionBuilderTest.kt index 2373d85568..3c2100c208 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/graphql/builder/GraphQLActionBuilderTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/graphql/builder/GraphQLActionBuilderTest.kt @@ -4,7 +4,7 @@ import org.evomaster.core.EMConfig import org.evomaster.core.problem.graphql.GraphQLAction import org.evomaster.core.problem.graphql.param.GQInputParam import org.evomaster.core.problem.graphql.param.GQReturnParam -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.collection.ArrayGene import org.evomaster.core.search.gene.collection.EnumGene diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3Test.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3Test.kt index c13a40ea2b..b2075c8f80 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3Test.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3Test.kt @@ -7,7 +7,7 @@ import org.evomaster.core.problem.rest.param.BodyParam import org.evomaster.core.problem.rest.param.FormParam import org.evomaster.core.problem.rest.resource.ResourceCluster import org.evomaster.core.problem.util.ParamUtil -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.ObjectGene import org.evomaster.core.search.gene.collection.ArrayGene import org.evomaster.core.search.gene.collection.EnumGene diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/SameObjectNameTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/SameObjectNameTest.kt index 062b68da8e..facf3730db 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/SameObjectNameTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/SameObjectNameTest.kt @@ -2,7 +2,7 @@ package org.evomaster.core.problem.rest import io.swagger.parser.OpenAPIParser import org.evomaster.core.problem.rest.param.BodyParam -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.ObjectGene import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualResourceTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualResourceTest.kt index dbbf781e52..85d4a807e0 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualResourceTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualResourceTest.kt @@ -4,7 +4,7 @@ import com.google.inject.* import org.evomaster.core.database.DbAction import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.rest.service.* -import org.evomaster.core.search.ActionFilter +import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.impact.impactinfocollection.ImpactUtils import org.evomaster.core.search.impact.impactinfocollection.ImpactsOfIndividual diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt index 073e5188eb..9215148f66 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt @@ -13,7 +13,7 @@ import org.evomaster.core.problem.rest.service.* import org.evomaster.core.problem.util.BindingBuilder import org.evomaster.core.problem.util.BindingBuilder.isExtraTaintParam import org.evomaster.core.problem.util.ParamUtil -import org.evomaster.core.search.ActionFilter +import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.Individual import org.evomaster.core.search.gene.Gene diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/resource/ResourceNodeCreationChainTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/resource/ResourceNodeCreationChainTest.kt index 294c761127..660aa5436f 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/resource/ResourceNodeCreationChainTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/resource/ResourceNodeCreationChainTest.kt @@ -5,8 +5,8 @@ import org.evomaster.core.EMConfig import org.evomaster.core.problem.rest.HttpVerb import org.evomaster.core.problem.rest.RestActionBuilderV3 import org.evomaster.core.problem.rest.RestCallAction -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionFilter +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.gene.optional.CustomMutationRateGene import org.evomaster.core.search.service.Randomness import org.junit.jupiter.api.Assertions.* diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/resource/ResourceNodeTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/resource/ResourceNodeTest.kt index 21a8b9182e..e71975a324 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/resource/ResourceNodeTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/resource/ResourceNodeTest.kt @@ -7,8 +7,8 @@ import org.evomaster.core.problem.rest.RestActionBuilderV3 import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.param.BodyParam import org.evomaster.core.problem.rest.param.QueryParam -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionFilter +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.gene.optional.CustomMutationRateGene import org.evomaster.core.search.gene.ObjectGene import org.evomaster.core.search.service.Randomness diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/resource/ResourceNodeWithDbTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/resource/ResourceNodeWithDbTest.kt index 57b0b64db0..b57d473708 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/resource/ResourceNodeWithDbTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/resource/ResourceNodeWithDbTest.kt @@ -9,8 +9,8 @@ import org.evomaster.core.database.DatabaseExecutor import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.problem.rest.RestActionBuilderV3 import org.evomaster.core.problem.rest.resource.dependency.BodyParamRelatedToTable -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionFilter +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.gene.Gene import org.evomaster.core.search.gene.numeric.LongGene import org.evomaster.core.search.service.Randomness @@ -152,7 +152,7 @@ class ResourceNodeWithDbTest { // /v3/api/rfoo/{rfooId} val getFoo = cluster.getResourceNode("/v3/api/rfoo/{rfooId}")!!.sampleRestResourceCalls("GET", randomness, maxTestSize = 10) - val previousGetFooId = getGenePredict(getFoo.seeActions(ActionFilter.NO_SQL).first(), "rfooId"){g: Gene-> g is LongGene } + val previousGetFooId = getGenePredict(getFoo.seeActions(ActionFilter.NO_SQL).first(), "rfooId"){ g: Gene-> g is LongGene } (previousGetFooId as LongGene).value = 40 getFoo.is2POST = true val createFoo = sqlInsertBuilder.createSqlInsertionAction("RFOO") @@ -161,7 +161,7 @@ class ResourceNodeWithDbTest { (createGetFooId as LongGene).value = 42 getFoo.initDbActions(createFoo, cluster, false, false) - val fooGetId = getGenePredict(getFoo.seeActions(ActionFilter.NO_SQL).first(), "rfooId"){g: Gene-> g is LongGene } + val fooGetId = getGenePredict(getFoo.seeActions(ActionFilter.NO_SQL).first(), "rfooId"){ g: Gene-> g is LongGene } val fooCreateGetId = getGenePredict(getFoo.seeActions(ActionFilter.ONLY_SQL).first(), "id") { g: Gene -> g is LongGene } assertEquals((fooGetId as LongGene).value, (fooCreateGetId as LongGene).value) assertEquals(42, fooGetId.value) @@ -172,10 +172,10 @@ class ResourceNodeWithDbTest { val fooBarDbActionToCreate = cluster.createSqlAction(listOf("RFOO", "RBAR"), sqlInsertBuilder, mutableListOf(), true, randomness = randomness) assertEquals(2, fooBarDbActionToCreate.size) getBar.initDbActions(fooBarDbActionToCreate, cluster, false, false) - val barFooId = getGenePredict(getBar.seeActions(ActionFilter.NO_SQL).first(), "rfooId"){g: Gene-> g is LongGene } - val barId = getGenePredict(getBar.seeActions(ActionFilter.NO_SQL).first(), "rbarId"){g: Gene-> g is LongGene } - val dbFooIdInGetBar = getGenePredict(getBar.seeActions(ActionFilter.ONLY_SQL).first(), "id"){g: Gene -> g is LongGene } - val dbBarIdInGetBar = getGenePredict(getBar.seeActions(ActionFilter.ONLY_SQL)[1], "id"){g: Gene -> g is LongGene } + val barFooId = getGenePredict(getBar.seeActions(ActionFilter.NO_SQL).first(), "rfooId"){ g: Gene-> g is LongGene } + val barId = getGenePredict(getBar.seeActions(ActionFilter.NO_SQL).first(), "rbarId"){ g: Gene-> g is LongGene } + val dbFooIdInGetBar = getGenePredict(getBar.seeActions(ActionFilter.ONLY_SQL).first(), "id"){ g: Gene -> g is LongGene } + val dbBarIdInGetBar = getGenePredict(getBar.seeActions(ActionFilter.ONLY_SQL)[1], "id"){ g: Gene -> g is LongGene } assertEquals((barFooId as LongGene).value, (dbFooIdInGetBar as LongGene).value) assertTrue(dbFooIdInGetBar.isDirectBoundWith(barFooId)) assertEquals((barId as LongGene).value, (dbBarIdInGetBar as LongGene).value) @@ -196,12 +196,12 @@ class ResourceNodeWithDbTest { } assertEquals(3, xyzDbActions.size) getXYZ.initDbActions(xyzDbActions, cluster, false, false) - val xyzFooId = getGenePredict(getXYZ.seeActions(ActionFilter.NO_SQL)[0], "rfooId"){g: Gene-> g is LongGene } - val xyzBarId = getGenePredict(getXYZ.seeActions(ActionFilter.NO_SQL)[0], "rbarId"){g: Gene-> g is LongGene } - val xyzId = getGenePredict(getXYZ.seeActions(ActionFilter.NO_SQL)[0], "rxyzId"){g: Gene-> g is LongGene } - val dbXYZFooId = getGenePredict(getXYZ.seeActions(ActionFilter.ONLY_SQL)[0], "id"){g: Gene-> g is LongGene } - val dbXYZBarId = getGenePredict(getXYZ.seeActions(ActionFilter.ONLY_SQL)[1], "id"){g: Gene-> g is LongGene } - val dbXYZId = getGenePredict(getXYZ.seeActions(ActionFilter.ONLY_SQL)[2], "id"){g: Gene-> g is LongGene } + val xyzFooId = getGenePredict(getXYZ.seeActions(ActionFilter.NO_SQL)[0], "rfooId"){ g: Gene-> g is LongGene } + val xyzBarId = getGenePredict(getXYZ.seeActions(ActionFilter.NO_SQL)[0], "rbarId"){ g: Gene-> g is LongGene } + val xyzId = getGenePredict(getXYZ.seeActions(ActionFilter.NO_SQL)[0], "rxyzId"){ g: Gene-> g is LongGene } + val dbXYZFooId = getGenePredict(getXYZ.seeActions(ActionFilter.ONLY_SQL)[0], "id"){ g: Gene-> g is LongGene } + val dbXYZBarId = getGenePredict(getXYZ.seeActions(ActionFilter.ONLY_SQL)[1], "id"){ g: Gene-> g is LongGene } + val dbXYZId = getGenePredict(getXYZ.seeActions(ActionFilter.ONLY_SQL)[2], "id"){ g: Gene-> g is LongGene } assertEquals((xyzFooId as LongGene).value, (dbXYZFooId as LongGene).value) assertEquals(42, xyzFooId.value) @@ -231,10 +231,10 @@ class ResourceNodeWithDbTest { } getXYZ.initDbActions(dbXYZ, cluster, false, false) - val xyzFooId = getGenePredict(getXYZ.seeActions(ActionFilter.NO_SQL)[0], "rfooId"){g: Gene-> g is LongGene } - val xyzBarId = getGenePredict(getXYZ.seeActions(ActionFilter.NO_SQL)[0], "rbarId"){g: Gene-> g is LongGene } - val dbXYZFooId = getGenePredict(getXYZ.seeActions(ActionFilter.ONLY_SQL)[0], "id"){g: Gene-> g is LongGene } - val dbXYZBarId = getGenePredict(getXYZ.seeActions(ActionFilter.ONLY_SQL)[1], "id"){g: Gene-> g is LongGene } + val xyzFooId = getGenePredict(getXYZ.seeActions(ActionFilter.NO_SQL)[0], "rfooId"){ g: Gene-> g is LongGene } + val xyzBarId = getGenePredict(getXYZ.seeActions(ActionFilter.NO_SQL)[0], "rbarId"){ g: Gene-> g is LongGene } + val dbXYZFooId = getGenePredict(getXYZ.seeActions(ActionFilter.ONLY_SQL)[0], "id"){ g: Gene-> g is LongGene } + val dbXYZBarId = getGenePredict(getXYZ.seeActions(ActionFilter.ONLY_SQL)[1], "id"){ g: Gene-> g is LongGene } val getBarNode = cluster.getResourceNode("/v3/api/rfoo/{rfooId}/rbar/{rbarId}")!! @@ -249,8 +249,8 @@ class ResourceNodeWithDbTest { assertFalse(getXYZ.isDeletable) assertTrue(getXYZ.shouldBefore.contains(getBar.getResourceNodeKey())) - val barFooId = getGenePredict(getBar.seeActions(ActionFilter.NO_SQL).first(), "rfooId"){g: Gene-> g is LongGene } - val barId = getGenePredict(getBar.seeActions(ActionFilter.NO_SQL).first(), "rbarId"){g: Gene-> g is LongGene } + val barFooId = getGenePredict(getBar.seeActions(ActionFilter.NO_SQL).first(), "rfooId"){ g: Gene-> g is LongGene } + val barId = getGenePredict(getBar.seeActions(ActionFilter.NO_SQL).first(), "rbarId"){ g: Gene-> g is LongGene } assertEquals((xyzBarId as LongGene).value, (barId as LongGene).value) assertEquals((dbXYZBarId as LongGene).value, barId.value) diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/seeding/postman/PostmanParserTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/seeding/postman/PostmanParserTest.kt index 5fdbed331f..e66699f461 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/seeding/postman/PostmanParserTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/seeding/postman/PostmanParserTest.kt @@ -9,7 +9,7 @@ import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.param.BodyParam import org.evomaster.core.problem.rest.param.HeaderParam import org.evomaster.core.problem.rest.param.PathParam -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.collection.ArrayGene import org.evomaster.core.search.gene.collection.EnumGene diff --git a/core/src/test/kotlin/org/evomaster/core/search/algorithms/constant/ConstantAction.kt b/core/src/test/kotlin/org/evomaster/core/search/algorithms/constant/ConstantAction.kt index 2cc4f65dd7..16e1053624 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/algorithms/constant/ConstantAction.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/algorithms/constant/ConstantAction.kt @@ -1,6 +1,6 @@ package org.evomaster.core.search.algorithms.constant -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.StructuralElement import org.evomaster.core.search.gene.Gene import org.evomaster.core.search.gene.numeric.IntegerGene diff --git a/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxAction.kt b/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxAction.kt index 44d5bbae19..35e8d83da3 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxAction.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/algorithms/onemax/OneMaxAction.kt @@ -1,6 +1,6 @@ package org.evomaster.core.search.algorithms.onemax -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.StructuralElement import org.evomaster.core.search.gene.collection.EnumGene import org.evomaster.core.search.gene.Gene diff --git a/core/src/test/kotlin/org/evomaster/core/search/gene/GeneUtilsTest.kt b/core/src/test/kotlin/org/evomaster/core/search/gene/GeneUtilsTest.kt index 65fe729c04..455ee9ea92 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/gene/GeneUtilsTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/gene/GeneUtilsTest.kt @@ -6,7 +6,7 @@ import org.evomaster.core.problem.graphql.GraphQLAction import org.evomaster.core.problem.graphql.builder.GraphQLActionBuilder import org.evomaster.core.problem.graphql.PetClinicCheckMain import org.evomaster.core.problem.graphql.param.GQReturnParam -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.collection.ArrayGene import org.evomaster.core.search.gene.collection.TupleGene import org.evomaster.core.search.gene.datetime.DateGene diff --git a/core/src/test/kotlin/org/evomaster/core/search/gene/binding/BindingAction.kt b/core/src/test/kotlin/org/evomaster/core/search/gene/binding/BindingAction.kt index 76a89ce3b7..dd66a0e8ec 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/gene/binding/BindingAction.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/gene/binding/BindingAction.kt @@ -1,7 +1,7 @@ package org.evomaster.core.search.gene.binding -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.StructuralElement import org.evomaster.core.search.gene.Gene diff --git a/core/src/test/kotlin/org/evomaster/core/search/gene/binding/BindingIndividual.kt b/core/src/test/kotlin/org/evomaster/core/search/gene/binding/BindingIndividual.kt index 35707b0c73..d2ddc29ae1 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/gene/binding/BindingIndividual.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/gene/binding/BindingIndividual.kt @@ -1,6 +1,6 @@ package org.evomaster.core.search.gene.binding -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.Individual import org.evomaster.core.search.StructuralElement import org.evomaster.core.search.gene.Gene diff --git a/core/src/test/kotlin/org/evomaster/core/search/impact/impactinfocollection/individual/IndividualGeneImpactTest.kt b/core/src/test/kotlin/org/evomaster/core/search/impact/impactinfocollection/individual/IndividualGeneImpactTest.kt index 59ff9f15ee..b5520c8fca 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/impact/impactinfocollection/individual/IndividualGeneImpactTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/impact/impactinfocollection/individual/IndividualGeneImpactTest.kt @@ -1,6 +1,7 @@ package org.evomaster.core.search.impact.impactinfocollection.individual import org.evomaster.core.EMConfig +import org.evomaster.core.search.action.Action import org.evomaster.core.output.EvaluatedIndividualBuilder.Companion.generateIndividualResults import org.evomaster.core.search.* import org.evomaster.core.search.gene.Gene diff --git a/core/src/test/kotlin/org/evomaster/core/search/matchproblem/PrimitiveTypeMatchAction.kt b/core/src/test/kotlin/org/evomaster/core/search/matchproblem/PrimitiveTypeMatchAction.kt index 12b7fb62c9..4defb2d250 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/matchproblem/PrimitiveTypeMatchAction.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/matchproblem/PrimitiveTypeMatchAction.kt @@ -1,7 +1,7 @@ package org.evomaster.core.search.matchproblem -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.Gene class PrimitiveTypeMatchAction(gene: Gene) : Action(listOf(gene)) { diff --git a/core/src/test/kotlin/org/evomaster/core/search/mutationweight/GeneWeightTestSchema.kt b/core/src/test/kotlin/org/evomaster/core/search/mutationweight/GeneWeightTestSchema.kt index fc7b2243ef..be8549e6be 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/mutationweight/GeneWeightTestSchema.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/mutationweight/GeneWeightTestSchema.kt @@ -11,7 +11,7 @@ import org.evomaster.core.problem.rest.RestActionBuilderV3 import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.enterprise.SampleType -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.datetime.DateGene import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.ObjectGene diff --git a/core/src/test/kotlin/org/evomaster/core/search/structuralelement/action/GraphQLActionStructureTest.kt b/core/src/test/kotlin/org/evomaster/core/search/structuralelement/action/GraphQLActionStructureTest.kt index 3b3d464914..0ee8627f00 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/structuralelement/action/GraphQLActionStructureTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/structuralelement/action/GraphQLActionStructureTest.kt @@ -3,7 +3,7 @@ package org.evomaster.core.search.structuralelement.action import org.evomaster.core.problem.graphql.GraphQLAction import org.evomaster.core.problem.graphql.builder.GraphQLActionBuilder import org.evomaster.core.problem.graphql.param.GQReturnParam -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.BooleanGene import org.evomaster.core.search.gene.ObjectGene import org.evomaster.core.search.structuralelement.StructuralElementBaseTest diff --git a/core/src/test/kotlin/org/evomaster/core/search/structuralelement/action/RestCallStructureTest.kt b/core/src/test/kotlin/org/evomaster/core/search/structuralelement/action/RestCallStructureTest.kt index 82af51bcc0..b342629f80 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/structuralelement/action/RestCallStructureTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/structuralelement/action/RestCallStructureTest.kt @@ -5,7 +5,7 @@ import org.evomaster.core.EMConfig import org.evomaster.core.problem.rest.RestActionBuilderV3 import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.param.BodyParam -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.ObjectGene import org.evomaster.core.search.structuralelement.StructuralElementBaseTest import org.junit.jupiter.api.Assertions.assertEquals diff --git a/core/src/test/kotlin/org/evomaster/core/search/structuralelement/resourcecall/RestResourceCallStructureTest.kt b/core/src/test/kotlin/org/evomaster/core/search/structuralelement/resourcecall/RestResourceCallStructureTest.kt index 40b741b297..6864126520 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/structuralelement/resourcecall/RestResourceCallStructureTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/structuralelement/resourcecall/RestResourceCallStructureTest.kt @@ -6,8 +6,8 @@ import org.evomaster.core.problem.rest.RestActionBuilderV3 import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.resource.ResourceCluster import org.evomaster.core.problem.rest.resource.RestResourceCalls -import org.evomaster.core.search.Action -import org.evomaster.core.search.ActionFilter +import org.evomaster.core.search.action.Action +import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.service.Randomness import org.evomaster.core.search.structuralelement.StructuralElementBaseTest import org.junit.jupiter.api.Assertions.assertEquals diff --git a/core/src/test/kotlin/org/evomaster/core/utils/BindingBuilderTest.kt b/core/src/test/kotlin/org/evomaster/core/utils/BindingBuilderTest.kt index c0e9b4e58a..bf0a94d302 100644 --- a/core/src/test/kotlin/org/evomaster/core/utils/BindingBuilderTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/utils/BindingBuilderTest.kt @@ -7,7 +7,7 @@ import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.param.BodyParam import org.evomaster.core.problem.rest.param.PathParam import org.evomaster.core.problem.util.BindingBuilder -import org.evomaster.core.search.Action +import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.optional.CustomMutationRateGene import org.evomaster.core.search.gene.numeric.LongGene import org.evomaster.core.search.gene.ObjectGene diff --git a/e2e-tests/e2e-tests-utils/src/test/java/org/evomaster/e2etests/utils/RestTestBase.java b/e2e-tests/e2e-tests-utils/src/test/java/org/evomaster/e2etests/utils/RestTestBase.java index 061e90d60d..a2443b2678 100644 --- a/e2e-tests/e2e-tests-utils/src/test/java/org/evomaster/e2etests/utils/RestTestBase.java +++ b/e2e-tests/e2e-tests-utils/src/test/java/org/evomaster/e2etests/utils/RestTestBase.java @@ -4,6 +4,8 @@ import org.evomaster.client.java.utils.SimpleLogger; import org.evomaster.core.Main; import org.evomaster.core.StaticCounter; +import org.evomaster.core.search.action.Action; +import org.evomaster.core.search.action.ActionResult; import org.evomaster.core.logging.TestLoggingUtil; import org.evomaster.core.problem.rest.*; import org.evomaster.core.search.*; diff --git a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/ResourceMIOHWTestBase.java b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/ResourceMIOHWTestBase.java index 460d29dd78..5200984eb2 100644 --- a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/ResourceMIOHWTestBase.java +++ b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/ResourceMIOHWTestBase.java @@ -1,8 +1,8 @@ package org.evomaster.e2etests.spring.examples.resource; import org.evomaster.core.problem.rest.resource.RestResourceCalls; -import org.evomaster.core.search.Action; -import org.evomaster.core.search.ActionFilter; +import org.evomaster.core.search.action.Action; +import org.evomaster.core.search.action.ActionFilter; import org.evomaster.core.search.gene.Gene; import java.util.*; diff --git a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/ResourceTestBase.java b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/ResourceTestBase.java index bcd8ed30b1..872f7778f2 100644 --- a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/ResourceTestBase.java +++ b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/ResourceTestBase.java @@ -2,7 +2,7 @@ import com.foo.rest.examples.spring.resource.ResourceRestController; import org.evomaster.core.problem.rest.*; -import org.evomaster.core.search.ActionResult; +import org.evomaster.core.search.action.ActionResult; import org.evomaster.core.search.EvaluatedIndividual; import org.evomaster.e2etests.spring.examples.SpringTestBase; import org.junit.jupiter.api.BeforeAll; diff --git a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/adaptivehm/ResourceDbMIOAndAdaptiveHMBasicTest.java b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/adaptivehm/ResourceDbMIOAndAdaptiveHMBasicTest.java index b4c07d2634..cea7b9b25e 100644 --- a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/adaptivehm/ResourceDbMIOAndAdaptiveHMBasicTest.java +++ b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/adaptivehm/ResourceDbMIOAndAdaptiveHMBasicTest.java @@ -9,7 +9,7 @@ import org.evomaster.core.problem.rest.service.ResourceManageService; import org.evomaster.core.problem.rest.service.ResourceRestMutator; import org.evomaster.core.problem.rest.service.RestResourceFitness; -import org.evomaster.core.search.ActionFilter; +import org.evomaster.core.search.action.ActionFilter; import org.evomaster.core.search.EvaluatedIndividual; import org.evomaster.core.search.impact.impactinfocollection.ImpactsOfIndividual; import org.evomaster.core.search.service.Archive; diff --git a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java index a7b83e12a2..d01fb3a9e5 100644 --- a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java +++ b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java @@ -6,9 +6,8 @@ import org.evomaster.core.problem.rest.resource.RestResourceNode; import org.evomaster.core.problem.rest.service.ResourceManageService; import org.evomaster.core.problem.util.BindingBuilder; -import org.evomaster.core.search.ActionFilter; +import org.evomaster.core.search.action.ActionFilter; import org.evomaster.core.search.Individual.GeneFilter; -import org.evomaster.core.search.gene.Gene; import org.evomaster.e2etests.spring.examples.resource.ResourceMIOHWTestBase; import org.junit.jupiter.api.Test; diff --git a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java index 088bf4d84a..6ad9af9d6c 100644 --- a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java +++ b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java @@ -10,7 +10,7 @@ import org.evomaster.core.problem.rest.service.RestResourceFitness; import org.evomaster.core.problem.rest.service.RestResourceStructureMutator; import org.evomaster.core.problem.util.BindingBuilder; -import org.evomaster.core.search.ActionFilter; +import org.evomaster.core.search.action.ActionFilter; import org.evomaster.core.search.EvaluatedIndividual; import org.evomaster.core.search.Individual.GeneFilter; import org.evomaster.core.search.service.mutator.MutatedGeneSpecification; diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/ResourceMIOHWTestBase.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/ResourceMIOHWTestBase.java index d9196719e1..899fc1f5c8 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/ResourceMIOHWTestBase.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/ResourceMIOHWTestBase.java @@ -1,8 +1,8 @@ package org.evomaster.e2etests.spring.examples.resource; import org.evomaster.core.problem.rest.resource.RestResourceCalls; -import org.evomaster.core.search.Action; -import org.evomaster.core.search.ActionFilter; +import org.evomaster.core.search.action.Action; +import org.evomaster.core.search.action.ActionFilter; import org.evomaster.core.search.gene.Gene; import java.util.*; diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/ResourceTestBase.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/ResourceTestBase.java index 0b4ce079ba..df918c3e20 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/ResourceTestBase.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/ResourceTestBase.java @@ -2,7 +2,7 @@ import com.foo.rest.examples.spring.resource.ResourceRestController; import org.evomaster.core.problem.rest.*; -import org.evomaster.core.search.ActionResult; +import org.evomaster.core.search.action.ActionResult; import org.evomaster.core.search.EvaluatedIndividual; import org.evomaster.e2etests.spring.examples.SpringTestBase; import org.junit.jupiter.api.BeforeAll; diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/adaptivehm/ResourceDbMIOAndAdaptiveHMBasicTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/adaptivehm/ResourceDbMIOAndAdaptiveHMBasicTest.java index 62eb000d5f..2bd69ee14b 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/adaptivehm/ResourceDbMIOAndAdaptiveHMBasicTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/adaptivehm/ResourceDbMIOAndAdaptiveHMBasicTest.java @@ -9,7 +9,7 @@ import org.evomaster.core.problem.rest.service.ResourceManageService; import org.evomaster.core.problem.rest.service.ResourceRestMutator; import org.evomaster.core.problem.rest.service.RestResourceFitness; -import org.evomaster.core.search.ActionFilter; +import org.evomaster.core.search.action.ActionFilter; import org.evomaster.core.search.EvaluatedIndividual; import org.evomaster.core.search.impact.impactinfocollection.ImpactsOfIndividual; import org.evomaster.core.search.service.Archive; diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java index a2d90cb094..c24fc6a5ae 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java @@ -6,7 +6,7 @@ import org.evomaster.core.problem.rest.resource.RestResourceNode; import org.evomaster.core.problem.rest.service.ResourceManageService; import org.evomaster.core.problem.util.BindingBuilder; -import org.evomaster.core.search.ActionFilter; +import org.evomaster.core.search.action.ActionFilter; import org.evomaster.core.search.Individual.GeneFilter; import org.evomaster.e2etests.spring.examples.resource.ResourceMIOHWTestBase; import org.junit.jupiter.api.Test; diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java index e13c6bc906..1bc7e4ebce 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/hypermutation/ResourceDbMIOAndHypermutationBasicTest.java @@ -10,7 +10,7 @@ import org.evomaster.core.problem.rest.service.RestResourceFitness; import org.evomaster.core.problem.rest.service.RestResourceStructureMutator; import org.evomaster.core.problem.util.BindingBuilder; -import org.evomaster.core.search.ActionFilter; +import org.evomaster.core.search.action.ActionFilter; import org.evomaster.core.search.EvaluatedIndividual; import org.evomaster.core.search.Individual.GeneFilter; import org.evomaster.core.search.service.mutator.MutatedGeneSpecification; diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/wiremock/service/ExternalServiceMockingFlakyEMTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/wiremock/service/ExternalServiceMockingFlakyEMTest.java index c66bd1bd8e..cfc78ac480 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/wiremock/service/ExternalServiceMockingFlakyEMTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/wiremock/service/ExternalServiceMockingFlakyEMTest.java @@ -6,8 +6,8 @@ import org.evomaster.core.problem.rest.HttpVerb; import org.evomaster.core.problem.rest.RestIndividual; import org.evomaster.core.problem.rest.resource.RestResourceCalls; -import org.evomaster.core.search.Action; -import org.evomaster.core.search.ActionFilter; +import org.evomaster.core.search.action.Action; +import org.evomaster.core.search.action.ActionFilter; import org.evomaster.core.search.EvaluatedIndividual; import org.evomaster.core.search.Solution; import org.evomaster.e2etests.spring.examples.SpringTestBase; diff --git a/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/org/evomaster/e2etests/spring/rpc/examples/numericstring/RPCSchemaHandlerTest.java b/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/org/evomaster/e2etests/spring/rpc/examples/numericstring/RPCSchemaHandlerTest.java index 707838d0e6..8261749312 100644 --- a/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/org/evomaster/e2etests/spring/rpc/examples/numericstring/RPCSchemaHandlerTest.java +++ b/e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/org/evomaster/e2etests/spring/rpc/examples/numericstring/RPCSchemaHandlerTest.java @@ -3,7 +3,7 @@ import com.google.inject.Injector; import org.evomaster.core.problem.rpc.RPCCallAction; import org.evomaster.core.problem.rpc.service.RPCSampler; -import org.evomaster.core.search.Action; +import org.evomaster.core.search.action.Action; import org.evomaster.core.search.gene.*; import org.evomaster.core.search.gene.numeric.BigDecimalGene; import org.evomaster.core.search.gene.optional.OptionalGene; From b5d280b00d5c19141cd9316d8ac4770b50eb3fb0 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Wed, 2 Aug 2023 08:39:47 +0200 Subject: [PATCH 143/345] refactoring DbAction into SqlAction --- .../rest/service/resource/ResourceTestBase.kt | 10 +- .../database/{DbAction.kt => SqlAction.kt} | 8 +- ...GeneBuilder.kt => SqlActionGeneBuilder.kt} | 4 +- .../{DbActionResult.kt => SqlActionResult.kt} | 10 +- ...Transformer.kt => SqlActionTransformer.kt} | 6 +- .../{DbActionUtils.kt => SqlActionUtils.kt} | 96 ++++----- .../core/database/SqlInsertBuilder.kt | 14 +- .../core/database/TableConstraintEvaluator.kt | 68 +++--- .../database/TableConstraintGeneCollector.kt | 68 +++--- .../org/evomaster/core/output/SqlWriter.kt | 18 +- .../core/output/service/ApiTestCaseWriter.kt | 10 +- .../core/problem/api/service/ApiWsFitness.kt | 20 -- .../core/problem/api/service/ApiWsSampler.kt | 9 - .../api/service/ApiWsStructureMutator.kt | 40 ++-- .../enterprise/EnterpriseIndividual.kt | 37 ++-- .../enterprise/service/EnterpriseFitness.kt | 40 ++-- .../enterprise/service/EnterpriseSampler.kt | 8 +- .../core/problem/graphql/GraphQLIndividual.kt | 7 +- .../problem/graphql/service/GraphQLFitness.kt | 4 +- .../problem/httpws/service/HttpWsFitness.kt | 11 +- .../core/problem/rest/RestIndividual.kt | 30 +-- .../problem/rest/resource/ResourceCluster.kt | 12 +- .../resource/ResourceImpactOfIndividual.kt | 8 +- .../rest/resource/RestResourceCalls.kt | 84 ++++---- .../problem/rest/resource/RestResourceNode.kt | 4 +- .../rest/resource/dependency/CreationChain.kt | 4 +- .../rest/service/ResourceDepManageService.kt | 36 ++-- .../rest/service/ResourceManageService.kt | 20 +- .../problem/rest/service/ResourceSampler.kt | 2 +- .../core/problem/rest/service/RestFitness.kt | 4 +- .../rest/service/RestResourceFitness.kt | 12 +- .../rest/service/RestResourceMutator.kt | 1 - .../service/RestResourceStructureMutator.kt | 6 +- .../rest/service/RestStructureMutator.kt | 6 +- .../core/problem/rpc/RPCIndividual.kt | 14 +- .../core/problem/rpc/service/RPCFitness.kt | 4 +- .../core/problem/util/BindingBuilder.kt | 28 +-- .../util/inference/DeriveResourceBinding.kt | 4 +- .../problem/util/inference/SimpleDeriveR2T.kt | 8 +- .../core/problem/webfrontend/WebIndividual.kt | 6 +- .../problem/webfrontend/service/WebFitness.kt | 4 +- .../core/search/EnvironmentAction.kt | 14 ++ .../evomaster/core/search/EvaluatedAction.kt | 6 +- .../core/search/EvaluatedIndividual.kt | 29 ++- .../org/evomaster/core/search/Individual.kt | 6 +- .../org/evomaster/core/search/Solution.kt | 4 +- .../ImpactsOfIndividual.kt | 6 +- .../InitializationActionImpacts.kt | 8 +- .../mutator/MutatedGeneSpecification.kt | 12 +- .../core/search/service/mutator/Mutator.kt | 4 +- .../search/service/mutator/StandardMutator.kt | 8 +- .../org/evomaster/core/taint/TaintAnalysis.kt | 4 +- .../core/database/DbActionGeneBuilderTest.kt | 4 +- .../core/database/DbActionTransformerTest.kt | 4 +- .../core/database/DbActionUtilsTest.kt | 194 +++++++++--------- .../database/TableConstraintEvaluatorTest.kt | 76 +++---- .../TableConstraintGeneCollectorTest.kt | 42 ++-- .../extract/h2/CatwatchSqlExtractTest.kt | 10 +- .../database/extract/h2/DoctorsExtractTest.kt | 8 +- .../extract/h2/ProxyPrintSqlExtractTest.kt | 8 +- .../database/extract/mysql/LikeCheckTest.kt | 4 +- .../extract/mysql/SQLJSONColumnTest.kt | 22 +- .../extract/mysql/SqlDateColumnTest.kt | 4 +- .../extract/mysql/SqlTextColumnTest.kt | 6 +- .../extract/postgres/ArrayTypesTest.kt | 12 +- .../extract/postgres/BinaryTypesTest.kt | 6 +- .../extract/postgres/BitStringTypesTest.kt | 4 +- .../extract/postgres/BooleanTypeTest.kt | 4 +- .../extract/postgres/CharacterTypesTest.kt | 6 +- .../extract/postgres/CompositeTypesTest.kt | 4 +- .../extract/postgres/DatetimeTypesTest.kt | 4 +- .../extract/postgres/EnumeratedTypesTest.kt | 4 +- .../extract/postgres/GeometricTypesTest.kt | 4 +- .../extract/postgres/JSONTypesTest.kt | 4 +- .../extract/postgres/LikeCheckTest.kt | 4 +- .../postgres/ManySimilarToChecksTest.kt | 4 +- .../extract/postgres/MonetaryTypesTest.kt | 4 +- .../extract/postgres/MultiRangeTypesTest.kt | 6 +- .../extract/postgres/NetworkTypesTest.kt | 4 +- .../extract/postgres/NumericTypesTest.kt | 8 +- .../postgres/ObjectIdentifierTypesTest.kt | 5 +- .../database/extract/postgres/PgLsnTest.kt | 5 +- .../extract/postgres/RangeTypesTest.kt | 6 +- .../extract/postgres/SimilarToCheckTest.kt | 4 +- .../extract/postgres/SqlDateColumnTest.kt | 4 +- .../extract/postgres/SqlJSONBColumnTest.kt | 22 +- .../extract/postgres/SqlTextColumnTest.kt | 6 +- .../extract/postgres/SqlUUIDColumnTest.kt | 8 +- .../extract/postgres/SqlXMLColumnTest.kt | 26 +-- .../extract/postgres/TextSearchTypesTest.kt | 10 +- .../database/extract/postgres/UUIDTypeTest.kt | 4 +- .../database/extract/postgres/XMLTypeTest.kt | 4 +- .../core/output/EvaluatedIndividualBuilder.kt | 16 +- .../core/output/TestCaseWriterTest.kt | 104 +++++----- .../evomaster/core/output/WriteJsonTest.kt | 10 +- .../org/evomaster/core/output/WriteXMLTest.kt | 8 +- .../individual/RestIndividualResourceTest.kt | 8 +- .../rest/individual/RestIndividualTestBase.kt | 7 +- .../RestResourceIndividualDisabledHMTest.kt | 16 +- .../core/search/gene/FlatViewTest.kt | 4 +- .../core/search/gene/GeneUtilsTest.kt | 7 +- .../search/gene/binding/BindingBuildTest.kt | 4 +- .../search/gene/sql/SqlTimestampGeneTest.kt | 8 +- .../search/impact/IndividualImpactTest.kt | 8 +- .../mutationweight/GeneWeightTestSchema.kt | 8 +- .../action/DbActionStructureTest.kt | 6 +- .../individual/RestIndividualStructureTest.kt | 6 +- .../DbDirectIntWithSqlEMTest.java | 4 +- .../resource/db/ResourceDbMIOBasicTest.java | 4 +- .../DbDirectIntWithSqlEMTest.java | 4 +- .../resource/db/ResourceDbMIOBasicTest.java | 4 +- 111 files changed, 832 insertions(+), 858 deletions(-) rename core/src/main/kotlin/org/evomaster/core/database/{DbAction.kt => SqlAction.kt} (93%) rename core/src/main/kotlin/org/evomaster/core/database/{DbActionGeneBuilder.kt => SqlActionGeneBuilder.kt} (99%) rename core/src/main/kotlin/org/evomaster/core/database/{DbActionResult.kt => SqlActionResult.kt} (81%) rename core/src/main/kotlin/org/evomaster/core/database/{DbActionTransformer.kt => SqlActionTransformer.kt} (94%) rename core/src/main/kotlin/org/evomaster/core/database/{DbActionUtils.kt => SqlActionUtils.kt} (85%) create mode 100644 core/src/main/kotlin/org/evomaster/core/search/EnvironmentAction.kt diff --git a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/ResourceTestBase.kt b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/ResourceTestBase.kt index eb1dcd8bc2..0d7aa7908f 100644 --- a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/ResourceTestBase.kt +++ b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/ResourceTestBase.kt @@ -12,8 +12,8 @@ import org.evomaster.core.TestUtils import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.action.ActionResult import org.evomaster.core.database.DatabaseExecutor -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionResult +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionResult import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.database.extract.h2.ExtractTestBaseH2 import org.evomaster.core.problem.rest.RestCallAction @@ -209,9 +209,9 @@ abstract class ResourceTestBase : ExtractTestBaseH2(), ResourceBasedTestInterfac ) : Boolean{ if(resourceCalls.seeActions(ActionFilter.ONLY_SQL).isEmpty()) return false - if(!(resourceCalls.seeActions(ActionFilter.ONLY_SQL) as List).any { it.table.name.equals(tableName, ignoreCase = true) }) return false + if(!(resourceCalls.seeActions(ActionFilter.ONLY_SQL) as List).any { it.table.name.equals(tableName, ignoreCase = true) }) return false - val dbGene = (resourceCalls.seeActions(ActionFilter.ONLY_SQL) as List).find { it.table.name.equals(tableName, ignoreCase = true) }!!.seeTopGenes().find { it.name.equals(colName, ignoreCase = true) }?: return false + val dbGene = (resourceCalls.seeActions(ActionFilter.ONLY_SQL) as List).find { it.table.name.equals(tableName, ignoreCase = true) }!!.seeTopGenes().find { it.name.equals(colName, ignoreCase = true) }?: return false return resourceCalls.seeActions(ActionFilter.ONLY_SQL).filterIsInstance().flatMap { it.parameters.filter { it.name == paramName } }.all { p-> ParamUtil.compareGenesWithValue(ParamUtil.getValueGene(dbGene!!), ParamUtil.getValueGene(p.gene)) @@ -452,7 +452,7 @@ abstract class ResourceTestBase : ExtractTestBaseH2(), ResourceBasedTestInterfac private fun generateIndividualResults(individual: Individual) : List = individual.seeActions( ActionFilter.ALL).map { - if (it is DbAction) DbActionResult().also { it.setInsertExecutionResult(true) } + if (it is SqlAction) SqlActionResult().also { it.setInsertExecutionResult(true) } else ActionResult() } } diff --git a/core/src/main/kotlin/org/evomaster/core/database/DbAction.kt b/core/src/main/kotlin/org/evomaster/core/database/SqlAction.kt similarity index 93% rename from core/src/main/kotlin/org/evomaster/core/database/DbAction.kt rename to core/src/main/kotlin/org/evomaster/core/database/SqlAction.kt index 8df175946b..a8e7ec9172 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/DbAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/SqlAction.kt @@ -12,7 +12,7 @@ import org.evomaster.core.search.gene.sql.SqlPrimaryKeyGene * An action executed on the database. * Typically, a SQL Insertion */ -class DbAction( +class SqlAction( /** * The involved table */ @@ -51,7 +51,7 @@ class DbAction( private val genes: List = (computedGenes ?: selectedColumns.map { - DbActionGeneBuilder().buildGene(id, table, it) + SqlActionGeneBuilder().buildGene(id, table, it) }).also { // init children for DbAction addChildren(it) @@ -75,7 +75,7 @@ class DbAction( based on the column name */ if (column.name.contains("time", ignoreCase = true)) { - return DbActionGeneBuilder().buildSqlTimestampGene(column.name, databaseType = column.databaseType) + return SqlActionGeneBuilder().buildSqlTimestampGene(column.name, databaseType = column.databaseType) } else { //go for a default string return StringGene(name = column.name, minLength = 0, maxLength = column.size) @@ -99,7 +99,7 @@ class DbAction( } override fun copyContent(): Action { - return DbAction(table, selectedColumns, id, genes.map(Gene::copy), representExistingData) + return SqlAction(table, selectedColumns, id, genes.map(Gene::copy), representExistingData) } override fun shouldCountForFitnessEvaluations(): Boolean { diff --git a/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt b/core/src/main/kotlin/org/evomaster/core/database/SqlActionGeneBuilder.kt similarity index 99% rename from core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt rename to core/src/main/kotlin/org/evomaster/core/database/SqlActionGeneBuilder.kt index 39ffc31f46..8297fefdfb 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/DbActionGeneBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/SqlActionGeneBuilder.kt @@ -32,7 +32,7 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory import java.math.BigDecimal -class DbActionGeneBuilder { +class SqlActionGeneBuilder { private fun getForeignKey(table: Table, column: Column): ForeignKey? { @@ -995,6 +995,6 @@ class DbActionGeneBuilder { // The double precision type has a range of around 1E-307 to 1E+308 with a precision of at least 15 digits val MIN_FLOAT8_VALUE: Double = MAX_FLOAT8_VALUE.unaryMinus() - private val log: Logger = LoggerFactory.getLogger(DbActionGeneBuilder::class.java) + private val log: Logger = LoggerFactory.getLogger(SqlActionGeneBuilder::class.java) } } diff --git a/core/src/main/kotlin/org/evomaster/core/database/DbActionResult.kt b/core/src/main/kotlin/org/evomaster/core/database/SqlActionResult.kt similarity index 81% rename from core/src/main/kotlin/org/evomaster/core/database/DbActionResult.kt rename to core/src/main/kotlin/org/evomaster/core/database/SqlActionResult.kt index d9b5fc1cb8..6a61322947 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/DbActionResult.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/SqlActionResult.kt @@ -5,17 +5,17 @@ import org.evomaster.core.search.action.ActionResult /** * sql insert action execution result */ -class DbActionResult : ActionResult { +class SqlActionResult : ActionResult { constructor(stopping: Boolean = false) : super(stopping) - constructor(other: DbActionResult): super(other) + constructor(other: SqlActionResult): super(other) companion object{ const val INSERT_SQL_EXECUTE_SUCCESSFULLY = "INSERT_SQL_EXECUTE_SUCCESSFULLY" } - override fun copy(): DbActionResult { - return DbActionResult(this) + override fun copy(): SqlActionResult { + return SqlActionResult(this) } /** @@ -31,6 +31,6 @@ class DbActionResult : ActionResult { fun getInsertExecutionResult() = getResultValue(INSERT_SQL_EXECUTE_SUCCESSFULLY)?.toBoolean()?:false override fun matchedType(action: Action): Boolean { - return action is DbAction + return action is SqlAction } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/database/DbActionTransformer.kt b/core/src/main/kotlin/org/evomaster/core/database/SqlActionTransformer.kt similarity index 94% rename from core/src/main/kotlin/org/evomaster/core/database/DbActionTransformer.kt rename to core/src/main/kotlin/org/evomaster/core/database/SqlActionTransformer.kt index 4f19127c7e..025ca6e47c 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/DbActionTransformer.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/SqlActionTransformer.kt @@ -9,18 +9,18 @@ import org.evomaster.core.search.gene.sql.SqlPrimaryKeyGene import org.evomaster.core.search.gene.sql.SqlWrapperGene -object DbActionTransformer { +object SqlActionTransformer { /** * @param sqlIdMap is a map from Insertion Id to generated Id in database */ - fun transform(insertions: List, sqlIdMap : Map = mapOf(), previousDbActions: MutableList = mutableListOf()) : DatabaseCommandDto { + fun transform(insertions: List, sqlIdMap : Map = mapOf(), previousSqlActions: MutableList = mutableListOf()) : DatabaseCommandDto { val list = mutableListOf() val previous = mutableListOf() previous.addAll( - previousDbActions.flatMap(DbAction::seeTopGenes) + previousSqlActions.flatMap(SqlAction::seeTopGenes) ) for (i in 0 until insertions.size) { diff --git a/core/src/main/kotlin/org/evomaster/core/database/DbActionUtils.kt b/core/src/main/kotlin/org/evomaster/core/database/SqlActionUtils.kt similarity index 85% rename from core/src/main/kotlin/org/evomaster/core/database/DbActionUtils.kt rename to core/src/main/kotlin/org/evomaster/core/database/SqlActionUtils.kt index 5944586ea4..61e1c92aa7 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/DbActionUtils.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/SqlActionUtils.kt @@ -10,11 +10,11 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory import org.evomaster.core.database.schema.Table -object DbActionUtils { +object SqlActionUtils { - private val log: Logger = LoggerFactory.getLogger(DbActionUtils::class.java) + private val log: Logger = LoggerFactory.getLogger(SqlActionUtils::class.java) - fun verifyForeignKeys(actions: List): Boolean { + fun verifyForeignKeys(actions: List): Boolean { for (i in 0 until actions.size) { @@ -56,7 +56,7 @@ object DbActionUtils { return true } - fun randomizeDbActionGenes(actions: List, randomness: Randomness) { + fun randomizeDbActionGenes(actions: List, randomness: Randomness) { /* At this point, SQL genes are particular, as they can have references to each other (eg Foreign Keys) @@ -91,7 +91,7 @@ object DbActionUtils { * Returns true if the action list was fixed without removing any action. * Returns false if actions needed to be removed */ - fun repairBrokenDbActionsList(actions: MutableList, + fun repairBrokenDbActionsList(actions: MutableList, randomness: Randomness, maxNumberOfAttemptsToRepairAnAction: Int = DEFAULT_MAX_NUMBER_OF_ATTEMPTS_TO_REPAIR_ACTIONS ): Boolean { @@ -166,7 +166,7 @@ object DbActionUtils { * Returns true iff all action are valid wrt the schema. * For example */ - fun verifyActions(actions: List): Boolean { + fun verifyActions(actions: List): Boolean { return verifyUniqueColumns(actions) && verifyForeignKeys(actions) } @@ -175,7 +175,7 @@ object DbActionUtils { * Returns true if a insertion tries to insert a repeated value * in a unique column */ - fun verifyUniqueColumns(actions: List): Boolean { + fun verifyUniqueColumns(actions: List): Boolean { val offendingGene = findFirstOffendingGeneWithIndex(actions) return (offendingGene.first == null) } @@ -190,24 +190,24 @@ object DbActionUtils { * If randomness is provided, the returning gene is randomly selected from all the genes in the constraint */ private fun checkIfTableConstraintsAreSatisfied( - dbAction: DbAction, - dbActionIndex: Int, - dbActions: List, - randomness: Randomness? = null + sqlAction: SqlAction, + dbActionIndex: Int, + sqlActions: List, + randomness: Randomness? = null ): Pair? { - val tableConstraints = dbAction.table.tableConstraints.filter { - it.tableName == dbAction.table.name + val tableConstraints = sqlAction.table.tableConstraints.filter { + it.tableName == sqlAction.table.name } for (tableConstraint in tableConstraints) { - val previousDbActions = if (dbActionIndex==0) listOf() else dbActions.subList(0, dbActionIndex - 1) + val previousDbActions = if (dbActionIndex==0) listOf() else sqlActions.subList(0, dbActionIndex - 1) val evaluator = TableConstraintEvaluator(previousDbActions) - if (tableConstraint.accept(evaluator, dbAction) == false) { + if (tableConstraint.accept(evaluator, sqlAction) == false) { // This constraint is not satisfied, collect all genes related to constraint val geneCollector = TableConstraintGeneCollector() - val genes = tableConstraint.accept(geneCollector, dbAction) + val genes = tableConstraint.accept(geneCollector, sqlAction) // it is expected that at least one gene should be involved in not satisfying this val chosenGene = if (randomness == null) { @@ -248,18 +248,18 @@ object DbActionUtils { val allGenes = actions.flatMap { it.seeTopGenes() } - val dbActions = mutableListOf() + val sqlActions = mutableListOf() for ((actionIndex, action) in actions.withIndex().sortedBy { it.index }) { - if (action !is DbAction) { + if (action !is SqlAction) { continue } - dbActions += action + sqlActions += action handleFKs(action, actionIndex)?.let{return it} handleUnique(action, actionIndex, uniqueColumnValues, allGenes, randomness)?.let { return it } handlePKs(action, actionIndex, pksValues, allGenes, randomness)?.let { return it } - checkIfTableConstraintsAreSatisfied(action, actionIndex, dbActions, randomness)?.let { return it } + checkIfTableConstraintsAreSatisfied(action, actionIndex, sqlActions, randomness)?.let { return it } } // check if all table constraints are satisfied @@ -269,7 +269,7 @@ object DbActionUtils { return Pair(null, -1) } - private fun handleFKs(action: DbAction, actionIndex: Int): Pair? { + private fun handleFKs(action: SqlAction, actionIndex: Int): Pair? { action.seeTopGenes().flatMap { it.flatView() } .filterIsInstance() @@ -278,11 +278,11 @@ object DbActionUtils { } private fun handleUnique( - action: DbAction, - actionIndex: Int, - uniqueColumnValues: MutableMap, MutableSet>, - all: List, - randomness: Randomness? = null + action: SqlAction, + actionIndex: Int, + uniqueColumnValues: MutableMap, MutableSet>, + all: List, + randomness: Randomness? = null ): Pair? { val tableName = action.table.name @@ -320,11 +320,11 @@ object DbActionUtils { } private fun handlePKs( - action: DbAction, - actionIndex: Int, - pksValues: MutableMap>, - all: List, - randomness: Randomness? = null + action: SqlAction, + actionIndex: Int, + pksValues: MutableMap>, + all: List, + randomness: Randomness? = null ): Pair? { if (action.table.primaryKeys().isEmpty()) { @@ -391,27 +391,27 @@ object DbActionUtils { } /** - * repair fk of [dbAction] based on primary keys of [previous] dbactions + * repair fk of [sqlAction] based on primary keys of [previous] dbactions * @return whether fk is fixed */ - fun repairFk(dbAction: DbAction, previous: MutableList) : Pair?>{ + fun repairFk(sqlAction: SqlAction, previous: MutableList) : Pair?>{ val pks = previous.flatMap { it.seeTopGenes() }.filterIsInstance() - val referDbActions = mutableListOf() + val referSqlActions = mutableListOf() - dbAction.seeTopGenes().flatMap { it.flatView() }.filterIsInstance().forEach { fk-> + sqlAction.seeTopGenes().flatMap { it.flatView() }.filterIsInstance().forEach { fk-> val needToFix = pks.none { p-> p.uniqueId == fk.uniqueIdOfPrimaryKey && p.tableName == fk.targetTable } if (needToFix){ val found = pks.find { fk.targetTable == it.tableName } if (found != null){ fk.uniqueIdOfPrimaryKey = found.uniqueId - referDbActions.add(previous.find { it.seeTopGenes().contains(found) }!!) + referSqlActions.add(previous.find { it.seeTopGenes().contains(found) }!!) }else return Pair(false, null) } } - return Pair(true, referDbActions) + return Pair(true, referSqlActions) } @@ -444,13 +444,13 @@ object DbActionUtils { * In this context, a FK of an insertion may refer to a PK that are in front of this insertion and belongs to other resource (referred resource). * During mutation, if the referred resource is modified (e.g., removed), the FK will be broken. */ - fun repairFK(dbAction: DbAction, previous : MutableList, createdDbActions : MutableList,sqlInsertBuilder: SqlInsertBuilder?, randomness: Randomness) : MutableList{ + fun repairFK(sqlAction: SqlAction, previous : MutableList, createdSqlActions : MutableList, sqlInsertBuilder: SqlInsertBuilder?, randomness: Randomness) : MutableList{ val repaired = mutableListOf() - if(dbAction.table.foreignKeys.isEmpty()) + if(sqlAction.table.foreignKeys.isEmpty()) return repaired val pks = previous.flatMap { it.seeTopGenes() }.filterIsInstance() - dbAction.seeTopGenes().flatMap { it.flatView() }.filterIsInstance().filter { fk-> pks.none { p-> p.uniqueId == fk.uniqueIdOfPrimaryKey } }.forEach { fk-> + sqlAction.seeTopGenes().flatMap { it.flatView() }.filterIsInstance().filter { fk-> pks.none { p-> p.uniqueId == fk.uniqueIdOfPrimaryKey } }.forEach { fk-> var found = pks.find { pk -> pk.tableName == fk.targetTable && pk.uniqueId != fk.uniqueIdOfPrimaryKey } if (found == null){ val created = sqlInsertBuilder?.createSqlInsertionAction(fk.targetTable, mutableSetOf(), enableSingleInsertionForTable= randomness.nextBoolean())?.toMutableList() @@ -464,7 +464,7 @@ object DbActionUtils { ?:throw IllegalStateException("fail to create target table (${fk.targetTable}) for ${fk.name}") repairFkForInsertions(created) - createdDbActions.addAll(created) + createdSqlActions.addAll(created) previous.addAll(created) repaired.addAll(created.flatMap { it.seeTopGenes() }.filterIsInstance()) } @@ -475,27 +475,27 @@ object DbActionUtils { return repaired } - fun repairFkForInsertions(dbActions: List){ - dbActions.forEachIndexed { index, dbAction -> + fun repairFkForInsertions(sqlActions: List){ + sqlActions.forEachIndexed { index, dbAction -> val fks = dbAction.seeTopGenes().flatMap { it.flatView() }.filterIsInstance() if (fks.any { !it.nullable && !it.isBound() } && index == 0) throw IllegalStateException("invalid insertion, there exists invalid fk at $index") - val pks = dbActions.subList(0, index).flatMap { it.seeTopGenes() }.filterIsInstance() + val pks = sqlActions.subList(0, index).flatMap { it.seeTopGenes() }.filterIsInstance() fks.filter { !it.nullable && !it.isBound() || pks.none { p->p.uniqueId == it.uniqueIdOfPrimaryKey }}.forEach {fk-> val found = pks.find { pk -> pk.tableName.equals(fk.targetTable, ignoreCase = true) } ?: throw IllegalStateException("fail to target table ${fk.targetTable} for the fk ${fk.name}") fk.uniqueIdOfPrimaryKey = found.uniqueId } } - if (!verifyForeignKeys(dbActions)) + if (!verifyForeignKeys(sqlActions)) throw IllegalStateException("FK repair fails") } /** - * @return a list of dbactions from [dbActions] whose related table is [tableName] + * @return a list of dbactions from [sqlActions] whose related table is [tableName] */ - fun findDbActionsByTableName(dbActions: List, tableName : String) : List{ - return dbActions.filter { it.table.name.equals(tableName, ignoreCase = true) } + fun findDbActionsByTableName(sqlActions: List, tableName : String) : List{ + return sqlActions.filter { it.table.name.equals(tableName, ignoreCase = true) } } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt b/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt index 01793b240e..a7bda199b1 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt @@ -618,7 +618,7 @@ class SqlInsertBuilder( * */ enableSingleInsertionForTable: Boolean = false - ): List { + ): List { history.add(tableName) @@ -652,7 +652,7 @@ class SqlInsertBuilder( } } - val insertion = DbAction(table, selectedColumns, counter++) + val insertion = SqlAction(table, selectedColumns, counter++) if (log.isTraceEnabled) { log.trace("create an insertion which is {} and the counter is {}", insertion.getResolvedName(), counter) } @@ -711,13 +711,13 @@ class SqlInsertBuilder( * For each row, create a DbAction containing only Primary Keys * and immutable data */ - fun extractExistingPKs(): List { + fun extractExistingPKs(): List { if (dbExecutor == null) { throw IllegalStateException("No Database Executor registered for this object") } - val list = mutableListOf() + val list = mutableListOf() for (table in tables.values) { @@ -754,7 +754,7 @@ class SqlInsertBuilder( genes.add(pk) } - val action = DbAction(table, pks.toSet(), id, genes, true) + val action = SqlAction(table, pks.toSet(), id, genes, true) list.add(action) } } @@ -782,7 +782,7 @@ class SqlInsertBuilder( pkValues: DataRowDto, useExtraSqlDbConstraints: Boolean, columnIds: List = mutableListOf() - ): DbAction { + ): SqlAction { if (dbExecutor == null) { throw IllegalStateException("No Database Executor registered for this object") @@ -846,7 +846,7 @@ class SqlInsertBuilder( } } - val db = DbAction(table, pks.toSet(), id, genes, true) + val db = SqlAction(table, pks.toSet(), id, genes, true) db.doInitialize() return db diff --git a/core/src/main/kotlin/org/evomaster/core/database/TableConstraintEvaluator.kt b/core/src/main/kotlin/org/evomaster/core/database/TableConstraintEvaluator.kt index 3dc31d043c..436eadcbe4 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/TableConstraintEvaluator.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/TableConstraintEvaluator.kt @@ -12,27 +12,27 @@ import java.util.regex.Pattern * The evaluator expects that the database is initially empty (only the previous actions * are considered for the evaluation). */ -class TableConstraintEvaluator(val previousActions: List = listOf()) - : TableConstraintVisitor { +class TableConstraintEvaluator(val previousActions: List = listOf()) + : TableConstraintVisitor { /** * Evaluates to true when both left and right expressions are evaluated to true */ - override fun visit(constraint: AndConstraint, dbAction: DbAction): Boolean { - val leftValue = constraint.left.accept(this, dbAction) - val rightValue = constraint.right.accept(this, dbAction) + override fun visit(constraint: AndConstraint, sqlAction: SqlAction): Boolean { + val leftValue = constraint.left.accept(this, sqlAction) + val rightValue = constraint.right.accept(this, sqlAction) return leftValue && rightValue } /** * Evaluates to true when both any of the OR-expressions is evaluated to true */ - override fun visit(constraint: OrConstraint, dbAction: DbAction): Boolean { + override fun visit(constraint: OrConstraint, sqlAction: SqlAction): Boolean { return constraint.constraintList .stream() .anyMatch { - it.accept(this, dbAction) + it.accept(this, sqlAction) } } @@ -40,24 +40,24 @@ class TableConstraintEvaluator(val previousActions: List = listOf()) * Evaluates to true when both left and right have the same evaluated value (i.e. * both evaluate to true or both evaluate to false) */ - override fun visit(constraint: IffConstraint, dbAction: DbAction): Boolean { - val leftValue = constraint.left.accept(this, dbAction) - val rightValue = constraint.right.accept(this, dbAction) + override fun visit(constraint: IffConstraint, sqlAction: SqlAction): Boolean { + val leftValue = constraint.left.accept(this, sqlAction) + val rightValue = constraint.right.accept(this, sqlAction) return leftValue == rightValue } /** * Evaluates to true if the column value is different than NULL */ - override fun visit(constraint: IsNotNullConstraint, dbAction: DbAction): Boolean { + override fun visit(constraint: IsNotNullConstraint, sqlAction: SqlAction): Boolean { // if the action is not referred to this action, we conclude // the action does not invalidate the constraint - if (dbAction.table.name != constraint.tableName) { + if (sqlAction.table.name != constraint.tableName) { return true } // get the gene with the corresponding column name - val gene = dbAction.seeTopGenes().firstOrNull { it.name == constraint.columnName } + val gene = sqlAction.seeTopGenes().firstOrNull { it.name == constraint.columnName } val isPresent = when (gene) { @@ -80,10 +80,10 @@ class TableConstraintEvaluator(val previousActions: List = listOf()) * Evaluates to true if the column value is not null and the column value is a * numerica value that is greater than the lower bound value */ - override fun visit(constraint: LowerBoundConstraint, dbAction: DbAction): Boolean { + override fun visit(constraint: LowerBoundConstraint, sqlAction: SqlAction): Boolean { // if the action is not referred to this action, we conclude // the action does not invalidate the constraint - if (dbAction.table.name != constraint.tableName) { + if (sqlAction.table.name != constraint.tableName) { return true } val columnName = constraint.columnName @@ -92,7 +92,7 @@ class TableConstraintEvaluator(val previousActions: List = listOf()) // will be NULL as default value. Therefore, the range is not satisfied // However, it is possible to specify DEFAULT values in the // TODO: Handle DEFAULT column values different than NULL - val gene = dbAction.seeTopGenes().firstOrNull { it.name == constraint.columnName } ?: return false + val gene = sqlAction.seeTopGenes().firstOrNull { it.name == constraint.columnName } ?: return false val numberGene = gene.flatView().filterIsInstance>().first() return constraint.lowerBound <= numberGene.toLong() @@ -101,10 +101,10 @@ class TableConstraintEvaluator(val previousActions: List = listOf()) /** * Evaluates to true if the column value is lesser or equal to the constant value */ - override fun visit(constraint: UpperBoundConstraint, dbAction: DbAction): Boolean { + override fun visit(constraint: UpperBoundConstraint, sqlAction: SqlAction): Boolean { // if the action is not referred to this action, we conclude // the action does not invalidate the constraint - if (dbAction.table.name != constraint.tableName) { + if (sqlAction.table.name != constraint.tableName) { return true } val columnName = constraint.columnName @@ -113,7 +113,7 @@ class TableConstraintEvaluator(val previousActions: List = listOf()) // will be NULL as default value. Therefore, the range is not satisfied // However, it is possible to specify DEFAULT values in the // TODO: Handle DEFAULT column values different than NULL - val gene = dbAction.seeTopGenes().firstOrNull { it.name == constraint.columnName } ?: return false + val gene = sqlAction.seeTopGenes().firstOrNull { it.name == constraint.columnName } ?: return false val numberGene = gene.flatView().filterIsInstance>().first() return numberGene.toLong() <= constraint.upperBound @@ -123,10 +123,10 @@ class TableConstraintEvaluator(val previousActions: List = listOf()) /** * Evaluates the table.column in range [minValue,maxValue] */ - override fun visit(constraint: RangeConstraint, dbAction: DbAction): Boolean { + override fun visit(constraint: RangeConstraint, sqlAction: SqlAction): Boolean { // if the action is not referred to this action, we conclude // the action does not invalidate the constraint - if (dbAction.table.name != constraint.tableName) { + if (sqlAction.table.name != constraint.tableName) { return true } val columnName = constraint.columnName @@ -135,7 +135,7 @@ class TableConstraintEvaluator(val previousActions: List = listOf()) // will be NULL as default value. Therefore, the range is not satisfied // However, it is possible to specify DEFAULT values in the // TODO: Handle DEFAULT column values different than NULL - val gene = dbAction.seeTopGenes().firstOrNull { it.name == constraint.columnName } ?: return false + val gene = sqlAction.seeTopGenes().firstOrNull { it.name == constraint.columnName } ?: return false val numberGene = gene.flatView().filterIsInstance>().first() return constraint.minValue <= numberGene.toLong() @@ -147,13 +147,13 @@ class TableConstraintEvaluator(val previousActions: List = listOf()) * Evaluates to true when the column value is not null and matches any of * the enumerated values */ - override fun visit(constraint: EnumConstraint, dbAction: DbAction): Boolean { + override fun visit(constraint: EnumConstraint, sqlAction: SqlAction): Boolean { // if the action is not referred to this action, we conclude // the action does not invalidate the constraint - if (dbAction.table.name != constraint.tableName) { + if (sqlAction.table.name != constraint.tableName) { return true } - val gene = dbAction.seeTopGenes().firstOrNull { it.name == constraint.columnName } ?: return false + val gene = sqlAction.seeTopGenes().firstOrNull { it.name == constraint.columnName } ?: return false val rawString = gene.getValueAsRawString() return constraint.valuesAsStrings.contains(rawString) @@ -164,11 +164,11 @@ class TableConstraintEvaluator(val previousActions: List = listOf()) * are unique with respect to this value. Includes partial support * for multi-column unique constraints */ - override fun visit(constraint: UniqueConstraint, dbAction: DbAction): Boolean { + override fun visit(constraint: UniqueConstraint, sqlAction: SqlAction): Boolean { val tableName = constraint.tableName val tuples = mutableListOf>() - val allActions = this.previousActions + dbAction + val allActions = this.previousActions + sqlAction for (previousAction in allActions) { if (previousAction.table.name != tableName) { // if the action is not related to this action, ignore the action @@ -208,7 +208,7 @@ class TableConstraintEvaluator(val previousActions: List = listOf()) /** * Unsupported constraints are true by default */ - override fun visit(constraint: UnsupportedTableConstraint, dbAction: DbAction): Boolean { + override fun visit(constraint: UnsupportedTableConstraint, sqlAction: SqlAction): Boolean { return true } @@ -218,7 +218,7 @@ class TableConstraintEvaluator(val previousActions: List = listOf()) * matches the LIKE pattern. Different Database Types (e.g. H2, POSTGRES, etc) * might have different pattern matching behaviour. */ - override fun visit(constraint: LikeConstraint, dbAction: DbAction): Boolean { + override fun visit(constraint: LikeConstraint, sqlAction: SqlAction): Boolean { val tableName = constraint.tableName val columnName = constraint.columnName val databaseType = constraint.databaseType @@ -226,12 +226,12 @@ class TableConstraintEvaluator(val previousActions: List = listOf()) // if the action is not referred to this action, we conclude // the action does not invalidate the constraint - if (dbAction.table.name != tableName) { + if (sqlAction.table.name != tableName) { return true } - val gene = dbAction.seeTopGenes().firstOrNull { it.name == columnName } ?: return false + val gene = sqlAction.seeTopGenes().firstOrNull { it.name == columnName } ?: return false val instance = gene.getValueAsRawString() val javaRegexPattern = when (databaseType) { @@ -245,7 +245,7 @@ class TableConstraintEvaluator(val previousActions: List = listOf()) } - override fun visit(constraint: SimilarToConstraint, dbAction: DbAction): Boolean { + override fun visit(constraint: SimilarToConstraint, sqlAction: SqlAction): Boolean { val tableName = constraint.tableName val columnName = constraint.columnName val databaseType = constraint.databaseType @@ -253,11 +253,11 @@ class TableConstraintEvaluator(val previousActions: List = listOf()) // if the action is not referred to this action, we conclude // the action does not invalidate the constraint - if (dbAction.table.name != tableName) { + if (sqlAction.table.name != tableName) { return true } - val gene = dbAction.seeTopGenes().firstOrNull { it.name == columnName } ?: return false + val gene = sqlAction.seeTopGenes().firstOrNull { it.name == columnName } ?: return false val instance = gene.getValueAsRawString() val javaRegexPattern = when (databaseType) { diff --git a/core/src/main/kotlin/org/evomaster/core/database/TableConstraintGeneCollector.kt b/core/src/main/kotlin/org/evomaster/core/database/TableConstraintGeneCollector.kt index 69fe705284..af4bb059ad 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/TableConstraintGeneCollector.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/TableConstraintGeneCollector.kt @@ -9,123 +9,123 @@ import java.util.stream.Collectors * of the constraint. */ class TableConstraintGeneCollector() - : TableConstraintVisitor, DbAction> { + : TableConstraintVisitor, SqlAction> { /** * Return all genes in left and right constraints */ - override fun visit(constraint: AndConstraint, dbAction: DbAction): Set { - val leftGenes = constraint.left.accept(this, dbAction) - val rightGenes = constraint.right.accept(this, dbAction) + override fun visit(constraint: AndConstraint, sqlAction: SqlAction): Set { + val leftGenes = constraint.left.accept(this, sqlAction) + val rightGenes = constraint.right.accept(this, sqlAction) return leftGenes + rightGenes } /** * Return all genes in all child constraints */ - override fun visit(constraint: OrConstraint, dbAction: DbAction): Set { + override fun visit(constraint: OrConstraint, sqlAction: SqlAction): Set { return constraint.constraintList .stream() .map { - it.accept(this, dbAction) + it.accept(this, sqlAction) }.collect(Collectors.toSet()).flatten().toSet() } /** * Return all genes in left and right constraints */ - override fun visit(constraint: IffConstraint, dbAction: DbAction): Set { - val leftGenes = constraint.left.accept(this, dbAction) - val rightGenes = constraint.right.accept(this, dbAction) + override fun visit(constraint: IffConstraint, sqlAction: SqlAction): Set { + val leftGenes = constraint.left.accept(this, sqlAction) + val rightGenes = constraint.right.accept(this, sqlAction) return leftGenes + rightGenes } /** * Return the gene in the not null constraint */ - override fun visit(constraint: IsNotNullConstraint, dbAction: DbAction): Set { + override fun visit(constraint: IsNotNullConstraint, sqlAction: SqlAction): Set { // if the action is not mentioned to this constraint's table, we safely conclude // that the genes do not affect the evaluation of this constraint - if (dbAction.table.name != constraint.tableName) { + if (sqlAction.table.name != constraint.tableName) { return setOf() } // return all columns that match the table name/column name (expected: one or none) - return dbAction.seeTopGenes().filter { it.name == constraint.columnName }.toSet() + return sqlAction.seeTopGenes().filter { it.name == constraint.columnName }.toSet() } /** * Return the gene in the lower bound constraint */ - override fun visit(constraint: LowerBoundConstraint, dbAction: DbAction): Set { + override fun visit(constraint: LowerBoundConstraint, sqlAction: SqlAction): Set { // if the action is not mentioned to this constraint's table, we safely conclude // that the genes do not affect the evaluation of this constraint - if (dbAction.table.name != constraint.tableName) { + if (sqlAction.table.name != constraint.tableName) { return setOf() } // return all columns that match the table name/column name (expected: one or none) - return dbAction.seeTopGenes().filter { it.name == constraint.columnName }.toSet() + return sqlAction.seeTopGenes().filter { it.name == constraint.columnName }.toSet() } /** * Return the gene in the upper bound constraint */ - override fun visit(constraint: UpperBoundConstraint, dbAction: DbAction): Set { + override fun visit(constraint: UpperBoundConstraint, sqlAction: SqlAction): Set { // if the action is not referred to this constraint's table, we safely conclude // that the genes do not affect the evaluation of this constraint - if (dbAction.table.name != constraint.tableName) { + if (sqlAction.table.name != constraint.tableName) { return setOf() } // return all columns that match the table name/column name (expected: one or none) - return dbAction.seeTopGenes().filter { it.name == constraint.columnName }.toSet() + return sqlAction.seeTopGenes().filter { it.name == constraint.columnName }.toSet() } /** * Return the gene in the range constraint */ - override fun visit(constraint: RangeConstraint, dbAction: DbAction): Set { + override fun visit(constraint: RangeConstraint, sqlAction: SqlAction): Set { // if the action is not referred to this constraint's table, we safely conclude // that the genes do not affect the evaluation of this constraint - if (dbAction.table.name != constraint.tableName) { + if (sqlAction.table.name != constraint.tableName) { return setOf() } // return all columns that match the table name/column name (expected: one or none) - return dbAction.seeTopGenes().filter { it.name == constraint.columnName }.toSet() + return sqlAction.seeTopGenes().filter { it.name == constraint.columnName }.toSet() } /** * Return the gene in the enum constraint (if the table name matches) */ - override fun visit(constraint: EnumConstraint, dbAction: DbAction): Set { + override fun visit(constraint: EnumConstraint, sqlAction: SqlAction): Set { // if the action is not referred to this constraint's table, we safely conclude // that the genes do not affect the evaluation of this constraint - if (dbAction.table.name != constraint.tableName) { + if (sqlAction.table.name != constraint.tableName) { return setOf() } // return all columns that match the table name/column name (expected: one or none) - return dbAction.seeTopGenes().filter { it.name == constraint.columnName }.toSet() + return sqlAction.seeTopGenes().filter { it.name == constraint.columnName }.toSet() } /** * Return all genes in the unique constraint (if the table name matches) */ - override fun visit(constraint: UniqueConstraint, dbAction: DbAction): Set { + override fun visit(constraint: UniqueConstraint, sqlAction: SqlAction): Set { // if the action is not referred to this constraint's table, we safely conclude // that the genes do not affect the evaluation of this constraint - if (dbAction.table.name != constraint.tableName) { + if (sqlAction.table.name != constraint.tableName) { return setOf() } // return all columns that match the table name/column name in the unique constraint (none, one or many) - return dbAction.seeTopGenes().filter { constraint.uniqueColumnNames.contains(it.name) }.toSet() + return sqlAction.seeTopGenes().filter { constraint.uniqueColumnNames.contains(it.name) }.toSet() } /** * Unsupported constraints return no genes */ - override fun visit(constraint: UnsupportedTableConstraint, dbAction: DbAction): Set { + override fun visit(constraint: UnsupportedTableConstraint, sqlAction: SqlAction): Set { return setOf() } @@ -133,27 +133,27 @@ class TableConstraintGeneCollector() /** * Return the gene in the like constraint (if the table name matches) */ - override fun visit(constraint: LikeConstraint, dbAction: DbAction): Set { + override fun visit(constraint: LikeConstraint, sqlAction: SqlAction): Set { // if the action is not referred to this constraint's table, we safely conclude // that the genes do not affect the evaluation of this constraint - if (dbAction.table.name != constraint.tableName) { + if (sqlAction.table.name != constraint.tableName) { return setOf() } // return all columns that match the table name/column name (expected: one or none) - return dbAction.seeTopGenes().filter { it.name == constraint.columnName }.toSet() + return sqlAction.seeTopGenes().filter { it.name == constraint.columnName }.toSet() } /** * Return the gene in the similarTo constraint (if the table name matches) */ - override fun visit(constraint: SimilarToConstraint, dbAction: DbAction): Set { + override fun visit(constraint: SimilarToConstraint, sqlAction: SqlAction): Set { // if the action is not referred to this constraint's table, we safely conclude // that the genes do not affect the evaluation of this constraint - if (dbAction.table.name != constraint.tableName) { + if (sqlAction.table.name != constraint.tableName) { return setOf() } // return all columns that match the table name/column name (expected: one or none) - return dbAction.seeTopGenes().filter { it.name == constraint.columnName }.toSet() + return sqlAction.seeTopGenes().filter { it.name == constraint.columnName }.toSet() } diff --git a/core/src/main/kotlin/org/evomaster/core/output/SqlWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/SqlWriter.kt index d8801fe54e..c2b1cb6ba9 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/SqlWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/SqlWriter.kt @@ -2,7 +2,7 @@ package org.evomaster.core.output import org.apache.commons.lang3.StringEscapeUtils import org.evomaster.core.Lazy -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.search.EvaluatedDbAction import org.evomaster.core.search.gene.Gene import org.evomaster.core.search.gene.ObjectGene @@ -28,13 +28,13 @@ object SqlWriter { * @param skipFailure specifies whether to skip failure tests */ fun handleDbInitialization( - format: OutputFormat, - dbInitialization: List, - lines: Lines, - allDbInitialization: List = dbInitialization.map { it.action }, - groupIndex: String ="", - insertionVars: MutableList>, - skipFailure: Boolean) { + format: OutputFormat, + dbInitialization: List, + lines: Lines, + allDbInitialization: List = dbInitialization.map { it.action }, + groupIndex: String ="", + insertionVars: MutableList>, + skipFailure: Boolean) { if (dbInitialization.isEmpty() || dbInitialization.none { !it.action.representExistingData && (!skipFailure || it.result.getInsertExecutionResult())}) { return @@ -118,7 +118,7 @@ object SqlWriter { } } - private fun handleFK(format: OutputFormat, fkg: SqlForeignKeyGene, action: DbAction, allActions: List): String { + private fun handleFK(format: OutputFormat, fkg: SqlForeignKeyGene, action: SqlAction, allActions: List): String { /* diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/ApiTestCaseWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/ApiTestCaseWriter.kt index b5635e1460..95bafcb8c2 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/ApiTestCaseWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/ApiTestCaseWriter.kt @@ -2,8 +2,8 @@ package org.evomaster.core.output.service import com.google.gson.Gson import com.google.gson.JsonSyntaxException -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionResult +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionResult import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.mongo.MongoDbActionResult import org.evomaster.core.output.* @@ -34,9 +34,9 @@ abstract class ApiTestCaseWriter : TestCaseWriter() { //FIXME this doing initializations, not field declaration //REFACTOR TO HANDLE MULTIPLE DATABASES - val initializingSqlActions = ind.individual.seeInitializingActions().filterIsInstance() + val initializingSqlActions = ind.individual.seeInitializingActions().filterIsInstance() val initializingSqlActionResults = (ind.seeResults(initializingSqlActions)) - if (initializingSqlActionResults.any { (it as? DbActionResult) == null }) + if (initializingSqlActionResults.any { (it as? SqlActionResult) == null }) throw IllegalStateException("the type of results are expected as DbActionResults") val initializingMongoActions = ind.individual.seeInitializingActions().filterIsInstance() @@ -49,7 +49,7 @@ abstract class ApiTestCaseWriter : TestCaseWriter() { SqlWriter.handleDbInitialization( format, initializingSqlActions.indices.map { - EvaluatedDbAction(initializingSqlActions[it], initializingSqlActionResults[it] as DbActionResult) + EvaluatedDbAction(initializingSqlActions[it], initializingSqlActionResults[it] as SqlActionResult) }, lines, insertionVars = insertionVars, skipFailure = config.skipFailureSQLInTestFile) } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsFitness.kt index 63b3018f58..705d26f644 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsFitness.kt @@ -1,31 +1,11 @@ package org.evomaster.core.problem.api.service import com.google.inject.Inject -import org.evomaster.client.java.controller.api.dto.ActionDto -import org.evomaster.client.java.controller.api.dto.HeuristicEntryDto import org.evomaster.client.java.controller.api.dto.SutInfoDto -import org.evomaster.client.java.controller.api.dto.TestResultsDto -import org.evomaster.client.java.instrumentation.shared.ObjectiveNaming -import org.evomaster.core.StaticCounter -import org.evomaster.core.database.DatabaseExecution -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionResult -import org.evomaster.core.database.DbActionTransformer -import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.output.service.TestSuiteWriter import org.evomaster.core.problem.enterprise.service.EnterpriseFitness -import org.evomaster.core.remote.service.RemoteController -import org.evomaster.core.search.action.Action -import org.evomaster.core.search.action.ActionResult -import org.evomaster.core.search.FitnessValue import org.evomaster.core.search.Individual -import org.evomaster.core.search.gene.string.StringGene -import org.evomaster.core.search.gene.regex.RegexGene -import org.evomaster.core.search.gene.sql.SqlAutoIncrementGene -import org.evomaster.core.search.gene.sql.SqlForeignKeyGene -import org.evomaster.core.search.gene.sql.SqlPrimaryKeyGene import org.evomaster.core.search.service.* -import org.evomaster.core.taint.TaintAnalysis import org.slf4j.Logger import org.slf4j.LoggerFactory import java.nio.file.Files diff --git a/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsSampler.kt index baa29abb6d..802795a221 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsSampler.kt @@ -1,16 +1,7 @@ package org.evomaster.core.problem.api.service -import com.google.inject.Inject -import org.evomaster.client.java.controller.api.dto.SutInfoDto -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionUtils -import org.evomaster.core.database.SqlInsertBuilder -import org.evomaster.core.output.OutputFormat import org.evomaster.core.problem.enterprise.service.EnterpriseSampler -import org.evomaster.core.remote.SutProblemException -import org.evomaster.core.remote.service.RemoteController import org.evomaster.core.search.Individual -import org.evomaster.core.search.service.Sampler import org.slf4j.Logger import org.slf4j.LoggerFactory diff --git a/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt b/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt index 4c44df83ed..f7c55eefb0 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt @@ -4,8 +4,8 @@ import com.google.inject.Inject import org.evomaster.client.java.controller.api.dto.database.execution.MongoFailedQuery import org.evomaster.core.EMConfig import org.evomaster.core.Lazy -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionUtils +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionUtils import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.api.ApiWsIndividual @@ -211,7 +211,7 @@ abstract class ApiWsStructureMutator : StructureMutator() { return } - val old = mutableListOf().plus(ind.seeInitializingActions().filterIsInstance()) + val old = mutableListOf().plus(ind.seeInitializingActions().filterIsInstance()) val addedInsertions = handleFailedWhereSQL(ind, fw, mutatedGenes, sampler) @@ -244,7 +244,7 @@ abstract class ApiWsStructureMutator : StructureMutator() { we might remove the condition check on representExistingData. */ if (ind.seeDbActions().isEmpty() - || !ind.seeDbActions().any { it is DbAction && it.representExistingData } + || !ind.seeDbActions().any { it is SqlAction && it.representExistingData } ) { /* @@ -270,7 +270,7 @@ abstract class ApiWsStructureMutator : StructureMutator() { // add fw into dbInitialization val max = config.maxSqlInitActionsPerMissingData - val initializingActions = ind.seeInitializingActions().filterIsInstance() + val initializingActions = ind.seeInitializingActions().filterIsInstance() var missing = findMissing(fw, initializingActions) @@ -304,7 +304,7 @@ abstract class ApiWsStructureMutator : StructureMutator() { imply generating an action for B as well. So, we need to recompute "missing" each time */ - missing = findMissing(fw, ind.seeInitializingActions().filterIsInstance()) + missing = findMissing(fw, ind.seeInitializingActions().filterIsInstance()) } if (config.generateSqlDataWithDSE) { @@ -331,7 +331,7 @@ abstract class ApiWsStructureMutator : StructureMutator() { return addedInsertions } - private fun findMissing(fw: Map>, dbactions: List): Map> { + private fun findMissing(fw: Map>, dbactions: List): Map> { return fw.filter { e -> //shouldn't have already an action adding such SQL data @@ -365,7 +365,7 @@ abstract class ApiWsStructureMutator : StructureMutator() { */ val candidatesToMutate = - individual.seeInitializingActions().filterIsInstance().filterNot { it.representExistingData } + individual.seeInitializingActions().filterIsInstance().filterNot { it.representExistingData } val tables = candidatesToMutate.map { it.table.name }.run { ifEmpty { getSqlInsertBuilder()!!.getTableNames() } } @@ -394,11 +394,11 @@ abstract class ApiWsStructureMutator : StructureMutator() { */ fun handleInitSqlAddition( individual: ApiWsIndividual, - add: List>, + add: List>, mutatedGenes: MutatedGeneSpecification? ) { individual.addInitializingDbActions(actions = add.flatten()) - mutatedGenes?.addedDbActions?.addAll(add) + mutatedGenes?.addedSqlActions?.addAll(add) } /** @@ -406,30 +406,30 @@ abstract class ApiWsStructureMutator : StructureMutator() { */ fun handleInitSqlRemoval( individual: ApiWsIndividual, - remove: List, + remove: List, mutatedGenes: MutatedGeneSpecification? ) { - val relatedRemove = mutableListOf() + val relatedRemove = mutableListOf() relatedRemove.addAll(remove) remove.forEach { getRelatedRemoveDbActions(individual, it, relatedRemove) } val set = relatedRemove.filterNot { it.representExistingData }.toSet().toMutableList() - mutatedGenes?.removedDbActions?.addAll(set.map { it to individual.seeInitializingActions().indexOf(it) }) + mutatedGenes?.removedSqlActions?.addAll(set.map { it to individual.seeInitializingActions().indexOf(it) }) individual.removeInitDbActions(set) } private fun getRelatedRemoveDbActions( ind: ApiWsIndividual, - remove: DbAction, - relatedRemove: MutableList + remove: SqlAction, + relatedRemove: MutableList ) { val pks = remove.seeTopGenes().flatMap { it.flatView() }.filterIsInstance() val index = ind.seeInitializingActions().indexOf(remove) if (index < ind.seeInitializingActions().size - 1 && pks.isNotEmpty()) { - val removeDbFKs = ind.seeInitializingActions().filterIsInstance() - .subList(index + 1, ind.seeInitializingActions().filterIsInstance().size).filter { + val removeDbFKs = ind.seeInitializingActions().filterIsInstance() + .subList(index + 1, ind.seeInitializingActions().filterIsInstance().size).filter { it.seeTopGenes().flatMap { g -> g.flatView() }.filterIsInstance() .any { fk -> pks.any { pk -> fk.uniqueIdOfPrimaryKey == pk.uniqueId } } } @@ -444,7 +444,7 @@ abstract class ApiWsStructureMutator : StructureMutator() { * @param name is the table name * @param num is a number of table with [name] to be added */ - fun createInsertSqlAction(name: String, num: Int): List> { + fun createInsertSqlAction(name: String, num: Int): List> { getSqlInsertBuilder() ?: throw IllegalStateException("attempt to create resource with SQL but the sqlBuilder is null") if (num <= 0) @@ -471,11 +471,11 @@ abstract class ApiWsStructureMutator : StructureMutator() { }) } - DbActionUtils.randomizeDbActionGenes(list.flatten(), randomness) + SqlActionUtils.randomizeDbActionGenes(list.flatten(), randomness) //FIXME refactoring list.flatten().forEach { it.seeTopGenes().forEach { g -> g.markAllAsInitialized() } } //FIXME broken elements are not removed from list - DbActionUtils.repairBrokenDbActionsList(list.flatten().toMutableList(), randomness) + SqlActionUtils.repairBrokenDbActionsList(list.flatten().toMutableList(), randomness) return list } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt index eb87d3cf31..f049d7b575 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt @@ -4,12 +4,11 @@ import org.evomaster.core.Lazy import org.evomaster.core.search.action.Action import org.evomaster.core.search.action.ActionComponent import org.evomaster.core.search.action.ActionFilter -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionUtils +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionUtils import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.api.ApiWsIndividual import org.evomaster.core.problem.externalservice.ApiExternalServiceAction -import org.evomaster.core.problem.graphql.GraphQLAction import org.evomaster.core.search.* import org.evomaster.core.search.gene.utils.GeneUtils import org.evomaster.core.search.service.Randomness @@ -82,12 +81,12 @@ abstract class EnterpriseIndividual( //TODO in future ll need to refactor to handle multiple databases and NoSQL ones //CHANGE: This is momentary. Needs refactor to handle multiple databases - val startIndexSQL = children.indexOfFirst { a -> a is DbAction } - val endIndexSQL = children.indexOfLast { a -> a is DbAction } + val startIndexSQL = children.indexOfFirst { a -> a is SqlAction } + val endIndexSQL = children.indexOfLast { a -> a is SqlAction } val startIndexMongo = children.indexOfFirst { a -> a is MongoDbAction } val endIndexMongo = children.indexOfLast { a -> a is MongoDbAction } - val db = ChildGroup(GroupsOfChildren.INITIALIZATION_SQL,{e -> e is ActionComponent && e.flatten().all { a -> a is DbAction }}, + val db = ChildGroup(GroupsOfChildren.INITIALIZATION_SQL,{e -> e is ActionComponent && e.flatten().all { a -> a is SqlAction }}, if(sizeDb==0) -1 else startIndexSQL , if(sizeDb==0) -1 else endIndexSQL ) @@ -95,7 +94,7 @@ abstract class EnterpriseIndividual( if(sizeDb==0) -1 else startIndexMongo , if(sizeDb==0) -1 else endIndexMongo ) - val main = ChildGroup(GroupsOfChildren.MAIN, {e -> e !is DbAction && e !is ApiExternalServiceAction }, + val main = ChildGroup(GroupsOfChildren.MAIN, {e -> e !is SqlAction && e !is ApiExternalServiceAction }, if(sizeMain == 0) -1 else sizeDb, if(sizeMain == 0) -1 else sizeDb + sizeMain - 1) return GroupsOfChildren(children, listOf(db, mongodb, main)) @@ -105,11 +104,11 @@ abstract class EnterpriseIndividual( /** * a list of db actions for its Initialization */ - private val dbInitialization: List + private val dbInitialization: List get() { return groupsView()!!.getAllInGroup(GroupsOfChildren.INITIALIZATION_SQL) .flatMap { (it as ActionComponent).flatten() } - .map { it as DbAction } + .map { it as SqlAction } } final override fun seeActions(filter: ActionFilter) : List{ @@ -117,15 +116,15 @@ abstract class EnterpriseIndividual( ActionFilter.ALL -> seeAllActions() ActionFilter.MAIN_EXECUTABLE -> groupsView()!!.getAllInGroup(GroupsOfChildren.MAIN) .flatMap { (it as ActionComponent).flatten() } - .filter { it !is DbAction && it !is ApiExternalServiceAction } + .filter { it !is SqlAction && it !is ApiExternalServiceAction } ActionFilter.INIT -> groupsView()!!.getAllInGroup(GroupsOfChildren.INITIALIZATION_SQL) .flatMap { (it as ActionComponent).flatten() } + groupsView()!!.getAllInGroup(GroupsOfChildren.INITIALIZATION_MONGO) .flatMap { (it as ActionComponent).flatten() } // WARNING: this can still return DbAction, MongoDbAction and External ones... ActionFilter.NO_INIT -> groupsView()!!.getAllInGroup(GroupsOfChildren.MAIN).flatMap { (it as ActionComponent).flatten() } - ActionFilter.ONLY_SQL -> seeAllActions().filterIsInstance() + ActionFilter.ONLY_SQL -> seeAllActions().filterIsInstance() ActionFilter.ONLY_MONGO -> seeAllActions().filterIsInstance() - ActionFilter.NO_SQL -> seeAllActions().filter { it !is DbAction } + ActionFilter.NO_SQL -> seeAllActions().filter { it !is SqlAction } ActionFilter.ONLY_EXTERNAL_SERVICE -> seeAllActions().filterIsInstance() ActionFilter.NO_EXTERNAL_SERVICE -> seeAllActions().filter { it !is ApiExternalServiceAction } } @@ -167,7 +166,7 @@ abstract class EnterpriseIndividual( * NOTE THAT if EMConfig.probOfApplySQLActionToCreateResources is 0.0, this method * would be same with [seeInitializingActions] */ - fun seeDbActions() : List = seeActions(ActionFilter.ONLY_SQL) as List + fun seeDbActions() : List = seeActions(ActionFilter.ONLY_SQL) as List fun seeMongoDbActions() : List = seeActions(ActionFilter.ONLY_MONGO) as List @@ -178,7 +177,7 @@ abstract class EnterpriseIndividual( fun seeExternalServiceActions() : List = seeActions(ActionFilter.ONLY_EXTERNAL_SERVICE) as List override fun verifyInitializationActions(): Boolean { - return DbActionUtils.verifyActions(seeInitializingActions().filterIsInstance()) + return SqlActionUtils.verifyActions(seeInitializingActions().filterIsInstance()) } override fun repairInitializationActions(randomness: Randomness) { @@ -205,7 +204,7 @@ abstract class EnterpriseIndividual( if (log.isTraceEnabled) log.trace("invoke GeneUtils.repairBrokenDbActionsList") val previous = dbInitialization.toMutableList() - DbActionUtils.repairBrokenDbActionsList(previous, randomness) + SqlActionUtils.repairBrokenDbActionsList(previous, randomness) resetInitializingActions(previous) Lazy.assert{verifyInitializationActions()} } @@ -249,8 +248,8 @@ abstract class EnterpriseIndividual( } } - private fun resetInitializingActions(actions: List){ - killChildren { it is DbAction } + private fun resetInitializingActions(actions: List){ + killChildren { it is SqlAction } // TODO: Can be merged with DbAction later addChildrenToGroup(getLastIndexOfDbActionToAdd(), actions, GroupsOfChildren.INITIALIZATION_SQL) } @@ -258,8 +257,8 @@ abstract class EnterpriseIndividual( /** * remove specified dbactions i.e., [actions] from [dbInitialization] */ - fun removeInitDbActions(actions: List) { - killChildren { it is DbAction && actions.contains(it)} + fun removeInitDbActions(actions: List) { + killChildren { it is SqlAction && actions.contains(it)} } /** diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt index b8c13c59af..62ff3bf0a9 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt @@ -7,9 +7,9 @@ import org.evomaster.client.java.controller.api.dto.TestResultsDto import org.evomaster.client.java.instrumentation.shared.ObjectiveNaming import org.evomaster.core.StaticCounter import org.evomaster.core.database.DatabaseExecution -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionResult -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionResult +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.mongo.MongoDbActionResult @@ -47,27 +47,27 @@ abstract class EnterpriseFitness : FitnessFunction() where T : Individual /** - * @param allDbActions specified the db actions to be executed + * @param allSqlActions specified the db actions to be executed * @param sqlIdMap indicates the map id of pk to generated id - * @param allSuccessBefore indicates whether all SQL before this [allDbActions] are executed successfully + * @param allSuccessBefore indicates whether all SQL before this [allSqlActions] are executed successfully * @param previous specified the previous db actions which have been executed - * @return whether [allDbActions] execute successfully + * @return whether [allSqlActions] execute successfully */ - fun doDbCalls(allDbActions : List, + fun doDbCalls(allSqlActions : List, sqlIdMap : MutableMap = mutableMapOf(), allSuccessBefore : Boolean = true, - previous: MutableList = mutableListOf(), + previous: MutableList = mutableListOf(), actionResults: MutableList ) : Boolean { - if (allDbActions.isEmpty()) { + if (allSqlActions.isEmpty()) { return true } - val dbresults = (allDbActions.indices).map { DbActionResult() } + val dbresults = (allSqlActions.indices).map { SqlActionResult() } actionResults.addAll(dbresults) - if (allDbActions.none { !it.representExistingData }) { + if (allSqlActions.none { !it.representExistingData }) { /* We are going to do an initialization of database only if there is data to add. @@ -75,20 +75,20 @@ abstract class EnterpriseFitness : FitnessFunction() where T : Individual existing data (which of course should not be re-inserted...) */ // other dbactions might bind with the representExistingData, so we still need to record sqlId here. - allDbActions.filter { it.representExistingData }.flatMap { it.seeTopGenes() }.filterIsInstance().forEach { + allSqlActions.filter { it.representExistingData }.flatMap { it.seeTopGenes() }.filterIsInstance().forEach { sqlIdMap.putIfAbsent(it.uniqueId, it.uniqueId) } - previous.addAll(allDbActions) + previous.addAll(allSqlActions) return true } - val startingIndex = allDbActions.indexOfLast { it.representExistingData } + 1 + val startingIndex = allSqlActions.indexOfLast { it.representExistingData } + 1 val dto = try { - DbActionTransformer.transform(allDbActions, sqlIdMap, previous) + SqlActionTransformer.transform(allSqlActions, sqlIdMap, previous) }catch (e : IllegalArgumentException){ // the failure might be due to previous failure if (!allSuccessBefore){ - previous.addAll(allDbActions) + previous.addAll(allSqlActions) return false } else throw e @@ -99,19 +99,19 @@ abstract class EnterpriseFitness : FitnessFunction() where T : Individual val map = sqlResults?.idMapping val executedResults = sqlResults?.executionResults - if ((executedResults?.size?:0) > allDbActions.size) - throw IllegalStateException("incorrect insertion execution results (${executedResults!!.size}) which is more than the size of insertions (${allDbActions.size}).") + if ((executedResults?.size?:0) > allSqlActions.size) + throw IllegalStateException("incorrect insertion execution results (${executedResults!!.size}) which is more than the size of insertions (${allSqlActions.size}).") executedResults?.forEachIndexed { index, b -> dbresults[startingIndex+index].setInsertExecutionResult(b) } - previous.addAll(allDbActions) + previous.addAll(allSqlActions) if (map == null) { LoggingUtil.uniqueWarn(log, "Failed in executing database command") return false }else{ - val expected = allDbActions.filter { !it.representExistingData } + val expected = allSqlActions.filter { !it.representExistingData } .flatMap { it.seeTopGenes() }.flatMap { it.flatView() } .filterIsInstance() .filter { it.gene is SqlAutoIncrementGene } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt index fb5e2a7aa0..6fe6eba917 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt @@ -2,7 +2,7 @@ package org.evomaster.core.problem.enterprise.service import com.google.inject.Inject import org.evomaster.client.java.controller.api.dto.SutInfoDto -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.mongo.MongoInsertBuilder @@ -27,7 +27,7 @@ abstract class EnterpriseSampler : Sampler() where T : Individual { var sqlInsertBuilder: SqlInsertBuilder? = null protected set - var existingSqlData : List = listOf() + var existingSqlData : List = listOf() protected set @@ -53,7 +53,7 @@ abstract class EnterpriseSampler : Sampler() where T : Individual { } } - fun sampleSqlInsertion(tableName: String, columns: Set): List { + fun sampleSqlInsertion(tableName: String, columns: Set): List { val extraConstraints = randomness.nextBoolean(apc.getExtraSqlDbConstraintsProbability()) val enableSingleInsertionForTable = randomness.nextBoolean(config.probOfEnablingSingleInsertionForTable) @@ -71,7 +71,7 @@ abstract class EnterpriseSampler : Sampler() where T : Individual { if (log.isTraceEnabled){ log.trace("at sampleSqlInsertion, {} insertions are added, and they are {}", actions.size, actions.joinToString(",") { - if (it is DbAction) it.getResolvedName() else it.getName() + if (it is SqlAction) it.getResolvedName() else it.getName() }) } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLIndividual.kt index b16f5cd19e..667944e5bf 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLIndividual.kt @@ -3,8 +3,7 @@ package org.evomaster.core.problem.graphql import org.evomaster.core.search.action.Action import org.evomaster.core.search.action.ActionComponent import org.evomaster.core.search.action.ActionFilter -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionUtils +import org.evomaster.core.database.SqlAction import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.api.ApiWsIndividual import org.evomaster.core.problem.enterprise.EnterpriseActionGroup @@ -24,7 +23,7 @@ class GraphQLIndividual( children = allActions, childTypeVerifier = { EnterpriseActionGroup::class.java.isAssignableFrom(it) - || DbAction::class.java.isAssignableFrom(it) + || SqlAction::class.java.isAssignableFrom(it) }, groups = groups ) { @@ -45,7 +44,7 @@ class GraphQLIndividual( return when (filter) { GeneFilter.ALL -> seeAllActions().flatMap(Action::seeTopGenes) GeneFilter.NO_SQL -> seeActions(ActionFilter.NO_SQL).flatMap(Action::seeTopGenes) - GeneFilter.ONLY_SQL -> seeDbActions().flatMap(DbAction::seeTopGenes) + GeneFilter.ONLY_SQL -> seeDbActions().flatMap(SqlAction::seeTopGenes) GeneFilter.ONLY_MONGO -> seeMongoDbActions().flatMap(MongoDbAction::seeTopGenes) GeneFilter.ONLY_EXTERNAL_SERVICE -> seeExternalServiceActions().flatMap(ApiExternalServiceAction::seeTopGenes) } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLFitness.kt index 0dc8140376..ae50ae54eb 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLFitness.kt @@ -5,7 +5,7 @@ import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper import org.evomaster.client.java.controller.api.dto.AdditionalInfoDto import org.evomaster.core.Lazy -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.problem.graphql.* import org.evomaster.core.problem.httpws.service.HttpWsFitness @@ -44,7 +44,7 @@ open class GraphQLFitness : HttpWsFitness() { val actionResults: MutableList = mutableListOf() - doDbCalls(individual.seeInitializingActions().filterIsInstance(), actionResults = actionResults) + doDbCalls(individual.seeInitializingActions().filterIsInstance(), actionResults = actionResults) val fv = FitnessValue(individual.size().toDouble()) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/httpws/service/HttpWsFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/httpws/service/HttpWsFitness.kt index 257c9bef72..7058a4704f 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/httpws/service/HttpWsFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/httpws/service/HttpWsFitness.kt @@ -3,8 +3,8 @@ package org.evomaster.core.problem.httpws.service import com.fasterxml.jackson.databind.ObjectMapper import org.evomaster.client.java.controller.api.dto.* import org.evomaster.core.StaticCounter -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.output.CookieWriter import org.evomaster.core.output.TokenWriter @@ -15,7 +15,6 @@ import org.evomaster.core.problem.httpws.HttpWsCallResult import org.evomaster.core.problem.rest.* import org.evomaster.core.problem.rest.param.HeaderParam import org.evomaster.core.remote.SutProblemException -import org.evomaster.core.search.action.Action import org.evomaster.core.search.Individual import org.glassfish.jersey.client.ClientConfig import org.glassfish.jersey.client.ClientProperties @@ -259,12 +258,12 @@ abstract class HttpWsFitness: ApiWsFitness() where T : Individual { if (log.isTraceEnabled){ log.trace("do {} InitializingActions: {}", ind.seeInitializingActions().size, - ind.seeInitializingActions().filterIsInstance().joinToString(","){ + ind.seeInitializingActions().filterIsInstance().joinToString(","){ it.getResolvedName() }) } - if (ind.seeInitializingActions().filterIsInstance().none { !it.representExistingData }) { + if (ind.seeInitializingActions().filterIsInstance().none { !it.representExistingData }) { /* We are going to do an initialization of database only if there is data to add. @@ -274,7 +273,7 @@ abstract class HttpWsFitness: ApiWsFitness() where T : Individual { return } - val dto = DbActionTransformer.transform(ind.seeInitializingActions().filterIsInstance()) + val dto = SqlActionTransformer.transform(ind.seeInitializingActions().filterIsInstance()) dto.idCounter = StaticCounter.getAndIncrease() val ok = rc.executeDatabaseCommand(dto) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt index 28fed86bb3..a7aa6108b1 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt @@ -3,8 +3,8 @@ package org.evomaster.core.problem.rest import org.evomaster.core.search.action.Action import org.evomaster.core.search.action.ActionComponent import org.evomaster.core.search.action.ActionFilter -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionUtils +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionUtils import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.api.ApiWsIndividual import org.evomaster.core.problem.enterprise.SampleType @@ -38,7 +38,7 @@ class RestIndividual( ): ApiWsIndividual(sampleType, trackOperator, index, allActions, childTypeVerifier = { RestResourceCalls::class.java.isAssignableFrom(it) - || DbAction::class.java.isAssignableFrom(it) || MongoDbAction::class.java.isAssignableFrom(it) + || SqlAction::class.java.isAssignableFrom(it) || MongoDbAction::class.java.isAssignableFrom(it) }, groups) { companion object{ @@ -49,7 +49,7 @@ class RestIndividual( resourceCalls: MutableList, sampleType: SampleType, sampleSpec: SamplerSpecification? = null, - dbInitialization: MutableList = mutableListOf(), + dbInitialization: MutableList = mutableListOf(), trackOperator: TrackOperator? = null, index : Int = -1 ) : this(sampleType, sampleSpec, trackOperator, index, mutableListOf().apply { @@ -59,11 +59,11 @@ class RestIndividual( constructor( actions: MutableList, sampleType: SampleType, - dbInitialization: MutableList = mutableListOf(), + dbInitialization: MutableList = mutableListOf(), trackOperator: TrackOperator? = null, index : Int = Traceable.DEFAULT_INDEX ) : this( - actions.map {RestResourceCalls(actions= listOf(it as RestCallAction), dbActions = listOf())}.toMutableList(), + actions.map {RestResourceCalls(actions= listOf(it as RestCallAction), sqlActions = listOf())}.toMutableList(), sampleType, null, dbInitialization, @@ -109,7 +109,7 @@ class RestIndividual( return when (filter) { GeneFilter.ALL -> seeAllActions().flatMap(Action::seeTopGenes) GeneFilter.NO_SQL -> seeActions(ActionFilter.NO_SQL).flatMap(Action::seeTopGenes) - GeneFilter.ONLY_SQL -> seeDbActions().flatMap(DbAction::seeTopGenes) + GeneFilter.ONLY_SQL -> seeDbActions().flatMap(SqlAction::seeTopGenes) GeneFilter.ONLY_MONGO -> seeMongoDbActions().flatMap(MongoDbAction::seeTopGenes) GeneFilter.ONLY_EXTERNAL_SERVICE -> seeExternalServiceActions().flatMap(ApiExternalServiceAction::seeTopGenes) } @@ -128,20 +128,20 @@ class RestIndividual( */ fun seeResource(filter: ResourceFilter) : List{ return when(filter){ - ResourceFilter.ALL -> seeInitializingActions().filterIsInstance().map { it.table.name }.plus( + ResourceFilter.ALL -> seeInitializingActions().filterIsInstance().map { it.table.name }.plus( getResourceCalls().map { it.getResourceKey() } ) ResourceFilter.NO_SQL -> getResourceCalls().map { it.getResourceKey() } - ResourceFilter.ONLY_SQL -> seeInitializingActions().filterIsInstance().map { it.table.name } - ResourceFilter.ONLY_SQL_EXISTING -> seeInitializingActions().filterIsInstance().filter { it.representExistingData }.map { it.table.name } - ResourceFilter.ONLY_SQL_INSERTION -> seeInitializingActions().filterIsInstance().filterNot { it.representExistingData }.map { it.table.name } + ResourceFilter.ONLY_SQL -> seeInitializingActions().filterIsInstance().map { it.table.name } + ResourceFilter.ONLY_SQL_EXISTING -> seeInitializingActions().filterIsInstance().filter { it.representExistingData }.map { it.table.name } + ResourceFilter.ONLY_SQL_INSERTION -> seeInitializingActions().filterIsInstance().filterNot { it.representExistingData }.map { it.table.name } } } //FIXME refactor override fun verifyInitializationActions(): Boolean { - return DbActionUtils.verifyActions(seeInitializingActions().filterIsInstance()) + return SqlActionUtils.verifyActions(seeInitializingActions().filterIsInstance()) } override fun copy(copyFilter: TraceableElementCopyFilter): RestIndividual { @@ -166,10 +166,10 @@ class RestIndividual( * TODO not sure whether build binding between fk and pk */ fun repairDbActionsInCalls(){ - val previous = mutableListOf() + val previous = mutableListOf() getResourceCalls().forEach { c-> c.repairFK(previous) - previous.addAll(c.seeActions(ONLY_SQL) as List) + previous.addAll(c.seeActions(ONLY_SQL) as List) } } @@ -218,7 +218,7 @@ class RestIndividual( } } - private fun getFirstIndexOfRestResourceCalls() = max(0, max(children.indexOfLast { it is DbAction }+1, children.indexOfFirst { it is RestResourceCalls })) + private fun getFirstIndexOfRestResourceCalls() = max(0, max(children.indexOfLast { it is SqlAction }+1, children.indexOfFirst { it is RestResourceCalls })) /** * replace the resourceCall at [position] with [resourceCalls] diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/ResourceCluster.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/ResourceCluster.kt index 45aa4e90a2..5381b7902b 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/ResourceCluster.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/ResourceCluster.kt @@ -3,8 +3,8 @@ package org.evomaster.core.problem.rest.resource import org.evomaster.client.java.controller.api.dto.database.operations.DataRowDto import org.evomaster.core.EMConfig import org.evomaster.core.Lazy -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionUtils +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionUtils import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.database.schema.Table import org.evomaster.core.problem.rest.RestCallAction @@ -156,16 +156,16 @@ class ResourceCluster { */ fun createSqlAction(tables: List, sqlInsertBuilder: SqlInsertBuilder, - previous: List, + previous: List, doNotCreateDuplicatedAction: Boolean, isInsertion: Boolean = true, randomness: Randomness, forceSynDataInDb: Boolean = false, useExtraSqlDbConstraints: Boolean = false, enableSingleInsertionForTable : Boolean = false - ) : MutableList{ - val sorted = DbActionUtils.sortTable(tables.mapNotNull { getTableByName(it) }.run { if (doNotCreateDuplicatedAction) this.distinct() else this }) - val added = mutableListOf() + ) : MutableList{ + val sorted = SqlActionUtils.sortTable(tables.mapNotNull { getTableByName(it) }.run { if (doNotCreateDuplicatedAction) this.distinct() else this }) + val added = mutableListOf() val preTables = previous.filter { !isInsertion || !it.representExistingData }.map { it.table.name }.toMutableList() diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/ResourceImpactOfIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/ResourceImpactOfIndividual.kt index de347c3bed..41cc9ab3d8 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/ResourceImpactOfIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/ResourceImpactOfIndividual.kt @@ -1,6 +1,6 @@ package org.evomaster.core.problem.rest.resource -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.search.FitnessValue import org.evomaster.core.search.impact.impactinfocollection.ActionStructureImpact @@ -64,7 +64,7 @@ class ResourceImpactOfIndividual : ImpactsOfIndividual { } } sqlTableSizeImpact = mutableMapOf().apply { - individual.seeInitializingActions().filterIsInstance().filterNot { it.representExistingData }.forEach { d-> + individual.seeInitializingActions().filterIsInstance().filterNot { it.representExistingData }.forEach { d-> putIfAbsent(d.table.name, IntegerGeneImpact("size")) } } @@ -130,8 +130,8 @@ class ResourceImpactOfIndividual : ImpactsOfIndividual { } } - val currentTs = current.seeInitializingActions().filterIsInstance().filterNot { it.representExistingData }.map { it.table.name } - val previousTs = previous.seeInitializingActions().filterIsInstance().filterNot { it.representExistingData }.map { it.table.name } + val currentTs = current.seeInitializingActions().filterIsInstance().filterNot { it.representExistingData }.map { it.table.name } + val previousTs = previous.seeInitializingActions().filterIsInstance().filterNot { it.representExistingData }.map { it.table.name } var anySqlChange = false currentTs.toSet().forEach { cr -> val tImpact = sqlTableSizeImpact.getOrPut(cr){IntegerGeneImpact("size")} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceCalls.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceCalls.kt index 70e9dc7c18..359e0cc41d 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceCalls.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceCalls.kt @@ -5,8 +5,8 @@ import org.evomaster.core.search.action.Action import org.evomaster.core.search.action.ActionComponent import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.action.ActionTree -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionUtils +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionUtils import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestIndividual @@ -29,7 +29,7 @@ import org.slf4j.LoggerFactory * @property template is a resource template, e.g., POST-GET * @property node is a resource node which creates [this] call. Note that [node] could null if it is not created by [ResourceSampler] * @property mainActions is a sequence of actions in the [RestResourceCalls] that follows [template] - * @property dbActions are used to initialize data for rest actions, either select from db or insert new data into db + * @property sqlActions are used to initialize data for rest actions, either select from db or insert new data into db * @param withBinding specifies whether to build binding between rest genes * @param randomness is required when [withBinding] is true * @@ -42,16 +42,16 @@ class RestResourceCalls( randomness: Randomness? = null ) : ActionTree( children, - { k -> DbAction::class.java.isAssignableFrom(k) || MongoDbAction::class.java.isAssignableFrom(k) || EnterpriseActionGroup::class.java.isAssignableFrom(k) } + { k -> SqlAction::class.java.isAssignableFrom(k) || MongoDbAction::class.java.isAssignableFrom(k) || EnterpriseActionGroup::class.java.isAssignableFrom(k) } ) { constructor( template: CallsTemplate? = null, node: RestResourceNode? = null, actions: List, - dbActions: List, withBinding: Boolean = false, randomness: Randomness? = null + sqlActions: List, withBinding: Boolean = false, randomness: Randomness? = null ) : this(template, node, mutableListOf().apply { - addAll(dbActions); + addAll(sqlActions); addAll(actions.map { a -> EnterpriseActionGroup(mutableListOf(a), RestCallAction::class.java) }) }, withBinding, randomness) @@ -78,9 +78,9 @@ class RestResourceCalls( return children.flatMap { it.flatten() }.filterIsInstance() } - private val dbActions: List + private val sqlActions: List get() { - return children.flatMap { it.flatten() }.filterIsInstance() + return children.flatMap { it.flatten() }.filterIsInstance() } private val mongoDbActions: List get() { @@ -178,13 +178,13 @@ class RestResourceCalls( */ fun seeActions(filter: ActionFilter): List { return when (filter) { - ActionFilter.ALL -> dbActions.plus(externalServiceActions).plus(mainActions) - ActionFilter.INIT -> dbActions.plus(mongoDbActions) - ActionFilter.ONLY_SQL -> dbActions + ActionFilter.ALL -> sqlActions.plus(externalServiceActions).plus(mainActions) + ActionFilter.INIT -> sqlActions.plus(mongoDbActions) + ActionFilter.ONLY_SQL -> sqlActions ActionFilter.NO_INIT, ActionFilter.NO_SQL -> externalServiceActions.plus(mainActions) ActionFilter.MAIN_EXECUTABLE -> mainActions ActionFilter.ONLY_EXTERNAL_SERVICE -> externalServiceActions - ActionFilter.NO_EXTERNAL_SERVICE -> dbActions.plus(mainActions) + ActionFilter.NO_EXTERNAL_SERVICE -> sqlActions.plus(mainActions) ActionFilter.ONLY_MONGO -> mongoDbActions } } @@ -199,8 +199,8 @@ class RestResourceCalls( /** * reset dbactions with [actions] */ - fun resetDbAction(actions: List) { - killChildren { it is DbAction } + fun resetDbAction(actions: List) { + killChildren { it is SqlAction } /* keep db action in the front of rest resource call, otherwise it might be a problem to get corresponding action result @@ -212,15 +212,15 @@ class RestResourceCalls( /** * remove dbaction based on [removePredict] */ - fun removeDbActionIf(removePredict: (DbAction) -> Boolean) { - val removed = dbActions.filter { removePredict(it) } + fun removeDbActionIf(removePredict: (SqlAction) -> Boolean) { + val removed = sqlActions.filter { removePredict(it) } resetDbAction(removed) } - private fun removeDbActions(remove: List) { + private fun removeDbActions(remove: List) { val removedGenes = remove.flatMap { it.seeTopGenes() }.flatMap { it.flatView() } killChildren(remove) - (dbActions.plus(mainActions).flatMap { it.seeTopGenes() }).flatMap { it.flatView() }.filter { it.isBoundGene() } + (sqlActions.plus(mainActions).flatMap { it.seeTopGenes() }).flatMap { it.flatView() }.filter { it.isBoundGene() } .forEach { it.cleanRemovedGenes(removedGenes) } @@ -231,7 +231,7 @@ class RestResourceCalls( * * */ private fun seeMutableSQLGenes(): List = getResourceNode() - .getMutableSQLGenes(dbActions, getRestTemplate(), is2POST) + .getMutableSQLGenes(sqlActions, getRestTemplate(), is2POST) /** @@ -248,7 +248,7 @@ class RestResourceCalls( randomness: Randomness? ) { // handling [this.dbActions] - if (this.dbActions.isNotEmpty() && doRemoveDuplicatedTable) { + if (this.sqlActions.isNotEmpty() && doRemoveDuplicatedTable) { removeDuplicatedDbActions(relatedResourceCalls, cluster, doRemoveDuplicatedTable, randomness) } @@ -298,19 +298,19 @@ class RestResourceCalls( return true } - fun repairFK(previous: List) { + fun repairFK(previous: List) { - if (!DbActionUtils.verifyForeignKeys(previous.plus(dbActions))) { + if (!SqlActionUtils.verifyForeignKeys(previous.plus(sqlActions))) { val current = previous.toMutableList() - dbActions.forEach { d -> - val ok = DbActionUtils.repairFk(d, current) + sqlActions.forEach { d -> + val ok = SqlActionUtils.repairFk(d, current) if (!ok.first) { throw IllegalStateException("fail to find pk in the previous dbactions") } current.add(d) } - Lazy.assert { DbActionUtils.verifyForeignKeys(previous.plus(dbActions)) } + Lazy.assert { SqlActionUtils.verifyForeignKeys(previous.plus(sqlActions)) } } } @@ -319,7 +319,7 @@ class RestResourceCalls( * @param withRest specifies whether to synchronize values based on rest actions ([withRest] is true) or db actions ([withRest] is false) */ private fun syncValues(withRest: Boolean = true) { - (if (withRest) mainActions else dbActions).forEach { + (if (withRest) mainActions else sqlActions).forEach { it.seeTopGenes().flatMap { i -> i.flatView() }.forEach { g -> g.syncBindingGenesBasedOnThis() } @@ -334,19 +334,19 @@ class RestResourceCalls( ) { val dbRelatedToTables = - calls.flatMap { it.seeActions(ActionFilter.ONLY_SQL) as List }.map { it.table.name }.toHashSet() + calls.flatMap { it.seeActions(ActionFilter.ONLY_SQL) as List }.map { it.table.name }.toHashSet() - val dbactionInOtherCalls = calls.flatMap { it.seeActions(ActionFilter.ONLY_SQL) as List } + val dbactionInOtherCalls = calls.flatMap { it.seeActions(ActionFilter.ONLY_SQL) as List } // remove duplicated dbactions if (doRemoveDuplicatedTable) { - val dbActionsToRemove = this.dbActions.filter { dbRelatedToTables.contains(it.table.name) } + val dbActionsToRemove = this.sqlActions.filter { dbRelatedToTables.contains(it.table.name) } if (dbActionsToRemove.isNotEmpty()) { removeDbActions(dbActionsToRemove) val frontDbActions = dbactionInOtherCalls.toMutableList() - this.dbActions + this.sqlActions .forEach { db -> // fix fk with front dbactions - val ok = DbActionUtils.repairFk(db, frontDbActions) + val ok = SqlActionUtils.repairFk(db, frontDbActions) if (!ok.first) { throw IllegalStateException("cannot fix the fk of ${db.getResolvedName()}") } @@ -359,7 +359,7 @@ class RestResourceCalls( bindRestActionBasedOnDbActions(listOf(ddb), cluster, false, false, randomness) } }else{ - Lazy.assert { dbActions.contains(ddb) } + Lazy.assert { sqlActions.contains(ddb) } } } frontDbActions.add(db) @@ -377,7 +377,7 @@ class RestResourceCalls( /** * init dbactions for [this] RestResourceCall which would build binding relationship with its rest [mainActions]. - * @param dbActions specified the dbactions to be initialized for this call + * @param sqlActions specified the dbactions to be initialized for this call * @param cluster specified the resource cluster * @param forceBindParamBasedOnDB specified whether to force to bind values of params in rest actions based on dbactions * @param dbRemovedDueToRepair specified whether any db action is removed due to repair process. @@ -385,7 +385,7 @@ class RestResourceCalls( * @param bindWith specified a list of resource call which might be bound with [this] */ fun initDbActions( - dbActions: List, + sqlActions: List, cluster: ResourceCluster, forceBindParamBasedOnDB: Boolean, dbRemovedDueToRepair: Boolean, @@ -393,22 +393,22 @@ class RestResourceCalls( bindWith: List? = null ) { bindWith?.forEach { p -> - val dependent = p.seeActions(ActionFilter.ONLY_SQL).any { dbActions.contains(it) } + val dependent = p.seeActions(ActionFilter.ONLY_SQL).any { sqlActions.contains(it) } if (dependent) { setDependentCall(p) } } - if (this.dbActions.isNotEmpty()) throw IllegalStateException("dbactions of this RestResourceCall is not empty") + if (this.sqlActions.isNotEmpty()) throw IllegalStateException("dbactions of this RestResourceCall is not empty") // db action should add in the front of rest actions - addChildren(0, dbActions) + addChildren(0, sqlActions) - bindRestActionBasedOnDbActions(dbActions, cluster, forceBindParamBasedOnDB, dbRemovedDueToRepair, randomness) + bindRestActionBasedOnDbActions(sqlActions, cluster, forceBindParamBasedOnDB, dbRemovedDueToRepair, randomness) } private fun bindRestActionBasedOnDbActions( - dbActions: List, + sqlActions: List, cluster: ResourceCluster, forceBindParamBasedOnDB: Boolean, dbRemovedDueToRepair: Boolean, @@ -416,7 +416,7 @@ class RestResourceCalls( ) { val paramInfo = getResourceNode().getPossiblyBoundParams(template!!.template, is2POST, randomness) - val paramToTables = SimpleDeriveResourceBinding.generateRelatedTables(paramInfo, this, dbActions) + val paramToTables = SimpleDeriveResourceBinding.generateRelatedTables(paramInfo, this, sqlActions) if (paramToTables.isEmpty()) return @@ -430,7 +430,7 @@ class RestResourceCalls( restaction, cluster.getResourceNode(restaction, true)!!, list, - dbActions, + sqlActions, forceBindParamBasedOnDB, dbRemovedDueToRepair, true @@ -513,7 +513,7 @@ class RestResourceCalls( * * if the action is bounded with existing data from db, it is not mutable */ - fun isMutable() = dbActions.none { + fun isMutable() = sqlActions.none { it.representExistingData } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceNode.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceNode.kt index 257bd34345..4025f34a78 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceNode.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceNode.kt @@ -1,7 +1,7 @@ package org.evomaster.core.problem.rest.resource import org.evomaster.core.Lazy -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.problem.rest.* import org.evomaster.core.problem.rest.param.BodyParam @@ -158,7 +158,7 @@ open class RestResourceNode( /** * @return mutable genes in [dbactions] and they do not bind with rest actions. */ - fun getMutableSQLGenes(dbactions: List, template: String, is2POST : Boolean) : List{ + fun getMutableSQLGenes(dbactions: List, template: String, is2POST : Boolean) : List{ val related = getPossiblyBoundParams(template, is2POST, null).map { resourceToTable.paramToTable[it.key] diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/dependency/CreationChain.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/dependency/CreationChain.kt index ad425ae6cd..019ade8c1b 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/dependency/CreationChain.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/dependency/CreationChain.kt @@ -1,6 +1,6 @@ package org.evomaster.core.problem.rest.resource.dependency -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.search.service.Randomness @@ -65,6 +65,6 @@ class PostCreationChain(val actions: MutableList, private var fa } } -class DBCreationChain(val actions: MutableList) : CreationChain() +class DBCreationChain(val actions: MutableList) : CreationChain() class CompositeCreationChain(val actions: MutableList) : CreationChain() \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceDepManageService.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceDepManageService.kt index 5860e245ee..556977099a 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceDepManageService.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceDepManageService.kt @@ -5,8 +5,8 @@ import org.evomaster.client.java.controller.api.dto.TestResultsDto import org.evomaster.client.java.controller.api.dto.database.execution.ExecutionDto import org.evomaster.core.EMConfig import org.evomaster.core.Lazy -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionUtils +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionUtils import org.evomaster.core.database.schema.Table import org.evomaster.core.problem.rest.HttpVerb import org.evomaster.core.problem.rest.RestCallAction @@ -999,14 +999,14 @@ class ResourceDepManageService { /** * @return a list of db actions of [ind] which are possibly not related to rest actions of [ind] */ - fun unRelatedSQL(ind: RestIndividual, candidates: List?) : List{ + fun unRelatedSQL(ind: RestIndividual, candidates: List?) : List{ val allrelated = getAllRelatedTables(ind) - return (candidates?:ind.seeInitializingActions().filterIsInstance().filterNot { it.representExistingData }).filterNot { allrelated.any { r-> r.equals(it.table.name, ignoreCase = true) } } + return (candidates?:ind.seeInitializingActions().filterIsInstance().filterNot { it.representExistingData }).filterNot { allrelated.any { r-> r.equals(it.table.name, ignoreCase = true) } } } - fun identifyUnRelatedSqlTable(ind: RestIndividual, candidates: List?) : List{ + fun identifyUnRelatedSqlTable(ind: RestIndividual, candidates: List?) : List{ val actions = unRelatedSQL(ind, candidates) - return if (actions.isNotEmpty()) actions.map { it.table.name } else ind.seeInitializingActions().filterIsInstance().filterNot { it.representExistingData }.map { it.table.name } + return if (actions.isNotEmpty()) actions.map { it.table.name } else ind.seeInitializingActions().filterIsInstance().filterNot { it.representExistingData }.map { it.table.name } } /** * add [num] related resources into [ind] with SQL @@ -1016,7 +1016,7 @@ class ResourceDepManageService { * Man: shall we set probability 1.0? because the related tables for the resource might be determinate based on * tracking of SQL execution. */ - fun addRelatedSQL(ind: RestIndividual, num: Int, probability: Double = 1.0) : List>{ + fun addRelatedSQL(ind: RestIndividual, num: Int, probability: Double = 1.0) : List>{ val other = randomness.choose(identifyRelatedSQL(ind, probability)) return createDbActions(other, num) } @@ -1031,7 +1031,7 @@ class ResourceDepManageService { if (allrelated.isNotEmpty() && randomness.nextBoolean(probability)){ val notincluded = allrelated.filterNot { - ind.seeInitializingActions().filterIsInstance().any { d-> it.equals(d.table.name, ignoreCase = true) } + ind.seeInitializingActions().filterIsInstance().any { d-> it.equals(d.table.name, ignoreCase = true) } } //prioritize notincluded related ones with a probability 0.8 return if (notincluded.isNotEmpty() && randomness.nextBoolean(0.8)){ @@ -1039,14 +1039,14 @@ class ResourceDepManageService { }else allrelated }else{ val left = rm.getTableInfo().keys.filterNot { - ind.seeInitializingActions().filterIsInstance().any { d-> it.equals(d.table.name, ignoreCase = true) } + ind.seeInitializingActions().filterIsInstance().any { d-> it.equals(d.table.name, ignoreCase = true) } } return if (left.isNotEmpty() && randomness.nextBoolean()) left.toSet() else rm.getTableInfo().keys } } - fun createDbActions(name : String, num : Int) : List>{ + fun createDbActions(name : String, num : Int) : List>{ rm.getSqlBuilder() ?:throw IllegalStateException("attempt to create resource with SQL but the sqlBuilder is null") if (num <= 0) throw IllegalArgumentException("invalid num (i.e.,$num) for creating resource") @@ -1064,8 +1064,8 @@ class ResourceDepManageService { }) } - DbActionUtils.randomizeDbActionGenes(list.flatten(), randomness) - DbActionUtils.repairBrokenDbActionsList(list.flatten().toMutableList(), randomness) + SqlActionUtils.randomizeDbActionGenes(list.flatten(), randomness) + SqlActionUtils.repairBrokenDbActionsList(list.flatten().toMutableList(), randomness) return list } @@ -1123,7 +1123,7 @@ class ResourceDepManageService { randomness, useExtraSqlDbConstraints = extraConstraints, enableSingleInsertionForTable= enableSingleInsertionForTable) - DbActionUtils.repairBrokenDbActionsList(added,randomness) + SqlActionUtils.repairBrokenDbActionsList(added,randomness) ind.addInitializingDbActions(actions = added) } @@ -1151,13 +1151,13 @@ class ResourceDepManageService { } /** - * @return extracted related tables for [call] regarding [dbActions] - * if [dbActions] is not empty, return related table from tables in [dbActions] - * if [dbActions] is empty, return all derived related table + * @return extracted related tables for [call] regarding [sqlActions] + * if [sqlActions] is not empty, return related table from tables in [sqlActions] + * if [sqlActions] is empty, return all derived related table */ - fun extractRelatedTablesForCall(call: RestResourceCalls, dbActions: MutableList = mutableListOf(), withSql : Boolean): MutableMap> { + fun extractRelatedTablesForCall(call: RestResourceCalls, sqlActions: MutableList = mutableListOf(), withSql : Boolean): MutableMap> { val paramsInfo = call.getResourceNode().getPossiblyBoundParams(call.getRestTemplate(), withSql, randomness) - return SimpleDeriveResourceBinding.generateRelatedTables(paramsInfo, call, dbActions) + return SimpleDeriveResourceBinding.generateRelatedTables(paramsInfo, call, sqlActions) } /** diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceManageService.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceManageService.kt index c079098c15..d96bb24122 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceManageService.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceManageService.kt @@ -3,8 +3,8 @@ package org.evomaster.core.problem.rest.service import com.google.inject.Inject import org.evomaster.core.EMConfig import org.evomaster.core.EMConfig.SqlInitResourceStrategy -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionUtils +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionUtils import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.rest.* @@ -224,7 +224,7 @@ class ResourceManageService { val created = handleDbActionForCall( call, forceSQLInsert, false, call.is2POST, - previousDbActions = bindWith?.flatMap { it.seeActions(ActionFilter.ONLY_SQL) as List} ?: listOf()) + previousSqlActions = bindWith?.flatMap { it.seeActions(ActionFilter.ONLY_SQL) as List} ?: listOf()) if(!created){ /* @@ -252,7 +252,7 @@ class ResourceManageService { forceInsert: Boolean = false, forceSelect: Boolean = false, employSQL: Boolean, - previousDbActions: List = listOf() + previousSqlActions: List = listOf() ) : Boolean{ val paramToTables = dm.extractRelatedTablesForCall(call, withSql = employSQL) @@ -266,7 +266,7 @@ class ResourceManageService { val enableSingleInsertionForTable = randomness.nextBoolean(config.probOfEnablingSingleInsertionForTable) val dbActions = cluster.createSqlAction( - relatedTables, getSqlBuilder()!!, previousDbActions, + relatedTables, getSqlBuilder()!!, previousSqlActions, doNotCreateDuplicatedAction = true, isInsertion = !employSQLSelect, randomness = randomness, useExtraSqlDbConstraints = extraConstraints, @@ -298,9 +298,9 @@ class ResourceManageService { } // might be useful for debugging - private fun containTables(dbActions: MutableList, tables: Set) : Boolean{ + private fun containTables(sqlActions: MutableList, tables: Set) : Boolean{ - val missing = tables.filter { t-> dbActions.none { d-> d.table.name.equals(t, ignoreCase = true) } } + val missing = tables.filter { t-> sqlActions.none { d-> d.table.name.equals(t, ignoreCase = true) } } if (missing.isNotEmpty()) log.warn("missing rows of tables {} to be created.", missing.joinToString(",") { it }) return missing.isEmpty() @@ -311,13 +311,13 @@ class ResourceManageService { private fun hasDBHandler() : Boolean = sqlInsertBuilder!=null - private fun repairDbActionsForResource(dbActions: MutableList) : Boolean{ + private fun repairDbActionsForResource(sqlActions: MutableList) : Boolean{ /** * First repair SQL Genes (i.e. SQL Timestamps) */ - GeneUtils.repairGenes(dbActions.flatMap { it.seeTopGenes() }) + GeneUtils.repairGenes(sqlActions.flatMap { it.seeTopGenes() }) - return DbActionUtils.repairBrokenDbActionsList(dbActions, randomness) + return SqlActionUtils.repairBrokenDbActionsList(sqlActions, randomness) } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampler.kt index 08ae3e9a4d..adbb8a6dce 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampler.kt @@ -230,7 +230,7 @@ open class ResourceSampler : AbstractRestSampler() { template = node.getTemplate(it.verb.toString()), node = node, actions = mutableListOf(it), - dbActions = listOf() + sqlActions = listOf() ) }.toMutableList() val ind = RestIndividual( diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt index 84ebaad959..0a2dc4fff6 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt @@ -1,6 +1,6 @@ package org.evomaster.core.problem.rest.service -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestCallResult @@ -34,7 +34,7 @@ open class RestFitness : AbstractRestFitness() { val actionResults: MutableList = mutableListOf() - doDbCalls(individual.seeInitializingActions().filterIsInstance(), actionResults = actionResults) + doDbCalls(individual.seeInitializingActions().filterIsInstance(), actionResults = actionResults) doMongoDbCalls(individual.seeInitializingActions().filterIsInstance(), actionResults = actionResults) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt index 9ed447c594..74b1328ba2 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt @@ -2,7 +2,7 @@ package org.evomaster.core.problem.rest.service import com.google.inject.Inject -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.enterprise.EnterpriseActionGroup import org.evomaster.core.problem.externalservice.httpws.HttpExternalServiceAction @@ -50,16 +50,16 @@ class RestResourceFitness : AbstractRestFitness() { This map is used to record the key mapping in SQL, e.g., PK, FK */ val sqlIdMap = mutableMapOf() - val executedDbActions = mutableListOf() + val executedSqlActions = mutableListOf() val actionResults: MutableList = mutableListOf() //whether there exist some SQL execution failure var failureBefore = doDbCalls( - individual.seeInitializingActions().filterIsInstance(), + individual.seeInitializingActions().filterIsInstance(), sqlIdMap, true, - executedDbActions, + executedSqlActions, actionResults ) @@ -82,10 +82,10 @@ class RestResourceFitness : AbstractRestFitness() { for (call in individual.getResourceCalls()) { val result = doDbCalls( - call.seeActions(ActionFilter.ONLY_SQL) as List, + call.seeActions(ActionFilter.ONLY_SQL) as List, sqlIdMap, failureBefore, - executedDbActions, + executedSqlActions, actionResults ) failureBefore = failureBefore || result diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceMutator.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceMutator.kt index 886f769229..4a08b15b05 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceMutator.kt @@ -1,7 +1,6 @@ package org.evomaster.core.problem.rest.service import com.google.inject.Inject -import org.evomaster.core.database.DbAction import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.rest.resource.RestResourceCalls import org.evomaster.core.search.EvaluatedIndividual diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceStructureMutator.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceStructureMutator.kt index c0b2a718ff..929d39dd41 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceStructureMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceStructureMutator.kt @@ -2,7 +2,7 @@ package org.evomaster.core.problem.rest.service import com.google.inject.Inject import org.evomaster.core.Lazy -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.problem.api.service.ApiWsStructureMutator import org.evomaster.core.problem.rest.RestCallAction @@ -177,7 +177,7 @@ class RestResourceStructureMutator : ApiWsStructureMutator() { val candidates = if (doesApplyDependencyHeuristics()) dm.identifyRelatedSQL(ind) else - ind.seeInitializingActions().filterIsInstance().map { it.table.name }.toSet() // adding an unrelated table would waste budget, then we add existing ones + ind.seeInitializingActions().filterIsInstance().map { it.table.name }.toSet() // adding an unrelated table would waste budget, then we add existing ones val selectedAdded = if (config.enableAdaptiveResourceStructureMutation){ adaptiveSelectResource(evaluatedIndividual, bySQL = true, candidates.toList(), targets) @@ -216,7 +216,7 @@ class RestResourceStructureMutator : ApiWsStructureMutator() { * It might be useful to reduce the useless db genes. */ private fun handleRemoveSQL(ind: RestIndividual, mutatedGenes: MutatedGeneSpecification?, evaluatedIndividual: EvaluatedIndividual?, targets: Set?){ - val availableToRemove = ind.seeInitializingActions().filterIsInstance().filterNot { it.representExistingData } + val availableToRemove = ind.seeInitializingActions().filterIsInstance().filterNot { it.representExistingData } // remove unrelated tables val candidates = if (doesApplyDependencyHeuristics()) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestStructureMutator.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestStructureMutator.kt index de2753a608..b72b551f52 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestStructureMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestStructureMutator.kt @@ -139,7 +139,7 @@ class RestStructureMutator : ApiWsStructureMutator() { POSTs */ //ind.seeActions().add(idx, post) - ind.addResourceCall(idx, RestResourceCalls(actions = mutableListOf(post), dbActions = listOf())) + ind.addResourceCall(idx, RestResourceCalls(actions = mutableListOf(post), sqlActions = listOf())) //save mutated genes mutatedGenes?.addRemovedOrAddedByAction( @@ -161,7 +161,7 @@ class RestStructureMutator : ApiWsStructureMutator() { //ind.seeActions().add(sampledAction) - ind.addResourceCall(restCalls = RestResourceCalls(actions = mutableListOf(sampledAction), dbActions = listOf())) + ind.addResourceCall(restCalls = RestResourceCalls(actions = mutableListOf(sampledAction), sqlActions = listOf())) //save mutated genes mutatedGenes?.addRemovedOrAddedByAction( @@ -203,7 +203,7 @@ class RestStructureMutator : ApiWsStructureMutator() { val sampledAction = sampler.sampleRandomAction(0.05) as RestCallAction val chosen = randomness.nextInt(ind.seeMainActionComponents().size) //ind.seeActions().add(chosen, sampledAction) - ind.addResourceCall(chosen, RestResourceCalls(actions = mutableListOf(sampledAction), dbActions = listOf())) + ind.addResourceCall(chosen, RestResourceCalls(actions = mutableListOf(sampledAction), sqlActions = listOf())) //save mutated genes mutatedGenes?.addRemovedOrAddedByAction( diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt index 02cb11ec2d..a60a9c5764 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt @@ -4,8 +4,8 @@ import org.evomaster.core.Lazy import org.evomaster.core.search.action.Action import org.evomaster.core.search.action.ActionComponent import org.evomaster.core.search.action.ActionFilter -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionUtils +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionUtils import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.api.ApiWsIndividual import org.evomaster.core.problem.enterprise.EnterpriseActionGroup @@ -33,7 +33,7 @@ class RPCIndividual( trackOperator, index, allActions, childTypeVerifier = { EnterpriseActionGroup::class.java.isAssignableFrom(it) - || DbAction::class.java.isAssignableFrom(it) + || SqlAction::class.java.isAssignableFrom(it) }, groups ) { @@ -45,7 +45,7 @@ class RPCIndividual( /* TODO might add sample type here as REST (check later) */ - dbInitialization: MutableList = mutableListOf(), + dbInitialization: MutableList = mutableListOf(), trackOperator: TrackOperator? = null, index: Int = -1 ) : this( @@ -74,7 +74,7 @@ class RPCIndividual( GeneFilter.ALL -> seeAllActions().flatMap(Action::seeTopGenes) GeneFilter.NO_SQL -> seeActions(ActionFilter.NO_SQL).flatMap(Action::seeTopGenes) GeneFilter.ONLY_MONGO -> seeMongoDbActions().flatMap(MongoDbAction::seeTopGenes) - GeneFilter.ONLY_SQL -> seeDbActions().flatMap(DbAction::seeTopGenes) + GeneFilter.ONLY_SQL -> seeDbActions().flatMap(SqlAction::seeTopGenes) GeneFilter.ONLY_EXTERNAL_SERVICE -> seeExternalServiceActions().flatMap(ApiExternalServiceAction::seeTopGenes) } } @@ -89,7 +89,7 @@ class RPCIndividual( fun seeIndexedRPCCalls(): Map = getIndexedChildren(RPCCallAction::class.java) override fun verifyInitializationActions(): Boolean { - return DbActionUtils.verifyActions(seeInitializingActions().filterIsInstance()) + return SqlActionUtils.verifyActions(seeInitializingActions().filterIsInstance()) } @@ -116,7 +116,7 @@ class RPCIndividual( killChildByIndex(getFirstIndexOfEnterpriseActionGroup() + position) as EnterpriseActionGroup } - private fun getFirstIndexOfEnterpriseActionGroup() = max(0, max(children.indexOfLast { it is DbAction }+1, children.indexOfFirst { it is EnterpriseActionGroup })) + private fun getFirstIndexOfEnterpriseActionGroup() = max(0, max(children.indexOfLast { it is SqlAction }+1, children.indexOfFirst { it is EnterpriseActionGroup })) override fun copyContent(): Individual { return RPCIndividual( diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt index 90def82f78..e1f5601d84 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt @@ -4,7 +4,7 @@ import com.google.inject.Inject import org.evomaster.client.java.controller.api.dto.AdditionalInfoDto import org.evomaster.client.java.controller.api.dto.problem.rpc.exception.RPCExceptionType import org.evomaster.core.Lazy -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.problem.api.service.ApiWsFitness import org.evomaster.core.problem.externalservice.rpc.RPCExternalServiceAction import org.evomaster.core.problem.externalservice.rpc.parm.ClassResponseParam @@ -45,7 +45,7 @@ class RPCFitness : ApiWsFitness() { // TODO handle auth val actionResults: MutableList = mutableListOf() - doDbCalls(individual.seeInitializingActions().filterIsInstance(), actionResults = actionResults) + doDbCalls(individual.seeInitializingActions().filterIsInstance(), actionResults = actionResults) val fv = FitnessValue(individual.size().toDouble()) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/util/BindingBuilder.kt b/core/src/main/kotlin/org/evomaster/core/problem/util/BindingBuilder.kt index ef8d37d001..e35b89bc61 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/util/BindingBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/util/BindingBuilder.kt @@ -4,8 +4,8 @@ import com.google.common.annotations.VisibleForTesting import org.evomaster.client.java.instrumentation.shared.TaintInputName import org.evomaster.core.Lazy import org.evomaster.core.StaticCounter -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionUtils +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionUtils import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.problem.api.param.Param import org.evomaster.core.problem.rest.RestCallAction @@ -130,9 +130,9 @@ object BindingBuilder { } /** - * bind [restAction] with [dbActions] + * bind [restAction] with [sqlActions] * @param restAction is the action - * @param dbActions specified the dbactions + * @param sqlActions specified the dbactions * @param forceBindParamBasedOnDB specified whether to force to bind values of params in rest actions based on dbactions * @param dbRemovedDueToRepair specified whether any db action is removed due to repair process. * Note that dbactions might be truncated in the db repair process, thus the table related to rest actions might be removed. @@ -141,11 +141,11 @@ object BindingBuilder { fun bindRestAndDbAction(restAction: RestCallAction, restNode: RestResourceNode, paramGeneBindMap: List, - dbActions: List, + sqlActions: List, forceBindParamBasedOnDB: Boolean = false, dbRemovedDueToRepair : Boolean, doBuildBindingGene: Boolean){ - buildBindRestActionBasedOnDbActions(restAction, restNode, paramGeneBindMap, dbActions, forceBindParamBasedOnDB, dbRemovedDueToRepair).forEach { p-> + buildBindRestActionBasedOnDbActions(restAction, restNode, paramGeneBindMap, sqlActions, forceBindParamBasedOnDB, dbRemovedDueToRepair).forEach { p-> bindValues(p, doBuildBindingGene) } } @@ -320,18 +320,18 @@ object BindingBuilder { } /** - * bind values between [restAction] and [dbActions] - * @param restAction is the action to be bounded with [dbActions] + * bind values between [restAction] and [sqlActions] + * @param restAction is the action to be bounded with [sqlActions] * @param restNode is the resource node for the [restAction] - * @param dbActions are the dbactions generated for the [call] - * @param bindingMap presents how to map the [restAction] and [dbActions] at Gene-level - * @param forceBindParamBasedOnDB specifies whether to bind params based on [dbActions] or reversed + * @param sqlActions are the dbactions generated for the [call] + * @param bindingMap presents how to map the [restAction] and [sqlActions] at Gene-level + * @param forceBindParamBasedOnDB specifies whether to bind params based on [sqlActions] or reversed * @param dbRemovedDueToRepair indicates whether the dbactions are removed due to repair. */ fun buildBindRestActionBasedOnDbActions(restAction: RestCallAction, restNode: RestResourceNode, paramGeneBindMap: List, - dbActions: List, + sqlActions: List, forceBindParamBasedOnDB: Boolean = false, dbRemovedDueToRepair : Boolean) : List>{ @@ -342,11 +342,11 @@ object BindingBuilder { } paramGeneBindMap.forEach { pToGene -> - val dbAction = DbActionUtils.findDbActionsByTableName(dbActions, pToGene.tableName).firstOrNull() + val dbAction = SqlActionUtils.findDbActionsByTableName(sqlActions, pToGene.tableName).firstOrNull() //there might due to a repair for dbactions if (dbAction == null && !dbRemovedDueToRepair) log.warn("cannot find ${pToGene.tableName} in db actions ${ - dbActions.joinToString(";") { it.table.name } + sqlActions.joinToString(";") { it.table.name } }") if(dbAction != null){ // columngene might be null if the column is nullable diff --git a/core/src/main/kotlin/org/evomaster/core/problem/util/inference/DeriveResourceBinding.kt b/core/src/main/kotlin/org/evomaster/core/problem/util/inference/DeriveResourceBinding.kt index a7e5bf42c5..1e294bdbf2 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/util/inference/DeriveResourceBinding.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/util/inference/DeriveResourceBinding.kt @@ -1,6 +1,6 @@ package org.evomaster.core.problem.util.inference -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.database.schema.Table import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.resource.ParamInfo @@ -19,7 +19,7 @@ interface DeriveResourceBinding { */ fun deriveResourceToTable(resourceNode : RestResourceNode, allTables : Map) - fun generateRelatedTables(paramsInfo: List, calls: RestResourceCalls, dbActions : List) : MutableMap>? = null + fun generateRelatedTables(paramsInfo: List, calls: RestResourceCalls, sqlActions : List) : MutableMap>? = null fun generateRelatedTables(ar: RestResourceNode) : MutableMap>? = null } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/util/inference/SimpleDeriveR2T.kt b/core/src/main/kotlin/org/evomaster/core/problem/util/inference/SimpleDeriveR2T.kt index b4e6bbfe7c..a539f65a5c 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/util/inference/SimpleDeriveR2T.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/util/inference/SimpleDeriveR2T.kt @@ -1,6 +1,6 @@ package org.evomaster.core.problem.util.inference -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.database.schema.Table import org.evomaster.core.problem.rest.HttpVerb import org.evomaster.core.problem.rest.RestCallAction @@ -216,17 +216,17 @@ object SimpleDeriveResourceBinding : DeriveResourceBinding { * * @param paramsInfo of calls to be bound * @param calls to be bound - * @param dbActions specifies the tables to be analyzed. + * @param sqlActions specifies the tables to be analyzed. * if [dbActions] is empty, the tables would be all related tables extracted from its resource node */ - override fun generateRelatedTables(paramsInfo: List, calls: RestResourceCalls, dbActions : List): MutableMap> { + override fun generateRelatedTables(paramsInfo: List, calls: RestResourceCalls, sqlActions : List): MutableMap> { val result = mutableMapOf>() val missingParams = paramsInfo.map { it.key } val resource = calls.getResourceNode() - val relatedTables = dbActions.map { it.table.name }.toHashSet() + val relatedTables = sqlActions.map { it.table.name }.toHashSet() val list = if(relatedTables.isEmpty()) getBindMap(missingParams.toSet(), resource.resourceToTable) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/WebIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/WebIndividual.kt index f2c2fa8f47..e559f5fac5 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/WebIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/WebIndividual.kt @@ -1,11 +1,9 @@ package org.evomaster.core.problem.webfrontend -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.problem.enterprise.EnterpriseActionGroup import org.evomaster.core.problem.enterprise.SampleType -import org.evomaster.core.problem.graphql.GraphQLIndividual import org.evomaster.core.problem.gui.GuiIndividual -import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.search.action.ActionComponent import org.evomaster.core.search.GroupsOfChildren import org.evomaster.core.search.Individual @@ -23,7 +21,7 @@ class WebIndividual( children = children, childTypeVerifier = { EnterpriseActionGroup::class.java.isAssignableFrom(it) - || DbAction::class.java.isAssignableFrom(it) + || SqlAction::class.java.isAssignableFrom(it) }, groups = groups ) { diff --git a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebFitness.kt index aae1b54141..c5c9ee656a 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebFitness.kt @@ -2,7 +2,7 @@ package org.evomaster.core.problem.webfrontend.service import org.evomaster.client.java.controller.api.dto.AdditionalInfoDto import org.evomaster.core.Lazy -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.problem.enterprise.service.EnterpriseFitness import org.evomaster.core.problem.webfrontend.* import org.evomaster.core.search.action.ActionResult @@ -45,7 +45,7 @@ class WebFitness : EnterpriseFitness() { val actionResults: MutableList = mutableListOf() - doDbCalls(individual.seeInitializingActions().filterIsInstance(), actionResults = actionResults) + doDbCalls(individual.seeInitializingActions().filterIsInstance(), actionResults = actionResults) val fv = FitnessValue(individual.size().toDouble()) diff --git a/core/src/main/kotlin/org/evomaster/core/search/EnvironmentAction.kt b/core/src/main/kotlin/org/evomaster/core/search/EnvironmentAction.kt new file mode 100644 index 0000000000..441377e949 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/search/EnvironmentAction.kt @@ -0,0 +1,14 @@ +package org.evomaster.core.search + +import org.evomaster.core.search.action.Action + + +/** + * An action used to setup the environment of a test: eg databases and external services + */ +abstract class EnvironmentAction(children: List) : Action(children){ + + override fun shouldCountForFitnessEvaluations(): Boolean { + return false + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/search/EvaluatedAction.kt b/core/src/main/kotlin/org/evomaster/core/search/EvaluatedAction.kt index 2bff428095..ccc3c08fcd 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/EvaluatedAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/EvaluatedAction.kt @@ -2,8 +2,8 @@ package org.evomaster.core.search import org.evomaster.core.search.action.Action import org.evomaster.core.search.action.ActionResult -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionResult +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionResult import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.mongo.MongoDbActionResult @@ -13,6 +13,6 @@ open class EvaluatedAction(open val action: Action, open val result: ActionResul /** * specialized evaluated db action */ -class EvaluatedDbAction(override val action: DbAction, override val result: DbActionResult) : EvaluatedAction(action, result) +class EvaluatedDbAction(override val action: SqlAction, override val result: SqlActionResult) : EvaluatedAction(action, result) class EvaluatedMongoDbAction(override val action: MongoDbAction, override val result: MongoDbActionResult) : EvaluatedAction(action, result) \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/search/EvaluatedIndividual.kt b/core/src/main/kotlin/org/evomaster/core/search/EvaluatedIndividual.kt index 2d0ff1728e..448a913b2f 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/EvaluatedIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/EvaluatedIndividual.kt @@ -9,8 +9,8 @@ import org.evomaster.core.search.tracer.TraceableElementCopyFilter import org.evomaster.core.search.tracer.TrackOperator import org.evomaster.core.Lazy import org.evomaster.core.search.action.Action -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionResult +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionResult import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.externalservice.ApiExternalServiceAction @@ -25,7 +25,6 @@ import org.evomaster.core.search.service.mutator.EvaluatedMutation import org.evomaster.core.search.tracer.TrackingHistory import org.slf4j.Logger import org.slf4j.LoggerFactory -import javax.security.sasl.AuthorizeCallback import kotlin.reflect.KClass /** @@ -197,9 +196,9 @@ class EvaluatedIndividual( list.add( dbResults.mapIndexed { index, actionResult -> EvaluatedDbAction( - (dbActions[index] as? DbAction) + (dbActions[index] as? SqlAction) ?: throw IllegalStateException("mismatched action type, expected is DbAction but it is ${dbActions[index]::class.java.simpleName}"), - (actionResult as? DbActionResult) + (actionResult as? SqlActionResult) ?: throw IllegalStateException("mismatched action result type, expected is DbActionResult but it is ${actionResult::class.java.simpleName}") ) } to restResult.mapIndexed { index, actionResult -> @@ -397,14 +396,14 @@ class EvaluatedIndividual( if (this.index == next.index) { //remove a number of resource with sql - if (mutatedGenes.removedDbActions.isNotEmpty()) { + if (mutatedGenes.removedSqlActions.isNotEmpty()) { impactInfo!!.removeInitializationImpacts( - mutatedGenes.removedDbActions, - individual.seeInitializingActions().count { it is DbAction && it.representExistingData }) + mutatedGenes.removedSqlActions, + individual.seeInitializingActions().count { it is SqlAction && it.representExistingData }) } - if (mutatedGenes.addedDbActions.isNotEmpty()) { - impactInfo!!.appendInitializationImpacts(mutatedGenes.addedDbActions) + if (mutatedGenes.addedSqlActions.isNotEmpty()) { + impactInfo!!.appendInitializationImpacts(mutatedGenes.addedSqlActions) } /* @@ -553,7 +552,7 @@ class EvaluatedIndividual( if (mutatedGenes.didAddInitializationGenes()) { Lazy.assert { impactInfo!!.getSQLExistingData() == individual.seeInitializingActions() - .count { it is DbAction && it.representExistingData } + .count { it is SqlAction && it.representExistingData } } } @@ -768,9 +767,9 @@ class EvaluatedIndividual( ) { impactInfo ?: throw IllegalStateException("there is no any impact initialized") - val allExistingData = individual.seeInitializingActions().filter { it is DbAction && it.representExistingData } + val allExistingData = individual.seeInitializingActions().filter { it is SqlAction && it.representExistingData } val diff = individual.seeInitializingActions() - .filter { !old.contains(it) && ((it is DbAction && !it.representExistingData) || it is MongoDbAction) } + .filter { !old.contains(it) && ((it is SqlAction && !it.representExistingData) || it is MongoDbAction) } if (allExistingData.isNotEmpty()) impactInfo.updateExistingSQLData(allExistingData.size) @@ -808,7 +807,7 @@ class EvaluatedIndividual( Lazy.assert { individual.seeInitializingActions() - .filter { (it is DbAction && !it.representExistingData) || it is MongoDbAction }.size == impactInfo.getSizeOfActionImpacts(true) + .filter { (it is SqlAction && !it.representExistingData) || it is MongoDbAction }.size == impactInfo.getSizeOfActionImpacts(true) } } @@ -894,6 +893,6 @@ class EvaluatedIndividual( return !invalid } private fun initializingActionClasses(): List> { - return listOf(MongoDbAction::class, DbAction::class) + return listOf(MongoDbAction::class, SqlAction::class) } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/search/Individual.kt b/core/src/main/kotlin/org/evomaster/core/search/Individual.kt index d86443967a..9959c28fcb 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/Individual.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/Individual.kt @@ -5,8 +5,8 @@ import org.evomaster.core.search.action.Action import org.evomaster.core.search.action.ActionComponent import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.action.ActionTree -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionUtils +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionUtils import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.problem.api.param.Param import org.evomaster.core.problem.externalservice.ApiExternalServiceAction @@ -146,7 +146,7 @@ abstract class Individual(override var trackOperator: TrackOperator? = null, groupsView()?.verifyGroups() - if(!DbActionUtils.verifyActions(seeInitializingActions().filterIsInstance())){ + if(!SqlActionUtils.verifyActions(seeInitializingActions().filterIsInstance())){ throw IllegalStateException("Initializing actions break SQL constraints") } diff --git a/core/src/main/kotlin/org/evomaster/core/search/Solution.kt b/core/src/main/kotlin/org/evomaster/core/search/Solution.kt index bb1af3d247..ce8cba9977 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/Solution.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/Solution.kt @@ -1,6 +1,6 @@ package org.evomaster.core.search -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.output.Termination import org.evomaster.core.problem.externalservice.httpws.HttpExternalServiceAction @@ -52,7 +52,7 @@ where T : Individual { } fun hasAnySqlAction() : Boolean{ - return individuals.any { ind -> ind.individual.seeAllActions().any { a -> a is DbAction}} + return individuals.any { ind -> ind.individual.seeAllActions().any { a -> a is SqlAction}} } fun hasAnyMongoAction() : Boolean{ diff --git a/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactsOfIndividual.kt b/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactsOfIndividual.kt index 437f103ce3..81affb184b 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactsOfIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactsOfIndividual.kt @@ -1,6 +1,6 @@ package org.evomaster.core.search.impact.impactinfocollection -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.search.action.Action import org.evomaster.core.search.FitnessValue @@ -187,7 +187,7 @@ open class ImpactsOfIndividual( * thus, we need to synchronize the action impacts based on the [individual] */ fun syncBasedOnIndividual(individual: Individual) { - val initActions = individual.seeInitializingActions().filter { it is DbAction || it is MongoDbAction } + val initActions = individual.seeInitializingActions().filter { it is SqlAction || it is MongoDbAction } //for initialization due to db action fixing val diff = initActions.size - initActionImpacts.getOriginalSize() if (diff < 0) { //truncation @@ -315,7 +315,7 @@ open class ImpactsOfIndividual( /** * remove impacts for initialization */ - fun removeInitializationImpacts(removed : List>, existingDataSize: Int){ + fun removeInitializationImpacts(removed : List>, existingDataSize: Int){ initActionImpacts.updateSizeOfExistingData(existingDataSize) initActionImpacts.removeInitialization(removed) } diff --git a/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/InitializationActionImpacts.kt b/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/InitializationActionImpacts.kt index 9441470ea6..fdf2fb899b 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/InitializationActionImpacts.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/InitializationActionImpacts.kt @@ -1,7 +1,7 @@ package org.evomaster.core.search.impact.impactinfocollection import org.evomaster.core.Lazy -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.search.action.Action import org.slf4j.Logger @@ -99,7 +99,7 @@ class InitializationActionImpacts(val abstract: Boolean, val enableImpactOnDupli * remove impacts of actions in initialization of the individual * @param removed to be removed, a list of Dbaction to its index */ - fun removeInitialization(removed: List>){ + fun removeInitialization(removed: List>){ val removedIndex = removed.map { it.second - existingSQLData}.sorted() val removedImpacts = removedIndex.map { completeSequence[it] } @@ -189,13 +189,13 @@ class InitializationActionImpacts(val abstract: Boolean, val enableImpactOnDupli * @param list actions after truncation */ fun truncation(list: List) { - val ignoreExisting = list.filterIsInstance().count{it.representExistingData } + val ignoreExisting = list.filterIsInstance().count{it.representExistingData } if (ignoreExisting != getExistingData()){ log.warn("mismatched existing data") } val original = completeSequence.size - val seq = list.filter{(it is DbAction && !it.representExistingData) || it is MongoDbAction } + val seq = list.filter{(it is SqlAction && !it.representExistingData) || it is MongoDbAction } if (seq.size > original) { log.warn("there are more db actions after the truncation") return diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/MutatedGeneSpecification.kt b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/MutatedGeneSpecification.kt index 77db5b6b7c..e516b52e9f 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/MutatedGeneSpecification.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/MutatedGeneSpecification.kt @@ -1,6 +1,6 @@ package org.evomaster.core.search.service.mutator -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.search.action.Action import org.evomaster.core.search.Individual import org.evomaster.core.search.gene.Gene @@ -28,8 +28,8 @@ data class MutatedGeneSpecification ( val addedInitializationGroup: MutableList> = mutableListOf(), //SQL resource handling - val addedDbActions : MutableList> = mutableListOf(), - val removedDbActions : MutableList> = mutableListOf(), + val addedSqlActions : MutableList> = mutableListOf(), + val removedSqlActions : MutableList> = mutableListOf(), // external service actions val addedExternalServiceActions : MutableList = mutableListOf() @@ -157,12 +157,12 @@ data class MutatedGeneSpecification ( // add, remove, swap, add_sql, remove_sql fun didStructureMutation() = mutatedGenes.any { it.type != MutatedType.MODIFY } || (mutatedInitGenes.plus(mutatedDbGenes)).any { it.type != MutatedType.MODIFY } - || addedDbActions.isNotEmpty() || removedDbActions.isNotEmpty() + || addedSqlActions.isNotEmpty() || removedSqlActions.isNotEmpty() fun isMutated(gene: Gene) = (mutatedInitGenes.plus(mutatedDbGenes)).any { it.gene == gene } || mutatedGenes.any { it.gene == gene } - || addedDbActions.flatten().any { it.seeTopGenes().contains(gene) } - || removedDbActions.map { it.first }.any { it.seeTopGenes().contains(gene) } + || addedSqlActions.flatten().any { it.seeTopGenes().contains(gene) } + || removedSqlActions.map { it.first }.any { it.seeTopGenes().contains(gene) } fun mutatedActionOrInit() = setOf((mutatedGenes.plus(mutatedDbGenes)).isEmpty(), mutatedInitGenes.isNotEmpty()) diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/Mutator.kt b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/Mutator.kt index 60f979384e..6441d86cda 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/Mutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/Mutator.kt @@ -3,7 +3,7 @@ package org.evomaster.core.search.service.mutator import com.google.inject.Inject import org.evomaster.core.EMConfig import org.evomaster.core.Lazy -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.problem.externalservice.httpws.service.HarvestActualHttpWsResponseHandler import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.Individual @@ -106,7 +106,7 @@ abstract class Mutator : TrackOperator where T : Individual { log.trace("mutator will be applied, and the individual contains {} dbactions which are", individual.individual.seeInitializingActions().size, individual.individual.seeInitializingActions().joinToString(","){ - if (it is DbAction) it.getResolvedName() else it.getName() + if (it is SqlAction) it.getResolvedName() else it.getName() } ) } diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/StandardMutator.kt b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/StandardMutator.kt index c5fb508a33..0f11ae9190 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/StandardMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/StandardMutator.kt @@ -4,8 +4,8 @@ import org.evomaster.core.EMConfig import org.evomaster.core.EMConfig.GeneMutationStrategy.ONE_OVER_N import org.evomaster.core.EMConfig.GeneMutationStrategy.ONE_OVER_N_BIASED_SQL import org.evomaster.core.Lazy -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionUtils +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionUtils import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.problem.api.ApiWsAction import org.evomaster.core.problem.api.param.Param @@ -293,8 +293,8 @@ open class StandardMutator : Mutator() where T : Individual { override fun postActionAfterMutation(mutatedIndividual: T, mutated: MutatedGeneSpecification?) { Lazy.assert { - DbActionUtils.verifyForeignKeys( - mutatedIndividual.seeInitializingActions().filterIsInstance() + SqlActionUtils.verifyForeignKeys( + mutatedIndividual.seeInitializingActions().filterIsInstance() ) } diff --git a/core/src/main/kotlin/org/evomaster/core/taint/TaintAnalysis.kt b/core/src/main/kotlin/org/evomaster/core/taint/TaintAnalysis.kt index 9cd8013d0b..0ebcc365da 100644 --- a/core/src/main/kotlin/org/evomaster/core/taint/TaintAnalysis.kt +++ b/core/src/main/kotlin/org/evomaster/core/taint/TaintAnalysis.kt @@ -5,7 +5,7 @@ import org.evomaster.client.java.instrumentation.shared.StringSpecialization import org.evomaster.client.java.instrumentation.shared.StringSpecializationInfo import org.evomaster.client.java.instrumentation.shared.TaintInputName import org.evomaster.client.java.instrumentation.shared.TaintType -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.problem.rest.RestActionBuilderV3 import org.evomaster.core.problem.rest.RestCallAction @@ -51,7 +51,7 @@ object TaintAnalysis { if (log.isTraceEnabled) { log.trace("do taint analysis for individual which contains dbactions: {} and rest actions: {}", individual.seeInitializingActions().joinToString(",") { - if (it is DbAction) it.getResolvedName() else it.getName() + if (it is SqlAction) it.getResolvedName() else it.getName() }, individual.seeAllActions().joinToString(",") { if (it is RestCallAction) it.resolvedPath() else it.getName() diff --git a/core/src/test/kotlin/org/evomaster/core/database/DbActionGeneBuilderTest.kt b/core/src/test/kotlin/org/evomaster/core/database/DbActionGeneBuilderTest.kt index ac6c85f104..225f450acd 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/DbActionGeneBuilderTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/DbActionGeneBuilderTest.kt @@ -16,7 +16,7 @@ class DbActionGeneBuilderTest { val randomness = Randomness() val javaRegex = "/foo/../bar/(left|right)/[0-9]{4}-[0-9]{2}-[0-9]{2}(/[0-9]*)?" - val gene = DbActionGeneBuilder().buildSimilarToRegexGene("w_id", javaRegex, databaseType = DatabaseType.POSTGRES) + val gene = SqlActionGeneBuilder().buildSimilarToRegexGene("w_id", javaRegex, databaseType = DatabaseType.POSTGRES) for (seed in 1..100L) { randomness.updateSeed(seed) @@ -45,7 +45,7 @@ class DbActionGeneBuilderTest { val chosenRegex = randomness.nextInt(0,likePatterns.size-1) - val gene = DbActionGeneBuilder().buildLikeRegexGene("f_id", likePatterns[chosenRegex], databaseType = DatabaseType.POSTGRES) + val gene = SqlActionGeneBuilder().buildLikeRegexGene("f_id", likePatterns[chosenRegex], databaseType = DatabaseType.POSTGRES) assertEquals("${DATABASE_REGEX_PREFIX}${likePatterns[chosenRegex]}", gene.sourceRegex) diff --git a/core/src/test/kotlin/org/evomaster/core/database/DbActionTransformerTest.kt b/core/src/test/kotlin/org/evomaster/core/database/DbActionTransformerTest.kt index f4e34b3435..eabaeb147c 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/DbActionTransformerTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/DbActionTransformerTest.kt @@ -7,8 +7,8 @@ class DbActionTransformerTest { @Test fun testEmpty() { - val actions = listOf() - val dto = DbActionTransformer.transform(actions) + val actions = listOf() + val dto = SqlActionTransformer.transform(actions) assertTrue(dto.insertions.isEmpty()) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/DbActionUtilsTest.kt b/core/src/test/kotlin/org/evomaster/core/database/DbActionUtilsTest.kt index cd1012cb31..992a1b0228 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/DbActionUtilsTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/DbActionUtilsTest.kt @@ -49,35 +49,35 @@ class DbActionUtilsTest { val gx0 = SqlPrimaryKeyGene(x.name, tableName, IntegerGene(x.name, 42), 1) val gy0 = SqlPrimaryKeyGene(y.name, tableName, IntegerGene(y.name, 66), 2) - val action0 = DbAction(table, setOf(x, y), 0L, listOf(gx0, gy0)) + val action0 = SqlAction(table, setOf(x, y), 0L, listOf(gx0, gy0)) - assertTrue(DbActionUtils.verifyUniqueColumns(listOf(action0))) + assertTrue(SqlActionUtils.verifyUniqueColumns(listOf(action0))) //second action with exact same PK val gx1 = SqlPrimaryKeyGene(x.name, tableName, IntegerGene(x.name, 42), 3) val gy1 = SqlPrimaryKeyGene(y.name, tableName, IntegerGene(y.name, 66), 4) - val action1 = DbAction(table, setOf(x, y), 1L, listOf(gx1, gy1)) + val action1 = SqlAction(table, setOf(x, y), 1L, listOf(gx1, gy1)) //validation should fail - assertFalse(DbActionUtils.verifyUniqueColumns(listOf(action0, action1))) + assertFalse(SqlActionUtils.verifyUniqueColumns(listOf(action0, action1))) //third action with inverted values val gx2 = SqlPrimaryKeyGene(x.name, tableName, IntegerGene(x.name, 66), 5) val gy2 = SqlPrimaryKeyGene(y.name, tableName, IntegerGene(y.name, 42), 6) - val action2 = DbAction(table, setOf(x, y), 2L, listOf(gx2, gy2)) + val action2 = SqlAction(table, setOf(x, y), 2L, listOf(gx2, gy2)) //should be fine - assertTrue(DbActionUtils.verifyUniqueColumns(listOf(action0, action2))) + assertTrue(SqlActionUtils.verifyUniqueColumns(listOf(action0, action2))) //fourth action with one column as same value val gx3 = SqlPrimaryKeyGene(x.name, tableName, IntegerGene(x.name, 42), 7) val gy3 = SqlPrimaryKeyGene(y.name, tableName, IntegerGene(y.name, 1234), 8) - val action3 = DbAction(table, setOf(x, y), 3L, listOf(gx3, gy3)) + val action3 = SqlAction(table, setOf(x, y), 3L, listOf(gx3, gy3)) //should still be fine, as PK is composed of 2 columns - assertTrue(DbActionUtils.verifyUniqueColumns(listOf(action0, action3))) + assertTrue(SqlActionUtils.verifyUniqueColumns(listOf(action0, action3))) } @Test @@ -107,35 +107,35 @@ class DbActionUtilsTest { val gx0 = SqlPrimaryKeyGene(x.name, tableName, IntegerGene(x.name, 42), 1) val gy0 = SqlPrimaryKeyGene(y.name, tableName, IntegerGene(y.name, 66), 2) - val action0 = DbAction(table, setOf(x, y), 0L, listOf(gx0, gy0)) + val action0 = SqlAction(table, setOf(x, y), 0L, listOf(gx0, gy0)) - assertTrue(DbActionUtils.verifyUniqueColumns(listOf(action0))) + assertTrue(SqlActionUtils.verifyUniqueColumns(listOf(action0))) //second action with exact same PK val gx1 = SqlPrimaryKeyGene(x.name, tableName, IntegerGene(x.name, 42), 3) val gy1 = SqlPrimaryKeyGene(y.name, tableName, IntegerGene(y.name, 66), 4) - val action1 = DbAction(table, setOf(x, y), 1L, listOf(gx1, gy1)) + val action1 = SqlAction(table, setOf(x, y), 1L, listOf(gx1, gy1)) //validation should fail - assertFalse(DbActionUtils.verifyUniqueColumns(listOf(action0, action1))) + assertFalse(SqlActionUtils.verifyUniqueColumns(listOf(action0, action1))) //third action with different y val gx2 = SqlPrimaryKeyGene(x.name, tableName, IntegerGene(x.name, 42), 5) val gy2 = SqlPrimaryKeyGene(y.name, tableName, IntegerGene(y.name, 77), 6) - val action2 = DbAction(table, setOf(x, y), 2L, listOf(gx2, gy2)) + val action2 = SqlAction(table, setOf(x, y), 2L, listOf(gx2, gy2)) //should still fail, due to unique x - assertFalse(DbActionUtils.verifyUniqueColumns(listOf(action0, action2))) + assertFalse(SqlActionUtils.verifyUniqueColumns(listOf(action0, action2))) //fourth action with same y, and different x val gx3 = SqlPrimaryKeyGene(x.name, tableName, IntegerGene(x.name, 1234), 7) val gy3 = SqlPrimaryKeyGene(y.name, tableName, IntegerGene(y.name, 66), 8) - val action3 = DbAction(table, setOf(x, y), 3L, listOf(gx3, gy3)) + val action3 = SqlAction(table, setOf(x, y), 3L, listOf(gx3, gy3)) //should be fine, as PK is composed of 2 columns, and y does not need to be unique - assertTrue(DbActionUtils.verifyUniqueColumns(listOf(action0, action3))) + assertTrue(SqlActionUtils.verifyUniqueColumns(listOf(action0, action3))) } @@ -148,18 +148,18 @@ class DbActionUtilsTest { val aTable = Table("myTable", setOf(uniqueColumn), HashSet()) val gene0 = StringGene(uniqueColumn.name, "stringValue0", 0, 10) - val action0 = DbAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene0)) + val action0 = SqlAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene0)) val gene1 = StringGene(uniqueColumn.name, "stringValue1", 0, 10) - val action1 = DbAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene1)) + val action1 = SqlAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene1)) val actions = mutableListOf(action0, action1) - assertTrue(DbActionUtils.verifyForeignKeys(actions)) - assertTrue(DbActionUtils.verifyUniqueColumns(actions)) + assertTrue(SqlActionUtils.verifyForeignKeys(actions)) + assertTrue(SqlActionUtils.verifyUniqueColumns(actions)) - assertTrue(DbActionUtils.verifyActions(actions)) + assertTrue(SqlActionUtils.verifyActions(actions)) } @@ -172,18 +172,18 @@ class DbActionUtilsTest { val aTable = Table("myTable", setOf(uniqueColumn), HashSet()) val gene0 = StringGene(uniqueColumn.name, "stringValue0", 0, 10) - val action0 = DbAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene0)) + val action0 = SqlAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene0)) val gene1 = StringGene(uniqueColumn.name, "stringValue0", 0, 10) - val action1 = DbAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene1)) + val action1 = SqlAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene1)) val actions = mutableListOf(action0, action1) - assertTrue(DbActionUtils.verifyForeignKeys(actions)) - assertTrue(DbActionUtils.verifyUniqueColumns(actions)) + assertTrue(SqlActionUtils.verifyForeignKeys(actions)) + assertTrue(SqlActionUtils.verifyUniqueColumns(actions)) - assertTrue(DbActionUtils.verifyActions(actions)) + assertTrue(SqlActionUtils.verifyActions(actions)) } @@ -197,22 +197,22 @@ class DbActionUtilsTest { val aTable = Table("myTable", setOf(uniqueColumn), HashSet()) val gene0 = StringGene(uniqueColumn.name, "stringValue0", 0, 10) - val action0 = DbAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene0)) + val action0 = SqlAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene0)) val gene1 = gene0.copy() - val action1 = DbAction(aTable, setOf(uniqueColumn), 1L, mutableListOf(gene1)) + val action1 = SqlAction(aTable, setOf(uniqueColumn), 1L, mutableListOf(gene1)) val actions = mutableListOf(action0, action1) - assertTrue(DbActionUtils.verifyForeignKeys(actions)) - assertFalse(DbActionUtils.verifyUniqueColumns(actions)) + assertTrue(SqlActionUtils.verifyForeignKeys(actions)) + assertFalse(SqlActionUtils.verifyUniqueColumns(actions)) - assertFalse(DbActionUtils.verifyActions(actions)) + assertFalse(SqlActionUtils.verifyActions(actions)) - val listWasNotTruncated = DbActionUtils.repairBrokenDbActionsList(actions, randomness) + val listWasNotTruncated = SqlActionUtils.repairBrokenDbActionsList(actions, randomness) assertEquals(true, listWasNotTruncated) - assertTrue(DbActionUtils.verifyActions(actions)) + assertTrue(SqlActionUtils.verifyActions(actions)) } @@ -225,19 +225,19 @@ class DbActionUtilsTest { val aTable = Table("myTable", setOf(uniqueColumn), HashSet()) val gene0 = StringGene(uniqueColumn.name, "stringValue0", 0, 10) - val action0 = DbAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene0)) + val action0 = SqlAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene0)) val gene1 = gene0.copy() - val action1 = DbAction(aTable, setOf(uniqueColumn), 1L, mutableListOf(gene1)) + val action1 = SqlAction(aTable, setOf(uniqueColumn), 1L, mutableListOf(gene1)) val actions = mutableListOf(action0, action1) - assertTrue(DbActionUtils.verifyForeignKeys(actions)) - assertFalse(DbActionUtils.verifyUniqueColumns(actions)) + assertTrue(SqlActionUtils.verifyForeignKeys(actions)) + assertFalse(SqlActionUtils.verifyUniqueColumns(actions)) - assertFalse(DbActionUtils.verifyActions(actions)) + assertFalse(SqlActionUtils.verifyActions(actions)) - val listWasNotTruncated = DbActionUtils.repairBrokenDbActionsList(actions, randomness, maxNumberOfAttemptsToRepairAnAction = 0) + val listWasNotTruncated = SqlActionUtils.repairBrokenDbActionsList(actions, randomness, maxNumberOfAttemptsToRepairAnAction = 0) assertEquals(false, listWasNotTruncated) } @@ -252,22 +252,22 @@ class DbActionUtilsTest { val aTable = Table("myTable", setOf(uniqueColumn), HashSet()) val gene0 = BooleanGene(uniqueColumn.name, true) - val action0 = DbAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene0)) + val action0 = SqlAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene0)) val gene1 = gene0.copy() - val action1 = DbAction(aTable, setOf(uniqueColumn), 1L, mutableListOf(gene1)) + val action1 = SqlAction(aTable, setOf(uniqueColumn), 1L, mutableListOf(gene1)) val actions = mutableListOf(action0, action1) - assertTrue(DbActionUtils.verifyForeignKeys(actions)) - assertFalse(DbActionUtils.verifyUniqueColumns(actions)) + assertTrue(SqlActionUtils.verifyForeignKeys(actions)) + assertFalse(SqlActionUtils.verifyUniqueColumns(actions)) - assertFalse(DbActionUtils.verifyActions(actions)) + assertFalse(SqlActionUtils.verifyActions(actions)) - val listWasNotTruncated = DbActionUtils.repairBrokenDbActionsList(actions, randomness) + val listWasNotTruncated = SqlActionUtils.repairBrokenDbActionsList(actions, randomness) assertEquals(true, listWasNotTruncated) - assertTrue(DbActionUtils.verifyActions(actions)) + assertTrue(SqlActionUtils.verifyActions(actions)) } @Test @@ -279,29 +279,29 @@ class DbActionUtilsTest { val aTable = Table("myTable", setOf(uniqueColumn), HashSet()) val gene0 = IntegerGene(uniqueColumn.name, 42) - val action0 = DbAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene0)) + val action0 = SqlAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene0)) val gene1 = gene0.copy() - val action1 = DbAction(aTable, setOf(uniqueColumn), 1L, mutableListOf(gene1)) + val action1 = SqlAction(aTable, setOf(uniqueColumn), 1L, mutableListOf(gene1)) val actions = mutableListOf(action0, action1) - assertTrue(DbActionUtils.verifyForeignKeys(actions)) - assertFalse(DbActionUtils.verifyUniqueColumns(actions)) + assertTrue(SqlActionUtils.verifyForeignKeys(actions)) + assertFalse(SqlActionUtils.verifyUniqueColumns(actions)) - assertFalse(DbActionUtils.verifyActions(actions)) + assertFalse(SqlActionUtils.verifyActions(actions)) - val listWasNotTruncated = DbActionUtils.repairBrokenDbActionsList(actions, randomness) + val listWasNotTruncated = SqlActionUtils.repairBrokenDbActionsList(actions, randomness) assertEquals(true, listWasNotTruncated) - assertTrue(DbActionUtils.verifyActions(actions)) + assertTrue(SqlActionUtils.verifyActions(actions)) } @Test fun testIllegalMaxNumberOfAttempts() { try { - DbActionUtils.repairBrokenDbActionsList(mutableListOf(), randomness, -1) + SqlActionUtils.repairBrokenDbActionsList(mutableListOf(), randomness, -1) fail("Expected IllegalArgumentException when maxNumberOfAttemptsToRepairAction argument is negative") } catch (e: IllegalArgumentException) { // expected @@ -317,27 +317,27 @@ class DbActionUtilsTest { val aTable = Table("myTable", setOf(uniqueColumn), HashSet()) val gene0 = BooleanGene(uniqueColumn.name, true) - val action0 = DbAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene0)) + val action0 = SqlAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene0)) val gene1 = gene0.copy() - val action1 = DbAction(aTable, setOf(uniqueColumn), 1L, mutableListOf(gene1)) + val action1 = SqlAction(aTable, setOf(uniqueColumn), 1L, mutableListOf(gene1)) val gene2 = gene0.copy() - val action2 = DbAction(aTable, setOf(uniqueColumn), 1L, mutableListOf(gene2)) + val action2 = SqlAction(aTable, setOf(uniqueColumn), 1L, mutableListOf(gene2)) val actions = mutableListOf(action0, action1, action2) - assertTrue(DbActionUtils.verifyForeignKeys(actions)) - assertFalse(DbActionUtils.verifyUniqueColumns(actions)) - assertFalse(DbActionUtils.verifyActions(actions)) + assertTrue(SqlActionUtils.verifyForeignKeys(actions)) + assertFalse(SqlActionUtils.verifyUniqueColumns(actions)) + assertFalse(SqlActionUtils.verifyActions(actions)) - val listWasNotTruncated = DbActionUtils.repairBrokenDbActionsList(actions, randomness) + val listWasNotTruncated = SqlActionUtils.repairBrokenDbActionsList(actions, randomness) assertEquals(false, listWasNotTruncated) - assertTrue(DbActionUtils.verifyForeignKeys(actions)) - assertTrue(DbActionUtils.verifyUniqueColumns(actions)) - assertTrue(DbActionUtils.verifyActions(actions)) + assertTrue(SqlActionUtils.verifyForeignKeys(actions)) + assertTrue(SqlActionUtils.verifyUniqueColumns(actions)) + assertTrue(SqlActionUtils.verifyActions(actions)) assertEquals(2, actions.size) } @@ -356,22 +356,22 @@ class DbActionUtilsTest { val innerGene = IntegerGene(uniqueColumn.name, 42) val uniqueId = 1001L val gene0 = SqlPrimaryKeyGene(uniqueColumn.name, aTable.name, innerGene, uniqueId) - val action0 = DbAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene0)) + val action0 = SqlAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene0)) val gene1 = gene0.copy() - val action1 = DbAction(aTable, setOf(uniqueColumn), 1L, mutableListOf(gene1)) + val action1 = SqlAction(aTable, setOf(uniqueColumn), 1L, mutableListOf(gene1)) val actions = mutableListOf(action0, action1) - assertTrue(DbActionUtils.verifyForeignKeys(actions)) - assertFalse(DbActionUtils.verifyUniqueColumns(actions)) + assertTrue(SqlActionUtils.verifyForeignKeys(actions)) + assertFalse(SqlActionUtils.verifyUniqueColumns(actions)) - assertFalse(DbActionUtils.verifyActions(actions)) + assertFalse(SqlActionUtils.verifyActions(actions)) - val repairWasSuccessful = DbActionUtils.repairBrokenDbActionsList(actions, randomness) + val repairWasSuccessful = SqlActionUtils.repairBrokenDbActionsList(actions, randomness) assertEquals(true, repairWasSuccessful) - assertTrue(DbActionUtils.verifyActions(actions)) + assertTrue(SqlActionUtils.verifyActions(actions)) } @Test @@ -385,17 +385,17 @@ class DbActionUtilsTest { val aTable = Table("myTable", setOf(uniqueColumn), HashSet()) val gene0 = SqlAutoIncrementGene(uniqueColumn.name) - val action0 = DbAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene0)) + val action0 = SqlAction(aTable, setOf(uniqueColumn), 0L, mutableListOf(gene0)) val gene1 = SqlAutoIncrementGene(uniqueColumn.name) - val action1 = DbAction(aTable, setOf(uniqueColumn), 1L, mutableListOf(gene1)) + val action1 = SqlAction(aTable, setOf(uniqueColumn), 1L, mutableListOf(gene1)) val actions = mutableListOf(action0, action1) - assertTrue(DbActionUtils.verifyForeignKeys(actions)) - assertTrue(DbActionUtils.verifyUniqueColumns(actions)) + assertTrue(SqlActionUtils.verifyForeignKeys(actions)) + assertTrue(SqlActionUtils.verifyUniqueColumns(actions)) - assertTrue(DbActionUtils.verifyActions(actions)) + assertTrue(SqlActionUtils.verifyActions(actions)) } @@ -424,21 +424,21 @@ class DbActionUtilsTest { val insertId0 = 1001L val pkGeneTable0 = SqlPrimaryKeyGene("Id", "Table0", integerGene, insertId0) - val action0 = DbAction(table0, setOf(idColumn), insertId0, listOf(pkGeneTable0)) + val action0 = SqlAction(table0, setOf(idColumn), insertId0, listOf(pkGeneTable0)) val insertId1 = 1002L val fkGene = SqlForeignKeyGene("Id", insertId1, "Table0", false, insertId0) val pkGeneTable1 = SqlPrimaryKeyGene("Id", "Table1", fkGene, insertId1) - val action1 = DbAction(table1, setOf(fkColumn), insertId1, listOf(pkGeneTable1)) + val action1 = SqlAction(table1, setOf(fkColumn), insertId1, listOf(pkGeneTable1)) val actions = mutableListOf(action0, action1) - assertTrue(DbActionUtils.verifyForeignKeys(actions)) - assertTrue(DbActionUtils.verifyUniqueColumns(actions)) + assertTrue(SqlActionUtils.verifyForeignKeys(actions)) + assertTrue(SqlActionUtils.verifyUniqueColumns(actions)) - assertTrue(DbActionUtils.verifyActions(actions)) + assertTrue(SqlActionUtils.verifyActions(actions)) } @@ -467,21 +467,21 @@ class DbActionUtilsTest { val insertId0 = 1001L val pkGeneTable0 = SqlPrimaryKeyGene("Id", "Table0", autoIncrementGene, insertId0) - val action0 = DbAction(table0, setOf(idColumn), insertId0, listOf(pkGeneTable0)) + val action0 = SqlAction(table0, setOf(idColumn), insertId0, listOf(pkGeneTable0)) val insertId1 = 1002L val fkGene = SqlForeignKeyGene("Id", insertId1, "Table0", false, insertId0) val pkGeneTable1 = SqlPrimaryKeyGene("Id", "Table1", fkGene, insertId1) - val action1 = DbAction(table1, setOf(fkColumn), insertId1, listOf(pkGeneTable1)) + val action1 = SqlAction(table1, setOf(fkColumn), insertId1, listOf(pkGeneTable1)) val actions = mutableListOf(action0, action1) - assertTrue(DbActionUtils.verifyForeignKeys(actions)) - assertTrue(DbActionUtils.verifyUniqueColumns(actions)) + assertTrue(SqlActionUtils.verifyForeignKeys(actions)) + assertTrue(SqlActionUtils.verifyUniqueColumns(actions)) - assertTrue(DbActionUtils.verifyActions(actions)) + assertTrue(SqlActionUtils.verifyActions(actions)) } @@ -509,38 +509,38 @@ class DbActionUtilsTest { val insertId0 = 1001L val autoIncrementGene0 = SqlAutoIncrementGene("Id") val pkGene0 = SqlPrimaryKeyGene("Id", "Table0", autoIncrementGene0, insertId0) - val action0 = DbAction(table0, setOf(idColumn), insertId0, listOf(pkGene0)) + val action0 = SqlAction(table0, setOf(idColumn), insertId0, listOf(pkGene0)) val insertId1 = 1002L val autoIncrementGene1 = SqlAutoIncrementGene("Id") val pkGene1 = SqlPrimaryKeyGene("Id", "Table0", autoIncrementGene1, insertId1) - val action1 = DbAction(table0, setOf(idColumn), insertId0, listOf(pkGene1)) + val action1 = SqlAction(table0, setOf(idColumn), insertId0, listOf(pkGene1)) val insertId2 = 1003L val fkGene0 = SqlForeignKeyGene("Id", insertId2, "Table0", false, insertId0) val pkGene2 = SqlPrimaryKeyGene("Id", "Table1", fkGene0, insertId2) - val action2 = DbAction(table1, setOf(fkColumn), insertId2, listOf(pkGene2)) + val action2 = SqlAction(table1, setOf(fkColumn), insertId2, listOf(pkGene2)) val insertId3 = 1003L val fkGene1 = SqlForeignKeyGene("Id", insertId3, "Table0", false, insertId0) val pkGene3 = SqlPrimaryKeyGene("Id", "Table1", fkGene1, insertId3) - val action3 = DbAction(table1, setOf(fkColumn), insertId3, listOf(pkGene3)) + val action3 = SqlAction(table1, setOf(fkColumn), insertId3, listOf(pkGene3)) val actions = mutableListOf(action0, action1, action2, action3) val ind = RestIndividual(mutableListOf(), SampleType.RANDOM,null,actions,null,-1) - assertTrue(DbActionUtils.verifyForeignKeys(actions)) - assertFalse(DbActionUtils.verifyUniqueColumns(actions)) + assertTrue(SqlActionUtils.verifyForeignKeys(actions)) + assertFalse(SqlActionUtils.verifyUniqueColumns(actions)) - assertFalse(DbActionUtils.verifyActions(actions)) + assertFalse(SqlActionUtils.verifyActions(actions)) - val listWasNotTruncated = DbActionUtils.repairBrokenDbActionsList(actions, randomness) + val listWasNotTruncated = SqlActionUtils.repairBrokenDbActionsList(actions, randomness) assertEquals(true, listWasNotTruncated) - assertTrue(DbActionUtils.verifyActions(actions)) + assertTrue(SqlActionUtils.verifyActions(actions)) } @Test @@ -561,10 +561,10 @@ class DbActionUtilsTest { val table1 = Table("Table1", setOf(fkColumn), setOf(foreignKey)) val set = listOf(table0, table0, table1) - val sorted = DbActionUtils.sortTable(set) + val sorted = SqlActionUtils.sortTable(set) assertEquals(listOf(table1, table0, table0), sorted) - val rsorted = DbActionUtils.sortTable(set.distinct(), true) + val rsorted = SqlActionUtils.sortTable(set.distinct(), true) assertEquals(listOf(table0, table1), rsorted) } } diff --git a/core/src/test/kotlin/org/evomaster/core/database/TableConstraintEvaluatorTest.kt b/core/src/test/kotlin/org/evomaster/core/database/TableConstraintEvaluatorTest.kt index 227531e9ce..ae575f55ba 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/TableConstraintEvaluatorTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/TableConstraintEvaluatorTest.kt @@ -20,7 +20,7 @@ class TableConstraintEvaluatorTest { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2, nullable=false) val constraint = LowerBoundConstraint("table0", "column0", -10L) val table = Table("table0", setOf(column), setOf(), setOf(constraint)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as IntegerGene).copyValueFrom(IntegerGene("column0", value = 0)) val evaluator = TableConstraintEvaluator() val value = constraint.accept(evaluator, action) @@ -32,7 +32,7 @@ class TableConstraintEvaluatorTest { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2, nullable=false) val constraint = LowerBoundConstraint("table0", "column0", 10L) val table = Table("table0", setOf(column), setOf(), setOf(constraint)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as IntegerGene).copyValueFrom(IntegerGene("column0", value = 0)) val evaluator = TableConstraintEvaluator() val value = constraint.accept(evaluator, action) @@ -44,7 +44,7 @@ class TableConstraintEvaluatorTest { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2, nullable=false) val constraint = UpperBoundConstraint("table0", "column0", 10L) val table = Table("table0", setOf(column), setOf(), setOf(constraint)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as IntegerGene).copyValueFrom(IntegerGene("column0", value = 0)) val evaluator = TableConstraintEvaluator() val value = constraint.accept(evaluator, action) @@ -56,7 +56,7 @@ class TableConstraintEvaluatorTest { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2, nullable=false) val constraint = UpperBoundConstraint("table0", "column0", -10L) val table = Table("table0", setOf(column), setOf(), setOf(constraint)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as IntegerGene).copyValueFrom(IntegerGene("column0", value = 0)) val evaluator = TableConstraintEvaluator() val value = constraint.accept(evaluator, action) @@ -68,7 +68,7 @@ class TableConstraintEvaluatorTest { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2, nullable=false) val constraint = RangeConstraint("table0", "column0", -10L, 10L) val table = Table("table0", setOf(column), setOf(), setOf(constraint)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as IntegerGene).copyValueFrom(IntegerGene("column0", value = 0)) val evaluator = TableConstraintEvaluator() val value = constraint.accept(evaluator, action) @@ -80,7 +80,7 @@ class TableConstraintEvaluatorTest { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2, nullable=false) val constraint = RangeConstraint("table0", "column0", -10L, 10L) val table = Table("table0", setOf(column), setOf(), setOf(constraint)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 100L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 100L) (action.seeTopGenes()[0] as IntegerGene).copyValueFrom(IntegerGene("column0", value = 1000)) val evaluator = TableConstraintEvaluator() val value = constraint.accept(evaluator, action) @@ -96,7 +96,7 @@ class TableConstraintEvaluatorTest { val constraint = AndConstraint("table0", lowerBound, upperBound) val table = Table("table0", setOf(column), setOf(), setOf(lowerBound, upperBound)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as IntegerGene).copyValueFrom(IntegerGene("column0", value = 0)) @@ -114,7 +114,7 @@ class TableConstraintEvaluatorTest { val constraint = AndConstraint("table0", lowerBound, upperBound) val table = Table("table0", setOf(column), setOf(), setOf(lowerBound, upperBound)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as IntegerGene).copyValueFrom(IntegerGene("column0", value = -15)) @@ -132,7 +132,7 @@ class TableConstraintEvaluatorTest { val constraint = OrConstraint("table0", lowerBound, upperBound) val table = Table("table0", setOf(column), setOf(), setOf(lowerBound, upperBound)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as IntegerGene).copyValueFrom(IntegerGene("column0", value = -15)) @@ -150,7 +150,7 @@ class TableConstraintEvaluatorTest { val constraint = OrConstraint("table0", lowerBound, upperBound) val table = Table("table0", setOf(column), setOf(), setOf(lowerBound, upperBound)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as IntegerGene).copyValueFrom(IntegerGene("column0", value = 0)) @@ -168,7 +168,7 @@ class TableConstraintEvaluatorTest { val constraint = IffConstraint("table0", lowerBound, upperBound) val table = Table("table0", setOf(column), setOf(), setOf(lowerBound, upperBound)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as IntegerGene).copyValueFrom(IntegerGene("column0", value = 0)) @@ -186,7 +186,7 @@ class TableConstraintEvaluatorTest { val constraint = IffConstraint("table0", lowerBound, upperBound) val table = Table("table0", setOf(column), setOf(), setOf(lowerBound, upperBound)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as IntegerGene).copyValueFrom(IntegerGene("column0", value = 0)) @@ -199,7 +199,7 @@ class TableConstraintEvaluatorTest { fun testDifferentTableUpperBound() { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2, nullable=false) val table = Table("table0", setOf(column), setOf(), setOf()) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as IntegerGene).copyValueFrom(IntegerGene("column0", value = 0)) val evaluator = TableConstraintEvaluator() @@ -213,7 +213,7 @@ class TableConstraintEvaluatorTest { fun testDifferentTableLowerBound() { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2, nullable=false) val table = Table("table0", setOf(column), setOf(), setOf()) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as IntegerGene).copyValueFrom(IntegerGene("column0", value = 0)) val evaluator = TableConstraintEvaluator() @@ -226,7 +226,7 @@ class TableConstraintEvaluatorTest { fun testDifferentTableRange() { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2, nullable=false) val table = Table("table0", setOf(column), setOf(), setOf()) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as IntegerGene).copyValueFrom(IntegerGene("column0", value = 0)) val evaluator = TableConstraintEvaluator() @@ -239,7 +239,7 @@ class TableConstraintEvaluatorTest { fun testTrueIsNotNullConstraint() { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2, nullable=false) val table = Table("table0", setOf(column), setOf(), setOf()) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as IntegerGene).copyValueFrom(IntegerGene("column0", value = 0)) val evaluator = TableConstraintEvaluator() @@ -252,7 +252,7 @@ class TableConstraintEvaluatorTest { fun testDifferentTableIsNotNullConstraint() { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2, nullable=false) val table = Table("table0", setOf(column), setOf(), setOf()) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as IntegerGene).copyValueFrom(IntegerGene("column0", value = 0)) val evaluator = TableConstraintEvaluator() @@ -265,7 +265,7 @@ class TableConstraintEvaluatorTest { fun testFalseIsNotNullConstraint() { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2, nullable=false) val table = Table("table0", setOf(column), setOf(), setOf()) - val action = DbAction(table = table, selectedColumns = setOf(), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(), id = 0L) val constraint = IsNotNullConstraint("table0", "column0") val evaluator = TableConstraintEvaluator() @@ -278,7 +278,7 @@ class TableConstraintEvaluatorTest { fun testIsNotNullConstraintOfNullableColumn() { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2, nullable=true) val table = Table("table0", setOf(column), setOf(), setOf()) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as NullableGene).isActive = false val constraint = IsNotNullConstraint("table0", "column0") @@ -291,7 +291,7 @@ class TableConstraintEvaluatorTest { fun testIsNotNullConstraintOfNullableColumnNullValue() { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2, nullable=true) val table = Table("table0", setOf(column), setOf(), setOf()) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as NullableGene).isActive = true val constraint = IsNotNullConstraint("table0", "column0") @@ -306,7 +306,7 @@ class TableConstraintEvaluatorTest { val column = Column("column0", ColumnDataType.TEXT, databaseType = DatabaseType.H2, enumValuesAsStrings = listOf("value0", "value1", "value2"), nullable=false) val constraint = EnumConstraint("table0", "column0", listOf("value0", "value1", "value2")) val table = Table("table0", setOf(column), setOf(), setOf(constraint)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val evaluator = TableConstraintEvaluator() val value = constraint.accept(evaluator, action) assertTrue(value) @@ -317,7 +317,7 @@ class TableConstraintEvaluatorTest { val column = Column("column0", ColumnDataType.TEXT, databaseType = DatabaseType.H2, enumValuesAsStrings = listOf("value0", "value1", "value2"), nullable=false) val constraint = EnumConstraint("table1", "column0", listOf("value0", "value1", "value2")) val table = Table("table0", setOf(column), setOf(), setOf(constraint)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val evaluator = TableConstraintEvaluator() val value = constraint.accept(evaluator, action) assertTrue(value) @@ -328,7 +328,7 @@ class TableConstraintEvaluatorTest { val column = Column("column0", ColumnDataType.TEXT, databaseType = DatabaseType.H2, enumValuesAsStrings = listOf("value0", "value1", "value2"), nullable=false) val constraint = EnumConstraint("table0", "column0", listOf("value0", "value1", "value2")) val table = Table("table0", setOf(column), setOf(), setOf(constraint)) - val action = DbAction(table = table, selectedColumns = setOf(), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(), id = 0L) val evaluator = TableConstraintEvaluator() val value = constraint.accept(evaluator, action) assertFalse(value) @@ -339,7 +339,7 @@ class TableConstraintEvaluatorTest { val column = Column("column0", ColumnDataType.TEXT, databaseType = DatabaseType.H2, nullable=false) val constraint = UniqueConstraint("table0", listOf("column0")) val table = Table("table0", setOf(column), setOf(), setOf(constraint)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as StringGene).copyValueFrom(StringGene("foo")) val evaluator = TableConstraintEvaluator() val value = constraint.accept(evaluator, action) @@ -351,10 +351,10 @@ class TableConstraintEvaluatorTest { val column = Column("column0", ColumnDataType.TEXT, databaseType = DatabaseType.H2, nullable=false) val constraint = UniqueConstraint("table0", listOf("column0")) val table = Table("table0", setOf(column), setOf(), setOf(constraint)) - val action0 = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action0 = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action0.seeTopGenes()[0] as StringGene).copyValueFrom(StringGene("foo")) - val action1 = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action1 = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action1.seeTopGenes()[0] as StringGene).copyValueFrom(StringGene("foo")) @@ -368,10 +368,10 @@ class TableConstraintEvaluatorTest { val column = Column("column0", ColumnDataType.TEXT, databaseType = DatabaseType.H2, nullable=false, size=10) val constraint = UniqueConstraint("table0", listOf("column0")) val table = Table("table0", setOf(column), setOf(), setOf(constraint)) - val action0 = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action0 = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action0.seeTopGenes()[0] as StringGene).copyValueFrom(StringGene("column0", value = "foo")) - val action1 = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action1 = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action1.seeTopGenes()[0] as StringGene).copyValueFrom(StringGene("column0", value = "bar")) val evaluator = TableConstraintEvaluator(listOf(action0)) @@ -383,7 +383,7 @@ class TableConstraintEvaluatorTest { fun testUniqueConstrainDifferentTable() { val column = Column("column0", ColumnDataType.TEXT, databaseType = DatabaseType.H2, nullable=false) val table = Table("table0", setOf(column), setOf(), setOf()) - val action0 = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action0 = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action0.seeTopGenes()[0] as StringGene).copyValueFrom(StringGene("column0", value = "foo")) val constraint = UniqueConstraint("table1", listOf("column0")) @@ -396,7 +396,7 @@ class TableConstraintEvaluatorTest { fun testUniqueConstrainNullValues() { val column = Column("column0", ColumnDataType.TEXT, databaseType = DatabaseType.H2, nullable=false) val table = Table("table0", setOf(column), setOf(), setOf()) - val action0 = DbAction(table = table, selectedColumns = setOf(), id = 0L) + val action0 = SqlAction(table = table, selectedColumns = setOf(), id = 0L) val constraint = UniqueConstraint("table0", listOf("column0")) val evaluator = TableConstraintEvaluator() @@ -415,9 +415,9 @@ class TableConstraintEvaluatorTest { val constraint = IffConstraint("table0", equalsConstraint, isNotNullConstraint) val table = Table("table0", setOf(statusColumn, pAtColumn), setOf(), setOf(constraint)) - val action = DbAction(table = table, selectedColumns = setOf(statusColumn, pAtColumn), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(statusColumn, pAtColumn), id = 0L) (action.seeTopGenes()[0] as StringGene).copyValueFrom(StringGene("status", value = "B")) - (action.seeTopGenes()[1] as DateTimeGene).copyValueFrom(DbActionGeneBuilder().buildSqlTimestampGene("p_at")) + (action.seeTopGenes()[1] as DateTimeGene).copyValueFrom(SqlActionGeneBuilder().buildSqlTimestampGene("p_at")) val evaluator = TableConstraintEvaluator() @@ -437,7 +437,7 @@ class TableConstraintEvaluatorTest { val table = Table("table0", setOf(column), setOf(), setOf()) val constraint = UnsupportedTableConstraint("table0", "this query was not parsed") - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val evaluator = TableConstraintEvaluator() val constraintValue = constraint.accept(evaluator, action) @@ -450,7 +450,7 @@ class TableConstraintEvaluatorTest { val table = Table("table0", setOf(column), setOf(), setOf()) val constraint = LikeConstraint("table0", "column0", "%hi_", ConstraintDatabaseType.POSTGRES) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as StringGene).copyValueFrom(StringGene("status", value = "hiX")) val evaluator = TableConstraintEvaluator() @@ -464,7 +464,7 @@ class TableConstraintEvaluatorTest { val table = Table("table0", setOf(column), setOf(), setOf()) val constraint = LikeConstraint("table0", "column0", "%hi_", ConstraintDatabaseType.POSTGRES) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as StringGene).copyValueFrom(StringGene("status", value = "not matches")) val evaluator = TableConstraintEvaluator() @@ -478,7 +478,7 @@ class TableConstraintEvaluatorTest { val table = Table("table0", setOf(column), setOf(), setOf()) val constraint = SimilarToConstraint("table0", "column0", "/foo/__/bar/(left|right)/[0-9]{4}-[0-9]{2}-[0-9]{2}(/[0-9]*)?", ConstraintDatabaseType.POSTGRES) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as StringGene).copyValueFrom(StringGene("column0", value = "/foo/XX/bar/left/0000-00-000")) val evaluator = TableConstraintEvaluator() @@ -492,7 +492,7 @@ class TableConstraintEvaluatorTest { val table = Table("table0", setOf(column), setOf(), setOf()) val constraint = SimilarToConstraint("table0", "column0", "/foo/__/bar/(left|right)/[0-9]{4}-[0-9]{2}-[0-9]{2}(/[0-9]*)?", ConstraintDatabaseType.POSTGRES) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as StringGene).copyValueFrom(StringGene("column0", value = "/foo/XXXX/bar/left/0000-00-000")) val evaluator = TableConstraintEvaluator() @@ -506,7 +506,7 @@ class TableConstraintEvaluatorTest { val table = Table("table0", setOf(column), setOf(), setOf()) val constraint = SimilarToConstraint("table1", "column0", "/foo/__/bar/(left|right)/[0-9]{4}-[0-9]{2}-[0-9]{2}(/[0-9]*)?", ConstraintDatabaseType.POSTGRES) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) (action.seeTopGenes()[0] as StringGene).copyValueFrom(StringGene("column0", value = "/foo/XXXX/bar/left/0000-00-000")) val evaluator = TableConstraintEvaluator() diff --git a/core/src/test/kotlin/org/evomaster/core/database/TableConstraintGeneCollectorTest.kt b/core/src/test/kotlin/org/evomaster/core/database/TableConstraintGeneCollectorTest.kt index 1e396efd95..119287cce5 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/TableConstraintGeneCollectorTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/TableConstraintGeneCollectorTest.kt @@ -16,7 +16,7 @@ class TableConstraintGeneCollectorTest { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2) val constraint = LowerBoundConstraint("table0", "column0", -10L) val table = Table("table0", setOf(column), setOf(), setOf(constraint)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val expectedGenes = action.seeTopGenes().toSet() @@ -30,7 +30,7 @@ class TableConstraintGeneCollectorTest { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2) val constraint = UpperBoundConstraint("table0", "column0", 10L) val table = Table("table0", setOf(column), setOf(), setOf(constraint)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val expectedGenes = action.seeTopGenes().toSet() @@ -44,7 +44,7 @@ class TableConstraintGeneCollectorTest { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2) val constraint = RangeConstraint("table0", "column0", -10L, 10L) val table = Table("table0", setOf(column), setOf(), setOf(constraint)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val expectedGenes = action.seeTopGenes().toSet() val geneCollector = TableConstraintGeneCollector() @@ -63,7 +63,7 @@ class TableConstraintGeneCollectorTest { val constraint = AndConstraint("table0", lowerBound, upperBound) val table = Table("table0", setOf(column), setOf(), setOf(lowerBound, upperBound)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val expectedGenes = action.seeTopGenes().toSet() val geneCollector = TableConstraintGeneCollector() @@ -81,7 +81,7 @@ class TableConstraintGeneCollectorTest { val constraint = OrConstraint("table0", lowerBound, upperBound) val table = Table("table0", setOf(column), setOf(), setOf(lowerBound, upperBound)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val expectedGenes = action.seeTopGenes().toSet() val geneCollector = TableConstraintGeneCollector() @@ -98,7 +98,7 @@ class TableConstraintGeneCollectorTest { val constraint = IffConstraint("table0", lowerBound, upperBound) val table = Table("table0", setOf(column), setOf(), setOf(lowerBound, upperBound)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val expectedGenes = action.seeTopGenes().toSet() val geneCollector = TableConstraintGeneCollector() @@ -110,7 +110,7 @@ class TableConstraintGeneCollectorTest { fun testDifferentTableUpperBound() { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2) val table = Table("table0", setOf(column), setOf(), setOf()) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val constraint = UpperBoundConstraint("table1", "column0", 10L) val expectedGenes = setOf() @@ -125,7 +125,7 @@ class TableConstraintGeneCollectorTest { fun testDifferentTableLowerBound() { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2) val table = Table("table0", setOf(column), setOf(), setOf()) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val constraint = LowerBoundConstraint("table1", "column0", 10L) val expectedGenes = setOf() @@ -138,7 +138,7 @@ class TableConstraintGeneCollectorTest { fun testDifferentTableRange() { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2) val table = Table("table0", setOf(column), setOf(), setOf()) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val constraint = RangeConstraint("table1", "column0", -10L, +10L) val expectedGenes = setOf() @@ -151,7 +151,7 @@ class TableConstraintGeneCollectorTest { fun testIsNotNullConstraint() { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2) val table = Table("table0", setOf(column), setOf(), setOf()) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val constraint = IsNotNullConstraint("table0", "column0") val expectedGenes = action.seeTopGenes().toSet() @@ -164,7 +164,7 @@ class TableConstraintGeneCollectorTest { fun testDifferentTableIsNotNullConstraint() { val column = Column("column0", ColumnDataType.INTEGER, databaseType = DatabaseType.H2) val table = Table("table0", setOf(column), setOf(), setOf()) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val constraint = IsNotNullConstraint("table1", "column0") val expectedGenes = setOf() @@ -179,7 +179,7 @@ class TableConstraintGeneCollectorTest { val column = Column("column0", ColumnDataType.TEXT, databaseType = DatabaseType.H2, enumValuesAsStrings = listOf("value0", "value1", "value2")) val constraint = EnumConstraint("table0", "column0", listOf("value0", "value1", "value2")) val table = Table("table0", setOf(column), setOf(), setOf(constraint)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val expectedGenes = action.seeTopGenes().toSet() val geneCollector = TableConstraintGeneCollector() @@ -193,7 +193,7 @@ class TableConstraintGeneCollectorTest { val column = Column("column0", ColumnDataType.TEXT, databaseType = DatabaseType.H2, enumValuesAsStrings = listOf("value0", "value1", "value2")) val constraint = EnumConstraint("table1", "column0", listOf("value0", "value1", "value2")) val table = Table("table0", setOf(column), setOf(), setOf(constraint)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val expectedGenes = setOf() val geneCollector = TableConstraintGeneCollector() @@ -206,7 +206,7 @@ class TableConstraintGeneCollectorTest { val column = Column("column0", ColumnDataType.TEXT, databaseType = DatabaseType.H2) val constraint = UniqueConstraint("table0", listOf("column0")) val table = Table("table0", setOf(column), setOf(), setOf(constraint)) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val expectedGenes = action.seeTopGenes().toSet() val geneCollector = TableConstraintGeneCollector() @@ -218,7 +218,7 @@ class TableConstraintGeneCollectorTest { fun testUniqueConstrainDifferentTable() { val column = Column("column0", ColumnDataType.TEXT, databaseType = DatabaseType.H2) val table = Table("table0", setOf(column), setOf(), setOf()) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val constraint = UniqueConstraint("table1", listOf("column0")) val expectedGenes = setOf() @@ -238,7 +238,7 @@ class TableConstraintGeneCollectorTest { val constraint = IffConstraint("table0", equalsConstraint, isNotNullConstraint) val table = Table("table0", setOf(statusColumn, pAtColumn), setOf(), setOf(constraint)) - val action = DbAction(table = table, selectedColumns = setOf(statusColumn, pAtColumn), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(statusColumn, pAtColumn), id = 0L) val expectedGenes = action.seeTopGenes().toSet() val collector = TableConstraintGeneCollector() @@ -252,7 +252,7 @@ class TableConstraintGeneCollectorTest { val table = Table("table0", setOf(column), setOf(), setOf()) val constraint = UnsupportedTableConstraint("table0", "this query was not parsed") - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val expectedGenes = setOf() val geneCollector = TableConstraintGeneCollector() @@ -266,7 +266,7 @@ class TableConstraintGeneCollectorTest { val table = Table("table0", setOf(column), setOf(), setOf()) val constraint = LikeConstraint("table0", "column0", "%hi_", ConstraintDatabaseType.POSTGRES) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val expectedGenes = action.seeTopGenes().toSet() val geneCollector = TableConstraintGeneCollector() @@ -281,7 +281,7 @@ class TableConstraintGeneCollectorTest { val table = Table("table0", setOf(column), setOf(), setOf()) val constraint = LikeConstraint("table1", "column0", "%hi_", ConstraintDatabaseType.POSTGRES) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val expectedGenes = setOf() val geneCollector = TableConstraintGeneCollector() @@ -295,7 +295,7 @@ class TableConstraintGeneCollectorTest { val table = Table("table0", setOf(column), setOf(), setOf()) val constraint = SimilarToConstraint("table0", "column0", "/foo/__/bar/(left|right)/[0-9]{4}-[0-9]{2}-[0-9]{2}(/[0-9]*)?", ConstraintDatabaseType.POSTGRES) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val expectedGenes = action.seeTopGenes().toSet() val geneCollector = TableConstraintGeneCollector() @@ -310,7 +310,7 @@ class TableConstraintGeneCollectorTest { val table = Table("table0", setOf(column), setOf(), setOf()) val constraint = SimilarToConstraint("table1", "column0", "/foo/__/bar/(left|right)/[0-9]{4}-[0-9]{2}-[0-9]{2}(/[0-9]*)?", ConstraintDatabaseType.POSTGRES) - val action = DbAction(table = table, selectedColumns = setOf(column), id = 0L) + val action = SqlAction(table = table, selectedColumns = setOf(column), id = 0L) val expectedGenes = setOf() val geneCollector = TableConstraintGeneCollector() val collectedGenes = constraint.accept(geneCollector, action) diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/CatwatchSqlExtractTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/h2/CatwatchSqlExtractTest.kt index 4ce98aead0..51e17cf20a 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/CatwatchSqlExtractTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/h2/CatwatchSqlExtractTest.kt @@ -3,8 +3,8 @@ package org.evomaster.core.database.extract.h2 import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer -import org.evomaster.core.database.DbActionUtils +import org.evomaster.core.database.SqlActionTransformer +import org.evomaster.core.database.SqlActionUtils import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.enterprise.SampleType @@ -56,7 +56,7 @@ class CatwatchSqlExtractTest : ExtractTestBaseH2(){ val ind = RestIndividual(mutableListOf(), SampleType.RANDOM,null,insertions.toMutableList(),null,-1) - DbActionUtils.randomizeDbActionGenes(insertions.toMutableList(), Randomness()) + SqlActionUtils.randomizeDbActionGenes(insertions.toMutableList(), Randomness()) assertEquals(2, insertions.size) assert(insertions[0].table.name.equals("PROJECT", ignoreCase = true)) @@ -64,7 +64,7 @@ class CatwatchSqlExtractTest : ExtractTestBaseH2(){ val projectId = (insertions[0].seeTopGenes().filterIsInstance()).first().uniqueId - val dtoForPersons = DbActionTransformer.transform(listOf(insertions[0]), execSqlIdMaps) + val dtoForPersons = SqlActionTransformer.transform(listOf(insertions[0]), execSqlIdMaps) val responseForPersons = SqlScriptRunner.execInsert(connection, dtoForPersons.insertions).idMapping assertNotNull(responseForPersons) @@ -72,7 +72,7 @@ class CatwatchSqlExtractTest : ExtractTestBaseH2(){ execSqlIdMaps.putAll(responseForPersons) - val dtoForDoctors = DbActionTransformer.transform(listOf(insertions[1]), execSqlIdMaps) + val dtoForDoctors = SqlActionTransformer.transform(listOf(insertions[1]), execSqlIdMaps) SqlScriptRunner.execInsert(connection, dtoForDoctors.insertions) val selections = SqlScriptRunner.execCommand(connection, "SELECT * FROM lANGUAGE_LIST WHERE PROJECT_ID = ${execSqlIdMaps.getValue(projectId)};"); diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/DoctorsExtractTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/h2/DoctorsExtractTest.kt index fd6c1bbb64..f128b7b3fd 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/DoctorsExtractTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/h2/DoctorsExtractTest.kt @@ -1,8 +1,8 @@ package org.evomaster.core.database.extract.h2 import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer -import org.evomaster.core.database.DbActionUtils +import org.evomaster.core.database.SqlActionTransformer +import org.evomaster.core.database.SqlActionUtils import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.enterprise.SampleType @@ -28,9 +28,9 @@ class DoctorsExtractTest : ExtractTestBaseH2() { //force binding val randomness = Randomness() - DbActionUtils.randomizeDbActionGenes(actions, randomness) + SqlActionUtils.randomizeDbActionGenes(actions, randomness) - val dto = DbActionTransformer.transform(actions) + val dto = SqlActionTransformer.transform(actions) assertEquals(actions.size, dto.insertions.size) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/ProxyPrintSqlExtractTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/h2/ProxyPrintSqlExtractTest.kt index 78c0129e5e..626ee7c807 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/ProxyPrintSqlExtractTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/h2/ProxyPrintSqlExtractTest.kt @@ -2,8 +2,8 @@ package org.evomaster.core.database.extract.h2 import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer -import org.evomaster.core.database.DbActionUtils +import org.evomaster.core.database.SqlActionTransformer +import org.evomaster.core.database.SqlActionUtils import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.enterprise.SampleType @@ -123,7 +123,7 @@ class ProxyPrintSqlExtractTest : ExtractTestBaseH2() { //force binding val randomness = Randomness() - DbActionUtils.randomizeDbActionGenes(actions, randomness) + SqlActionUtils.randomizeDbActionGenes(actions, randomness) /* - PRINT_REQUESTS request has a FK to CONSUMER @@ -140,7 +140,7 @@ class ProxyPrintSqlExtractTest : ExtractTestBaseH2() { assertTrue(fk.isBound()) assertTrue(fk.isReferenceToNonPrintable(all)) - val dto = DbActionTransformer.transform(actions) + val dto = SqlActionTransformer.transform(actions) assertEquals(actions.size, dto.insertions.size) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/LikeCheckTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/LikeCheckTest.kt index 09454144ab..e27baf82b5 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/LikeCheckTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/LikeCheckTest.kt @@ -2,7 +2,7 @@ package org.evomaster.core.database.extract.mysql import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.regex.RegexGene import org.evomaster.core.search.service.Randomness @@ -41,7 +41,7 @@ class LikeCheckTest : ExtractTestBaseMySQL() { regexGene.randomize(randomness, false) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SQLJSONColumnTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SQLJSONColumnTest.kt index b0fba27a26..ae5f94d997 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SQLJSONColumnTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SQLJSONColumnTest.kt @@ -2,8 +2,8 @@ package org.evomaster.core.database.extract.mysql import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.collection.ArrayGene @@ -50,7 +50,7 @@ class SQLJSONColumnTest : ExtractTestBaseMySQL() { val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) @@ -81,14 +81,14 @@ class SQLJSONColumnTest : ExtractTestBaseMySQL() { val objectGene = ObjectGene("jsondata", fields = listOf(IntegerGene("integerValue", value = 0), StringGene("stringValue", value = "Hello World"), BooleanGene("booleanValue", value = false))) val newGene = SqlJSONGene("jsondata", objectGene) - val newInsertAction = DbAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(genes[0], newGene)) + val newInsertAction = SqlAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(genes[0], newGene)) val query = "Select * from people where id=%s".format(idValue) val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(listOf(newInsertAction)) + val dbCommandDto = SqlActionTransformer.transform(listOf(newInsertAction)) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) @@ -125,14 +125,14 @@ class SQLJSONColumnTest : ExtractTestBaseMySQL() { val objectGene = ObjectGene("jsondata", fields = listOf(DoubleGene("doubleValue", value = Math.PI))) val newGene = SqlJSONGene("jsondata", objectGene) - val newInsertAction = DbAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(genes[0], newGene)) + val newInsertAction = SqlAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(genes[0], newGene)) val query = "Select * from people where id=%s".format(idValue) val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(listOf(newInsertAction)) + val dbCommandDto = SqlActionTransformer.transform(listOf(newInsertAction)) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) @@ -171,14 +171,14 @@ class SQLJSONColumnTest : ExtractTestBaseMySQL() { val objectGene = ObjectGene("jsondata", fields = listOf(arrayGene)) val newGene = SqlJSONGene("jsondata", objectGene) - val newInsertAction = DbAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(genes[0], newGene)) + val newInsertAction = SqlAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(genes[0], newGene)) val query = "Select * from people where id=%s".format(idValue) val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(listOf(newInsertAction)) + val dbCommandDto = SqlActionTransformer.transform(listOf(newInsertAction)) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) @@ -215,14 +215,14 @@ class SQLJSONColumnTest : ExtractTestBaseMySQL() { val objectGene = ObjectGene("jsondata", fields = listOf(innerObjectGene)) val newGene = SqlJSONGene("jsondata", objectGene) - val newInsertAction = DbAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(genes[0], newGene)) + val newInsertAction = SqlAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(genes[0], newGene)) val query = "Select * from people where id=%s".format(idValue) val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(listOf(newInsertAction)) + val dbCommandDto = SqlActionTransformer.transform(listOf(newInsertAction)) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SqlDateColumnTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SqlDateColumnTest.kt index a53a59e8f2..883d4b8929 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SqlDateColumnTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SqlDateColumnTest.kt @@ -2,7 +2,7 @@ package org.evomaster.core.database.extract.mysql import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.datetime.DateGene import org.evomaster.core.search.gene.numeric.IntegerGene @@ -47,7 +47,7 @@ class SqlDateColumnTest : ExtractTestBaseMySQL() { val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SqlTextColumnTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SqlTextColumnTest.kt index 4a83965182..141a1857c7 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SqlTextColumnTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SqlTextColumnTest.kt @@ -2,7 +2,7 @@ package org.evomaster.core.database.extract.mysql import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.string.StringGene @@ -47,7 +47,7 @@ class SqlTextColumnTest : ExtractTestBaseMySQL() { val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) @@ -80,7 +80,7 @@ class SqlTextColumnTest : ExtractTestBaseMySQL() { val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ArrayTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ArrayTypesTest.kt index cd752a6ce2..fd03c3a70e 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ArrayTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ArrayTypesTest.kt @@ -3,7 +3,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.sql.SqlMultidimensionalArrayGene @@ -160,7 +160,7 @@ class ArrayTypesTest : ExtractTestBasePostgres() { ) actions.forEach{ it.seeTopGenes().forEach { it.doInitialize(rand) }} - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } @@ -196,7 +196,7 @@ class ArrayTypesTest : ExtractTestBasePostgres() { assertEquals("\"{NULL}\"", nullableArrayColumn.getValueAsPrintableString()) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } @@ -231,7 +231,7 @@ class ArrayTypesTest : ExtractTestBasePostgres() { assertEquals("\"{\"Hello World\"}\"", stringArrayColumn.getValueAsPrintableString()) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } @@ -266,7 +266,7 @@ class ArrayTypesTest : ExtractTestBasePostgres() { assertEquals("\"{\"Hello\\\"World\"}\"", stringArrayColumn.getValueAsPrintableString()) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } @@ -302,7 +302,7 @@ class ArrayTypesTest : ExtractTestBasePostgres() { assertEquals("\"{\"Hello'World\"}\"", stringArrayColumn.getValueAsPrintableString()) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BinaryTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BinaryTypesTest.kt index dd300ec84c..f7d66fd665 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BinaryTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BinaryTypesTest.kt @@ -3,7 +3,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.collection.ArrayGene import org.evomaster.core.search.gene.numeric.IntegerGene @@ -42,7 +42,7 @@ class BinaryTypesTest : ExtractTestBasePostgres() { assertTrue(genes[0] is SqlBinaryStringGene) //character varying assertEquals(0, (genes[0] as SqlBinaryStringGene).minSize) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } @@ -91,7 +91,7 @@ class BinaryTypesTest : ExtractTestBasePostgres() { assertEquals("\"\\x002aff10\"",sqlBinaryStringGene.getValueAsPrintableString()) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BitStringTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BitStringTypesTest.kt index a1d39a2eb0..83b7383a57 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BitStringTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BitStringTypesTest.kt @@ -3,7 +3,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.collection.ArrayGene import org.evomaster.core.search.gene.BooleanGene @@ -54,7 +54,7 @@ class BitStringTypesTest : ExtractTestBasePostgres() { assertEquals(0, bitVaryingColumnGene.minSize) assertEquals(10, bitVaryingColumnGene.maxSize) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BooleanTypeTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BooleanTypeTest.kt index a38b6a5dd4..a9919014da 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BooleanTypeTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BooleanTypeTest.kt @@ -3,7 +3,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.* import org.junit.jupiter.api.Assertions.* @@ -39,7 +39,7 @@ class BooleanTypeTest : ExtractTestBasePostgres() { assertEquals(1, genes.size) assertTrue(genes[0] is BooleanGene) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/CharacterTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/CharacterTypesTest.kt index 56155b52ca..03146d849f 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/CharacterTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/CharacterTypesTest.kt @@ -3,7 +3,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.string.StringGene import org.junit.jupiter.api.Assertions.* @@ -67,7 +67,7 @@ class CharacterTypesTest : ExtractTestBasePostgres() { (genes[3] as StringGene).value = "f" (genes[4] as StringGene).value = "f" - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } @@ -101,7 +101,7 @@ class CharacterTypesTest : ExtractTestBasePostgres() { (genes[3] as StringGene).value = "f" (genes[4] as StringGene).value = "Hello\"" - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/CompositeTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/CompositeTypesTest.kt index ef969c221f..458bd79860 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/CompositeTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/CompositeTypesTest.kt @@ -3,7 +3,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test @@ -102,7 +102,7 @@ class CompositeTypesTest : ExtractTestBasePostgres() { ) ) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/DatetimeTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/DatetimeTypesTest.kt index a0f73b506a..edf2490a33 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/DatetimeTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/DatetimeTypesTest.kt @@ -3,7 +3,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.datetime.DateGene import org.evomaster.core.search.gene.datetime.DateTimeGene @@ -53,7 +53,7 @@ class DatetimeTypesTest : ExtractTestBasePostgres() { assertTrue(genes[4] is TimeGene) assertTrue(genes[5] is SqlTimeIntervalGene) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/EnumeratedTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/EnumeratedTypesTest.kt index 30712f5b2d..9e79859150 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/EnumeratedTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/EnumeratedTypesTest.kt @@ -3,7 +3,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.collection.EnumGene import org.junit.jupiter.api.Assertions.* @@ -39,7 +39,7 @@ class EnumeratedTypesTest : ExtractTestBasePostgres() { assertEquals(1, genes.size) assertTrue(genes[0] is EnumGene<*>) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/GeometricTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/GeometricTypesTest.kt index e5d4e1cc80..314554d675 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/GeometricTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/GeometricTypesTest.kt @@ -3,7 +3,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.collection.ArrayGene import org.evomaster.core.search.gene.sql.geometric.* @@ -65,7 +65,7 @@ class GeometricTypesTest : ExtractTestBasePostgres() { val circleGene = genes[6] as SqlCircleGene - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val resultSet = SqlScriptRunner.execCommand(connection, "SELECT * FROM GeometricTypes;") diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/JSONTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/JSONTypesTest.kt index 26c90f9ecd..954ff31943 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/JSONTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/JSONTypesTest.kt @@ -3,7 +3,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.sql.SqlJSONGene import org.evomaster.core.search.gene.sql.SqlJSONPathGene @@ -45,7 +45,7 @@ class JSONTypesTest : ExtractTestBasePostgres() { assertTrue(genes[1] is SqlJSONGene) assertTrue(genes[2] is SqlJSONPathGene) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/LikeCheckTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/LikeCheckTest.kt index 5290bc527a..4f04a7131f 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/LikeCheckTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/LikeCheckTest.kt @@ -2,7 +2,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.regex.RegexGene import org.evomaster.core.search.service.Randomness @@ -47,7 +47,7 @@ class LikeCheckTest : ExtractTestBasePostgres() { regexGene.randomize(randomness, false) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ManySimilarToChecksTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ManySimilarToChecksTest.kt index d78e1bbe8d..a4272caabd 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ManySimilarToChecksTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ManySimilarToChecksTest.kt @@ -2,7 +2,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.regex.RegexGene import org.evomaster.core.search.service.Randomness @@ -69,7 +69,7 @@ class ManySimilarToChecksTest : ExtractTestBasePostgres() { val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/MonetaryTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/MonetaryTypesTest.kt index b4d41ce81c..7881ddc8e0 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/MonetaryTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/MonetaryTypesTest.kt @@ -3,7 +3,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.numeric.BigDecimalGene import org.junit.jupiter.api.Assertions.* @@ -36,7 +36,7 @@ class MonetaryTypesTest : ExtractTestBasePostgres() { assertTrue(genes[0] is BigDecimalGene) // money assertEquals(2, (genes[0] as BigDecimalGene).scale) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/MultiRangeTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/MultiRangeTypesTest.kt index ab9ff1d421..2ee86221b6 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/MultiRangeTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/MultiRangeTypesTest.kt @@ -3,7 +3,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.datetime.DateGene @@ -59,7 +59,7 @@ class MultiRangeTypesTest : ExtractTestBasePostgres() { assertTrue(genes[5] is SqlMultiRangeGene<*>) assertTrue(genes[6] is SqlMultiRangeGene<*>) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } @@ -202,7 +202,7 @@ class MultiRangeTypesTest : ExtractTestBasePostgres() { newDateRangeGene.copyValueFrom(daterangeGene) dateMultiRangeGene.rangeGenes.addElement(newDateRangeGene.apply { doInitialize() }) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/NetworkTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/NetworkTypesTest.kt index 2a2a09b292..70125fc912 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/NetworkTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/NetworkTypesTest.kt @@ -3,7 +3,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.network.CidrGene import org.evomaster.core.search.gene.network.InetGene @@ -51,7 +51,7 @@ class NetworkTypesTest : ExtractTestBasePostgres() { assertEquals(MacAddrGene.MACADDR6_SIZE, (genes[2] as MacAddrGene).size()) assertEquals(MacAddrGene.MACADDR8_SIZE, (genes[3] as MacAddrGene).size()) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val resultSet = SqlScriptRunner.execCommand(connection, "SELECT * FROM NetworkTypes;") diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/NumericTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/NumericTypesTest.kt index c4947ad858..16509d7eb9 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/NumericTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/NumericTypesTest.kt @@ -3,7 +3,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.numeric.DoubleGene import org.evomaster.core.search.gene.numeric.FloatGene @@ -57,7 +57,7 @@ class NumericTypesTest : ExtractTestBasePostgres() { assertTrue(genes[8] is SqlAutoIncrementGene) // serial assertTrue(genes[9] is SqlAutoIncrementGene) // bigserial - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } @@ -104,13 +104,13 @@ class NumericTypesTest : ExtractTestBasePostgres() { realColumnGene.value = "1E+38".toDouble() doublePrecisionColumnGene.value = "1E+308".toDouble() - var dbCommandDto = DbActionTransformer.transform(actions) + var dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) realColumnGene.value = "-1E+38".toDouble() doublePrecisionColumnGene.value = "-1E+308".toDouble() - dbCommandDto = DbActionTransformer.transform(actions) + dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ObjectIdentifierTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ObjectIdentifierTypesTest.kt index 3505184772..c21473db23 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ObjectIdentifierTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ObjectIdentifierTypesTest.kt @@ -3,9 +3,8 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder -import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.numeric.IntegerGene import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test @@ -149,7 +148,7 @@ class ObjectIdentifierTypesTest : ExtractTestBasePostgres() { ) ) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/PgLsnTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/PgLsnTest.kt index 24105fd603..c4efbbae03 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/PgLsnTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/PgLsnTest.kt @@ -3,9 +3,8 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder -import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.numeric.LongGene import org.evomaster.core.search.gene.sql.SqlLogSeqNumberGene @@ -93,7 +92,7 @@ class PgLsnTest : ExtractTestBasePostgres() { leftPartGene.value = 4294967295L rightPartGene.value = 4294967295L - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/RangeTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/RangeTypesTest.kt index f5d78ceb3a..a91a7cbba7 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/RangeTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/RangeTypesTest.kt @@ -3,7 +3,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.datetime.DateGene @@ -58,7 +58,7 @@ class RangeTypesTest : ExtractTestBasePostgres() { assertTrue(genes[4] is SqlRangeGene<*>) assertTrue(genes[5] is SqlRangeGene<*>) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } @@ -176,7 +176,7 @@ class RangeTypesTest : ExtractTestBasePostgres() { genes[4].copyValueFrom(timestamprangeGene) genes[5].copyValueFrom(daterangeGene) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SimilarToCheckTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SimilarToCheckTest.kt index b2effcf642..f11aca506f 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SimilarToCheckTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SimilarToCheckTest.kt @@ -2,7 +2,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.regex.RegexGene import org.evomaster.core.search.service.Randomness @@ -50,7 +50,7 @@ class SimilarToCheckTest : ExtractTestBasePostgres() { val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlDateColumnTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlDateColumnTest.kt index b4bf8440fb..c8de0963d3 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlDateColumnTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlDateColumnTest.kt @@ -2,7 +2,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.datetime.DateGene import org.evomaster.core.search.gene.numeric.IntegerGene @@ -51,7 +51,7 @@ class SqlDateColumnTest : ExtractTestBasePostgres() { val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlJSONBColumnTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlJSONBColumnTest.kt index dd7561038b..967df8acd7 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlJSONBColumnTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlJSONBColumnTest.kt @@ -2,8 +2,8 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.collection.ArrayGene @@ -53,7 +53,7 @@ class SqlJSONBColumnTest : ExtractTestBasePostgres() { val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) @@ -84,14 +84,14 @@ class SqlJSONBColumnTest : ExtractTestBasePostgres() { val objectGene = ObjectGene("jsondata", fields = listOf(IntegerGene("integerValue", value = 0), StringGene("stringValue", value = "Hello World"), BooleanGene("booleanValue", value = false))) val newGene = SqlJSONGene("jsondata", objectGene) - val newInsertAction = DbAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(genes[0], newGene)) + val newInsertAction = SqlAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(genes[0], newGene)) val query = "Select * from people where id=%s".format(idValue) val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(listOf(newInsertAction)) + val dbCommandDto = SqlActionTransformer.transform(listOf(newInsertAction)) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) @@ -131,14 +131,14 @@ class SqlJSONBColumnTest : ExtractTestBasePostgres() { val objectGene = ObjectGene("jsondata", fields = listOf(DoubleGene("doubleValue", value = Math.PI))) val newGene = SqlJSONGene("jsondata", objectGene) - val newInsertAction = DbAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(genes[0], newGene)) + val newInsertAction = SqlAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(genes[0], newGene)) val query = "Select * from people where id=%s".format(idValue) val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(listOf(newInsertAction)) + val dbCommandDto = SqlActionTransformer.transform(listOf(newInsertAction)) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) @@ -180,14 +180,14 @@ class SqlJSONBColumnTest : ExtractTestBasePostgres() { val objectGene = ObjectGene("jsondata", fields = listOf(arrayGene)) val newGene = SqlJSONGene("jsondata", objectGene) - val newInsertAction = DbAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(genes[0], newGene)) + val newInsertAction = SqlAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(genes[0], newGene)) val query = "Select * from people where id=%s".format(idValue) val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(listOf(newInsertAction)) + val dbCommandDto = SqlActionTransformer.transform(listOf(newInsertAction)) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) @@ -226,14 +226,14 @@ class SqlJSONBColumnTest : ExtractTestBasePostgres() { val objectGene = ObjectGene("jsondata", fields = listOf(innerObjectGene)) val newGene = SqlJSONGene("jsondata", objectGene) - val newInsertAction = DbAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(genes[0], newGene)) + val newInsertAction = SqlAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(genes[0], newGene)) val query = "Select * from people where id=%s".format(idValue) val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(listOf(newInsertAction)) + val dbCommandDto = SqlActionTransformer.transform(listOf(newInsertAction)) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlTextColumnTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlTextColumnTest.kt index ea7956f229..1fb72985e2 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlTextColumnTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlTextColumnTest.kt @@ -3,7 +3,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.optional.NullableGene @@ -74,7 +74,7 @@ class SqlTextColumnTest : ExtractTestBasePostgres() { val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) @@ -107,7 +107,7 @@ class SqlTextColumnTest : ExtractTestBasePostgres() { val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlUUIDColumnTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlUUIDColumnTest.kt index 9d3eb4448f..5fe69d4d06 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlUUIDColumnTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlUUIDColumnTest.kt @@ -2,7 +2,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.optional.NullableGene @@ -50,7 +50,7 @@ class SqlUUIDColumnTest : ExtractTestBasePostgres() { val idValue = ((genes[0] as SqlPrimaryKeyGene).gene as IntegerGene).value val expectedUUID = (genes[1] as UUIDGene).getValueAsUUID() - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) val query = "Select * from purchases where id=%s".format(idValue) @@ -106,7 +106,7 @@ class SqlUUIDColumnTest : ExtractTestBasePostgres() { val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) @@ -154,7 +154,7 @@ class SqlUUIDColumnTest : ExtractTestBasePostgres() { val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlXMLColumnTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlXMLColumnTest.kt index d5716a4400..f4adea7582 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlXMLColumnTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlXMLColumnTest.kt @@ -2,8 +2,8 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.numeric.IntegerGene @@ -50,7 +50,7 @@ class SqlXMLColumnTest : ExtractTestBasePostgres() { val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) @@ -74,14 +74,14 @@ class SqlXMLColumnTest : ExtractTestBasePostgres() { val action = actions[0] val newGene = SqlXMLGene("xmldata", ObjectGene("anElement", listOf(IntegerGene("integerElement", value = 0)))) - val newInsertAction = DbAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(newGene)) + val newInsertAction = SqlAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(newGene)) val query = "Select * from x" val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(listOf(newInsertAction)) + val dbCommandDto = SqlActionTransformer.transform(listOf(newInsertAction)) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) @@ -105,14 +105,14 @@ class SqlXMLColumnTest : ExtractTestBasePostgres() { val action = actions[0] val newGene = SqlXMLGene("xmldata", ObjectGene("anElement", listOf(BooleanGene("booleanElement", value = false)))) - val newInsertAction = DbAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(newGene)) + val newInsertAction = SqlAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(newGene)) val query = "Select * from x" val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(listOf(newInsertAction)) + val dbCommandDto = SqlActionTransformer.transform(listOf(newInsertAction)) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) @@ -136,14 +136,14 @@ class SqlXMLColumnTest : ExtractTestBasePostgres() { val action = actions[0] val newGene = SqlXMLGene("xmldata", ObjectGene("anElement", listOf(StringGene("stringElement", value = "Hello World")))) - val newInsertAction = DbAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(newGene)) + val newInsertAction = SqlAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(newGene)) val query = "Select * from x" val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(listOf(newInsertAction)) + val dbCommandDto = SqlActionTransformer.transform(listOf(newInsertAction)) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) @@ -167,14 +167,14 @@ class SqlXMLColumnTest : ExtractTestBasePostgres() { val action = actions[0] val newGene = SqlXMLGene("xmldata", ObjectGene("anElement", listOf(StringGene("stringElement", value = "This should be escaped")))) - val newInsertAction = DbAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(newGene)) + val newInsertAction = SqlAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(newGene)) val query = "Select * from x" val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(listOf(newInsertAction)) + val dbCommandDto = SqlActionTransformer.transform(listOf(newInsertAction)) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) @@ -203,14 +203,14 @@ class SqlXMLColumnTest : ExtractTestBasePostgres() { val newGene = SqlXMLGene("xmldata", parentElement) val expectedXML = newGene.getValueAsPrintableString(mode = GeneUtils.EscapeMode.XML) - val newInsertAction = DbAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(newGene)) + val newInsertAction = SqlAction(table = action.table, selectedColumns = action.selectedColumns, id = action.geInsertionId(), computedGenes = listOf(newGene)) val query = "Select * from x" val queryResultBeforeInsertion = SqlScriptRunner.execCommand(connection, query) assertTrue(queryResultBeforeInsertion.isEmpty) - val dbCommandDto = DbActionTransformer.transform(listOf(newInsertAction)) + val dbCommandDto = SqlActionTransformer.transform(listOf(newInsertAction)) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) val queryResultAfterInsertion = SqlScriptRunner.execCommand(connection, query) diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/TextSearchTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/TextSearchTypesTest.kt index b27af1eca2..e1cc16bc12 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/TextSearchTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/TextSearchTypesTest.kt @@ -3,7 +3,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.string.StringGene import org.evomaster.core.search.gene.sql.textsearch.SqlTextSearchQueryGene @@ -53,7 +53,7 @@ class TextSearchTypesTest : ExtractTestBasePostgres() { textSearchQueryElementGene0.value = "foo" textSearchQueryGene.queryLexemes.addElement(textSearchQueryElementGene0) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } @@ -76,7 +76,7 @@ class TextSearchTypesTest : ExtractTestBasePostgres() { ) ) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } @@ -118,7 +118,7 @@ class TextSearchTypesTest : ExtractTestBasePostgres() { textSearchQueryGene.queryLexemes.addElement(gene0) textSearchQueryGene.queryLexemes.addElement(gene1) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } @@ -153,7 +153,7 @@ class TextSearchTypesTest : ExtractTestBasePostgres() { val stringGene = textSearchVectorGene.getViewOfChildren()[0] as StringGene stringGene.value = " " - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UUIDTypeTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UUIDTypeTest.kt index db756c84d2..633c4c1cbd 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UUIDTypeTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UUIDTypeTest.kt @@ -3,7 +3,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.UUIDGene import org.junit.jupiter.api.Assertions.* @@ -40,7 +40,7 @@ class UUIDTypeTest : ExtractTestBasePostgres() { assertTrue(genes[0] is UUIDGene) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/XMLTypeTest.kt b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/XMLTypeTest.kt index 669749677f..dcaf3d8a9f 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/XMLTypeTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/XMLTypeTest.kt @@ -3,7 +3,7 @@ package org.evomaster.core.database.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.DbActionTransformer +import org.evomaster.core.database.SqlActionTransformer import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.search.gene.sql.SqlXMLGene import org.junit.jupiter.api.Assertions.* @@ -40,7 +40,7 @@ class XMLTypeTest : ExtractTestBasePostgres() { assertTrue(genes[0] is SqlXMLGene) - val dbCommandDto = DbActionTransformer.transform(actions) + val dbCommandDto = SqlActionTransformer.transform(actions) SqlScriptRunner.execInsert(connection, dbCommandDto.insertions) } diff --git a/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt b/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt index 0b9d88c1c0..5b599e2f35 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt @@ -3,8 +3,8 @@ package org.evomaster.core.output import org.evomaster.core.TestUtils import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.action.ActionResult -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionResult +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionResult import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.externalservice.ApiExternalServiceAction import org.evomaster.core.problem.externalservice.rpc.DbAsExternalServiceAction @@ -25,7 +25,7 @@ import org.evomaster.core.search.gene.string.StringGene class EvaluatedIndividualBuilder { companion object { - fun buildEvaluatedIndividual(dbInitialization: MutableList): Triple> { + fun buildEvaluatedIndividual(dbInitialization: MutableList): Triple> { val format = OutputFormat.JAVA_JUNIT_4 val baseUrlOfSut = "baseUrlOfSut" @@ -44,16 +44,16 @@ class EvaluatedIndividualBuilder { } fun generateIndividualResults(individual: Individual) : List = individual.seeActions(ActionFilter.ALL).map { - if (it is DbAction) DbActionResult().also { it.setInsertExecutionResult(true) } + if (it is SqlAction) SqlActionResult().also { it.setInsertExecutionResult(true) } else ActionResult() } fun buildResourceEvaluatedIndividual( - dbInitialization: MutableList, - groups: MutableList, MutableList>>, - results: List = dbInitialization.map { DbActionResult().also { it.setInsertExecutionResult(true) } }.plus( + dbInitialization: MutableList, + groups: MutableList, MutableList>>, + results: List = dbInitialization.map { SqlActionResult().also { it.setInsertExecutionResult(true) } }.plus( groups.flatMap { g-> - g.first.map { DbActionResult().also { it.setInsertExecutionResult(true) } }.plus(g.second.map { RestCallResult().also { it.setTimedout(true) } }) + g.first.map { SqlActionResult().also { it.setInsertExecutionResult(true) } }.plus(g.second.map { RestCallResult().also { it.setTimedout(true) } }) } ), format: OutputFormat = OutputFormat.JAVA_JUNIT_4 diff --git a/core/src/test/kotlin/org/evomaster/core/output/TestCaseWriterTest.kt b/core/src/test/kotlin/org/evomaster/core/output/TestCaseWriterTest.kt index d458033664..ddc1248ba4 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/TestCaseWriterTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/TestCaseWriterTest.kt @@ -3,9 +3,9 @@ package org.evomaster.core.output import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.core.EMConfig import org.evomaster.core.TestUtils -import org.evomaster.core.database.DbAction -import org.evomaster.core.database.DbActionGeneBuilder -import org.evomaster.core.database.DbActionResult +import org.evomaster.core.database.SqlAction +import org.evomaster.core.database.SqlActionGeneBuilder +import org.evomaster.core.database.SqlActionResult import org.evomaster.core.database.schema.Column import org.evomaster.core.database.schema.ColumnDataType.* import org.evomaster.core.database.schema.ForeignKey @@ -44,7 +44,7 @@ class TestCaseWriterTest { fun testEmptyDbInitialization() { - val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(emptyList().toMutableList()) + val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(emptyList().toMutableList()) val config = getConfig(format) @@ -73,7 +73,7 @@ class TestCaseWriterTest { val gene = StringGene(aColumn.name, "stringValue", 0, 12) - val insertIntoTableAction = DbAction(aTable, setOf(aColumn), id, mutableListOf(gene)) + val insertIntoTableAction = SqlAction(aTable, setOf(aColumn), id, mutableListOf(gene)) val dbInitialization = mutableListOf(insertIntoTableAction) @@ -108,7 +108,7 @@ class TestCaseWriterTest { - private fun buildEvaluatedIndividual(dbInitialization: MutableList): Triple> { + private fun buildEvaluatedIndividual(dbInitialization: MutableList): Triple> { val format = OutputFormat.JAVA_JUNIT_4 val baseUrlOfSut = "baseUrlOfSut" @@ -122,7 +122,7 @@ class TestCaseWriterTest { val fitnessVal = FitnessValue(0.0) - val results = dbInitialization.map { DbActionResult().also { it.setInsertExecutionResult(true) } } + val results = dbInitialization.map { SqlActionResult().also { it.setInsertExecutionResult(true) } } val ei = EvaluatedIndividual(fitnessVal, individual, results) return Triple(format, baseUrlOfSut, ei) @@ -137,11 +137,11 @@ class TestCaseWriterTest { val gene0 = StringGene(aColumn.name, "stringValue0", 0, 16) - val insertIntoTableAction0 = DbAction(aTable, setOf(aColumn), 0L, mutableListOf(gene0)) + val insertIntoTableAction0 = SqlAction(aTable, setOf(aColumn), 0L, mutableListOf(gene0)) val gene1 = StringGene(aColumn.name, "stringValue1", 0, 16) - val insertIntoTableAction1 = DbAction(aTable, setOf(aColumn), 1L, mutableListOf(gene1)) + val insertIntoTableAction1 = SqlAction(aTable, setOf(aColumn), 1L, mutableListOf(gene1)) val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insertIntoTableAction0, insertIntoTableAction1)) @@ -189,7 +189,7 @@ class TestCaseWriterTest { val gene0 = StringGene(column0.name, "stringValue0", 0, 16) val gene1 = StringGene(column1.name, "stringValue1", 0, 16) - val insertIntoTableAction = DbAction(aTable, setOf(column0, column1), id, mutableListOf(gene0, gene1)) + val insertIntoTableAction = SqlAction(aTable, setOf(column0, column1), id, mutableListOf(gene0, gene1)) val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insertIntoTableAction)) val config = getConfig(format) @@ -233,7 +233,7 @@ class TestCaseWriterTest { val integerGene = IntegerGene(idColumn.name, 42, 0, 50) val stringGene = StringGene(nameColumn.name, "nameValue", 0, 10) - val insertIntoTableAction = DbAction(aTable, setOf(idColumn, nameColumn), id, listOf(integerGene, stringGene)) + val insertIntoTableAction = SqlAction(aTable, setOf(idColumn, nameColumn), id, listOf(integerGene, stringGene)) val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insertIntoTableAction)) val config = getConfig(format) @@ -279,7 +279,7 @@ class TestCaseWriterTest { val primaryKeyGene = SqlPrimaryKeyGene(idColumn.name, "myTable", integerGene, 10) val stringGene = StringGene(nameColumn.name, "nameValue", 0, 10) - val insertIntoTableAction = DbAction(aTable, setOf(idColumn, nameColumn), id, listOf(primaryKeyGene, stringGene)) + val insertIntoTableAction = SqlAction(aTable, setOf(idColumn, nameColumn), id, listOf(primaryKeyGene, stringGene)) val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insertIntoTableAction)) val config = getConfig(format) @@ -328,13 +328,13 @@ class TestCaseWriterTest { val firstInsertionId = 1001L - val insertIntoTable0 = DbAction(table0, setOf(idColumn), firstInsertionId, listOf(primaryKeyTable0Gene)) + val insertIntoTable0 = SqlAction(table0, setOf(idColumn), firstInsertionId, listOf(primaryKeyTable0Gene)) val secondInsertionId = 1002L val foreignKeyGene = SqlForeignKeyGene(fkColumn.name, secondInsertionId, "Table0", false, uniqueIdOfPrimaryKey = pkGeneUniqueId) - val insertIntoTable1 = DbAction(table1, setOf(idColumn, fkColumn), secondInsertionId, listOf(primaryKeyTable1Gene, foreignKeyGene)) + val insertIntoTable1 = SqlAction(table1, setOf(idColumn, fkColumn), secondInsertionId, listOf(primaryKeyTable1Gene, foreignKeyGene)) - val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insertIntoTable0.copy() as DbAction, insertIntoTable1.copy() as DbAction)) + val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insertIntoTable0.copy() as SqlAction, insertIntoTable1.copy() as SqlAction)) val config = getConfig(format) val test = TestCase(test = ei, name = "test") @@ -379,7 +379,7 @@ class TestCaseWriterTest { val gene = BooleanGene(aColumn.name, false) - val insertIntoTableAction = DbAction(aTable, setOf(aColumn), id, mutableListOf(gene)) + val insertIntoTableAction = SqlAction(aTable, setOf(aColumn), id, mutableListOf(gene)) val dbInitialization = mutableListOf(insertIntoTableAction) @@ -427,13 +427,13 @@ class TestCaseWriterTest { val firstInsertionId = 1001L - val insertIntoTable0 = DbAction(table0, setOf(idColumn), firstInsertionId, listOf(primaryKeyTable0Gene)) + val insertIntoTable0 = SqlAction(table0, setOf(idColumn), firstInsertionId, listOf(primaryKeyTable0Gene)) val secondInsertionId = 1002L val foreignKeyGene = SqlForeignKeyGene(fkColumn.name, secondInsertionId, "Table0", true, -1L) - val insertIntoTable1 = DbAction(table1, setOf(idColumn, fkColumn), secondInsertionId, listOf(primaryKeyTable1Gene, foreignKeyGene)) + val insertIntoTable1 = SqlAction(table1, setOf(idColumn, fkColumn), secondInsertionId, listOf(primaryKeyTable1Gene, foreignKeyGene)) - val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insertIntoTable0.copy() as DbAction, insertIntoTable1.copy() as DbAction)) + val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insertIntoTable0.copy() as SqlAction, insertIntoTable1.copy() as SqlAction)) val config = getConfig(format) val test = TestCase(test = ei, name = "test") @@ -478,9 +478,9 @@ class TestCaseWriterTest { val id = 0L - val gene = DbActionGeneBuilder().buildSqlTimestampGene(aColumn.name) + val gene = SqlActionGeneBuilder().buildSqlTimestampGene(aColumn.name) - val insertIntoTableAction = DbAction(aTable, setOf(aColumn), id, mutableListOf(gene)) + val insertIntoTableAction = SqlAction(aTable, setOf(aColumn), id, mutableListOf(gene)) val dbInitialization = mutableListOf(insertIntoTableAction) @@ -522,15 +522,15 @@ class TestCaseWriterTest { val gene0 = StringGene(aColumn.name, "stringValue0", 0, 16) - val insertIntoTableAction0 = DbAction(aTable, setOf(aColumn), 0L, mutableListOf(gene0)) + val insertIntoTableAction0 = SqlAction(aTable, setOf(aColumn), 0L, mutableListOf(gene0)) val gene1 = StringGene(aColumn.name, "stringValue1", 0, 16) - val insertIntoTableAction1 = DbAction(aTable, setOf(aColumn), 1L, mutableListOf(gene1)) + val insertIntoTableAction1 = SqlAction(aTable, setOf(aColumn), 1L, mutableListOf(gene1)) val gene2 = StringGene(aColumn.name, "stringValue2", 0, 16) - val insertIntoTableAction2 = DbAction(aTable, setOf(aColumn), 2L, mutableListOf(gene2)) + val insertIntoTableAction2 = SqlAction(aTable, setOf(aColumn), 2L, mutableListOf(gene2)) val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insertIntoTableAction0, insertIntoTableAction1, insertIntoTableAction2)) val config = getConfig(format) @@ -579,19 +579,19 @@ class TestCaseWriterTest { val pkGeneUniqueId = 12345L - val timeStampGene = DbActionGeneBuilder().buildSqlTimestampGene(idColumn.name) + val timeStampGene = SqlActionGeneBuilder().buildSqlTimestampGene(idColumn.name) val primaryKeyTable0Gene = SqlPrimaryKeyGene(idColumn.name, "Table0", timeStampGene, pkGeneUniqueId) val primaryKeyTable1Gene = SqlPrimaryKeyGene(idColumn.name, "Table1", timeStampGene, 10) val firstInsertionId = 1001L - val insertIntoTable0 = DbAction(table0, setOf(idColumn), firstInsertionId, listOf(primaryKeyTable0Gene)) + val insertIntoTable0 = SqlAction(table0, setOf(idColumn), firstInsertionId, listOf(primaryKeyTable0Gene)) val secondInsertionId = 1002L val foreignKeyGene = SqlForeignKeyGene(fkColumn.name, secondInsertionId, "Table0", false, pkGeneUniqueId) - val insertIntoTable1 = DbAction(table1, setOf(idColumn, fkColumn), secondInsertionId, listOf(primaryKeyTable1Gene, foreignKeyGene)) + val insertIntoTable1 = SqlAction(table1, setOf(idColumn, fkColumn), secondInsertionId, listOf(primaryKeyTable1Gene, foreignKeyGene)) - val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insertIntoTable0.copy() as DbAction, insertIntoTable1.copy() as DbAction)) + val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insertIntoTable0.copy() as SqlAction, insertIntoTable1.copy() as SqlAction)) val config = getConfig(format) val test = TestCase(test = ei, name = "test") @@ -638,19 +638,19 @@ class TestCaseWriterTest { val insertId0 = 1001L val autoGene = SqlAutoIncrementGene(table0_Id.name) val pkGene0 = SqlPrimaryKeyGene(table0_Id.name, "Table0", autoGene, insertId0) - val insert0 = DbAction(table0, setOf(table0_Id), insertId0, listOf(pkGene0)) + val insert0 = SqlAction(table0, setOf(table0_Id), insertId0, listOf(pkGene0)) val insertId1 = 1002L val fkGene0 = SqlForeignKeyGene(table1_Id.name, insertId1, "Table0", false, insertId0) val pkGene1 = SqlPrimaryKeyGene(table1_Id.name, "Table1", fkGene0, insertId1) - val insert1 = DbAction(table1, setOf(table1_Id), insertId1, listOf(pkGene1)) + val insert1 = SqlAction(table1, setOf(table1_Id), insertId1, listOf(pkGene1)) val insertId2 = 1003L val fkGene1 = SqlForeignKeyGene(table2_Id.name, insertId2, "Table1", false, insertId1) val pkGene2 = SqlPrimaryKeyGene(table2_Id.name, "Table2", fkGene1, insertId2) - val insert2 = DbAction(table2, setOf(table2_Id), insertId2, listOf(pkGene2)) + val insert2 = SqlAction(table2, setOf(table2_Id), insertId2, listOf(pkGene2)) val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insert0, insert1, insert2)) val config = getConfig(format) @@ -696,7 +696,7 @@ class TestCaseWriterTest { val autoGene = SqlAutoIncrementGene(table.name) val pkGene0 = SqlPrimaryKeyGene(idColumn.name, "Table0", autoGene, 10) val dateGene = DateGene(dateColumn.name) - val insert = DbAction(table, setOf(idColumn, dateColumn), 0L, listOf(pkGene0, dateGene)) + val insert = SqlAction(table, setOf(idColumn, dateColumn), 0L, listOf(pkGene0, dateGene)) val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insert)) val config = getConfig(format) @@ -739,7 +739,7 @@ class TestCaseWriterTest { val autoGene = SqlAutoIncrementGene(table.name) val pkGene0 = SqlPrimaryKeyGene(idColumn.name, "Table0", autoGene, 10) val uuidGene = UUIDGene(uuidColumn.name) - val insert = DbAction(table, setOf(idColumn, uuidColumn), 0L, listOf(pkGene0, uuidGene)) + val insert = SqlAction(table, setOf(idColumn, uuidColumn), 0L, listOf(pkGene0, uuidGene)) val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insert)) val config = getConfig(format) @@ -782,7 +782,7 @@ class TestCaseWriterTest { val autoGene = SqlAutoIncrementGene(table.name) val pkGene0 = SqlPrimaryKeyGene(idColumn.name, "Table0", autoGene, 10) val objectGene = ObjectGene(jsonbColumn.name, listOf()) - val insert = DbAction(table, setOf(idColumn, jsonbColumn), 0L, listOf(pkGene0, objectGene)) + val insert = SqlAction(table, setOf(idColumn, jsonbColumn), 0L, listOf(pkGene0, objectGene)) val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insert)) val config = getConfig(format) @@ -826,7 +826,7 @@ class TestCaseWriterTest { val autoGene = SqlAutoIncrementGene(table.name) val pkGene0 = SqlPrimaryKeyGene(idColumn.name, "Table0", autoGene, 10) val objectGene = ObjectGene(jsonbColumn.name, listOf(IntegerGene("integerField"))) - val insert = DbAction(table, setOf(idColumn, jsonbColumn), 0L, listOf(pkGene0, objectGene)) + val insert = SqlAction(table, setOf(idColumn, jsonbColumn), 0L, listOf(pkGene0, objectGene)) val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insert)) val config = getConfig(format) @@ -868,7 +868,7 @@ class TestCaseWriterTest { val autoGene = SqlAutoIncrementGene(table.name) val pkGene0 = SqlPrimaryKeyGene(idColumn.name, "Table0", autoGene, 10) val objectGene = ObjectGene(jsonbColumn.name, listOf(BooleanGene("booleanField"))) - val insert = DbAction(table, setOf(idColumn, jsonbColumn), 0L, listOf(pkGene0, objectGene)) + val insert = SqlAction(table, setOf(idColumn, jsonbColumn), 0L, listOf(pkGene0, objectGene)) val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insert)) val config = getConfig(format) @@ -910,7 +910,7 @@ class TestCaseWriterTest { val autoGene = SqlAutoIncrementGene(table.name) val pkGene0 = SqlPrimaryKeyGene(idColumn.name, "Table0", autoGene, 10) val objectGene = ObjectGene(jsonbColumn.name, listOf(StringGene("stringField"))) - val insert = DbAction(table, setOf(idColumn, jsonbColumn), 0L, listOf(pkGene0, objectGene)) + val insert = SqlAction(table, setOf(idColumn, jsonbColumn), 0L, listOf(pkGene0, objectGene)) val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insert)) val config = getConfig(format) @@ -1029,10 +1029,10 @@ class TestCaseWriterTest { val pkFoo = SqlPrimaryKeyGene(fooId.name, "Foo", integerGene, pkGeneUniqueId) val pkBar = SqlPrimaryKeyGene(fooId.name, "Bar", integerGene, 10) val fooInsertionId = 1001L - val fooInsertion = DbAction(foo, setOf(fooId), fooInsertionId, listOf(pkFoo)) + val fooInsertion = SqlAction(foo, setOf(fooId), fooInsertionId, listOf(pkFoo)) val barInsertionId = 1002L val foreignKeyGene = SqlForeignKeyGene(fkId.name, barInsertionId, "Foo", false, uniqueIdOfPrimaryKey = pkGeneUniqueId) - val barInsertion = DbAction(bar, setOf(fooId, fkId), barInsertionId, listOf(pkBar, foreignKeyGene)) + val barInsertion = SqlAction(bar, setOf(fooId, fkId), barInsertionId, listOf(pkBar, foreignKeyGene)) val fooAction = RestCallAction("1", HttpVerb.GET, RestPath("/foo"), mutableListOf()) val barAction = RestCallAction("2", HttpVerb.GET, RestPath("/bar"), mutableListOf()) @@ -1040,8 +1040,8 @@ class TestCaseWriterTest { val (format, baseUrlOfSut, ei) = buildResourceEvaluatedIndividual( dbInitialization = mutableListOf(), groups = mutableListOf( - (mutableListOf(fooInsertion.copy() as DbAction) to mutableListOf(fooAction.copy() as RestCallAction)), - (mutableListOf(barInsertion.copy() as DbAction) to mutableListOf(barAction.copy() as RestCallAction)) + (mutableListOf(fooInsertion.copy() as SqlAction) to mutableListOf(fooAction.copy() as RestCallAction)), + (mutableListOf(barInsertion.copy() as SqlAction) to mutableListOf(barAction.copy() as RestCallAction)) ) ) @@ -1101,17 +1101,17 @@ public void test() throws Exception { val pkFoo = SqlPrimaryKeyGene(fooId.name, "Foo", integerGene, pkGeneUniqueId) val pkBar = SqlPrimaryKeyGene(fooId.name, "Bar", integerGene, 10) val fooInsertionId = 1001L - val fooInsertion = DbAction(foo, setOf(fooId), fooInsertionId, listOf(pkFoo)) + val fooInsertion = SqlAction(foo, setOf(fooId), fooInsertionId, listOf(pkFoo)) val barInsertionId = 1002L val foreignKeyGene = SqlForeignKeyGene(fkId.name, barInsertionId, "Foo", false, uniqueIdOfPrimaryKey = pkGeneUniqueId) - val barInsertion = DbAction(bar, setOf(fooId, fkId), barInsertionId, listOf(pkBar, foreignKeyGene)) + val barInsertion = SqlAction(bar, setOf(fooId, fkId), barInsertionId, listOf(pkBar, foreignKeyGene)) val fooAction = RestCallAction("1", HttpVerb.GET, RestPath("/foo"), mutableListOf()) val barAction = RestCallAction("2", HttpVerb.GET, RestPath("/bar"), mutableListOf()) val groups = mutableListOf( - (mutableListOf(fooInsertion.copy() as DbAction) to mutableListOf(fooAction.copy() as RestCallAction)), - (mutableListOf(barInsertion.copy() as DbAction) to mutableListOf(barAction.copy() as RestCallAction)) + (mutableListOf(fooInsertion.copy() as SqlAction) to mutableListOf(fooAction.copy() as RestCallAction)), + (mutableListOf(barInsertion.copy() as SqlAction) to mutableListOf(barAction.copy() as RestCallAction)) ) val (format, baseUrlOfSut, ei) = buildResourceEvaluatedIndividual( @@ -1121,8 +1121,8 @@ public void test() throws Exception { val fooInsertionResult = ei.seeResults(groups[0].first) assertEquals(1, fooInsertionResult.size) - assertTrue(fooInsertionResult[0] is DbActionResult) - (fooInsertionResult[0] as DbActionResult).setInsertExecutionResult(false) + assertTrue(fooInsertionResult[0] is SqlActionResult) + (fooInsertionResult[0] as SqlActionResult).setInsertExecutionResult(false) val config = getConfig(format) config.resourceSampleStrategy = EMConfig.ResourceSamplingStrategy.ConArchive @@ -1170,8 +1170,8 @@ public void test() throws Exception { val (format, baseUrlOfSut, ei) = buildResourceEvaluatedIndividual( dbInitialization = mutableListOf(), groups = mutableListOf( - (mutableListOf() to mutableListOf(fooAction)), - (mutableListOf() to mutableListOf(barAction)) + (mutableListOf() to mutableListOf(fooAction)), + (mutableListOf() to mutableListOf(barAction)) ) ) @@ -1239,7 +1239,7 @@ public void test() throws Exception { val (format, baseUrlOfSut, ei) = buildResourceEvaluatedIndividual( dbInitialization = mutableListOf(), groups = mutableListOf( - (mutableListOf() to mutableListOf(fooAction)) + (mutableListOf() to mutableListOf(fooAction)) ), results = mutableListOf(fooResult), format = OutputFormat.JS_JEST @@ -1312,7 +1312,7 @@ public void test() throws Exception { val (format, baseUrlOfSut, ei) = buildResourceEvaluatedIndividual( dbInitialization = mutableListOf(), groups = mutableListOf( - (mutableListOf() to mutableListOf(fooAction)) + (mutableListOf() to mutableListOf(fooAction)) ), results = mutableListOf(fooResult), format = OutputFormat.JS_JEST @@ -1368,7 +1368,7 @@ public void test() throws Exception { val (format, baseUrlOfSut, ei) = buildResourceEvaluatedIndividual( dbInitialization = mutableListOf(), groups = mutableListOf( - (mutableListOf() to mutableListOf(fooAction)) + (mutableListOf() to mutableListOf(fooAction)) ), results = mutableListOf(fooResult), format = OutputFormat.JS_JEST diff --git a/core/src/test/kotlin/org/evomaster/core/output/WriteJsonTest.kt b/core/src/test/kotlin/org/evomaster/core/output/WriteJsonTest.kt index 356d98ef54..6b6e51e0e0 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/WriteJsonTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/WriteJsonTest.kt @@ -2,7 +2,7 @@ package org.evomaster.core.output import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.core.EMConfig -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.database.schema.Column import org.evomaster.core.database.schema.ColumnDataType import org.evomaster.core.database.schema.ForeignKey @@ -44,7 +44,7 @@ class WriteJsonTest { val autoGene = SqlAutoIncrementGene(table.name) val pkGene0 = SqlPrimaryKeyGene(idColumn.name, "Table0", autoGene, 10) val objectGene = ObjectGene(jsonbColumn.name, listOf()) - val insert = DbAction(table, setOf(idColumn, jsonbColumn), 0L, listOf(pkGene0, objectGene)) + val insert = SqlAction(table, setOf(idColumn, jsonbColumn), 0L, listOf(pkGene0, objectGene)) val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insert)) val config = getConfig(format) @@ -90,7 +90,7 @@ class WriteJsonTest { val autoGene = SqlAutoIncrementGene(table.name) val pkGene0 = SqlPrimaryKeyGene(idColumn.name, "Table0", autoGene, 10) val objectGene = ObjectGene(jsonbColumn.name, listOf(IntegerGene("integerField"))) - val insert = DbAction(table, setOf(idColumn, jsonbColumn), 0L, listOf(pkGene0, objectGene)) + val insert = SqlAction(table, setOf(idColumn, jsonbColumn), 0L, listOf(pkGene0, objectGene)) val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insert)) val config = getConfig(format) @@ -134,7 +134,7 @@ class WriteJsonTest { val autoGene = SqlAutoIncrementGene(table.name) val pkGene0 = SqlPrimaryKeyGene(idColumn.name, "Table0", autoGene, 10) val objectGene = ObjectGene(jsonbColumn.name, listOf(BooleanGene("booleanField"))) - val insert = DbAction(table, setOf(idColumn, jsonbColumn), 0L, listOf(pkGene0, objectGene)) + val insert = SqlAction(table, setOf(idColumn, jsonbColumn), 0L, listOf(pkGene0, objectGene)) val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insert)) val config = getConfig(format) @@ -178,7 +178,7 @@ class WriteJsonTest { val autoGene = SqlAutoIncrementGene(table.name) val pkGene0 = SqlPrimaryKeyGene(idColumn.name, "Table0", autoGene, 10) val objectGene = ObjectGene(jsonbColumn.name, listOf(StringGene("stringField"))) - val insert = DbAction(table, setOf(idColumn, jsonbColumn), 0L, listOf(pkGene0, objectGene)) + val insert = SqlAction(table, setOf(idColumn, jsonbColumn), 0L, listOf(pkGene0, objectGene)) val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insert)) val config = getConfig(format) diff --git a/core/src/test/kotlin/org/evomaster/core/output/WriteXMLTest.kt b/core/src/test/kotlin/org/evomaster/core/output/WriteXMLTest.kt index 15164223e7..e906032ead 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/WriteXMLTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/WriteXMLTest.kt @@ -2,7 +2,7 @@ package org.evomaster.core.output import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.core.EMConfig -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.database.schema.Column import org.evomaster.core.database.schema.ColumnDataType import org.evomaster.core.database.schema.Table @@ -35,7 +35,7 @@ class WriteXMLTest { val objectGene = ObjectGene("anElement", listOf()) val sqlXMLGene = SqlXMLGene("xmlColumn", objectGene) - val insert = DbAction(table, setOf(xmlColumn), 0L, listOf(sqlXMLGene)) + val insert = SqlAction(table, setOf(xmlColumn), 0L, listOf(sqlXMLGene)) val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insert)) val config = getConfig(format) @@ -82,7 +82,7 @@ class WriteXMLTest { val objectGene = ObjectGene("anElement", listOf(child0, child1, child2, child3)) val sqlXMLGene = SqlXMLGene("xmlColumn", objectGene) - val insert = DbAction(table, setOf(xmlColumn), 0L, listOf(sqlXMLGene)) + val insert = SqlAction(table, setOf(xmlColumn), 0L, listOf(sqlXMLGene)) val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insert)) val config = getConfig(format) @@ -125,7 +125,7 @@ class WriteXMLTest { val objectGene = ObjectGene("anElement", listOf(stringGene)) val sqlXMLGene = SqlXMLGene("xmlColumn", objectGene) - val insert = DbAction(table, setOf(xmlColumn), 0L, listOf(sqlXMLGene)) + val insert = SqlAction(table, setOf(xmlColumn), 0L, listOf(sqlXMLGene)) val (format, baseUrlOfSut, ei) = buildEvaluatedIndividual(mutableListOf(insert)) val config = getConfig(format) diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualResourceTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualResourceTest.kt index 85d4a807e0..a8ef0a352e 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualResourceTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualResourceTest.kt @@ -1,7 +1,7 @@ package org.evomaster.core.problem.rest.individual import com.google.inject.* -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.rest.service.* import org.evomaster.core.search.action.ActionFilter @@ -80,7 +80,7 @@ class RestIndividualResourceTest : RestIndividualTestBase() { val existingData = mutatedImpact.getSQLExistingData() assertEquals( existingData, - mutated.individual.seeInitializingActions().filterIsInstance().count { it.representExistingData }) + mutated.individual.seeInitializingActions().filterIsInstance().count { it.representExistingData }) val currentInit = mutatedImpact.initActionImpacts.getOriginalSize(includeExistingSQLData = true) @@ -90,7 +90,7 @@ class RestIndividualResourceTest : RestIndividualTestBase() { assertEquals(mutatedInit.size, currentInit) // check whether impact info is consistent with individual after mutation - mutated.individual.seeInitializingActions().filterIsInstance().forEachIndexed { index, dbAction -> + mutated.individual.seeInitializingActions().filterIsInstance().forEachIndexed { index, dbAction -> if (!dbAction.representExistingData) { val impact = mutatedImpact.initActionImpacts.getImpactOfAction(dbAction.getName(), index) assertNotNull(impact) @@ -118,7 +118,7 @@ class RestIndividualResourceTest : RestIndividualTestBase() { var improved = 0 var anyMutated = 0 mutated.individual.seeActions(ActionFilter.ALL).forEachIndexed { index, action -> - if (action !is DbAction || !action.representExistingData) { + if (action !is SqlAction || !action.representExistingData) { action.seeTopGenes().filter { it.isMutable() }.forEach { g -> val impactId = ImpactUtils.generateGeneId(mutated.individual, g) val fromInit = mutatedInit.contains(action) diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualTestBase.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualTestBase.kt index d3fb2a3c76..0013bf8614 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualTestBase.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualTestBase.kt @@ -25,7 +25,7 @@ import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor import org.evomaster.core.BaseModule import org.evomaster.core.EMConfig -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.database.SqlInsertBuilder import org.evomaster.core.database.schema.ColumnDataType import org.evomaster.core.problem.rest.RestCallAction @@ -45,7 +45,6 @@ import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Disabled import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource @@ -190,7 +189,7 @@ abstract class RestIndividualTestBase { if (ind.seeDbActions().isNotEmpty()){ // all db actions should be before rest actions ind.getResourceCalls().forEach { r-> - val dbIndexes = r.getIndexedChildren(DbAction::class.java).keys + val dbIndexes = r.getIndexedChildren(SqlAction::class.java).keys val restIndexes = r.getIndexedChildren(RestCallAction::class.java).keys assertTrue(restIndexes.all { it > (dbIndexes.maxOrNull() ?: -1) @@ -231,7 +230,7 @@ abstract class RestIndividualTestBase { private fun checkActionIndex(mutated: RestIndividual){ assertEquals(0, mutated.getIndexedChildren(RestCallAction::class.java).size) - val indexedDb = mutated.getIndexedChildren(DbAction::class.java) + val indexedDb = mutated.getIndexedChildren(SqlAction::class.java) val indexedRest = mutated.getIndexedChildren(RestResourceCalls::class.java) val dbBeforeRest = indexedRest.keys.all { it > (indexedDb.keys.maxOrNull() ?: -1) } diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt index 9215148f66..0ad3086839 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt @@ -1,7 +1,7 @@ package org.evomaster.core.problem.rest.individual import com.google.inject.* -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.database.schema.Table import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.enterprise.SampleType @@ -82,9 +82,9 @@ class RestResourceIndividualDisabledHMTest : RestIndividualTestBase(){ override fun getFitnessFunction(): AbstractRestFitness = ff - private fun sampleDbAction(table : Table) : List{ + private fun sampleDbAction(table : Table) : List{ val actions = sqlInsertBuilder!!.createSqlInsertionAction(table.name) - return actions.map { it.copy() as DbAction } + return actions.map { it.copy() as SqlAction } } private fun sampleResourceCall(resNode: RestResourceNode? = null): RestResourceCalls { @@ -100,19 +100,19 @@ class RestResourceIndividualDisabledHMTest : RestIndividualTestBase(){ } private fun sampleRestIndividual(dbSize : Int, resourceSize: Int): RestIndividual{ - val dbActions = mutableListOf() + val sqlActions = mutableListOf() val resoureCalls = mutableListOf() do { val table = randomness.choose(cluster.getTableInfo().values) - dbActions.addAll(sampleDbAction(table)) - }while (dbActions.size < dbSize) + sqlActions.addAll(sampleDbAction(table)) + }while (sqlActions.size < dbSize) do { val node = randomness.choose(cluster.getCluster().values) resoureCalls.add(sampleResourceCall(node)) }while (resoureCalls.size < resourceSize) - return RestIndividual(dbInitialization = dbActions, resourceCalls = resoureCalls, sampleType = SampleType.RANDOM) + return RestIndividual(dbInitialization = sqlActions, resourceCalls = resoureCalls, sampleType = SampleType.RANDOM) } /** @@ -272,7 +272,7 @@ class RestResourceIndividualDisabledHMTest : RestIndividualTestBase(){ if (dbActions.isNotEmpty()){ val g = ParamUtil.getValueGene(nosql[0]) if (g !is DateGene - || dbActions.any { (it as? DbAction)?.representExistingData == false } // DateGene cannot bind with ImmutableDataHolderGene now + || dbActions.any { (it as? SqlAction)?.representExistingData == false } // DateGene cannot bind with ImmutableDataHolderGene now ){ // rest gene should be bound with at least one sql gene assertTrue(g.isBoundGene()) diff --git a/core/src/test/kotlin/org/evomaster/core/search/gene/FlatViewTest.kt b/core/src/test/kotlin/org/evomaster/core/search/gene/FlatViewTest.kt index 4a8561135d..73385325ee 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/gene/FlatViewTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/gene/FlatViewTest.kt @@ -1,6 +1,6 @@ package org.evomaster.core.search.gene -import org.evomaster.core.database.DbActionGeneBuilder +import org.evomaster.core.database.SqlActionGeneBuilder import org.evomaster.core.search.gene.datetime.DateGene import org.evomaster.core.search.gene.numeric.FloatGene import org.evomaster.core.search.gene.numeric.IntegerGene @@ -21,7 +21,7 @@ class FlatViewTest { @Test fun testExcludeTimeStampGene() { - val gene = DbActionGeneBuilder().buildSqlTimestampGene("gene") + val gene = SqlActionGeneBuilder().buildSqlTimestampGene("gene") assertEquals(1, gene.flatView { true }.size) } diff --git a/core/src/test/kotlin/org/evomaster/core/search/gene/GeneUtilsTest.kt b/core/src/test/kotlin/org/evomaster/core/search/gene/GeneUtilsTest.kt index 455ee9ea92..45f9761626 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/gene/GeneUtilsTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/gene/GeneUtilsTest.kt @@ -1,6 +1,6 @@ package org.evomaster.core.search.gene -import org.evomaster.core.database.DbActionGeneBuilder +import org.evomaster.core.database.SqlActionGeneBuilder import org.evomaster.core.problem.graphql.GqlConst import org.evomaster.core.problem.graphql.GraphQLAction import org.evomaster.core.problem.graphql.builder.GraphQLActionBuilder @@ -13,7 +13,6 @@ import org.evomaster.core.search.gene.datetime.DateGene import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.optional.OptionalGene import org.evomaster.core.search.gene.placeholder.CycleObjectGene -import org.evomaster.core.search.gene.placeholder.LimitObjectGene import org.evomaster.core.search.gene.string.StringGene import org.evomaster.core.search.gene.utils.GeneUtils import org.junit.jupiter.api.Assertions.* @@ -61,7 +60,7 @@ internal class GeneUtilsTest { @Test fun testRepairBrokenSqlTimestampGene() { - val sqlTimestampGene = DbActionGeneBuilder().buildSqlTimestampGene("timestamp") + val sqlTimestampGene = SqlActionGeneBuilder().buildSqlTimestampGene("timestamp") sqlTimestampGene.date.apply { year.value = 1998 month.value = 4 @@ -91,7 +90,7 @@ internal class GeneUtilsTest { @Test fun testFlatViewWithExcludeDateGene() { - val sqlTimestampGene = DbActionGeneBuilder().buildSqlTimestampGene("timestamp") + val sqlTimestampGene = SqlActionGeneBuilder().buildSqlTimestampGene("timestamp") sqlTimestampGene.date.apply { year.value = 1998 month.value = 4 diff --git a/core/src/test/kotlin/org/evomaster/core/search/gene/binding/BindingBuildTest.kt b/core/src/test/kotlin/org/evomaster/core/search/gene/binding/BindingBuildTest.kt index 2d677ff41d..1af31c75d7 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/gene/binding/BindingBuildTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/gene/binding/BindingBuildTest.kt @@ -1,7 +1,7 @@ package org.evomaster.core.search.gene.binding import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.database.schema.Column import org.evomaster.core.database.schema.ColumnDataType import org.evomaster.core.database.schema.Table @@ -64,7 +64,7 @@ class BindingBuildTest { val c3 = Column("intValue", ColumnDataType.INT4, databaseType = DatabaseType.H2) val c4 = Column("floatValue", ColumnDataType.REAL, databaseType = DatabaseType.H2) val table = Table("foo", columns = setOf(c1,c2,c3,c4), foreignKeys = setOf()) - val dbAction = DbAction(table, setOf(c1,c2,c3,c4), 0L, representExistingData = false) + val sqlAction = SqlAction(table, setOf(c1,c2,c3,c4), 0L, representExistingData = false) val ancestorPath = RestPath("/api/foo") val f1 = LongGene("id", 1L) diff --git a/core/src/test/kotlin/org/evomaster/core/search/gene/sql/SqlTimestampGeneTest.kt b/core/src/test/kotlin/org/evomaster/core/search/gene/sql/SqlTimestampGeneTest.kt index 740e6f8421..f85401b482 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/gene/sql/SqlTimestampGeneTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/gene/sql/SqlTimestampGeneTest.kt @@ -1,6 +1,6 @@ package org.evomaster.core.search.gene.sql -import org.evomaster.core.database.DbActionGeneBuilder +import org.evomaster.core.database.SqlActionGeneBuilder import org.evomaster.core.search.service.Randomness import org.junit.jupiter.api.Test import java.time.LocalDate @@ -15,7 +15,7 @@ class SqlTimestampGeneTest { @Test fun testValidLocalDateTime() { - val gene = DbActionGeneBuilder().buildSqlTimestampGene("timestamp") + val gene = SqlActionGeneBuilder().buildSqlTimestampGene("timestamp") val rawString = gene.getValueAsRawString() val formatter = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT_PATTERN) LocalDateTime.parse(rawString, formatter) @@ -23,7 +23,7 @@ class SqlTimestampGeneTest { @Test fun testValidDate() { - val gene = DbActionGeneBuilder().buildSqlTimestampGene("timestamp") + val gene = SqlActionGeneBuilder().buildSqlTimestampGene("timestamp") val rawString = gene.date.getValueAsRawString() val formatter = DateTimeFormatter.ISO_LOCAL_DATE LocalDate.parse(rawString, formatter) @@ -34,7 +34,7 @@ class SqlTimestampGeneTest { val randomness = Randomness() - val gene = DbActionGeneBuilder().buildSqlTimestampGene("timestamp") + val gene = SqlActionGeneBuilder().buildSqlTimestampGene("timestamp") for (i in 1..10000) { gene.randomize(randomness, tryToForceNewValue = true) diff --git a/core/src/test/kotlin/org/evomaster/core/search/impact/IndividualImpactTest.kt b/core/src/test/kotlin/org/evomaster/core/search/impact/IndividualImpactTest.kt index 2066e6cb0f..897dc0ed65 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/impact/IndividualImpactTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/impact/IndividualImpactTest.kt @@ -1,7 +1,7 @@ package org.evomaster.core.search.impact import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.database.schema.Column import org.evomaster.core.database.schema.ColumnDataType import org.evomaster.core.database.schema.Table @@ -21,12 +21,12 @@ import org.junit.jupiter.api.Test */ class IndividualImpactTest { - private fun generateFakeDbAction(pkId : Long, pkGeneUniqueId: Long) : DbAction{ + private fun generateFakeDbAction(pkId : Long, pkGeneUniqueId: Long) : SqlAction{ val fooId = Column("Id", ColumnDataType.INTEGER, 10, primaryKey = true, databaseType = DatabaseType.H2) val foo = Table("Foo", setOf(fooId), setOf()) val integerGene = IntegerGene(fooId.name) val pkFoo = SqlPrimaryKeyGene(fooId.name, "Foo", integerGene, pkGeneUniqueId) - return DbAction(foo, setOf(fooId), pkId, listOf(pkFoo)) + return SqlAction(foo, setOf(fooId), pkId, listOf(pkFoo)) } private fun generateFakeRestAction(id: String) : RestCallAction{ @@ -48,7 +48,7 @@ class IndividualImpactTest { val fkId = Column("fkId", ColumnDataType.INTEGER, 10, primaryKey = false, databaseType = DatabaseType.H2) val foreignKeyGene = SqlForeignKeyGene(fkId.name, barInsertionId, "Foo", false, uniqueIdOfPrimaryKey = pkGeneUniqueId) val bar = Table("Bar", setOf(fooId, fkId), setOf()) - val barInsertion = DbAction(bar, setOf(fooId, fkId), barInsertionId, listOf(pkBar, foreignKeyGene)) + val barInsertion = SqlAction(bar, setOf(fooId, fkId), barInsertionId, listOf(pkBar, foreignKeyGene)) val queryIdParam = QueryParam("id", IntegerGene("id")) diff --git a/core/src/test/kotlin/org/evomaster/core/search/mutationweight/GeneWeightTestSchema.kt b/core/src/test/kotlin/org/evomaster/core/search/mutationweight/GeneWeightTestSchema.kt index be8549e6be..f9d967001d 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/mutationweight/GeneWeightTestSchema.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/mutationweight/GeneWeightTestSchema.kt @@ -3,7 +3,7 @@ package org.evomaster.core.search.mutationweight import io.swagger.parser.OpenAPIParser import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.core.EMConfig -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.database.schema.Column import org.evomaster.core.database.schema.ColumnDataType import org.evomaster.core.database.schema.Table @@ -48,14 +48,14 @@ object GeneWeightTestSchema { val gk0 = SqlPrimaryKeyGene(key.name, table.name, IntegerGene(key.name, 1), 1) val gd0 = DateGene(date.name) val gj0 = ObjectGene(info.name, listOf(DateGene("field1"), IntegerGene("field2", 2))) - val action0 = DbAction(table, setOf(key, date, info), 0L, listOf(gk0, gd0, gj0)) + val action0 = SqlAction(table, setOf(key, date, info), 0L, listOf(gk0, gd0, gj0)) - val dbActions = (0 until numSQLAction).map { action0.copy() as DbAction }.toMutableList() + val sqlActions = (0 until numSQLAction).map { action0.copy() as SqlAction }.toMutableList() val action1 = actions[name]?: throw IllegalArgumentException("$name cannot found in defined schema") // 1 dbaction with 3 genes, and 1 restaction with 1 bodyGene and 1 contentType - return RestIndividual(actions = (0 until numRestAction).map { action1.copy() as RestCallAction }.toMutableList(), dbInitialization = dbActions, sampleType = SampleType.RANDOM) + return RestIndividual(actions = (0 until numRestAction).map { action1.copy() as RestCallAction }.toMutableList(), dbInitialization = sqlActions, sampleType = SampleType.RANDOM) } } diff --git a/core/src/test/kotlin/org/evomaster/core/search/structuralelement/action/DbActionStructureTest.kt b/core/src/test/kotlin/org/evomaster/core/search/structuralelement/action/DbActionStructureTest.kt index ec56df969a..3dbc473f3e 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/structuralelement/action/DbActionStructureTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/structuralelement/action/DbActionStructureTest.kt @@ -1,7 +1,7 @@ package org.evomaster.core.search.structuralelement.action import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.database.schema.Column import org.evomaster.core.database.schema.ColumnDataType import org.evomaster.core.database.schema.Table @@ -12,7 +12,7 @@ import org.evomaster.core.search.gene.sql.SqlPrimaryKeyGene import org.evomaster.core.search.structuralelement.StructuralElementBaseTest class DbActionStructureTest: StructuralElementBaseTest(){ - override fun getStructuralElement(): DbAction { + override fun getStructuralElement(): SqlAction { val key = Column("key", ColumnDataType.INTEGER, 10, primaryKey = true, databaseType = DatabaseType.H2) @@ -23,7 +23,7 @@ class DbActionStructureTest: StructuralElementBaseTest(){ val gk0 = SqlPrimaryKeyGene(key.name, table.name, IntegerGene(key.name, 1), 1) val gd0 = DateGene(date.name) val gj0 = ObjectGene(info.name, listOf(DateGene("field1"), IntegerGene("field2", 2))) - return DbAction(table, setOf(key, date, info), 0L, listOf(gk0, gd0, gj0)) + return SqlAction(table, setOf(key, date, info), 0L, listOf(gk0, gd0, gj0)) } diff --git a/core/src/test/kotlin/org/evomaster/core/search/structuralelement/individual/RestIndividualStructureTest.kt b/core/src/test/kotlin/org/evomaster/core/search/structuralelement/individual/RestIndividualStructureTest.kt index 169ec44cce..412eebc9d6 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/structuralelement/individual/RestIndividualStructureTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/structuralelement/individual/RestIndividualStructureTest.kt @@ -1,7 +1,7 @@ package org.evomaster.core.search.structuralelement.individual import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType -import org.evomaster.core.database.DbAction +import org.evomaster.core.database.SqlAction import org.evomaster.core.database.schema.Column import org.evomaster.core.database.schema.ColumnDataType import org.evomaster.core.database.schema.ForeignKey @@ -48,13 +48,13 @@ class RestIndividualStructureTest : StructuralElementBaseTest(){ val insertId0 = 1001L val pkGeneFoo = SqlPrimaryKeyGene("Id", "Foo", IntegerGene("Id", 1, 0, 10), insertId0) - val action0 = DbAction(foo, setOf(idColumn), insertId0, listOf(pkGeneFoo)) + val action0 = SqlAction(foo, setOf(idColumn), insertId0, listOf(pkGeneFoo)) val insertId1 = 1002L val pkGeneBar = SqlPrimaryKeyGene("Id", "Bar", IntegerGene("Id", 2, 0, 10), insertId0) val fkGene = SqlForeignKeyGene("fooId", insertId1, "Foo", false, insertId0) - val action1 = DbAction(bar, setOf(barIdColumn, fkColumn), insertId1, listOf(pkGeneBar, fkGene)) + val action1 = SqlAction(bar, setOf(barIdColumn, fkColumn), insertId1, listOf(pkGeneBar, fkGene)) val fooNode = ResourceNodeCluster.cluster.getResourceNode("/v3/api/rfoo/{rfooId}") diff --git a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/db/directintwithsql/DbDirectIntWithSqlEMTest.java b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/db/directintwithsql/DbDirectIntWithSqlEMTest.java index ef20926a4d..0c65d231ed 100644 --- a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/db/directintwithsql/DbDirectIntWithSqlEMTest.java +++ b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/db/directintwithsql/DbDirectIntWithSqlEMTest.java @@ -5,7 +5,7 @@ import com.google.inject.TypeLiteral; import org.evomaster.core.EMConfig; import org.evomaster.core.Main; -import org.evomaster.core.database.DbAction; +import org.evomaster.core.database.SqlAction; import org.evomaster.core.problem.rest.HttpVerb; import org.evomaster.core.problem.rest.RestCallAction; import org.evomaster.core.problem.rest.RestCallResult; @@ -139,7 +139,7 @@ public void testSteps() { //now, try to execute an action in which as well we add SQL data - List insertions = sampler.sampleSqlInsertion("DB_DIRECT_INT_ENTITY", Collections.singleton("*")); + List insertions = sampler.sampleSqlInsertion("DB_DIRECT_INT_ENTITY", Collections.singleton("*")); assertEquals(1, insertions.size()); //extract the x/y values from the random call diff --git a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java index d01fb3a9e5..403b93acbb 100644 --- a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java +++ b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java @@ -1,7 +1,7 @@ package org.evomaster.e2etests.spring.examples.resource.db; import com.google.inject.Injector; -import org.evomaster.core.database.DbAction; +import org.evomaster.core.database.SqlAction; import org.evomaster.core.problem.rest.resource.RestResourceCalls; import org.evomaster.core.problem.rest.resource.RestResourceNode; import org.evomaster.core.problem.rest.service.ResourceManageService; @@ -60,7 +60,7 @@ public void testResourceSamplerAndParamBinding() { calls.clear(); rmanger.sampleCall(raIdKey, true, calls, 10, true, Collections.emptyList(), "GET"); assertEquals(2, calls.get(0).seeActions(ActionFilter.ALL).size()); - assert( calls.get(0).seeActions(ActionFilter.ALL).get(0) instanceof DbAction); + assert( calls.get(0).seeActions(ActionFilter.ALL).get(0) instanceof SqlAction); assertEquals(3, calls.get(0).seeActions(ActionFilter.ALL).get(0).seeTopGenes().size()); //check whether the gene binding with rest action is removed with seeGenes(GeneFilter.ONLY_SQL) assertEquals(2, calls.get(0).seeGenes(GeneFilter.ONLY_SQL).size()); diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/db/directintwithsql/DbDirectIntWithSqlEMTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/db/directintwithsql/DbDirectIntWithSqlEMTest.java index fe9a7bbd21..2790503e84 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/db/directintwithsql/DbDirectIntWithSqlEMTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/db/directintwithsql/DbDirectIntWithSqlEMTest.java @@ -5,7 +5,7 @@ import com.google.inject.TypeLiteral; import org.evomaster.core.EMConfig; import org.evomaster.core.Main; -import org.evomaster.core.database.DbAction; +import org.evomaster.core.database.SqlAction; import org.evomaster.core.problem.rest.HttpVerb; import org.evomaster.core.problem.rest.RestCallAction; import org.evomaster.core.problem.rest.RestCallResult; @@ -140,7 +140,7 @@ public void testSteps() { //now, try to execute an action in which as well we add SQL data - List insertions = sampler.sampleSqlInsertion("DB_DIRECT_INT_ENTITY", Collections.singleton("*")); + List insertions = sampler.sampleSqlInsertion("DB_DIRECT_INT_ENTITY", Collections.singleton("*")); assertEquals(1, insertions.size()); //extract the x/y values from the random call diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java index c24fc6a5ae..6724442dd1 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java @@ -1,7 +1,7 @@ package org.evomaster.e2etests.spring.examples.resource.db; import com.google.inject.Injector; -import org.evomaster.core.database.DbAction; +import org.evomaster.core.database.SqlAction; import org.evomaster.core.problem.rest.resource.RestResourceCalls; import org.evomaster.core.problem.rest.resource.RestResourceNode; import org.evomaster.core.problem.rest.service.ResourceManageService; @@ -60,7 +60,7 @@ public void testResourceSamplerAndParamBinding() { calls.clear(); rmanger.sampleCall(raIdKey, true, calls, 10, true, Collections.emptyList(), "GET"); assertEquals(2, calls.get(0).seeActions(ActionFilter.ALL).size()); - assert( calls.get(0).seeActions(ActionFilter.ALL).get(0) instanceof DbAction); + assert( calls.get(0).seeActions(ActionFilter.ALL).get(0) instanceof SqlAction); assertEquals(3, calls.get(0).seeActions(ActionFilter.ALL).get(0).seeTopGenes().size()); //check whether the gene binding with rest action is removed with seeGenes(GeneFilter.ONLY_SQL) assertEquals(2, calls.get(0).seeGenes(GeneFilter.ONLY_SQL).size()); From 3a608029abe56e1dc27e1bac8ce4bd60dc528a39 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Wed, 2 Aug 2023 08:45:24 +0200 Subject: [PATCH 144/345] introducing EnvironmentAction --- .../kotlin/org/evomaster/core/database/SqlAction.kt | 11 ++++------- .../kotlin/org/evomaster/core/mongo/MongoDbAction.kt | 7 ++----- .../externalservice/ApiExternalServiceAction.kt | 3 ++- .../httpws/HttpExternalServiceAction.kt | 3 --- .../externalservice/rpc/DbAsExternalServiceAction.kt | 3 --- .../externalservice/rpc/RPCExternalServiceAction.kt | 3 --- .../org/evomaster/core/search/EnvironmentAction.kt | 3 ++- 7 files changed, 10 insertions(+), 23 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/database/SqlAction.kt b/core/src/main/kotlin/org/evomaster/core/database/SqlAction.kt index a8e7ec9172..277dbb008e 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/SqlAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/database/SqlAction.kt @@ -2,6 +2,7 @@ package org.evomaster.core.database import org.evomaster.core.database.schema.Column import org.evomaster.core.database.schema.Table +import org.evomaster.core.search.EnvironmentAction import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.Gene import org.evomaster.core.search.gene.placeholder.ImmutableDataHolderGene @@ -9,8 +10,8 @@ import org.evomaster.core.search.gene.string.StringGene import org.evomaster.core.search.gene.sql.SqlPrimaryKeyGene /** - * An action executed on the database. - * Typically, a SQL Insertion + * An action executed on a SQL database. + * Typically, a SQL Insertion operation. */ class SqlAction( /** @@ -29,7 +30,7 @@ class SqlAction( * This is very helpful when dealing with Foreign Keys. */ val representExistingData: Boolean = false -) : Action(listOf()) { +) : EnvironmentAction(listOf()) { init { /* @@ -102,10 +103,6 @@ class SqlAction( return SqlAction(table, selectedColumns, id, genes.map(Gene::copy), representExistingData) } - override fun shouldCountForFitnessEvaluations(): Boolean { - return false - } - fun geInsertionId(): Long { return this.id } diff --git a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt index b81214f428..783723870b 100644 --- a/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/mongo/MongoDbAction.kt @@ -1,6 +1,7 @@ package org.evomaster.core.mongo import org.evomaster.core.problem.rest.RestActionBuilderV3.createObjectGenesForDTOs +import org.evomaster.core.search.EnvironmentAction import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.Gene import java.util.* @@ -19,7 +20,7 @@ class MongoDbAction( */ val documentsType: String, computedGenes: List? = null -) : Action(listOf()) { +) : EnvironmentAction(listOf()) { private val genes: List = (computedGenes ?: computeGenes()).also { addChildren(it) } @@ -40,10 +41,6 @@ class MongoDbAction( return genes } - override fun shouldCountForFitnessEvaluations(): Boolean { - return false - } - override fun copyContent(): Action { return MongoDbAction(database, collection, documentsType, genes.map(Gene::copy)) } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/ApiExternalServiceAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/ApiExternalServiceAction.kt index 58cae726f9..720ce249a9 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/ApiExternalServiceAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/ApiExternalServiceAction.kt @@ -1,6 +1,7 @@ package org.evomaster.core.problem.externalservice import org.evomaster.core.problem.externalservice.param.ResponseParam +import org.evomaster.core.search.EnvironmentAction import org.evomaster.core.search.action.Action abstract class ApiExternalServiceAction( @@ -16,7 +17,7 @@ abstract class ApiExternalServiceAction( response: ResponseParam, active : Boolean = false, used : Boolean = false -) : Action(listOf(response)){ +) : EnvironmentAction(listOf(response)){ companion object{ /** diff --git a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/httpws/HttpExternalServiceAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/httpws/HttpExternalServiceAction.kt index 8bb06b2a60..c8b21d8741 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/httpws/HttpExternalServiceAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/httpws/HttpExternalServiceAction.kt @@ -77,9 +77,6 @@ class HttpExternalServiceAction( return response.genes } - override fun shouldCountForFitnessEvaluations(): Boolean { - return false - } /** * Each external service will have a WireMock instance representing that diff --git a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/DbAsExternalServiceAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/DbAsExternalServiceAction.kt index e4e4fbed77..3280dc00f3 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/DbAsExternalServiceAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/DbAsExternalServiceAction.kt @@ -47,9 +47,6 @@ class DbAsExternalServiceAction ( return response.genes } - override fun shouldCountForFitnessEvaluations(): Boolean { - return false - } override fun copyContent(): StructuralElement { return DbAsExternalServiceAction(commandName, descriptiveInfo, requestRuleIdentifier, response.copy() as ClassResponseParam, active, used) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/RPCExternalServiceAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/RPCExternalServiceAction.kt index 87a9844a86..d62a277203 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/RPCExternalServiceAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/externalservice/rpc/RPCExternalServiceAction.kt @@ -53,9 +53,6 @@ class RPCExternalServiceAction( return response.genes } - override fun shouldCountForFitnessEvaluations(): Boolean { - return false - } override fun copyContent(): RPCExternalServiceAction { return RPCExternalServiceAction(interfaceName, functionName, inputParamTypes?.toList(), descriptiveInfo, requestRuleIdentifier, response.copy() as ClassResponseParam, active, used) diff --git a/core/src/main/kotlin/org/evomaster/core/search/EnvironmentAction.kt b/core/src/main/kotlin/org/evomaster/core/search/EnvironmentAction.kt index 441377e949..7fa3a8b4b6 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/EnvironmentAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/EnvironmentAction.kt @@ -8,7 +8,8 @@ import org.evomaster.core.search.action.Action */ abstract class EnvironmentAction(children: List) : Action(children){ - override fun shouldCountForFitnessEvaluations(): Boolean { + final override fun shouldCountForFitnessEvaluations(): Boolean { + //Setup actions should never count for fitness evaluation return false } } \ No newline at end of file From 669fa49270ce27b55f894aef11de8ea9dcacf3f1 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Wed, 2 Aug 2023 08:54:31 +0200 Subject: [PATCH 145/345] refactoring sql package --- .../rest/service/resource/ResourceTestBase.kt | 10 +++++----- .../resource/model/SimpleResourceSampler.kt | 2 +- .../kotlin/org/evomaster/core/output/SqlWriter.kt | 2 +- .../core/output/service/ApiTestCaseWriter.kt | 4 ++-- .../problem/api/service/ApiWsStructureMutator.kt | 6 +++--- .../problem/enterprise/EnterpriseIndividual.kt | 4 ++-- .../enterprise/service/EnterpriseFitness.kt | 8 ++++---- .../enterprise/service/EnterpriseSampler.kt | 4 ++-- .../core/problem/graphql/GraphQLIndividual.kt | 2 +- .../core/problem/graphql/service/GraphQLFitness.kt | 2 +- .../graphql/service/GraphQLStructureMutator.kt | 2 +- .../core/problem/httpws/service/HttpWsFitness.kt | 4 ++-- .../evomaster/core/problem/rest/RestIndividual.kt | 4 ++-- .../core/problem/rest/resource/ResourceCluster.kt | 8 ++++---- .../rest/resource/ResourceImpactOfIndividual.kt | 2 +- .../problem/rest/resource/RestResourceCalls.kt | 4 ++-- .../core/problem/rest/resource/RestResourceNode.kt | 2 +- .../rest/resource/dependency/CreationChain.kt | 2 +- .../resource/dependency/ResourceRelatedToTable.kt | 2 +- .../rest/service/ResourceDepManageService.kt | 6 +++--- .../problem/rest/service/ResourceManageService.kt | 6 +++--- .../core/problem/rest/service/ResourceSampler.kt | 2 +- .../core/problem/rest/service/RestFitness.kt | 2 +- .../problem/rest/service/RestResourceFitness.kt | 2 +- .../rest/service/RestResourceStructureMutator.kt | 4 ++-- .../problem/rest/service/RestStructureMutator.kt | 2 +- .../evomaster/core/problem/rpc/RPCIndividual.kt | 4 ++-- .../core/problem/rpc/service/RPCFitness.kt | 2 +- .../problem/rpc/service/RPCStructureMutator.kt | 2 +- .../evomaster/core/problem/util/BindingBuilder.kt | 4 ++-- .../util/inference/DeriveResourceBinding.kt | 4 ++-- .../core/problem/util/inference/SimpleDeriveR2T.kt | 4 ++-- .../core/problem/webfrontend/WebIndividual.kt | 2 +- .../core/problem/webfrontend/service/WebFitness.kt | 2 +- .../core/remote/service/RemoteController.kt | 2 +- .../org/evomaster/core/search/EvaluatedAction.kt | 4 ++-- .../evomaster/core/search/EvaluatedIndividual.kt | 4 ++-- .../org/evomaster/core/search/FitnessValue.kt | 3 +-- .../kotlin/org/evomaster/core/search/Individual.kt | 4 ++-- .../kotlin/org/evomaster/core/search/Solution.kt | 2 +- .../impactinfocollection/ImpactsOfIndividual.kt | 2 +- .../InitializationActionImpacts.kt | 2 +- .../core/search/service/ExecutionInfoReporter.kt | 2 +- .../core/search/service/ExtraHeuristicsLogger.kt | 2 +- .../service/mutator/MutatedGeneSpecification.kt | 2 +- .../core/search/service/mutator/Mutator.kt | 2 +- .../core/search/service/mutator/StandardMutator.kt | 4 ++-- .../core/{database => sql}/DatabaseExecution.kt | 2 +- .../core/{database => sql}/DatabaseExecutor.kt | 2 +- .../PostgresToJavaRegExTranslator.kt | 2 +- .../{database => sql}/ReplaceValuesDeParser.kt | 2 +- .../core/{database => sql}/RowColumnValues.kt | 2 +- .../core/{database => sql}/SQLGenerator.kt | 8 ++++---- .../evomaster/core/{database => sql}/SqlAction.kt | 6 +++--- .../core/{database => sql}/SqlActionGeneBuilder.kt | 10 +++++----- .../core/{database => sql}/SqlActionResult.kt | 2 +- .../core/{database => sql}/SqlActionTransformer.kt | 2 +- .../core/{database => sql}/SqlActionUtils.kt | 4 ++-- .../core/{database => sql}/SqlExecutionInfo.kt | 2 +- .../core/{database => sql}/SqlInsertBuilder.kt | 6 +++--- .../{database => sql}/TableConstraintEvaluator.kt | 2 +- .../TableConstraintGeneCollector.kt | 2 +- .../core/{database => sql}/schema/Column.kt | 2 +- .../{database => sql}/schema/ColumnDataType.kt | 2 +- .../core/{database => sql}/schema/ColumnFactory.kt | 6 +++--- .../core/{database => sql}/schema/CompositeType.kt | 2 +- .../core/{database => sql}/schema/ForeignKey.kt | 2 +- .../core/{database => sql}/schema/Table.kt | 2 +- .../{database => sql}/schema/h2/H2GeometryType.kt | 2 +- .../schema/h2/H2GeometryTypeParser.kt | 2 +- .../org/evomaster/core/taint/TaintAnalysis.kt | 2 +- .../core/output/EvaluatedIndividualBuilder.kt | 4 ++-- .../evomaster/core/output/TestCaseWriterTest.kt | 14 +++++++------- .../org/evomaster/core/output/WriteJsonTest.kt | 10 +++++----- .../org/evomaster/core/output/WriteXMLTest.kt | 8 ++++---- .../rest/individual/RestIndividualResourceTest.kt | 2 +- .../rest/individual/RestIndividualTestBase.kt | 6 +++--- .../RestResourceIndividualDisabledHMTest.kt | 4 ++-- .../rest/resource/ResourceNodeWithDbTest.kt | 4 ++-- .../org/evomaster/core/search/gene/FlatViewTest.kt | 2 +- .../evomaster/core/search/gene/GeneUtilsTest.kt | 2 +- .../core/search/gene/binding/BindingBuildTest.kt | 8 ++++---- .../core/search/gene/sql/SqlTimestampGeneTest.kt | 2 +- .../core/search/impact/IndividualImpactTest.kt | 8 ++++---- .../search/mutationweight/GeneWeightTestSchema.kt | 8 ++++---- .../action/DbActionStructureTest.kt | 8 ++++---- .../individual/RestIndividualStructureTest.kt | 10 +++++----- .../{database => sql}/DbActionGeneBuilderTest.kt | 2 +- .../core/{database => sql}/DbActionRepairTest.kt | 8 ++++---- .../{database => sql}/DbActionTransformerTest.kt | 2 +- .../core/{database => sql}/DbActionUtilsTest.kt | 10 +++++----- .../PostgresToJavaRegExTranslatorTest.kt | 2 +- .../core/{database => sql}/SqlInsertBuilderTest.kt | 2 +- .../TableConstraintEvaluatorTest.kt | 8 ++++---- .../TableConstraintGeneCollectorTest.kt | 8 ++++---- .../extract/h2/AlterTableCheckEnumTest.kt | 2 +- .../extract/h2/AlterTableCheckTest.kt | 2 +- .../extract/h2/AlterTableUniqueTest.kt | 2 +- .../extract/h2/CatwatchSqlExtractTest.kt | 8 ++++---- .../extract/h2/CreateTableCheckEnumTest.kt | 2 +- .../extract/h2/CreateTableCheckTest.kt | 2 +- .../extract/h2/DoctorsExtractTest.kt | 8 ++++---- .../extract/h2/ExtractTestBaseH2.kt | 2 +- .../extract/h2/FeaturesServiceSqlExtractTest.kt | 2 +- .../extract/h2/NewsSqlExtractTest.kt | 2 +- .../extract/h2/OcvnExtractTest.kt | 4 ++-- .../extract/h2/ProxyPrintSqlExtractTest.kt | 8 ++++---- .../extract/h2/ScoutApiSqlExtractTest.kt | 2 +- .../extract/mysql/AlterTableExtractCheckTest.kt | 2 +- .../extract/mysql/AlterTableUniqueTest.kt | 2 +- .../extract/mysql/CreateTableBoundedNumberTest.kt | 4 ++-- .../extract/mysql/CreateTableCheckT1Test.kt | 2 +- .../extract/mysql/CreateTableCheckTest.kt | 2 +- .../extract/mysql/CreateTableEnumTest.kt | 4 ++-- .../extract/mysql/CreateTableUniqueTest.kt | 2 +- .../extract/mysql/ExtractTestBaseMySQL.kt | 2 +- .../extract/mysql/LikeCheckTest.kt | 6 +++--- .../extract/mysql/SQLJSONColumnTest.kt | 8 ++++---- .../extract/mysql/SqlDateColumnTest.kt | 6 +++--- .../extract/mysql/SqlDateTypesColumnTest.kt | 4 ++-- .../extract/mysql/SqlTextColumnTest.kt | 6 +++--- .../extract/postgres/AlterTableExtractCheckTest.kt | 2 +- .../postgres/AlterTableExtractUniqueTest.kt | 2 +- .../extract/postgres/ArrayTypesTest.kt | 6 +++--- .../extract/postgres/BinaryTypesTest.kt | 6 +++--- .../extract/postgres/BitStringTypesTest.kt | 6 +++--- .../extract/postgres/BooleanTypeTest.kt | 6 +++--- .../extract/postgres/CharacterTypesTest.kt | 6 +++--- .../extract/postgres/CompositeTypesTest.kt | 6 +++--- .../postgres/CreateTableExtractCheckTest.kt | 2 +- .../extract/postgres/DatetimeTypesTest.kt | 6 +++--- .../extract/postgres/EnumeratedTypesTest.kt | 6 +++--- .../extract/postgres/ExtractTestBasePostgres.kt | 2 +- .../extract/postgres/GeometricTypesTest.kt | 6 +++--- .../extract/postgres/Ind0ExtractTest.kt | 4 ++-- .../extract/postgres/JSONTypesTest.kt | 6 +++--- .../extract/postgres/LikeCheckTest.kt | 6 +++--- .../extract/postgres/ManySimilarToChecksTest.kt | 6 +++--- .../extract/postgres/MonetaryTypesTest.kt | 6 +++--- .../extract/postgres/MultiRangeTypesTest.kt | 6 +++--- .../extract/postgres/NetworkTypesTest.kt | 6 +++--- .../extract/postgres/NumericTypesTest.kt | 6 +++--- .../extract/postgres/ObjectIdentifierTypesTest.kt | 6 +++--- .../extract/postgres/PgLsnTest.kt | 6 +++--- .../extract/postgres/RangeTypesTest.kt | 6 +++--- .../extract/postgres/SimilarToCheckTest.kt | 6 +++--- .../extract/postgres/SqlDateColumnTest.kt | 6 +++--- .../extract/postgres/SqlJSONBColumnTest.kt | 8 ++++---- .../extract/postgres/SqlTextColumnTest.kt | 6 +++--- .../extract/postgres/SqlUUIDColumnTest.kt | 6 +++--- .../extract/postgres/SqlXMLColumnTest.kt | 8 ++++---- .../extract/postgres/TextSearchTypesTest.kt | 6 +++--- .../extract/postgres/UUIDTypeTest.kt | 6 +++--- .../postgres/UnsupportedBitCompositeTypeTest.kt | 2 +- .../postgres/UnsupportedCharCompositeTypeTest.kt | 2 +- .../UnsupportedNumericCompositeTypeTest.kt | 2 +- .../postgres/UnsupportedVarbitCompositeTypeTest.kt | 2 +- .../UnsupportedVarcharCompositeTypeTest.kt | 2 +- .../extract/postgres/XMLTypeTest.kt | 6 +++--- .../insertion/h2/H2InsertValueTest.kt | 2 +- .../insertion/mysql/MySQLInsertValueTest.kt | 2 +- .../insertion/postgres/PostgresInsertValueTest.kt | 2 +- .../{database => sql}/schema/ColumnFactoryTest.kt | 3 +-- .../schema/h2/H2GeometryTypeParserTest.kt | 2 +- .../directintwithsql/DbDirectIntWithSqlEMTest.java | 2 +- .../resource/db/ResourceDbMIOBasicTest.java | 2 +- .../directintwithsql/DbDirectIntWithSqlEMTest.java | 2 +- .../resource/db/ResourceDbMIOBasicTest.java | 2 +- 168 files changed, 341 insertions(+), 343 deletions(-) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/DatabaseExecution.kt (99%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/DatabaseExecutor.kt (96%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/PostgresToJavaRegExTranslator.kt (98%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/ReplaceValuesDeParser.kt (97%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/RowColumnValues.kt (75%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/SQLGenerator.kt (95%) mode change 100755 => 100644 rename core/src/main/kotlin/org/evomaster/core/{database => sql}/SqlAction.kt (96%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/SqlActionGeneBuilder.kt (99%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/SqlActionResult.kt (96%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/SqlActionTransformer.kt (99%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/SqlActionUtils.kt (99%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/SqlExecutionInfo.kt (87%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/SqlInsertBuilder.kt (99%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/TableConstraintEvaluator.kt (99%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/TableConstraintGeneCollector.kt (99%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/schema/Column.kt (97%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/schema/ColumnDataType.kt (99%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/schema/ColumnFactory.kt (97%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/schema/CompositeType.kt (74%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/schema/ForeignKey.kt (75%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/schema/Table.kt (90%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/schema/h2/H2GeometryType.kt (88%) rename core/src/main/kotlin/org/evomaster/core/{database => sql}/schema/h2/H2GeometryTypeParser.kt (98%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/DbActionGeneBuilderTest.kt (98%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/DbActionRepairTest.kt (86%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/DbActionTransformerTest.kt (89%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/DbActionUtilsTest.kt (98%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/PostgresToJavaRegExTranslatorTest.kt (97%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/SqlInsertBuilderTest.kt (99%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/TableConstraintEvaluatorTest.kt (99%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/TableConstraintGeneCollectorTest.kt (98%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/h2/AlterTableCheckEnumTest.kt (96%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/h2/AlterTableCheckTest.kt (96%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/h2/AlterTableUniqueTest.kt (97%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/h2/CatwatchSqlExtractTest.kt (94%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/h2/CreateTableCheckEnumTest.kt (96%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/h2/CreateTableCheckTest.kt (96%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/h2/DoctorsExtractTest.kt (83%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/h2/ExtractTestBaseH2.kt (95%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/h2/FeaturesServiceSqlExtractTest.kt (97%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/h2/NewsSqlExtractTest.kt (94%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/h2/OcvnExtractTest.kt (97%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/h2/ProxyPrintSqlExtractTest.kt (97%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/h2/ScoutApiSqlExtractTest.kt (98%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/mysql/AlterTableExtractCheckTest.kt (96%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/mysql/AlterTableUniqueTest.kt (97%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/mysql/CreateTableBoundedNumberTest.kt (97%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/mysql/CreateTableCheckT1Test.kt (97%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/mysql/CreateTableCheckTest.kt (96%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/mysql/CreateTableEnumTest.kt (94%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/mysql/CreateTableUniqueTest.kt (97%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/mysql/ExtractTestBaseMySQL.kt (97%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/mysql/LikeCheckTest.kt (90%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/mysql/SQLJSONColumnTest.kt (97%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/mysql/SqlDateColumnTest.kt (94%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/mysql/SqlDateTypesColumnTest.kt (89%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/mysql/SqlTextColumnTest.kt (95%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/AlterTableExtractCheckTest.kt (95%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/AlterTableExtractUniqueTest.kt (96%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/ArrayTypesTest.kt (98%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/BinaryTypesTest.kt (94%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/BitStringTypesTest.kt (92%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/BooleanTypeTest.kt (88%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/CharacterTypesTest.kt (95%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/CompositeTypesTest.kt (96%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/CreateTableExtractCheckTest.kt (95%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/DatetimeTypesTest.kt (91%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/EnumeratedTypesTest.kt (88%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/ExtractTestBasePostgres.kt (98%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/GeometricTypesTest.kt (95%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/Ind0ExtractTest.kt (97%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/JSONTypesTest.kt (90%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/LikeCheckTest.kt (90%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/ManySimilarToChecksTest.kt (95%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/MonetaryTypesTest.kt (88%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/MultiRangeTypesTest.kt (98%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/NetworkTypesTest.kt (93%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/NumericTypesTest.kt (96%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/ObjectIdentifierTypesTest.kt (97%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/PgLsnTest.kt (95%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/RangeTypesTest.kt (97%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/SimilarToCheckTest.kt (94%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/SqlDateColumnTest.kt (94%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/SqlJSONBColumnTest.kt (97%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/SqlTextColumnTest.kt (96%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/SqlUUIDColumnTest.kt (97%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/SqlXMLColumnTest.kt (97%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/TextSearchTypesTest.kt (96%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/UUIDTypeTest.kt (88%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/UnsupportedBitCompositeTypeTest.kt (90%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/UnsupportedCharCompositeTypeTest.kt (90%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/UnsupportedNumericCompositeTypeTest.kt (90%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/UnsupportedVarbitCompositeTypeTest.kt (90%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/UnsupportedVarcharCompositeTypeTest.kt (90%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/extract/postgres/XMLTypeTest.kt (88%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/insertion/h2/H2InsertValueTest.kt (98%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/insertion/mysql/MySQLInsertValueTest.kt (99%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/insertion/postgres/PostgresInsertValueTest.kt (99%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/schema/ColumnFactoryTest.kt (96%) rename core/src/test/kotlin/org/evomaster/core/{database => sql}/schema/h2/H2GeometryTypeParserTest.kt (99%) diff --git a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/ResourceTestBase.kt b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/ResourceTestBase.kt index 0d7aa7908f..fdc0ab7222 100644 --- a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/ResourceTestBase.kt +++ b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/ResourceTestBase.kt @@ -11,11 +11,11 @@ import org.evomaster.core.EMConfig import org.evomaster.core.TestUtils import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.action.ActionResult -import org.evomaster.core.database.DatabaseExecutor -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionResult -import org.evomaster.core.database.SqlInsertBuilder -import org.evomaster.core.database.extract.h2.ExtractTestBaseH2 +import org.evomaster.core.sql.DatabaseExecutor +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionResult +import org.evomaster.core.sql.SqlInsertBuilder +import org.evomaster.core.sql.extract.h2.ExtractTestBaseH2 import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.enterprise.SampleType diff --git a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/model/SimpleResourceSampler.kt b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/model/SimpleResourceSampler.kt index 8f856268d3..e229174342 100644 --- a/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/model/SimpleResourceSampler.kt +++ b/core-it/src/test/kotlin/org/evomaster/core/problem/rest/service/resource/model/SimpleResourceSampler.kt @@ -2,7 +2,7 @@ package org.evomaster.core.problem.rest.service.resource.model import io.swagger.parser.OpenAPIParser import org.evomaster.core.EMConfig -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.problem.rest.RestActionBuilderV3 import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.service.ResourceSampler diff --git a/core/src/main/kotlin/org/evomaster/core/output/SqlWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/SqlWriter.kt index c2b1cb6ba9..fee52a7be1 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/SqlWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/SqlWriter.kt @@ -2,7 +2,7 @@ package org.evomaster.core.output import org.apache.commons.lang3.StringEscapeUtils import org.evomaster.core.Lazy -import org.evomaster.core.database.SqlAction +import org.evomaster.core.sql.SqlAction import org.evomaster.core.search.EvaluatedDbAction import org.evomaster.core.search.gene.Gene import org.evomaster.core.search.gene.ObjectGene diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/ApiTestCaseWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/ApiTestCaseWriter.kt index 95bafcb8c2..d61b39ea2a 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/ApiTestCaseWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/ApiTestCaseWriter.kt @@ -2,8 +2,8 @@ package org.evomaster.core.output.service import com.google.gson.Gson import com.google.gson.JsonSyntaxException -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionResult +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionResult import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.mongo.MongoDbActionResult import org.evomaster.core.output.* diff --git a/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt b/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt index f7c55eefb0..c87d27e4b6 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/api/service/ApiWsStructureMutator.kt @@ -4,9 +4,9 @@ import com.google.inject.Inject import org.evomaster.client.java.controller.api.dto.database.execution.MongoFailedQuery import org.evomaster.core.EMConfig import org.evomaster.core.Lazy -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionUtils -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionUtils +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.api.ApiWsIndividual import org.evomaster.core.problem.enterprise.EnterpriseActionGroup diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt index f049d7b575..401e6794c5 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/EnterpriseIndividual.kt @@ -4,8 +4,8 @@ import org.evomaster.core.Lazy import org.evomaster.core.search.action.Action import org.evomaster.core.search.action.ActionComponent import org.evomaster.core.search.action.ActionFilter -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionUtils +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionUtils import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.api.ApiWsIndividual import org.evomaster.core.problem.externalservice.ApiExternalServiceAction diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt index 62ff3bf0a9..0f04c9d99b 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseFitness.kt @@ -6,10 +6,10 @@ import org.evomaster.client.java.controller.api.dto.HeuristicEntryDto import org.evomaster.client.java.controller.api.dto.TestResultsDto import org.evomaster.client.java.instrumentation.shared.ObjectiveNaming import org.evomaster.core.StaticCounter -import org.evomaster.core.database.DatabaseExecution -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionResult -import org.evomaster.core.database.SqlActionTransformer +import org.evomaster.core.sql.DatabaseExecution +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionResult +import org.evomaster.core.sql.SqlActionTransformer import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.mongo.MongoDbActionResult diff --git a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt index 6fe6eba917..04e3dd581a 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseSampler.kt @@ -2,8 +2,8 @@ package org.evomaster.core.problem.enterprise.service import com.google.inject.Inject import org.evomaster.client.java.controller.api.dto.SutInfoDto -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.mongo.MongoInsertBuilder import org.evomaster.core.output.OutputFormat diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLIndividual.kt index 667944e5bf..45b973c165 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/GraphQLIndividual.kt @@ -3,7 +3,7 @@ package org.evomaster.core.problem.graphql import org.evomaster.core.search.action.Action import org.evomaster.core.search.action.ActionComponent import org.evomaster.core.search.action.ActionFilter -import org.evomaster.core.database.SqlAction +import org.evomaster.core.sql.SqlAction import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.api.ApiWsIndividual import org.evomaster.core.problem.enterprise.EnterpriseActionGroup diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLFitness.kt index ae50ae54eb..cecfb1e306 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLFitness.kt @@ -5,7 +5,7 @@ import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper import org.evomaster.client.java.controller.api.dto.AdditionalInfoDto import org.evomaster.core.Lazy -import org.evomaster.core.database.SqlAction +import org.evomaster.core.sql.SqlAction import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.problem.graphql.* import org.evomaster.core.problem.httpws.service.HttpWsFitness diff --git a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLStructureMutator.kt b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLStructureMutator.kt index 6820fc4f19..31c57e1c2b 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLStructureMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/graphql/service/GraphQLStructureMutator.kt @@ -2,7 +2,7 @@ package org.evomaster.core.problem.graphql.service import com.google.inject.Inject import org.evomaster.core.Lazy -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.problem.enterprise.EnterpriseActionGroup import org.evomaster.core.problem.graphql.GraphQLAction import org.evomaster.core.problem.graphql.GraphQLIndividual diff --git a/core/src/main/kotlin/org/evomaster/core/problem/httpws/service/HttpWsFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/httpws/service/HttpWsFitness.kt index 7058a4704f..e6f30b06b1 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/httpws/service/HttpWsFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/httpws/service/HttpWsFitness.kt @@ -3,8 +3,8 @@ package org.evomaster.core.problem.httpws.service import com.fasterxml.jackson.databind.ObjectMapper import org.evomaster.client.java.controller.api.dto.* import org.evomaster.core.StaticCounter -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionTransformer +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionTransformer import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.output.CookieWriter import org.evomaster.core.output.TokenWriter diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt index a7aa6108b1..9db9c8b8a0 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestIndividual.kt @@ -3,8 +3,8 @@ package org.evomaster.core.problem.rest import org.evomaster.core.search.action.Action import org.evomaster.core.search.action.ActionComponent import org.evomaster.core.search.action.ActionFilter -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionUtils +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionUtils import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.api.ApiWsIndividual import org.evomaster.core.problem.enterprise.SampleType diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/ResourceCluster.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/ResourceCluster.kt index 5381b7902b..3a5ccec466 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/ResourceCluster.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/ResourceCluster.kt @@ -3,10 +3,10 @@ package org.evomaster.core.problem.rest.resource import org.evomaster.client.java.controller.api.dto.database.operations.DataRowDto import org.evomaster.core.EMConfig import org.evomaster.core.Lazy -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionUtils -import org.evomaster.core.database.SqlInsertBuilder -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionUtils +import org.evomaster.core.sql.SqlInsertBuilder +import org.evomaster.core.sql.schema.Table import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.util.inference.SimpleDeriveResourceBinding diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/ResourceImpactOfIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/ResourceImpactOfIndividual.kt index 41cc9ab3d8..e12301cd24 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/ResourceImpactOfIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/ResourceImpactOfIndividual.kt @@ -1,6 +1,6 @@ package org.evomaster.core.problem.rest.resource -import org.evomaster.core.database.SqlAction +import org.evomaster.core.sql.SqlAction import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.search.FitnessValue import org.evomaster.core.search.impact.impactinfocollection.ActionStructureImpact diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceCalls.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceCalls.kt index 359e0cc41d..9ade2710c6 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceCalls.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceCalls.kt @@ -5,8 +5,8 @@ import org.evomaster.core.search.action.Action import org.evomaster.core.search.action.ActionComponent import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.action.ActionTree -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionUtils +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionUtils import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestIndividual diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceNode.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceNode.kt index 4025f34a78..bac6df3d2f 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceNode.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/RestResourceNode.kt @@ -1,7 +1,7 @@ package org.evomaster.core.problem.rest.resource import org.evomaster.core.Lazy -import org.evomaster.core.database.SqlAction +import org.evomaster.core.sql.SqlAction import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.problem.rest.* import org.evomaster.core.problem.rest.param.BodyParam diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/dependency/CreationChain.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/dependency/CreationChain.kt index 019ade8c1b..81f8faedfc 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/dependency/CreationChain.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/dependency/CreationChain.kt @@ -1,6 +1,6 @@ package org.evomaster.core.problem.rest.resource.dependency -import org.evomaster.core.database.SqlAction +import org.evomaster.core.sql.SqlAction import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.search.service.Randomness diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/dependency/ResourceRelatedToTable.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/dependency/ResourceRelatedToTable.kt index 2df96db831..edbaeb0d9a 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/dependency/ResourceRelatedToTable.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/resource/dependency/ResourceRelatedToTable.kt @@ -1,7 +1,7 @@ package org.evomaster.core.problem.rest.resource.dependency import org.evomaster.client.java.controller.api.dto.database.execution.ExecutionDto -import org.evomaster.core.database.SQLKey +import org.evomaster.core.sql.SQLKey import org.evomaster.core.problem.rest.param.BodyParam import org.evomaster.core.problem.api.param.Param import org.evomaster.core.problem.util.inference.model.MatchedInfo diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceDepManageService.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceDepManageService.kt index 556977099a..aff8a5c2ca 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceDepManageService.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceDepManageService.kt @@ -5,9 +5,9 @@ import org.evomaster.client.java.controller.api.dto.TestResultsDto import org.evomaster.client.java.controller.api.dto.database.execution.ExecutionDto import org.evomaster.core.EMConfig import org.evomaster.core.Lazy -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionUtils -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionUtils +import org.evomaster.core.sql.schema.Table import org.evomaster.core.problem.rest.HttpVerb import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestIndividual diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceManageService.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceManageService.kt index d96bb24122..f02bc94e40 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceManageService.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceManageService.kt @@ -3,9 +3,9 @@ package org.evomaster.core.problem.rest.service import com.google.inject.Inject import org.evomaster.core.EMConfig import org.evomaster.core.EMConfig.SqlInitResourceStrategy -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionUtils -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionUtils +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.rest.* import org.evomaster.core.problem.httpws.auth.HttpWsAuthenticationInfo diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampler.kt index adbb8a6dce..0e325e3803 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/ResourceSampler.kt @@ -2,7 +2,7 @@ package org.evomaster.core.problem.rest.service import com.google.inject.Inject import org.evomaster.client.java.controller.api.dto.SutInfoDto -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.rest.* import org.evomaster.core.problem.httpws.auth.NoAuth diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt index 0a2dc4fff6..7e6e33a476 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestFitness.kt @@ -1,6 +1,6 @@ package org.evomaster.core.problem.rest.service -import org.evomaster.core.database.SqlAction +import org.evomaster.core.sql.SqlAction import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestCallResult diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt index 74b1328ba2..c43edf1e1e 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceFitness.kt @@ -2,7 +2,7 @@ package org.evomaster.core.problem.rest.service import com.google.inject.Inject -import org.evomaster.core.database.SqlAction +import org.evomaster.core.sql.SqlAction import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.enterprise.EnterpriseActionGroup import org.evomaster.core.problem.externalservice.httpws.HttpExternalServiceAction diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceStructureMutator.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceStructureMutator.kt index 929d39dd41..8470b8abfe 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceStructureMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestResourceStructureMutator.kt @@ -2,8 +2,8 @@ package org.evomaster.core.problem.rest.service import com.google.inject.Inject import org.evomaster.core.Lazy -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.problem.api.service.ApiWsStructureMutator import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestIndividual diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestStructureMutator.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestStructureMutator.kt index b72b551f52..4ea1635881 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestStructureMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestStructureMutator.kt @@ -2,7 +2,7 @@ package org.evomaster.core.problem.rest.service import com.google.inject.Inject import org.evomaster.core.Lazy -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.problem.api.service.ApiWsStructureMutator import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.rest.* diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt index a60a9c5764..2b7f7487ad 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/RPCIndividual.kt @@ -4,8 +4,8 @@ import org.evomaster.core.Lazy import org.evomaster.core.search.action.Action import org.evomaster.core.search.action.ActionComponent import org.evomaster.core.search.action.ActionFilter -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionUtils +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionUtils import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.api.ApiWsIndividual import org.evomaster.core.problem.enterprise.EnterpriseActionGroup diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt index e1f5601d84..4cd8c4b6cd 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCFitness.kt @@ -4,7 +4,7 @@ import com.google.inject.Inject import org.evomaster.client.java.controller.api.dto.AdditionalInfoDto import org.evomaster.client.java.controller.api.dto.problem.rpc.exception.RPCExceptionType import org.evomaster.core.Lazy -import org.evomaster.core.database.SqlAction +import org.evomaster.core.sql.SqlAction import org.evomaster.core.problem.api.service.ApiWsFitness import org.evomaster.core.problem.externalservice.rpc.RPCExternalServiceAction import org.evomaster.core.problem.externalservice.rpc.parm.ClassResponseParam diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCStructureMutator.kt b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCStructureMutator.kt index 2d02a461f2..237d58a058 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCStructureMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rpc/service/RPCStructureMutator.kt @@ -1,7 +1,7 @@ package org.evomaster.core.problem.rpc.service import com.google.inject.Inject -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.problem.api.service.ApiWsStructureMutator import org.evomaster.core.problem.rpc.RPCIndividual import org.evomaster.core.search.EvaluatedIndividual diff --git a/core/src/main/kotlin/org/evomaster/core/problem/util/BindingBuilder.kt b/core/src/main/kotlin/org/evomaster/core/problem/util/BindingBuilder.kt index e35b89bc61..61a7105a0c 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/util/BindingBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/util/BindingBuilder.kt @@ -4,8 +4,8 @@ import com.google.common.annotations.VisibleForTesting import org.evomaster.client.java.instrumentation.shared.TaintInputName import org.evomaster.core.Lazy import org.evomaster.core.StaticCounter -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionUtils +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionUtils import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.problem.api.param.Param import org.evomaster.core.problem.rest.RestCallAction diff --git a/core/src/main/kotlin/org/evomaster/core/problem/util/inference/DeriveResourceBinding.kt b/core/src/main/kotlin/org/evomaster/core/problem/util/inference/DeriveResourceBinding.kt index 1e294bdbf2..a6ae006177 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/util/inference/DeriveResourceBinding.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/util/inference/DeriveResourceBinding.kt @@ -1,7 +1,7 @@ package org.evomaster.core.problem.util.inference -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.schema.Table import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.resource.ParamInfo import org.evomaster.core.problem.rest.resource.RestResourceCalls diff --git a/core/src/main/kotlin/org/evomaster/core/problem/util/inference/SimpleDeriveR2T.kt b/core/src/main/kotlin/org/evomaster/core/problem/util/inference/SimpleDeriveR2T.kt index a539f65a5c..4946699781 100755 --- a/core/src/main/kotlin/org/evomaster/core/problem/util/inference/SimpleDeriveR2T.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/util/inference/SimpleDeriveR2T.kt @@ -1,7 +1,7 @@ package org.evomaster.core.problem.util.inference -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.schema.Table import org.evomaster.core.problem.rest.HttpVerb import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.param.BodyParam diff --git a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/WebIndividual.kt b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/WebIndividual.kt index e559f5fac5..1f5e0cb9c9 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/WebIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/WebIndividual.kt @@ -1,6 +1,6 @@ package org.evomaster.core.problem.webfrontend -import org.evomaster.core.database.SqlAction +import org.evomaster.core.sql.SqlAction import org.evomaster.core.problem.enterprise.EnterpriseActionGroup import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.gui.GuiIndividual diff --git a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebFitness.kt index c5c9ee656a..831e986f89 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/webfrontend/service/WebFitness.kt @@ -2,7 +2,7 @@ package org.evomaster.core.problem.webfrontend.service import org.evomaster.client.java.controller.api.dto.AdditionalInfoDto import org.evomaster.core.Lazy -import org.evomaster.core.database.SqlAction +import org.evomaster.core.sql.SqlAction import org.evomaster.core.problem.enterprise.service.EnterpriseFitness import org.evomaster.core.problem.webfrontend.* import org.evomaster.core.search.action.ActionResult diff --git a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteController.kt b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteController.kt index 75c4ef07dd..5c0d242713 100644 --- a/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteController.kt +++ b/core/src/main/kotlin/org/evomaster/core/remote/service/RemoteController.kt @@ -1,7 +1,7 @@ package org.evomaster.core.remote.service import org.evomaster.client.java.controller.api.dto.* -import org.evomaster.core.database.DatabaseExecutor +import org.evomaster.core.sql.DatabaseExecutor /** diff --git a/core/src/main/kotlin/org/evomaster/core/search/EvaluatedAction.kt b/core/src/main/kotlin/org/evomaster/core/search/EvaluatedAction.kt index ccc3c08fcd..c85c79ac0b 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/EvaluatedAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/EvaluatedAction.kt @@ -2,8 +2,8 @@ package org.evomaster.core.search import org.evomaster.core.search.action.Action import org.evomaster.core.search.action.ActionResult -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionResult +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionResult import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.mongo.MongoDbActionResult diff --git a/core/src/main/kotlin/org/evomaster/core/search/EvaluatedIndividual.kt b/core/src/main/kotlin/org/evomaster/core/search/EvaluatedIndividual.kt index 448a913b2f..5c9039c565 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/EvaluatedIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/EvaluatedIndividual.kt @@ -9,8 +9,8 @@ import org.evomaster.core.search.tracer.TraceableElementCopyFilter import org.evomaster.core.search.tracer.TrackOperator import org.evomaster.core.Lazy import org.evomaster.core.search.action.Action -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionResult +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionResult import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.problem.externalservice.ApiExternalServiceAction diff --git a/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt b/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt index 0450f2c324..eab96231db 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/FitnessValue.kt @@ -3,9 +3,8 @@ package org.evomaster.core.search import org.evomaster.client.java.controller.api.dto.BootTimeInfoDto import org.evomaster.client.java.controller.api.dto.database.execution.MongoFailedQuery import org.evomaster.core.EMConfig -import org.evomaster.core.database.DatabaseExecution +import org.evomaster.core.sql.DatabaseExecution import org.evomaster.core.EMConfig.SecondaryObjectiveStrategy.* -import org.evomaster.core.Lazy import org.evomaster.core.mongo.MongoExecution import org.evomaster.core.problem.externalservice.httpws.HttpWsExternalService import org.evomaster.core.problem.externalservice.httpws.HttpExternalServiceRequest diff --git a/core/src/main/kotlin/org/evomaster/core/search/Individual.kt b/core/src/main/kotlin/org/evomaster/core/search/Individual.kt index 9959c28fcb..a2afc0eb09 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/Individual.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/Individual.kt @@ -5,8 +5,8 @@ import org.evomaster.core.search.action.Action import org.evomaster.core.search.action.ActionComponent import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.action.ActionTree -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionUtils +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionUtils import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.problem.api.param.Param import org.evomaster.core.problem.externalservice.ApiExternalServiceAction diff --git a/core/src/main/kotlin/org/evomaster/core/search/Solution.kt b/core/src/main/kotlin/org/evomaster/core/search/Solution.kt index ce8cba9977..1b1e46fb26 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/Solution.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/Solution.kt @@ -1,6 +1,6 @@ package org.evomaster.core.search -import org.evomaster.core.database.SqlAction +import org.evomaster.core.sql.SqlAction import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.output.Termination import org.evomaster.core.problem.externalservice.httpws.HttpExternalServiceAction diff --git a/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactsOfIndividual.kt b/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactsOfIndividual.kt index 81affb184b..c76678003d 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactsOfIndividual.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/ImpactsOfIndividual.kt @@ -1,6 +1,6 @@ package org.evomaster.core.search.impact.impactinfocollection -import org.evomaster.core.database.SqlAction +import org.evomaster.core.sql.SqlAction import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.search.action.Action import org.evomaster.core.search.FitnessValue diff --git a/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/InitializationActionImpacts.kt b/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/InitializationActionImpacts.kt index fdf2fb899b..74fa743d17 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/InitializationActionImpacts.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/impact/impactinfocollection/InitializationActionImpacts.kt @@ -1,7 +1,7 @@ package org.evomaster.core.search.impact.impactinfocollection import org.evomaster.core.Lazy -import org.evomaster.core.database.SqlAction +import org.evomaster.core.sql.SqlAction import org.evomaster.core.mongo.MongoDbAction import org.evomaster.core.search.action.Action import org.slf4j.Logger diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/ExecutionInfoReporter.kt b/core/src/main/kotlin/org/evomaster/core/search/service/ExecutionInfoReporter.kt index 247d90b1f4..9a2ea33bcd 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/ExecutionInfoReporter.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/ExecutionInfoReporter.kt @@ -2,7 +2,7 @@ package org.evomaster.core.search.service import com.google.inject.Inject import org.evomaster.core.EMConfig -import org.evomaster.core.database.DatabaseExecution +import org.evomaster.core.sql.DatabaseExecution import org.evomaster.core.search.action.Action import org.evomaster.core.search.Individual import org.evomaster.core.utils.ReportWriter.wrapWithQuotation diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/ExtraHeuristicsLogger.kt b/core/src/main/kotlin/org/evomaster/core/search/service/ExtraHeuristicsLogger.kt index 470bac3696..129c1f9eb9 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/ExtraHeuristicsLogger.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/ExtraHeuristicsLogger.kt @@ -7,7 +7,7 @@ import net.sf.jsqlparser.util.deparser.SelectDeParser import net.sf.jsqlparser.util.deparser.StatementDeParser import org.evomaster.client.java.controller.api.dto.HeuristicEntryDto import org.evomaster.core.EMConfig -import org.evomaster.core.database.ReplaceValuesDeParser +import org.evomaster.core.sql.ReplaceValuesDeParser import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/MutatedGeneSpecification.kt b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/MutatedGeneSpecification.kt index e516b52e9f..a2fa6553db 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/MutatedGeneSpecification.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/MutatedGeneSpecification.kt @@ -1,6 +1,6 @@ package org.evomaster.core.search.service.mutator -import org.evomaster.core.database.SqlAction +import org.evomaster.core.sql.SqlAction import org.evomaster.core.search.action.Action import org.evomaster.core.search.Individual import org.evomaster.core.search.gene.Gene diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/Mutator.kt b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/Mutator.kt index 6441d86cda..20117d75b9 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/Mutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/Mutator.kt @@ -3,7 +3,7 @@ package org.evomaster.core.search.service.mutator import com.google.inject.Inject import org.evomaster.core.EMConfig import org.evomaster.core.Lazy -import org.evomaster.core.database.SqlAction +import org.evomaster.core.sql.SqlAction import org.evomaster.core.problem.externalservice.httpws.service.HarvestActualHttpWsResponseHandler import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.Individual diff --git a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/StandardMutator.kt b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/StandardMutator.kt index 0f11ae9190..4391cc4968 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/service/mutator/StandardMutator.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/service/mutator/StandardMutator.kt @@ -4,8 +4,8 @@ import org.evomaster.core.EMConfig import org.evomaster.core.EMConfig.GeneMutationStrategy.ONE_OVER_N import org.evomaster.core.EMConfig.GeneMutationStrategy.ONE_OVER_N_BIASED_SQL import org.evomaster.core.Lazy -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionUtils +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionUtils import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.problem.api.ApiWsAction import org.evomaster.core.problem.api.param.Param diff --git a/core/src/main/kotlin/org/evomaster/core/database/DatabaseExecution.kt b/core/src/main/kotlin/org/evomaster/core/sql/DatabaseExecution.kt similarity index 99% rename from core/src/main/kotlin/org/evomaster/core/database/DatabaseExecution.kt rename to core/src/main/kotlin/org/evomaster/core/sql/DatabaseExecution.kt index 99c65a6404..26a35e39d5 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/DatabaseExecution.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/DatabaseExecution.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database +package org.evomaster.core.sql import org.evomaster.client.java.controller.api.dto.database.execution.ExecutionDto import org.evomaster.client.java.controller.api.dto.database.execution.SqlExecutionLogDto diff --git a/core/src/main/kotlin/org/evomaster/core/database/DatabaseExecutor.kt b/core/src/main/kotlin/org/evomaster/core/sql/DatabaseExecutor.kt similarity index 96% rename from core/src/main/kotlin/org/evomaster/core/database/DatabaseExecutor.kt rename to core/src/main/kotlin/org/evomaster/core/sql/DatabaseExecutor.kt index 8ec33cb36d..cc31393667 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/DatabaseExecutor.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/DatabaseExecutor.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database +package org.evomaster.core.sql import org.evomaster.client.java.controller.api.dto.database.operations.* diff --git a/core/src/main/kotlin/org/evomaster/core/database/PostgresToJavaRegExTranslator.kt b/core/src/main/kotlin/org/evomaster/core/sql/PostgresToJavaRegExTranslator.kt similarity index 98% rename from core/src/main/kotlin/org/evomaster/core/database/PostgresToJavaRegExTranslator.kt rename to core/src/main/kotlin/org/evomaster/core/sql/PostgresToJavaRegExTranslator.kt index 1cec63e19d..01eabbe02b 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/PostgresToJavaRegExTranslator.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/PostgresToJavaRegExTranslator.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database +package org.evomaster.core.sql class PostgresToJavaRegExTranslator { diff --git a/core/src/main/kotlin/org/evomaster/core/database/ReplaceValuesDeParser.kt b/core/src/main/kotlin/org/evomaster/core/sql/ReplaceValuesDeParser.kt similarity index 97% rename from core/src/main/kotlin/org/evomaster/core/database/ReplaceValuesDeParser.kt rename to core/src/main/kotlin/org/evomaster/core/sql/ReplaceValuesDeParser.kt index 3402334c43..7950ae0435 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/ReplaceValuesDeParser.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/ReplaceValuesDeParser.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database +package org.evomaster.core.sql import net.sf.jsqlparser.expression.* import net.sf.jsqlparser.util.deparser.ExpressionDeParser diff --git a/core/src/main/kotlin/org/evomaster/core/database/RowColumnValues.kt b/core/src/main/kotlin/org/evomaster/core/sql/RowColumnValues.kt similarity index 75% rename from core/src/main/kotlin/org/evomaster/core/database/RowColumnValues.kt rename to core/src/main/kotlin/org/evomaster/core/sql/RowColumnValues.kt index 7911139fc7..3d74d658d1 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/RowColumnValues.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/RowColumnValues.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database +package org.evomaster.core.sql class RowColumnValues { diff --git a/core/src/main/kotlin/org/evomaster/core/database/SQLGenerator.kt b/core/src/main/kotlin/org/evomaster/core/sql/SQLGenerator.kt old mode 100755 new mode 100644 similarity index 95% rename from core/src/main/kotlin/org/evomaster/core/database/SQLGenerator.kt rename to core/src/main/kotlin/org/evomaster/core/sql/SQLGenerator.kt index 27dce4cf89..509d362b72 --- a/core/src/main/kotlin/org/evomaster/core/database/SQLGenerator.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/SQLGenerator.kt @@ -1,8 +1,8 @@ -package org.evomaster.core.database +package org.evomaster.core.sql -import org.evomaster.core.database.schema.Column -import org.evomaster.core.database.schema.ColumnDataType -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.schema.Column +import org.evomaster.core.sql.schema.ColumnDataType +import org.evomaster.core.sql.schema.Table /** * this is a utility to handle Sql command generation diff --git a/core/src/main/kotlin/org/evomaster/core/database/SqlAction.kt b/core/src/main/kotlin/org/evomaster/core/sql/SqlAction.kt similarity index 96% rename from core/src/main/kotlin/org/evomaster/core/database/SqlAction.kt rename to core/src/main/kotlin/org/evomaster/core/sql/SqlAction.kt index 277dbb008e..48148e76d4 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/SqlAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/SqlAction.kt @@ -1,7 +1,7 @@ -package org.evomaster.core.database +package org.evomaster.core.sql -import org.evomaster.core.database.schema.Column -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.schema.Column +import org.evomaster.core.sql.schema.Table import org.evomaster.core.search.EnvironmentAction import org.evomaster.core.search.action.Action import org.evomaster.core.search.gene.Gene diff --git a/core/src/main/kotlin/org/evomaster/core/database/SqlActionGeneBuilder.kt b/core/src/main/kotlin/org/evomaster/core/sql/SqlActionGeneBuilder.kt similarity index 99% rename from core/src/main/kotlin/org/evomaster/core/database/SqlActionGeneBuilder.kt rename to core/src/main/kotlin/org/evomaster/core/sql/SqlActionGeneBuilder.kt index 8297fefdfb..b5622803b7 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/SqlActionGeneBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/SqlActionGeneBuilder.kt @@ -1,11 +1,11 @@ -package org.evomaster.core.database +package org.evomaster.core.sql import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.instrumentation.shared.RegexSharedUtils -import org.evomaster.core.database.schema.Column -import org.evomaster.core.database.schema.ColumnDataType -import org.evomaster.core.database.schema.ForeignKey -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.schema.Column +import org.evomaster.core.sql.schema.ColumnDataType +import org.evomaster.core.sql.schema.ForeignKey +import org.evomaster.core.sql.schema.Table import org.evomaster.core.parser.RegexHandler import org.evomaster.core.parser.RegexHandler.createGeneForPostgresLike import org.evomaster.core.parser.RegexHandler.createGeneForPostgresSimilarTo diff --git a/core/src/main/kotlin/org/evomaster/core/database/SqlActionResult.kt b/core/src/main/kotlin/org/evomaster/core/sql/SqlActionResult.kt similarity index 96% rename from core/src/main/kotlin/org/evomaster/core/database/SqlActionResult.kt rename to core/src/main/kotlin/org/evomaster/core/sql/SqlActionResult.kt index 6a61322947..9696f29252 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/SqlActionResult.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/SqlActionResult.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database +package org.evomaster.core.sql import org.evomaster.core.search.action.Action import org.evomaster.core.search.action.ActionResult diff --git a/core/src/main/kotlin/org/evomaster/core/database/SqlActionTransformer.kt b/core/src/main/kotlin/org/evomaster/core/sql/SqlActionTransformer.kt similarity index 99% rename from core/src/main/kotlin/org/evomaster/core/database/SqlActionTransformer.kt rename to core/src/main/kotlin/org/evomaster/core/sql/SqlActionTransformer.kt index 025ca6e47c..f30eacfbc6 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/SqlActionTransformer.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/SqlActionTransformer.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database +package org.evomaster.core.sql import org.evomaster.client.java.controller.api.dto.database.operations.DatabaseCommandDto import org.evomaster.client.java.controller.api.dto.database.operations.InsertionDto diff --git a/core/src/main/kotlin/org/evomaster/core/database/SqlActionUtils.kt b/core/src/main/kotlin/org/evomaster/core/sql/SqlActionUtils.kt similarity index 99% rename from core/src/main/kotlin/org/evomaster/core/database/SqlActionUtils.kt rename to core/src/main/kotlin/org/evomaster/core/sql/SqlActionUtils.kt index 61e1c92aa7..baca2044ea 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/SqlActionUtils.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/SqlActionUtils.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database +package org.evomaster.core.sql import org.evomaster.core.Lazy import org.evomaster.core.search.action.Action @@ -8,7 +8,7 @@ import org.evomaster.core.search.gene.sql.SqlPrimaryKeyGene import org.evomaster.core.search.service.Randomness import org.slf4j.Logger import org.slf4j.LoggerFactory -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.schema.Table object SqlActionUtils { diff --git a/core/src/main/kotlin/org/evomaster/core/database/SqlExecutionInfo.kt b/core/src/main/kotlin/org/evomaster/core/sql/SqlExecutionInfo.kt similarity index 87% rename from core/src/main/kotlin/org/evomaster/core/database/SqlExecutionInfo.kt rename to core/src/main/kotlin/org/evomaster/core/sql/SqlExecutionInfo.kt index b0b8bc6623..13a3a92d9f 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/SqlExecutionInfo.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/SqlExecutionInfo.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database +package org.evomaster.core.sql /** * sql execution info diff --git a/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt b/core/src/main/kotlin/org/evomaster/core/sql/SqlInsertBuilder.kt similarity index 99% rename from core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt rename to core/src/main/kotlin/org/evomaster/core/sql/SqlInsertBuilder.kt index a7bda199b1..ac20cf35e5 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/SqlInsertBuilder.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/SqlInsertBuilder.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database +package org.evomaster.core.sql import org.evomaster.client.java.controller.api.dto.database.operations.DataRowDto import org.evomaster.client.java.controller.api.dto.database.operations.DatabaseCommandDto @@ -8,8 +8,8 @@ import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.api.dto.database.schema.DbSchemaDto import org.evomaster.client.java.controller.api.dto.database.schema.ExtraConstraintsDto import org.evomaster.client.java.controller.api.dto.database.schema.TableDto -import org.evomaster.core.database.schema.* -import org.evomaster.core.database.schema.ColumnFactory.createColumnFromDto +import org.evomaster.core.sql.schema.* +import org.evomaster.core.sql.schema.ColumnFactory.createColumnFromDto import org.evomaster.core.search.gene.Gene import org.evomaster.core.search.gene.placeholder.ImmutableDataHolderGene import org.evomaster.core.search.gene.sql.SqlPrimaryKeyGene diff --git a/core/src/main/kotlin/org/evomaster/core/database/TableConstraintEvaluator.kt b/core/src/main/kotlin/org/evomaster/core/sql/TableConstraintEvaluator.kt similarity index 99% rename from core/src/main/kotlin/org/evomaster/core/database/TableConstraintEvaluator.kt rename to core/src/main/kotlin/org/evomaster/core/sql/TableConstraintEvaluator.kt index 436eadcbe4..63cb1ba689 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/TableConstraintEvaluator.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/TableConstraintEvaluator.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database +package org.evomaster.core.sql import org.evomaster.core.search.gene.Gene import org.evomaster.core.search.gene.numeric.NumberGene diff --git a/core/src/main/kotlin/org/evomaster/core/database/TableConstraintGeneCollector.kt b/core/src/main/kotlin/org/evomaster/core/sql/TableConstraintGeneCollector.kt similarity index 99% rename from core/src/main/kotlin/org/evomaster/core/database/TableConstraintGeneCollector.kt rename to core/src/main/kotlin/org/evomaster/core/sql/TableConstraintGeneCollector.kt index af4bb059ad..7aaa8c7f09 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/TableConstraintGeneCollector.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/TableConstraintGeneCollector.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database +package org.evomaster.core.sql import org.evomaster.core.search.gene.Gene import org.evomaster.dbconstraint.* diff --git a/core/src/main/kotlin/org/evomaster/core/database/schema/Column.kt b/core/src/main/kotlin/org/evomaster/core/sql/schema/Column.kt similarity index 97% rename from core/src/main/kotlin/org/evomaster/core/database/schema/Column.kt rename to core/src/main/kotlin/org/evomaster/core/sql/schema/Column.kt index 0fac08cc30..8aa8d0f648 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/schema/Column.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/schema/Column.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.schema +package org.evomaster.core.sql.schema import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType diff --git a/core/src/main/kotlin/org/evomaster/core/database/schema/ColumnDataType.kt b/core/src/main/kotlin/org/evomaster/core/sql/schema/ColumnDataType.kt similarity index 99% rename from core/src/main/kotlin/org/evomaster/core/database/schema/ColumnDataType.kt rename to core/src/main/kotlin/org/evomaster/core/sql/schema/ColumnDataType.kt index e6dfe2b00e..252d5ef0a7 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/schema/ColumnDataType.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/schema/ColumnDataType.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.schema +package org.evomaster.core.sql.schema /** * SQL Data types from databases diff --git a/core/src/main/kotlin/org/evomaster/core/database/schema/ColumnFactory.kt b/core/src/main/kotlin/org/evomaster/core/sql/schema/ColumnFactory.kt similarity index 97% rename from core/src/main/kotlin/org/evomaster/core/database/schema/ColumnFactory.kt rename to core/src/main/kotlin/org/evomaster/core/sql/schema/ColumnFactory.kt index 69293e3c2c..e091d79918 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/schema/ColumnFactory.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/schema/ColumnFactory.kt @@ -1,11 +1,11 @@ -package org.evomaster.core.database.schema +package org.evomaster.core.sql.schema import org.evomaster.client.java.controller.api.dto.database.schema.ColumnDto import org.evomaster.client.java.controller.api.dto.database.schema.CompositeTypeColumnDto import org.evomaster.client.java.controller.api.dto.database.schema.CompositeTypeDto import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType -import org.evomaster.core.database.schema.h2.H2GeometryType -import org.evomaster.core.database.schema.h2.H2GeometryTypeParser +import org.evomaster.core.sql.schema.h2.H2GeometryType +import org.evomaster.core.sql.schema.h2.H2GeometryTypeParser object ColumnFactory { diff --git a/core/src/main/kotlin/org/evomaster/core/database/schema/CompositeType.kt b/core/src/main/kotlin/org/evomaster/core/sql/schema/CompositeType.kt similarity index 74% rename from core/src/main/kotlin/org/evomaster/core/database/schema/CompositeType.kt rename to core/src/main/kotlin/org/evomaster/core/sql/schema/CompositeType.kt index 0fe532f439..9defbd5dbe 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/schema/CompositeType.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/schema/CompositeType.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.schema +package org.evomaster.core.sql.schema /** * diff --git a/core/src/main/kotlin/org/evomaster/core/database/schema/ForeignKey.kt b/core/src/main/kotlin/org/evomaster/core/sql/schema/ForeignKey.kt similarity index 75% rename from core/src/main/kotlin/org/evomaster/core/database/schema/ForeignKey.kt rename to core/src/main/kotlin/org/evomaster/core/sql/schema/ForeignKey.kt index 88b71833ed..b9c6d7c004 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/schema/ForeignKey.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/schema/ForeignKey.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.schema +package org.evomaster.core.sql.schema /** * diff --git a/core/src/main/kotlin/org/evomaster/core/database/schema/Table.kt b/core/src/main/kotlin/org/evomaster/core/sql/schema/Table.kt similarity index 90% rename from core/src/main/kotlin/org/evomaster/core/database/schema/Table.kt rename to core/src/main/kotlin/org/evomaster/core/sql/schema/Table.kt index 74957313cd..a8dd1e5c35 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/schema/Table.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/schema/Table.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.schema +package org.evomaster.core.sql.schema import org.evomaster.dbconstraint.TableConstraint diff --git a/core/src/main/kotlin/org/evomaster/core/database/schema/h2/H2GeometryType.kt b/core/src/main/kotlin/org/evomaster/core/sql/schema/h2/H2GeometryType.kt similarity index 88% rename from core/src/main/kotlin/org/evomaster/core/database/schema/h2/H2GeometryType.kt rename to core/src/main/kotlin/org/evomaster/core/sql/schema/h2/H2GeometryType.kt index b5c0e51224..60cc4ca023 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/schema/h2/H2GeometryType.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/schema/h2/H2GeometryType.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.schema.h2 +package org.evomaster.core.sql.schema.h2 /** * Represents the results of parsing a H2 Geometry diff --git a/core/src/main/kotlin/org/evomaster/core/database/schema/h2/H2GeometryTypeParser.kt b/core/src/main/kotlin/org/evomaster/core/sql/schema/h2/H2GeometryTypeParser.kt similarity index 98% rename from core/src/main/kotlin/org/evomaster/core/database/schema/h2/H2GeometryTypeParser.kt rename to core/src/main/kotlin/org/evomaster/core/sql/schema/h2/H2GeometryTypeParser.kt index db369f4ccc..ad926b6af7 100644 --- a/core/src/main/kotlin/org/evomaster/core/database/schema/h2/H2GeometryTypeParser.kt +++ b/core/src/main/kotlin/org/evomaster/core/sql/schema/h2/H2GeometryTypeParser.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.schema.h2 +package org.evomaster.core.sql.schema.h2 import java.util.regex.Pattern diff --git a/core/src/main/kotlin/org/evomaster/core/taint/TaintAnalysis.kt b/core/src/main/kotlin/org/evomaster/core/taint/TaintAnalysis.kt index 0ebcc365da..86047f17c1 100644 --- a/core/src/main/kotlin/org/evomaster/core/taint/TaintAnalysis.kt +++ b/core/src/main/kotlin/org/evomaster/core/taint/TaintAnalysis.kt @@ -5,7 +5,7 @@ import org.evomaster.client.java.instrumentation.shared.StringSpecialization import org.evomaster.client.java.instrumentation.shared.StringSpecializationInfo import org.evomaster.client.java.instrumentation.shared.TaintInputName import org.evomaster.client.java.instrumentation.shared.TaintType -import org.evomaster.core.database.SqlAction +import org.evomaster.core.sql.SqlAction import org.evomaster.core.logging.LoggingUtil import org.evomaster.core.problem.rest.RestActionBuilderV3 import org.evomaster.core.problem.rest.RestCallAction diff --git a/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt b/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt index 5b599e2f35..ace2238648 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/EvaluatedIndividualBuilder.kt @@ -3,8 +3,8 @@ package org.evomaster.core.output import org.evomaster.core.TestUtils import org.evomaster.core.search.action.ActionFilter import org.evomaster.core.search.action.ActionResult -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionResult +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionResult import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.externalservice.ApiExternalServiceAction import org.evomaster.core.problem.externalservice.rpc.DbAsExternalServiceAction diff --git a/core/src/test/kotlin/org/evomaster/core/output/TestCaseWriterTest.kt b/core/src/test/kotlin/org/evomaster/core/output/TestCaseWriterTest.kt index ddc1248ba4..c7a8468a58 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/TestCaseWriterTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/TestCaseWriterTest.kt @@ -3,13 +3,13 @@ package org.evomaster.core.output import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.core.EMConfig import org.evomaster.core.TestUtils -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionGeneBuilder -import org.evomaster.core.database.SqlActionResult -import org.evomaster.core.database.schema.Column -import org.evomaster.core.database.schema.ColumnDataType.* -import org.evomaster.core.database.schema.ForeignKey -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionGeneBuilder +import org.evomaster.core.sql.SqlActionResult +import org.evomaster.core.sql.schema.Column +import org.evomaster.core.sql.schema.ColumnDataType.* +import org.evomaster.core.sql.schema.ForeignKey +import org.evomaster.core.sql.schema.Table import org.evomaster.core.output.EvaluatedIndividualBuilder.Companion.buildResourceEvaluatedIndividual import org.evomaster.core.output.service.PartialOracles import org.evomaster.core.output.service.RestTestCaseWriter diff --git a/core/src/test/kotlin/org/evomaster/core/output/WriteJsonTest.kt b/core/src/test/kotlin/org/evomaster/core/output/WriteJsonTest.kt index 6b6e51e0e0..2e6043bdc1 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/WriteJsonTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/WriteJsonTest.kt @@ -2,11 +2,11 @@ package org.evomaster.core.output import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.core.EMConfig -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.schema.Column -import org.evomaster.core.database.schema.ColumnDataType -import org.evomaster.core.database.schema.ForeignKey -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.schema.Column +import org.evomaster.core.sql.schema.ColumnDataType +import org.evomaster.core.sql.schema.ForeignKey +import org.evomaster.core.sql.schema.Table import org.evomaster.core.output.EvaluatedIndividualBuilder.Companion.buildEvaluatedIndividual import org.evomaster.core.output.service.PartialOracles import org.evomaster.core.output.service.RestTestCaseWriter diff --git a/core/src/test/kotlin/org/evomaster/core/output/WriteXMLTest.kt b/core/src/test/kotlin/org/evomaster/core/output/WriteXMLTest.kt index e906032ead..678db2db68 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/WriteXMLTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/WriteXMLTest.kt @@ -2,10 +2,10 @@ package org.evomaster.core.output import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.core.EMConfig -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.schema.Column -import org.evomaster.core.database.schema.ColumnDataType -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.schema.Column +import org.evomaster.core.sql.schema.ColumnDataType +import org.evomaster.core.sql.schema.Table import org.evomaster.core.output.EvaluatedIndividualBuilder.Companion.buildEvaluatedIndividual import org.evomaster.core.output.service.PartialOracles import org.evomaster.core.output.service.RestTestCaseWriter diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualResourceTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualResourceTest.kt index a8ef0a352e..afc3b93f0f 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualResourceTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualResourceTest.kt @@ -1,7 +1,7 @@ package org.evomaster.core.problem.rest.individual import com.google.inject.* -import org.evomaster.core.database.SqlAction +import org.evomaster.core.sql.SqlAction import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.rest.service.* import org.evomaster.core.search.action.ActionFilter diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualTestBase.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualTestBase.kt index 0013bf8614..c9825629f9 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualTestBase.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestIndividualTestBase.kt @@ -25,9 +25,9 @@ import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor import org.evomaster.core.BaseModule import org.evomaster.core.EMConfig -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlInsertBuilder -import org.evomaster.core.database.schema.ColumnDataType +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlInsertBuilder +import org.evomaster.core.sql.schema.ColumnDataType import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.rest.resource.RestResourceCalls diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt index 0ad3086839..cc5bbad676 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/individual/RestResourceIndividualDisabledHMTest.kt @@ -1,8 +1,8 @@ package org.evomaster.core.problem.rest.individual import com.google.inject.* -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.schema.Table import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.rest.resource.ResourceCluster diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/resource/ResourceNodeWithDbTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/resource/ResourceNodeWithDbTest.kt index b57d473708..bfb9193238 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/resource/ResourceNodeWithDbTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/resource/ResourceNodeWithDbTest.kt @@ -5,8 +5,8 @@ import org.evomaster.client.java.controller.api.dto.database.operations.* import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor import org.evomaster.core.EMConfig -import org.evomaster.core.database.DatabaseExecutor -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.DatabaseExecutor +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.problem.rest.RestActionBuilderV3 import org.evomaster.core.problem.rest.resource.dependency.BodyParamRelatedToTable import org.evomaster.core.search.action.Action diff --git a/core/src/test/kotlin/org/evomaster/core/search/gene/FlatViewTest.kt b/core/src/test/kotlin/org/evomaster/core/search/gene/FlatViewTest.kt index 73385325ee..046f908210 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/gene/FlatViewTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/gene/FlatViewTest.kt @@ -1,6 +1,6 @@ package org.evomaster.core.search.gene -import org.evomaster.core.database.SqlActionGeneBuilder +import org.evomaster.core.sql.SqlActionGeneBuilder import org.evomaster.core.search.gene.datetime.DateGene import org.evomaster.core.search.gene.numeric.FloatGene import org.evomaster.core.search.gene.numeric.IntegerGene diff --git a/core/src/test/kotlin/org/evomaster/core/search/gene/GeneUtilsTest.kt b/core/src/test/kotlin/org/evomaster/core/search/gene/GeneUtilsTest.kt index 45f9761626..4d02407d28 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/gene/GeneUtilsTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/gene/GeneUtilsTest.kt @@ -1,6 +1,6 @@ package org.evomaster.core.search.gene -import org.evomaster.core.database.SqlActionGeneBuilder +import org.evomaster.core.sql.SqlActionGeneBuilder import org.evomaster.core.problem.graphql.GqlConst import org.evomaster.core.problem.graphql.GraphQLAction import org.evomaster.core.problem.graphql.builder.GraphQLActionBuilder diff --git a/core/src/test/kotlin/org/evomaster/core/search/gene/binding/BindingBuildTest.kt b/core/src/test/kotlin/org/evomaster/core/search/gene/binding/BindingBuildTest.kt index 1af31c75d7..f7fb2a78b7 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/gene/binding/BindingBuildTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/gene/binding/BindingBuildTest.kt @@ -1,10 +1,10 @@ package org.evomaster.core.search.gene.binding import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.schema.Column -import org.evomaster.core.database.schema.ColumnDataType -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.schema.Column +import org.evomaster.core.sql.schema.ColumnDataType +import org.evomaster.core.sql.schema.Table import org.evomaster.core.problem.rest.HttpVerb import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestPath diff --git a/core/src/test/kotlin/org/evomaster/core/search/gene/sql/SqlTimestampGeneTest.kt b/core/src/test/kotlin/org/evomaster/core/search/gene/sql/SqlTimestampGeneTest.kt index f85401b482..fe7a1bf31c 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/gene/sql/SqlTimestampGeneTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/gene/sql/SqlTimestampGeneTest.kt @@ -1,6 +1,6 @@ package org.evomaster.core.search.gene.sql -import org.evomaster.core.database.SqlActionGeneBuilder +import org.evomaster.core.sql.SqlActionGeneBuilder import org.evomaster.core.search.service.Randomness import org.junit.jupiter.api.Test import java.time.LocalDate diff --git a/core/src/test/kotlin/org/evomaster/core/search/impact/IndividualImpactTest.kt b/core/src/test/kotlin/org/evomaster/core/search/impact/IndividualImpactTest.kt index 897dc0ed65..1e5b8554f3 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/impact/IndividualImpactTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/impact/IndividualImpactTest.kt @@ -1,10 +1,10 @@ package org.evomaster.core.search.impact import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.schema.Column -import org.evomaster.core.database.schema.ColumnDataType -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.schema.Column +import org.evomaster.core.sql.schema.ColumnDataType +import org.evomaster.core.sql.schema.Table import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.rest.* import org.evomaster.core.problem.rest.param.QueryParam diff --git a/core/src/test/kotlin/org/evomaster/core/search/mutationweight/GeneWeightTestSchema.kt b/core/src/test/kotlin/org/evomaster/core/search/mutationweight/GeneWeightTestSchema.kt index f9d967001d..07db2e2e73 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/mutationweight/GeneWeightTestSchema.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/mutationweight/GeneWeightTestSchema.kt @@ -3,10 +3,10 @@ package org.evomaster.core.search.mutationweight import io.swagger.parser.OpenAPIParser import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.core.EMConfig -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.schema.Column -import org.evomaster.core.database.schema.ColumnDataType -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.schema.Column +import org.evomaster.core.sql.schema.ColumnDataType +import org.evomaster.core.sql.schema.Table import org.evomaster.core.problem.rest.RestActionBuilderV3 import org.evomaster.core.problem.rest.RestCallAction import org.evomaster.core.problem.rest.RestIndividual diff --git a/core/src/test/kotlin/org/evomaster/core/search/structuralelement/action/DbActionStructureTest.kt b/core/src/test/kotlin/org/evomaster/core/search/structuralelement/action/DbActionStructureTest.kt index 3dbc473f3e..41b8785be3 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/structuralelement/action/DbActionStructureTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/structuralelement/action/DbActionStructureTest.kt @@ -1,10 +1,10 @@ package org.evomaster.core.search.structuralelement.action import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.schema.Column -import org.evomaster.core.database.schema.ColumnDataType -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.schema.Column +import org.evomaster.core.sql.schema.ColumnDataType +import org.evomaster.core.sql.schema.Table import org.evomaster.core.search.gene.datetime.DateGene import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.ObjectGene diff --git a/core/src/test/kotlin/org/evomaster/core/search/structuralelement/individual/RestIndividualStructureTest.kt b/core/src/test/kotlin/org/evomaster/core/search/structuralelement/individual/RestIndividualStructureTest.kt index 412eebc9d6..26226f65c3 100644 --- a/core/src/test/kotlin/org/evomaster/core/search/structuralelement/individual/RestIndividualStructureTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/search/structuralelement/individual/RestIndividualStructureTest.kt @@ -1,11 +1,11 @@ package org.evomaster.core.search.structuralelement.individual import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.schema.Column -import org.evomaster.core.database.schema.ColumnDataType -import org.evomaster.core.database.schema.ForeignKey -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.schema.Column +import org.evomaster.core.sql.schema.ColumnDataType +import org.evomaster.core.sql.schema.ForeignKey +import org.evomaster.core.sql.schema.Table import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.rest.param.BodyParam diff --git a/core/src/test/kotlin/org/evomaster/core/database/DbActionGeneBuilderTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/DbActionGeneBuilderTest.kt similarity index 98% rename from core/src/test/kotlin/org/evomaster/core/database/DbActionGeneBuilderTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/DbActionGeneBuilderTest.kt index 225f450acd..cf2b338b92 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/DbActionGeneBuilderTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/DbActionGeneBuilderTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database +package org.evomaster.core.sql import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.core.search.gene.regex.RegexGene.Companion.DATABASE_REGEX_PREFIX diff --git a/core/src/test/kotlin/org/evomaster/core/database/DbActionRepairTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/DbActionRepairTest.kt similarity index 86% rename from core/src/test/kotlin/org/evomaster/core/database/DbActionRepairTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/DbActionRepairTest.kt index e335166004..2a30bf5258 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/DbActionRepairTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/DbActionRepairTest.kt @@ -1,9 +1,9 @@ -package org.evomaster.core.database +package org.evomaster.core.sql import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType -import org.evomaster.core.database.schema.Column -import org.evomaster.core.database.schema.ColumnDataType -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.schema.Column +import org.evomaster.core.sql.schema.ColumnDataType +import org.evomaster.core.sql.schema.Table import org.evomaster.dbconstraint.EnumConstraint import org.evomaster.dbconstraint.IffConstraint import org.evomaster.dbconstraint.IsNotNullConstraint diff --git a/core/src/test/kotlin/org/evomaster/core/database/DbActionTransformerTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/DbActionTransformerTest.kt similarity index 89% rename from core/src/test/kotlin/org/evomaster/core/database/DbActionTransformerTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/DbActionTransformerTest.kt index eabaeb147c..fc1f67749e 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/DbActionTransformerTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/DbActionTransformerTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database +package org.evomaster.core.sql import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test diff --git a/core/src/test/kotlin/org/evomaster/core/database/DbActionUtilsTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/DbActionUtilsTest.kt similarity index 98% rename from core/src/test/kotlin/org/evomaster/core/database/DbActionUtilsTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/DbActionUtilsTest.kt index 992a1b0228..06af4490f4 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/DbActionUtilsTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/DbActionUtilsTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database +package org.evomaster.core.sql import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType -import org.evomaster.core.database.schema.Column -import org.evomaster.core.database.schema.ColumnDataType -import org.evomaster.core.database.schema.ForeignKey -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.schema.Column +import org.evomaster.core.sql.schema.ColumnDataType +import org.evomaster.core.sql.schema.ForeignKey +import org.evomaster.core.sql.schema.Table import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.search.gene.* diff --git a/core/src/test/kotlin/org/evomaster/core/database/PostgresToJavaRegExTranslatorTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/PostgresToJavaRegExTranslatorTest.kt similarity index 97% rename from core/src/test/kotlin/org/evomaster/core/database/PostgresToJavaRegExTranslatorTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/PostgresToJavaRegExTranslatorTest.kt index 4f2e3f0c13..6b1c1960a4 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/PostgresToJavaRegExTranslatorTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/PostgresToJavaRegExTranslatorTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database +package org.evomaster.core.sql import org.junit.Assert import org.junit.jupiter.api.Test diff --git a/core/src/test/kotlin/org/evomaster/core/database/SqlInsertBuilderTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/SqlInsertBuilderTest.kt similarity index 99% rename from core/src/test/kotlin/org/evomaster/core/database/SqlInsertBuilderTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/SqlInsertBuilderTest.kt index 6960c432e5..034a3b7a45 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/SqlInsertBuilderTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/SqlInsertBuilderTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database +package org.evomaster.core.sql import org.evomaster.client.java.controller.api.dto.database.operations.* import org.evomaster.client.java.controller.db.SqlScriptRunner diff --git a/core/src/test/kotlin/org/evomaster/core/database/TableConstraintEvaluatorTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/TableConstraintEvaluatorTest.kt similarity index 99% rename from core/src/test/kotlin/org/evomaster/core/database/TableConstraintEvaluatorTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/TableConstraintEvaluatorTest.kt index ae575f55ba..47ab094932 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/TableConstraintEvaluatorTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/TableConstraintEvaluatorTest.kt @@ -1,9 +1,9 @@ -package org.evomaster.core.database +package org.evomaster.core.sql import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType -import org.evomaster.core.database.schema.Column -import org.evomaster.core.database.schema.ColumnDataType -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.schema.Column +import org.evomaster.core.sql.schema.ColumnDataType +import org.evomaster.core.sql.schema.Table import org.evomaster.core.search.gene.datetime.DateTimeGene import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.string.StringGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/TableConstraintGeneCollectorTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/TableConstraintGeneCollectorTest.kt similarity index 98% rename from core/src/test/kotlin/org/evomaster/core/database/TableConstraintGeneCollectorTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/TableConstraintGeneCollectorTest.kt index 119287cce5..a82d33c78a 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/TableConstraintGeneCollectorTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/TableConstraintGeneCollectorTest.kt @@ -1,9 +1,9 @@ -package org.evomaster.core.database +package org.evomaster.core.sql import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType -import org.evomaster.core.database.schema.Column -import org.evomaster.core.database.schema.ColumnDataType -import org.evomaster.core.database.schema.Table +import org.evomaster.core.sql.schema.Column +import org.evomaster.core.sql.schema.ColumnDataType +import org.evomaster.core.sql.schema.Table import org.evomaster.core.search.gene.Gene import org.evomaster.dbconstraint.* import org.junit.Assert.assertEquals diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/AlterTableCheckEnumTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/AlterTableCheckEnumTest.kt similarity index 96% rename from core/src/test/kotlin/org/evomaster/core/database/extract/h2/AlterTableCheckEnumTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/h2/AlterTableCheckEnumTest.kt index 687b94d326..0b46309798 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/AlterTableCheckEnumTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/AlterTableCheckEnumTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.h2 +package org.evomaster.core.sql.extract.h2 import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/AlterTableCheckTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/AlterTableCheckTest.kt similarity index 96% rename from core/src/test/kotlin/org/evomaster/core/database/extract/h2/AlterTableCheckTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/h2/AlterTableCheckTest.kt index bcd9a13658..45606d621f 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/AlterTableCheckTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/AlterTableCheckTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.h2 +package org.evomaster.core.sql.extract.h2 import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/AlterTableUniqueTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/AlterTableUniqueTest.kt similarity index 97% rename from core/src/test/kotlin/org/evomaster/core/database/extract/h2/AlterTableUniqueTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/h2/AlterTableUniqueTest.kt index eaec79e122..dedb7ef7e4 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/AlterTableUniqueTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/AlterTableUniqueTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.h2 +package org.evomaster.core.sql.extract.h2 import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/CatwatchSqlExtractTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/CatwatchSqlExtractTest.kt similarity index 94% rename from core/src/test/kotlin/org/evomaster/core/database/extract/h2/CatwatchSqlExtractTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/h2/CatwatchSqlExtractTest.kt index 51e17cf20a..64426195e2 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/CatwatchSqlExtractTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/CatwatchSqlExtractTest.kt @@ -1,11 +1,11 @@ -package org.evomaster.core.database.extract.h2 +package org.evomaster.core.sql.extract.h2 import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlActionUtils -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlActionUtils +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.search.gene.sql.SqlPrimaryKeyGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/CreateTableCheckEnumTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/CreateTableCheckEnumTest.kt similarity index 96% rename from core/src/test/kotlin/org/evomaster/core/database/extract/h2/CreateTableCheckEnumTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/h2/CreateTableCheckEnumTest.kt index b799413f22..c0eaf7ae5c 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/CreateTableCheckEnumTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/CreateTableCheckEnumTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.h2 +package org.evomaster.core.sql.extract.h2 import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/CreateTableCheckTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/CreateTableCheckTest.kt similarity index 96% rename from core/src/test/kotlin/org/evomaster/core/database/extract/h2/CreateTableCheckTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/h2/CreateTableCheckTest.kt index ddea2bc59f..0952a06132 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/CreateTableCheckTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/CreateTableCheckTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.h2 +package org.evomaster.core.sql.extract.h2 import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/DoctorsExtractTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/DoctorsExtractTest.kt similarity index 83% rename from core/src/test/kotlin/org/evomaster/core/database/extract/h2/DoctorsExtractTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/h2/DoctorsExtractTest.kt index f128b7b3fd..f4b7164662 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/DoctorsExtractTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/DoctorsExtractTest.kt @@ -1,9 +1,9 @@ -package org.evomaster.core.database.extract.h2 +package org.evomaster.core.sql.extract.h2 import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlActionUtils -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlActionUtils +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.search.service.Randomness diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/ExtractTestBaseH2.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/ExtractTestBaseH2.kt similarity index 95% rename from core/src/test/kotlin/org/evomaster/core/database/extract/h2/ExtractTestBaseH2.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/h2/ExtractTestBaseH2.kt index fe38d25b18..6c1a4e5cea 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/ExtractTestBaseH2.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/ExtractTestBaseH2.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.h2 +package org.evomaster.core.sql.extract.h2 import org.evomaster.client.java.controller.db.SqlScriptRunner import org.junit.jupiter.api.AfterAll diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/FeaturesServiceSqlExtractTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/FeaturesServiceSqlExtractTest.kt similarity index 97% rename from core/src/test/kotlin/org/evomaster/core/database/extract/h2/FeaturesServiceSqlExtractTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/h2/FeaturesServiceSqlExtractTest.kt index a22c6bd942..6b2758946b 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/FeaturesServiceSqlExtractTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/FeaturesServiceSqlExtractTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.h2 +package org.evomaster.core.sql.extract.h2 import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/NewsSqlExtractTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/NewsSqlExtractTest.kt similarity index 94% rename from core/src/test/kotlin/org/evomaster/core/database/extract/h2/NewsSqlExtractTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/h2/NewsSqlExtractTest.kt index f114d0cd21..1ecb9e2a4e 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/NewsSqlExtractTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/NewsSqlExtractTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.h2 +package org.evomaster.core.sql.extract.h2 import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/OcvnExtractTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/OcvnExtractTest.kt similarity index 97% rename from core/src/test/kotlin/org/evomaster/core/database/extract/h2/OcvnExtractTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/h2/OcvnExtractTest.kt index 6150ed9c6a..de17fbddc6 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/OcvnExtractTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/OcvnExtractTest.kt @@ -1,8 +1,8 @@ -package org.evomaster.core.database.extract.h2 +package org.evomaster.core.sql.extract.h2 import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.optional.NullableGene import org.evomaster.core.search.gene.string.StringGene import org.junit.jupiter.api.Assertions.* diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/ProxyPrintSqlExtractTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/ProxyPrintSqlExtractTest.kt similarity index 97% rename from core/src/test/kotlin/org/evomaster/core/database/extract/h2/ProxyPrintSqlExtractTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/h2/ProxyPrintSqlExtractTest.kt index 626ee7c807..09dea3c8c9 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/ProxyPrintSqlExtractTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/ProxyPrintSqlExtractTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.h2 +package org.evomaster.core.sql.extract.h2 import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlActionUtils -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlActionUtils +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.problem.rest.RestIndividual import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.search.gene.sql.SqlAutoIncrementGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/ScoutApiSqlExtractTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/ScoutApiSqlExtractTest.kt similarity index 98% rename from core/src/test/kotlin/org/evomaster/core/database/extract/h2/ScoutApiSqlExtractTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/h2/ScoutApiSqlExtractTest.kt index 87e3baf104..f17002054e 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/h2/ScoutApiSqlExtractTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/h2/ScoutApiSqlExtractTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.h2 +package org.evomaster.core.sql.extract.h2 import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/AlterTableExtractCheckTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/AlterTableExtractCheckTest.kt similarity index 96% rename from core/src/test/kotlin/org/evomaster/core/database/extract/mysql/AlterTableExtractCheckTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/AlterTableExtractCheckTest.kt index 9c441a6df6..a7dec4f5eb 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/AlterTableExtractCheckTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/AlterTableExtractCheckTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.mysql +package org.evomaster.core.sql.extract.mysql import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/AlterTableUniqueTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/AlterTableUniqueTest.kt similarity index 97% rename from core/src/test/kotlin/org/evomaster/core/database/extract/mysql/AlterTableUniqueTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/AlterTableUniqueTest.kt index 27e62919e3..ec0b11db72 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/AlterTableUniqueTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/AlterTableUniqueTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.mysql +package org.evomaster.core.sql.extract.mysql import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/CreateTableBoundedNumberTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/CreateTableBoundedNumberTest.kt similarity index 97% rename from core/src/test/kotlin/org/evomaster/core/database/extract/mysql/CreateTableBoundedNumberTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/CreateTableBoundedNumberTest.kt index 457afbc31c..107b834f32 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/CreateTableBoundedNumberTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/CreateTableBoundedNumberTest.kt @@ -1,8 +1,8 @@ -package org.evomaster.core.database.extract.mysql +package org.evomaster.core.sql.extract.mysql import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.numeric.BigDecimalGene import org.evomaster.core.search.gene.numeric.DoubleGene import org.evomaster.core.search.gene.numeric.IntegerGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/CreateTableCheckT1Test.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/CreateTableCheckT1Test.kt similarity index 97% rename from core/src/test/kotlin/org/evomaster/core/database/extract/mysql/CreateTableCheckT1Test.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/CreateTableCheckT1Test.kt index 6f9df58157..29deffbaab 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/CreateTableCheckT1Test.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/CreateTableCheckT1Test.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.mysql +package org.evomaster.core.sql.extract.mysql import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/CreateTableCheckTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/CreateTableCheckTest.kt similarity index 96% rename from core/src/test/kotlin/org/evomaster/core/database/extract/mysql/CreateTableCheckTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/CreateTableCheckTest.kt index 4273193567..caad519e16 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/CreateTableCheckTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/CreateTableCheckTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.mysql +package org.evomaster.core.sql.extract.mysql import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/CreateTableEnumTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/CreateTableEnumTest.kt similarity index 94% rename from core/src/test/kotlin/org/evomaster/core/database/extract/mysql/CreateTableEnumTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/CreateTableEnumTest.kt index 6d1f4bb476..8b4142d230 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/CreateTableEnumTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/CreateTableEnumTest.kt @@ -1,7 +1,7 @@ -package org.evomaster.core.database.extract.mysql +package org.evomaster.core.sql.extract.mysql import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.collection.EnumGene import org.evomaster.core.search.gene.optional.NullableGene import org.junit.jupiter.api.Assertions.* diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/CreateTableUniqueTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/CreateTableUniqueTest.kt similarity index 97% rename from core/src/test/kotlin/org/evomaster/core/database/extract/mysql/CreateTableUniqueTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/CreateTableUniqueTest.kt index 832e812ed7..f4a9e14ac2 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/CreateTableUniqueTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/CreateTableUniqueTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.mysql +package org.evomaster.core.sql.extract.mysql import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/ExtractTestBaseMySQL.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/ExtractTestBaseMySQL.kt similarity index 97% rename from core/src/test/kotlin/org/evomaster/core/database/extract/mysql/ExtractTestBaseMySQL.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/ExtractTestBaseMySQL.kt index 506a5e786c..c392a5ad26 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/ExtractTestBaseMySQL.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/ExtractTestBaseMySQL.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.mysql +package org.evomaster.core.sql.extract.mysql import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.DbCleaner diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/LikeCheckTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/LikeCheckTest.kt similarity index 90% rename from core/src/test/kotlin/org/evomaster/core/database/extract/mysql/LikeCheckTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/LikeCheckTest.kt index e27baf82b5..b3b0cddf44 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/LikeCheckTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/LikeCheckTest.kt @@ -1,9 +1,9 @@ -package org.evomaster.core.database.extract.mysql +package org.evomaster.core.sql.extract.mysql import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.regex.RegexGene import org.evomaster.core.search.service.Randomness import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SQLJSONColumnTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/SQLJSONColumnTest.kt similarity index 97% rename from core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SQLJSONColumnTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/SQLJSONColumnTest.kt index ae5f94d997..484bf08eab 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SQLJSONColumnTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/SQLJSONColumnTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.mysql +package org.evomaster.core.sql.extract.mysql import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.collection.ArrayGene import org.evomaster.core.search.gene.numeric.DoubleGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SqlDateColumnTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/SqlDateColumnTest.kt similarity index 94% rename from core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SqlDateColumnTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/SqlDateColumnTest.kt index 883d4b8929..5e70dc35cf 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SqlDateColumnTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/SqlDateColumnTest.kt @@ -1,9 +1,9 @@ -package org.evomaster.core.database.extract.mysql +package org.evomaster.core.sql.extract.mysql import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.datetime.DateGene import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.sql.SqlPrimaryKeyGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SqlDateTypesColumnTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/SqlDateTypesColumnTest.kt similarity index 89% rename from core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SqlDateTypesColumnTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/SqlDateTypesColumnTest.kt index 5a7a21da93..43ba019c35 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SqlDateTypesColumnTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/SqlDateTypesColumnTest.kt @@ -1,7 +1,7 @@ -package org.evomaster.core.database.extract.mysql +package org.evomaster.core.sql.extract.mysql import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.datetime.DateTimeGene import org.evomaster.core.search.gene.datetime.TimeGene import org.junit.jupiter.api.Assertions.* diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SqlTextColumnTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/SqlTextColumnTest.kt similarity index 95% rename from core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SqlTextColumnTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/SqlTextColumnTest.kt index 141a1857c7..35609b6b8a 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/mysql/SqlTextColumnTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/mysql/SqlTextColumnTest.kt @@ -1,9 +1,9 @@ -package org.evomaster.core.database.extract.mysql +package org.evomaster.core.sql.extract.mysql import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.string.StringGene import org.evomaster.core.search.gene.optional.NullableGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/AlterTableExtractCheckTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/AlterTableExtractCheckTest.kt similarity index 95% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/AlterTableExtractCheckTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/AlterTableExtractCheckTest.kt index 976baac3b3..7eb88aa2ae 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/AlterTableExtractCheckTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/AlterTableExtractCheckTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/AlterTableExtractUniqueTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/AlterTableExtractUniqueTest.kt similarity index 96% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/AlterTableExtractUniqueTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/AlterTableExtractUniqueTest.kt index ea2e46ca77..c772cbbe27 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/AlterTableExtractUniqueTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/AlterTableExtractUniqueTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ArrayTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/ArrayTypesTest.kt similarity index 98% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ArrayTypesTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/ArrayTypesTest.kt index fd03c3a70e..871075cece 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ArrayTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/ArrayTypesTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.sql.SqlMultidimensionalArrayGene import org.evomaster.core.search.gene.optional.NullableGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BinaryTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/BinaryTypesTest.kt similarity index 94% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BinaryTypesTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/BinaryTypesTest.kt index f7d66fd665..60493a715b 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BinaryTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/BinaryTypesTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.collection.ArrayGene import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.sql.SqlBinaryStringGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BitStringTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/BitStringTypesTest.kt similarity index 92% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BitStringTypesTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/BitStringTypesTest.kt index 83b7383a57..06c98ee472 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BitStringTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/BitStringTypesTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.collection.ArrayGene import org.evomaster.core.search.gene.BooleanGene import org.evomaster.core.search.gene.sql.SqlBitStringGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BooleanTypeTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/BooleanTypeTest.kt similarity index 88% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BooleanTypeTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/BooleanTypeTest.kt index a9919014da..ebf4a9f836 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/BooleanTypeTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/BooleanTypeTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.* import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/CharacterTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/CharacterTypesTest.kt similarity index 95% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/CharacterTypesTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/CharacterTypesTest.kt index 03146d849f..f8e9e762a1 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/CharacterTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/CharacterTypesTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.string.StringGene import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/CompositeTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/CompositeTypesTest.kt similarity index 96% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/CompositeTypesTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/CompositeTypesTest.kt index 458bd79860..996d4a8562 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/CompositeTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/CompositeTypesTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/CreateTableExtractCheckTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/CreateTableExtractCheckTest.kt similarity index 95% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/CreateTableExtractCheckTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/CreateTableExtractCheckTest.kt index 183b9c8d24..dd651f8acd 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/CreateTableExtractCheckTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/CreateTableExtractCheckTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/DatetimeTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/DatetimeTypesTest.kt similarity index 91% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/DatetimeTypesTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/DatetimeTypesTest.kt index edf2490a33..b5abf3571a 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/DatetimeTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/DatetimeTypesTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.datetime.DateGene import org.evomaster.core.search.gene.datetime.DateTimeGene import org.evomaster.core.search.gene.sql.time.SqlTimeIntervalGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/EnumeratedTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/EnumeratedTypesTest.kt similarity index 88% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/EnumeratedTypesTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/EnumeratedTypesTest.kt index 9e79859150..0a1d18bfcc 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/EnumeratedTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/EnumeratedTypesTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.collection.EnumGene import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ExtractTestBasePostgres.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/ExtractTestBasePostgres.kt similarity index 98% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ExtractTestBasePostgres.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/ExtractTestBasePostgres.kt index f2a9ca97d0..d793a7d736 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ExtractTestBasePostgres.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/ExtractTestBasePostgres.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.core.KGenericContainer diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/GeometricTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/GeometricTypesTest.kt similarity index 95% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/GeometricTypesTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/GeometricTypesTest.kt index 314554d675..252c035a8a 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/GeometricTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/GeometricTypesTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.collection.ArrayGene import org.evomaster.core.search.gene.sql.geometric.* import org.junit.jupiter.api.Assertions.* diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/Ind0ExtractTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/Ind0ExtractTest.kt similarity index 97% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/Ind0ExtractTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/Ind0ExtractTest.kt index d8c6e2bfc9..bcaac62956 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/Ind0ExtractTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/Ind0ExtractTest.kt @@ -1,8 +1,8 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.UUIDGene import org.evomaster.core.search.gene.datetime.DateGene import org.evomaster.core.search.gene.regex.RegexGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/JSONTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/JSONTypesTest.kt similarity index 90% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/JSONTypesTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/JSONTypesTest.kt index 954ff31943..8cc00c9a3b 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/JSONTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/JSONTypesTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.sql.SqlJSONGene import org.evomaster.core.search.gene.sql.SqlJSONPathGene import org.junit.jupiter.api.Assertions.* diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/LikeCheckTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/LikeCheckTest.kt similarity index 90% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/LikeCheckTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/LikeCheckTest.kt index 4f04a7131f..10b38fbe2b 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/LikeCheckTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/LikeCheckTest.kt @@ -1,9 +1,9 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.regex.RegexGene import org.evomaster.core.search.service.Randomness import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ManySimilarToChecksTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/ManySimilarToChecksTest.kt similarity index 95% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ManySimilarToChecksTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/ManySimilarToChecksTest.kt index a4272caabd..292c751421 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ManySimilarToChecksTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/ManySimilarToChecksTest.kt @@ -1,9 +1,9 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.regex.RegexGene import org.evomaster.core.search.service.Randomness import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/MonetaryTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/MonetaryTypesTest.kt similarity index 88% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/MonetaryTypesTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/MonetaryTypesTest.kt index 7881ddc8e0..0f2a8c58f0 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/MonetaryTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/MonetaryTypesTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.numeric.BigDecimalGene import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/MultiRangeTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/MultiRangeTypesTest.kt similarity index 98% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/MultiRangeTypesTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/MultiRangeTypesTest.kt index 2ee86221b6..65a1ef6789 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/MultiRangeTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/MultiRangeTypesTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.datetime.DateGene import org.evomaster.core.search.gene.datetime.DateTimeGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/NetworkTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/NetworkTypesTest.kt similarity index 93% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/NetworkTypesTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/NetworkTypesTest.kt index 70125fc912..9a46f6ac97 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/NetworkTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/NetworkTypesTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.network.CidrGene import org.evomaster.core.search.gene.network.InetGene import org.evomaster.core.search.gene.network.MacAddrGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/NumericTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/NumericTypesTest.kt similarity index 96% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/NumericTypesTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/NumericTypesTest.kt index 16509d7eb9..5e14f5ee75 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/NumericTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/NumericTypesTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.numeric.DoubleGene import org.evomaster.core.search.gene.numeric.FloatGene import org.evomaster.core.search.gene.numeric.IntegerGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ObjectIdentifierTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/ObjectIdentifierTypesTest.kt similarity index 97% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ObjectIdentifierTypesTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/ObjectIdentifierTypesTest.kt index c21473db23..c6fd8f9e56 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/ObjectIdentifierTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/ObjectIdentifierTypesTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.numeric.IntegerGene import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/PgLsnTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/PgLsnTest.kt similarity index 95% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/PgLsnTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/PgLsnTest.kt index c4efbbae03..fd9e8646c4 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/PgLsnTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/PgLsnTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.numeric.LongGene import org.evomaster.core.search.gene.sql.SqlLogSeqNumberGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/RangeTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/RangeTypesTest.kt similarity index 97% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/RangeTypesTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/RangeTypesTest.kt index a91a7cbba7..5c6d52065c 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/RangeTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/RangeTypesTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.datetime.DateGene import org.evomaster.core.search.gene.datetime.DateTimeGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SimilarToCheckTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/SimilarToCheckTest.kt similarity index 94% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SimilarToCheckTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/SimilarToCheckTest.kt index f11aca506f..127cb9a2fa 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SimilarToCheckTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/SimilarToCheckTest.kt @@ -1,9 +1,9 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.regex.RegexGene import org.evomaster.core.search.service.Randomness import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlDateColumnTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/SqlDateColumnTest.kt similarity index 94% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlDateColumnTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/SqlDateColumnTest.kt index c8de0963d3..30769cbaef 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlDateColumnTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/SqlDateColumnTest.kt @@ -1,9 +1,9 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.datetime.DateGene import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.sql.SqlPrimaryKeyGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlJSONBColumnTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/SqlJSONBColumnTest.kt similarity index 97% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlJSONBColumnTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/SqlJSONBColumnTest.kt index 967df8acd7..dafc010ea9 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlJSONBColumnTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/SqlJSONBColumnTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.collection.ArrayGene import org.evomaster.core.search.gene.numeric.DoubleGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlTextColumnTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/SqlTextColumnTest.kt similarity index 96% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlTextColumnTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/SqlTextColumnTest.kt index 1fb72985e2..bb2631518c 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlTextColumnTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/SqlTextColumnTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.optional.NullableGene import org.evomaster.core.search.gene.sql.SqlPrimaryKeyGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlUUIDColumnTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/SqlUUIDColumnTest.kt similarity index 97% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlUUIDColumnTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/SqlUUIDColumnTest.kt index 5fe69d4d06..d3cbdbf242 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlUUIDColumnTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/SqlUUIDColumnTest.kt @@ -1,9 +1,9 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.optional.NullableGene import org.evomaster.core.search.gene.sql.SqlPrimaryKeyGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlXMLColumnTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/SqlXMLColumnTest.kt similarity index 97% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlXMLColumnTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/SqlXMLColumnTest.kt index f4adea7582..94ab7913ff 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/SqlXMLColumnTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/SqlXMLColumnTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlAction -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlAction +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.* import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.sql.SqlXMLGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/TextSearchTypesTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/TextSearchTypesTest.kt similarity index 96% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/TextSearchTypesTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/TextSearchTypesTest.kt index e1cc16bc12..bff8844bee 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/TextSearchTypesTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/TextSearchTypesTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.string.StringGene import org.evomaster.core.search.gene.sql.textsearch.SqlTextSearchQueryGene import org.evomaster.core.search.gene.sql.textsearch.SqlTextSearchVectorGene diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UUIDTypeTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/UUIDTypeTest.kt similarity index 88% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UUIDTypeTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/UUIDTypeTest.kt index 633c4c1cbd..bdb709216e 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UUIDTypeTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/UUIDTypeTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.UUIDGene import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UnsupportedBitCompositeTypeTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/UnsupportedBitCompositeTypeTest.kt similarity index 90% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UnsupportedBitCompositeTypeTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/UnsupportedBitCompositeTypeTest.kt index 3fb15d41f0..5e9acd6382 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UnsupportedBitCompositeTypeTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/UnsupportedBitCompositeTypeTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.internal.db.SchemaExtractor import org.junit.jupiter.api.Test diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UnsupportedCharCompositeTypeTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/UnsupportedCharCompositeTypeTest.kt similarity index 90% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UnsupportedCharCompositeTypeTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/UnsupportedCharCompositeTypeTest.kt index 3566ea8a03..cbe241dc5c 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UnsupportedCharCompositeTypeTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/UnsupportedCharCompositeTypeTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.internal.db.SchemaExtractor import org.junit.jupiter.api.Test diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UnsupportedNumericCompositeTypeTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/UnsupportedNumericCompositeTypeTest.kt similarity index 90% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UnsupportedNumericCompositeTypeTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/UnsupportedNumericCompositeTypeTest.kt index 0b04f0e8a6..dcbc01eb0f 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UnsupportedNumericCompositeTypeTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/UnsupportedNumericCompositeTypeTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.internal.db.SchemaExtractor import org.junit.jupiter.api.Test diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UnsupportedVarbitCompositeTypeTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/UnsupportedVarbitCompositeTypeTest.kt similarity index 90% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UnsupportedVarbitCompositeTypeTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/UnsupportedVarbitCompositeTypeTest.kt index cd1976260e..7c71696670 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UnsupportedVarbitCompositeTypeTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/UnsupportedVarbitCompositeTypeTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.internal.db.SchemaExtractor import org.junit.jupiter.api.Test diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UnsupportedVarcharCompositeTypeTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/UnsupportedVarcharCompositeTypeTest.kt similarity index 90% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UnsupportedVarcharCompositeTypeTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/UnsupportedVarcharCompositeTypeTest.kt index 90cc74f5a5..30e71ad99c 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/UnsupportedVarcharCompositeTypeTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/UnsupportedVarcharCompositeTypeTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.internal.db.SchemaExtractor import org.junit.jupiter.api.Test diff --git a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/XMLTypeTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/XMLTypeTest.kt similarity index 88% rename from core/src/test/kotlin/org/evomaster/core/database/extract/postgres/XMLTypeTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/XMLTypeTest.kt index dcaf3d8a9f..d7080a3273 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/extract/postgres/XMLTypeTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/extract/postgres/XMLTypeTest.kt @@ -1,10 +1,10 @@ -package org.evomaster.core.database.extract.postgres +package org.evomaster.core.sql.extract.postgres import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.client.java.controller.internal.db.SchemaExtractor -import org.evomaster.core.database.SqlActionTransformer -import org.evomaster.core.database.SqlInsertBuilder +import org.evomaster.core.sql.SqlActionTransformer +import org.evomaster.core.sql.SqlInsertBuilder import org.evomaster.core.search.gene.sql.SqlXMLGene import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test diff --git a/core/src/test/kotlin/org/evomaster/core/database/insertion/h2/H2InsertValueTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/insertion/h2/H2InsertValueTest.kt similarity index 98% rename from core/src/test/kotlin/org/evomaster/core/database/insertion/h2/H2InsertValueTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/insertion/h2/H2InsertValueTest.kt index d93bdaf000..f5bece7a78 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/insertion/h2/H2InsertValueTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/insertion/h2/H2InsertValueTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.insertion.h2 +package org.evomaster.core.sql.insertion.h2 import org.evomaster.client.java.controller.db.SqlScriptRunner import org.junit.jupiter.api.Assertions.* diff --git a/core/src/test/kotlin/org/evomaster/core/database/insertion/mysql/MySQLInsertValueTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/insertion/mysql/MySQLInsertValueTest.kt similarity index 99% rename from core/src/test/kotlin/org/evomaster/core/database/insertion/mysql/MySQLInsertValueTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/insertion/mysql/MySQLInsertValueTest.kt index d661718814..bdf6b0350a 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/insertion/mysql/MySQLInsertValueTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/insertion/mysql/MySQLInsertValueTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.insertion.mysql +package org.evomaster.core.sql.insertion.mysql import org.evomaster.client.java.controller.db.SqlScriptRunner import org.evomaster.core.KGenericContainer diff --git a/core/src/test/kotlin/org/evomaster/core/database/insertion/postgres/PostgresInsertValueTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/insertion/postgres/PostgresInsertValueTest.kt similarity index 99% rename from core/src/test/kotlin/org/evomaster/core/database/insertion/postgres/PostgresInsertValueTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/insertion/postgres/PostgresInsertValueTest.kt index d374cd6b26..a74ad0012d 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/insertion/postgres/PostgresInsertValueTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/insertion/postgres/PostgresInsertValueTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.insertion.postgres +package org.evomaster.core.sql.insertion.postgres import com.google.gson.Gson import org.evomaster.client.java.controller.db.SqlScriptRunner diff --git a/core/src/test/kotlin/org/evomaster/core/database/schema/ColumnFactoryTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/schema/ColumnFactoryTest.kt similarity index 96% rename from core/src/test/kotlin/org/evomaster/core/database/schema/ColumnFactoryTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/schema/ColumnFactoryTest.kt index 0e00fb36bf..d5dbc13606 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/schema/ColumnFactoryTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/schema/ColumnFactoryTest.kt @@ -1,9 +1,8 @@ -package org.evomaster.core.database.schema +package org.evomaster.core.sql.schema import org.evomaster.client.java.controller.api.dto.database.schema.ColumnDto import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType import org.hamcrest.CoreMatchers.containsString -import org.hamcrest.CoreMatchers.equalTo import org.junit.Assert.assertEquals import org.junit.Assert.assertThat import org.junit.jupiter.api.Test diff --git a/core/src/test/kotlin/org/evomaster/core/database/schema/h2/H2GeometryTypeParserTest.kt b/core/src/test/kotlin/org/evomaster/core/sql/schema/h2/H2GeometryTypeParserTest.kt similarity index 99% rename from core/src/test/kotlin/org/evomaster/core/database/schema/h2/H2GeometryTypeParserTest.kt rename to core/src/test/kotlin/org/evomaster/core/sql/schema/h2/H2GeometryTypeParserTest.kt index 9832f77f05..947ded6e9d 100644 --- a/core/src/test/kotlin/org/evomaster/core/database/schema/h2/H2GeometryTypeParserTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/sql/schema/h2/H2GeometryTypeParserTest.kt @@ -1,4 +1,4 @@ -package org.evomaster.core.database.schema.h2 +package org.evomaster.core.sql.schema.h2 import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test diff --git a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/db/directintwithsql/DbDirectIntWithSqlEMTest.java b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/db/directintwithsql/DbDirectIntWithSqlEMTest.java index 0c65d231ed..332a88288c 100644 --- a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/db/directintwithsql/DbDirectIntWithSqlEMTest.java +++ b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/db/directintwithsql/DbDirectIntWithSqlEMTest.java @@ -5,7 +5,7 @@ import com.google.inject.TypeLiteral; import org.evomaster.core.EMConfig; import org.evomaster.core.Main; -import org.evomaster.core.database.SqlAction; +import org.evomaster.core.sql.SqlAction; import org.evomaster.core.problem.rest.HttpVerb; import org.evomaster.core.problem.rest.RestCallAction; import org.evomaster.core.problem.rest.RestCallResult; diff --git a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java index 403b93acbb..eadbeb9f75 100644 --- a/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java +++ b/e2e-tests/spring-rest-h2-v1/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java @@ -1,7 +1,7 @@ package org.evomaster.e2etests.spring.examples.resource.db; import com.google.inject.Injector; -import org.evomaster.core.database.SqlAction; +import org.evomaster.core.sql.SqlAction; import org.evomaster.core.problem.rest.resource.RestResourceCalls; import org.evomaster.core.problem.rest.resource.RestResourceNode; import org.evomaster.core.problem.rest.service.ResourceManageService; diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/db/directintwithsql/DbDirectIntWithSqlEMTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/db/directintwithsql/DbDirectIntWithSqlEMTest.java index 2790503e84..a95bfab5cf 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/db/directintwithsql/DbDirectIntWithSqlEMTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/db/directintwithsql/DbDirectIntWithSqlEMTest.java @@ -5,7 +5,7 @@ import com.google.inject.TypeLiteral; import org.evomaster.core.EMConfig; import org.evomaster.core.Main; -import org.evomaster.core.database.SqlAction; +import org.evomaster.core.sql.SqlAction; import org.evomaster.core.problem.rest.HttpVerb; import org.evomaster.core.problem.rest.RestCallAction; import org.evomaster.core.problem.rest.RestCallResult; diff --git a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java index 6724442dd1..b318e6dd14 100644 --- a/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java +++ b/e2e-tests/spring-rest-openapi-v2/src/test/java/org/evomaster/e2etests/spring/examples/resource/db/ResourceDbMIOBasicTest.java @@ -1,7 +1,7 @@ package org.evomaster.e2etests.spring.examples.resource.db; import com.google.inject.Injector; -import org.evomaster.core.database.SqlAction; +import org.evomaster.core.sql.SqlAction; import org.evomaster.core.problem.rest.resource.RestResourceCalls; import org.evomaster.core.problem.rest.resource.RestResourceNode; import org.evomaster.core.problem.rest.service.ResourceManageService; From 7a31b746fb03479df7c7c34b17682c7301a5613d Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Thu, 3 Aug 2023 14:51:56 +0200 Subject: [PATCH 146/345] comment, for branch --- .../org/evomaster/core/problem/rest/RestActionBuilderV3.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3.kt index 56d4f13dd0..69123be4c5 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/RestActionBuilderV3.kt @@ -1203,6 +1203,7 @@ object RestActionBuilderV3 { 0 -> warn("There is _NO_ usable RESTful API endpoint defined in the schema configuration") 1 -> info("There is only one usable RESTful API endpoint defined in the schema configuration") else -> info("There are $n usable RESTful API endpoints defined in the schema configuration") + FIXME do same for GraphQL } if (errorEndpoints.isNotEmpty()){ From 34796ee47c7a06eeed7f3f2b55611cbef8037775 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Thu, 3 Aug 2023 15:53:09 +0200 Subject: [PATCH 147/345] reviewing exp configs --- .../kotlin/org/evomaster/core/EMConfig.kt | 93 +++++++++---------- 1 file changed, 42 insertions(+), 51 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt index a5a91800e1..75e0fbdbfc 100644 --- a/core/src/main/kotlin/org/evomaster/core/EMConfig.kt +++ b/core/src/main/kotlin/org/evomaster/core/EMConfig.kt @@ -589,6 +589,16 @@ class EMConfig { annotation class Regex(val regex: String) + /** + * For internal configurations that we introduced just to get more info on EM, + * with aim of debugging issues. + */ + @Target(AnnotationTarget.PROPERTY) + @MustBeDocumented + annotation class Debug + + + /** * A double value between 0 and 1 */ @@ -826,7 +836,6 @@ class EMConfig { "This is achieved by turning the option --testSuiteSplitType to CLUSTER") var executiveSummary = true - @Experimental @Cfg("The Distance Metric Last Line may use several values for epsilon." + "During experimentation, it may be useful to adjust these values. Epsilon describes the size of the neighbourhood used for clustering, so may result in different clustering results." + "Epsilon should be between 0.0 and 1.0. If the value is outside of that range, epsilon will use the default of 0.8.") @@ -834,7 +843,6 @@ class EMConfig { @Max(1.0) var lastLineEpsilon = 0.8 - @Experimental @Cfg("The Distance Metric Error Text may use several values for epsilon." + "During experimentation, it may be useful to adjust these values. Epsilon describes the size of the neighbourhood used for clustering, so may result in different clustering results." + "Epsilon should be between 0.0 and 1.0. If the value is outside of that range, epsilon will use the default of 0.8.") @@ -1083,12 +1091,12 @@ class EMConfig { @Cfg("Whether to print how much search done so far") var showProgress = true - @Experimental + @Debug @Cfg("Whether or not enable a search process monitor for archiving evaluated individuals and Archive regarding an evaluation of search. " + "This is only needed when running experiments with different parameter settings") var enableProcessMonitor = false - @Experimental + @Debug @Cfg("Specify a format to save the process data") var processFormat = ProcessDataFormat.JSON_ALL @@ -1109,18 +1117,17 @@ class EMConfig { TARGET_TEST_IND } - @Experimental + @Debug @Cfg("Specify a folder to save results when a search monitor is enabled") @Folder var processFiles = "process_data" - @Experimental + @Debug @Cfg("Specify how often to save results when a search monitor is enabled, and 0.0 presents to record all evaluated individual") @Max(50.0) @Min(0.0) var processInterval = 0.0 - @Experimental @Cfg("Whether to enable tracking the history of modifications of the individuals during the search") var enableTrackIndividual = false @@ -1129,7 +1136,6 @@ class EMConfig { "Note that we enforced that set enableTrackIndividual false when enableTrackEvaluatedIndividual is true since information of individual is part of evaluated individual") var enableTrackEvaluatedIndividual = true - @Experimental @Cfg("Specify a maxLength of tracking when enableTrackIndividual or enableTrackEvaluatedIndividual is true. " + "Note that the value should be specified with a non-negative number or -1 (for tracking all history)") @Min(-1.0) @@ -1203,7 +1209,6 @@ class EMConfig { var extraHeader = true - @Experimental @Cfg("Percentage [0.0,1.0] of elapsed time in the search while trying to infer any extra query parameter and" + " header. After this time has passed, those attempts stop. ") @PercentageAsProbability(false) @@ -1247,11 +1252,11 @@ class EMConfig { @Probability var probOfEnablingResourceDependencyHeuristics = 0.95 - @Experimental + @Debug @Cfg("Specify whether to export derived dependencies among resources") var exportDependencies = false - @Experimental + @Debug @Cfg("Specify a file that saves derived dependencies") @FilePath var dependencyFile = "dependencies.csv" @@ -1351,19 +1356,18 @@ class EMConfig { @Experimental @Cfg("Specify a probability of applying length handling") + @Probability var probOfHandlingLength = 0.0 @Experimental - @Cfg("Specify a max size of a test to be targeted when either DPC_INCREASING or DPC_DECREASING is enabeld") + @Cfg("Specify a max size of a test to be targeted when either DPC_INCREASING or DPC_DECREASING is enabled") var dpcTargetTestSize = 1 - @Experimental @Cfg("Specify a minimal number of rows in a table that enables selection (i.e., SELECT sql) to prepare resources for REST Action. " + - "In other word, if the number is less than the specified, insertion is always applied.") + "In other words, if the number is less than the specified, insertion is always applied.") @Min(0.0) var minRowOfTable = 10 - @Experimental @Cfg("Specify a probability that enables selection (i.e., SELECT sql) of data from database instead of insertion (i.e., INSERT sql) for preparing resources for REST actions") @Probability(false) var probOfSelectFromDatabase = 0.1 @@ -1371,16 +1375,16 @@ class EMConfig { @Cfg("Whether to apply text/name analysis to derive relationships between name entities, e.g., a resource identifier with a name of table") var doesApplyNameMatching = true - @Experimental + @Deprecated("Experiment results were not good, and library is huge in terms of MBs...") @Cfg("Whether to employ NLP parser to process text. " + "Note that to enable this parser, it is required to build the EvoMaster with the resource profile, i.e., mvn clean install -Presourceexp -DskipTests") var enableNLPParser = false - @Experimental + @Debug @Cfg("Whether to save mutated gene info, which is typically used for debugging mutation") var saveMutationInfo = false - @Experimental + @Debug @Cfg("Specify a path to save mutation details which is useful for debugging mutation") @FilePath var mutatedGeneFile = "mutatedGeneInfo.csv" @@ -1407,7 +1411,7 @@ class EMConfig { */ EXPANDED_UPDATED_NOT_COVERED_TARGET, /** - * only employ current not covered targets obtainedby archive + * only employ current not covered targets obtained by archive * * e.g., mutate an individual with 10times, at first, the current not covered target is {A, B} * after the 2nd mutation, A is covered, C is newly reached, @@ -1416,31 +1420,28 @@ class EMConfig { UPDATED_NOT_COVERED_TARGET } - @Experimental + @Debug @Cfg("Whether to record targets when the number is more than 100") var recordExceededTargets = false - @Experimental + @Debug @Cfg("Specify a path to save all not covered targets when the number is more than 100") @FilePath var exceedTargetsFile = "exceedTargets.txt" - @Experimental + @Cfg("Specify a probability to apply S1iR when resource sampling strategy is 'Customized'") @Probability(false) var S1iR: Double = 0.25 - @Experimental @Cfg("Specify a probability to apply S1dR when resource sampling strategy is 'Customized'") @Probability(false) var S1dR: Double = 0.25 - @Experimental @Cfg("Specify a probability to apply S2dR when resource sampling strategy is 'Customized'") @Probability(false) var S2dR: Double = 0.25 - @Experimental @Cfg("Specify a probability to apply SMdR when resource sampling strategy is 'Customized'") @Probability(false) var SMdR: Double = 0.25 @@ -1451,7 +1452,6 @@ class EMConfig { @Cfg("Whether to specialize sql gene selection to mutation") var specializeSQLGeneSelection = true - @Experimental @Cfg("Specify a starting percentage of genes of an individual to mutate") @PercentageAsProbability(false) var startingPerOfGenesToMutate = 0.5 @@ -1466,7 +1466,7 @@ class EMConfig { @Probability var probOfArchiveMutation = 0.5 - @Experimental + @Debug @Cfg("Specify whether to collect impact info that provides an option to enable of collecting impact info when archive-based gene selection is disable. ") var doCollectImpact = false @@ -1502,20 +1502,20 @@ class EMConfig { @Cfg("Specify whether to enable weight-based mutation selection for selecting genes to mutate for a gene") var enableWeightBasedMutationRateSelectionForGene = true - @Experimental + @Debug @Cfg("Whether to save archive info after each of mutation, which is typically useful for debugging mutation and archive") var saveArchiveAfterMutation = false - @Experimental + @Debug @Cfg("Specify a path to save archive after each mutation during search, only useful for debugging") @FilePath var archiveAfterMutationFile = "archive.csv" - @Experimental + @Debug @Cfg("Whether to save impact info after each of mutation, which is typically useful debugging impact driven solutions and mutation") var saveImpactAfterMutation = false - @Experimental + @Debug @Cfg("Specify a path to save collected impact info after each mutation during search, only useful for debugging") @FilePath var impactAfterMutationFile = "impactSnapshot.csv" @@ -1566,11 +1566,11 @@ class EMConfig { ADAPTIVE } - @Experimental + @Debug @Cfg("Specify whether to export derived impacts among genes") var exportImpacts = false - @Experimental + @Debug @Cfg("Specify a path to save derived genes") @FilePath var impactFile = "impact.csv" @@ -1610,7 +1610,6 @@ class EMConfig { @FilePath var coveredTargetFile = "coveredTargets.txt" - @Experimental @Cfg("Specify a format to organize the covered targets by the search") var coveredTargetSortedBy = SortCoveredTargetBy.NAME @@ -1633,6 +1632,8 @@ class EMConfig { @Cfg("Only for debugging. Concentrate search on only one single REST endpoint") var endpointFocus : String? = null + //TODO Andrea/Man. will need to discuss how this can be refactored for RPC as well + @Experimental @Cfg("Whether to seed EvoMaster with some initial test cases. These test cases will be used and evolved throughout the search process") var seedTestCases = false @@ -1669,7 +1670,6 @@ class EMConfig { */ val defaultTreeDepth = 4 - @Experimental @Cfg("Maximum tree depth in mutations/queries to be evaluated." + " This is to avoid issues when dealing with huge graphs in GraphQL") @Min(1.0) @@ -1681,7 +1681,7 @@ class EMConfig { "Note that a negative number means all existing data would be sampled") var maximumExistingDataToSampleInDb = -1 - @Experimental + @Debug @Cfg("Whether to output executed sql info") var outputExecutedSQL = OutputExecutedSQL.NONE @@ -1701,17 +1701,15 @@ class EMConfig { ONCE_EXECUTED } - @Experimental + @Debug @Cfg("Specify a path to save all executed sql commands to a file (default is 'sql.txt')") var saveExecutedSQLToFile : String = "sql.txt" - @Experimental @Cfg("Whether to enable extra targets for responses, e.g., regarding nullable response, having extra targets for whether it is null") - var enableRPCExtraResponseTargets = false + var enableRPCExtraResponseTargets = true - @Experimental @Cfg("Whether to enable customized responses indicating business logic") - var enableRPCCustomizedResponseTargets = false + var enableRPCCustomizedResponseTargets = true @Experimental @Cfg("Whether to generate RPC endpoint invocation which is independent from EM driver.") @@ -1725,13 +1723,11 @@ class EMConfig { @Cfg("Whether to enable customized RPC Test output if 'customizeRPCTestOutput' is implemented") var enableRPCCustomizedTestOutput = false - @Experimental @Cfg("Specify a maximum number of data in a collection to be asserted in the generated tests." + " Note that zero means that only the size of the collection will be asserted." + " A negative value means all data in the collection will be asserted (i.e., no limit).") var maxAssertionForDataInCollection = 3 - @Experimental @Cfg("Specify whether to employ smart database clean to clear data in the database if the SUT has." + "`null` represents to employ the setting specified on the EM driver side") var employSmartDbClean : Boolean? = null @@ -1762,7 +1758,6 @@ class EMConfig { " but problematic if too much") var minimizeThresholdForLoss = 0.2 - @Experimental @FilePath(true) @Regex("(.*jacoco.*\\.jar)|(^$)") @Cfg("Path on filesystem of where JaCoCo Agent jar file is located." + @@ -1771,7 +1766,6 @@ class EMConfig { " Note that this only impact the generated output test cases.") var jaCoCoAgentLocation = "" - @Experimental @FilePath(true) @Regex("(.*jacoco.*\\.jar)|(^$)") @Cfg("Path on filesystem of where JaCoCo CLI jar file is located." + @@ -1780,7 +1774,6 @@ class EMConfig { " Note that this only impact the generated output test cases.") var jaCoCoCliLocation = "" - @Experimental @FilePath(true) @Cfg(" Destination file for JaCoCo." + " Option meaningful only for External Drivers for JVM." + @@ -1788,12 +1781,10 @@ class EMConfig { " Note that this only impact the generated output test cases.") var jaCoCoOutputFile = "" - @Experimental @Min(0.0) @Max(maxTcpPort) @Cfg("Port used by JaCoCo to export coverage reports") var jaCoCoPort = 8899 - @Experimental @FilePath @Cfg("Command for 'java' used in the External Drivers." + " Useful for when there are different JDK installed on same machine without the need" + @@ -1841,6 +1832,7 @@ class EMConfig { @Experimental @Cfg("Specify test resource path where to save mocked responses as separated files") + //TODO need proper constraint checking var testResourcePathToSaveMockedResponse = "" @Cfg("Whether to analyze how SQL databases are accessed to infer extra constraints from the business logic." + @@ -1908,19 +1900,18 @@ class EMConfig { @Probability(activating = true) var probOfEnablingSingleInsertionForTable = 0.5 + @Debug @Cfg("Whether to record info of executed actions during search") - @Experimental var recordExecutedMainActionInfo = false + @Debug @Cfg("Specify a path to save all executed main actions to a file (default is 'executedMainActions.txt')") - @Experimental var saveExecutedMainActionInfo = "executedMainActions.txt" @Cfg("Specify prefixes of targets (e.g., MethodReplacement, Success_Call, Local) which will exclude in impact collection. " + "Multiple exclusions should be separated with semicolon (i.e., ;).") @Regex(targetExclusionRegex) - @Experimental var excludeTargetsForImpactCollection = "${IdMapper.LOCAL_OBJECTIVE_KEY};${ObjectiveNaming.METHOD_REPLACEMENT}" var excludedTargetsForImpactCollection : List = extractExcludedTargetsForImpactCollection() From 8e9d7b3d5921d4fbf7d98f422ba2027db6730dc8 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Thu, 3 Aug 2023 16:09:11 +0200 Subject: [PATCH 148/345] reverting wrong merge --- README.md | 100 +++++++++++++++++------------------ docs/write_driver.md | 123 +++++++++++++++++++------------------------ 2 files changed, 102 insertions(+), 121 deletions(-) diff --git a/README.md b/README.md index ed39c8b28b..25954630b3 100644 --- a/README.md +++ b/README.md @@ -12,86 +12,84 @@ [![Github All Releases](https://img.shields.io/github/downloads/emresearch/evomaster/total.svg)](https://github.com/EMResearch/EvoMaster/releases) -### Summary +### Summary -_EvoMaster_ ([www.evomaster.org](http://evomaster.org)) is the first (2016) open-source AI-driven tool +_EvoMaster_ ([www.evomaster.org](http://evomaster.org)) is the first (2016) open-source AI-driven tool that automatically *generates* system-level test cases for web/enterprise applications. This is related to [Fuzzing](https://en.wikipedia.org/wiki/Fuzzing). Not only _EvoMaster_ can generate inputs that find program crashes, but also it generates small effective test suites that can be used for _regression testing_. _EvoMaster_ is an AI driven tool. -In particular, internally it uses an [Evolutionary Algorithm](https://en.wikipedia.org/wiki/Evolutionary_algorithm) -and [Dynamic Program Analysis](https://en.wikipedia.org/wiki/Dynamic_program_analysis) to be +In particular, internally it uses an [Evolutionary Algorithm](https://en.wikipedia.org/wiki/Evolutionary_algorithm) +and [Dynamic Program Analysis](https://en.wikipedia.org/wiki/Dynamic_program_analysis) to be able to generate effective test cases. -The approach is to *evolve* test cases from an initial population of +The approach is to *evolve* test cases from an initial population of random ones, trying to maximize measures like code coverage and fault detection. -_EvoMaster_ uses several kinds of AI heuristics to improve performance even further, +_EvoMaster_ uses several kinds of AI heuristics to improve performance even further, building on decades of research in the field of [Search-Based Software Testing](https://en.wikipedia.org/wiki/Search-based_software_engineering). __Key features__: -* _Web APIs_: At the moment, _EvoMaster_ can generate test cases for __REST__, __GraphQL__ and __RPC__ (e.g., __gRPC__ and __Thrift__) APIs. +* _Web APIs_: At the moment, _EvoMaster_ can generate test cases for __REST__ and __GraphQL__ APIs. * _Blackbox_ testing mode: can run on any API (regardless of its programming language, e.g., Python and Go). - However, results for blackbox testing will be worse than whitebox testing (e.g., due to lack of code analysis). + However, results for blackbox testing will be worse than whitebox testing (e.g., due to lack of code analysis). -* _Whitebox_ testing mode: can be used for APIs compiled to +* _Whitebox_ testing mode: can be used for APIs compiled to JVM (e.g., Java and Kotlin). _EvoMaster_ analyses the bytecode of the tested applications, and uses several heuristics such as _testability transformations_ and _taint analysis_ to be able to generate - more effective test cases. We support JDK __8__ and the major LTS versions after that (currently JDK __17__). Might work on other JVM versions, but we provide __NO__ support for it. - Note: there is initial support for other languages as well, like for example JavaScript/TypeScript and C#, but they are not in a stable, feature-complete state yet. + more effective test cases. We support JDK __8__ and the major LTS versions after that (currently JDK __17__). Might work on other JVM versions, but we provide __NO__ support for it. + Note: there is initial support for other languages as well, like for example JavaScript/TypeScript, but they are not in a stable, feature-complete state yet. -* _Installation_: we provide installers for the main operating systems: Windows (`.msi`), +* _Installation_: we provide installers for the main operating systems: Windows (`.msi`), OSX (`.dmg`) and Linux (`.deb`). We also provide an uber-fat JAR file. -* _State-of-the-art_: an [independent study (2022)](https://arxiv.org/abs/2204.08348), comparing 10 fuzzers on 20 RESTful APIs, shows that _EvoMaster_ gives the best results. +* _State-of-the-art_: an [independent study (2022)](https://arxiv.org/abs/2204.08348), comparing 10 fuzzers on 20 RESTful APIs, shows that _EvoMaster_ gives the best results. -* _Schema_: REST APIs must provide a schema in [OpenAPI/Swagger](https://swagger.io) +* _Schema_: REST APIs must provide a schema in [OpenAPI/Swagger](https://swagger.io) format (either _v2_ or _v3_). * _Output_: the tool generates _JUnit_ (version 4 or 5) tests, written in either Java or Kotlin. There is initial support for other formats. For complete list, see the documentation for the CLI parameter [--outputFormat](docs/options.md). * _Fault detection_: _EvoMaster_ can generate tests cases that reveal faults/bugs in the tested applications. - Different heuristics are employed, like checking for 500 status codes and mismatches from the API schemas. + Different heuristics are employed, like checking for 500 status codes and mismatches from the API schemas. * _Self-contained tests_: the generated tests do start/stop the application, binding to an ephemeral port. This means that the generated tests can be used for _regression testing_ (e.g., added to the Git repository - of the application, and run with any build tool such as Maven and Gradle). + of the application, and run with any build tool such as Maven and Gradle). + - * _SQL handling_: _EvoMaster_ can intercept and analyse all communications done with SQL databases, and use such information to generate higher code coverage test cases. Furthermore, it can generate data directly - into the databases, and have such initialization automatically added in the generated tests. - At the moment, _EvoMaster_ supports _Postgres_, _MySQL_ and _H2_ databases. + into the databases, and have such initialization automatically added in the generated tests. + At the moment, _EvoMaster_ supports _Postgres_, _MySQL_ and _H2_ databases. -* _Authentication_: we support auth based on authentication headers and cookies. +* _Authentication_: we support auth based on authentication headers and cookies. __Known limitations__: * _Driver_: to be used for _whitebox_ testing, users need to write a [driver manually](docs/write_driver.md). We recommend to try _blackbox_ mode first (should just need a few minutes to get it up and running) to get - an idea of what _EvoMaster_ can do for you. + an idea of what _EvoMaster_ can do for you. -* _Execution time_: to get good results, you might need to run the search for several hours. +* _Execution time_: to get good results, you might need to run the search for several hours. We recommend to first try the search for 10 minutes, just to get an idea of what type of tests can be generated. But, then, you should run _EvoMaster_ for something like between 1 and 24 hours (the longer the better, but it is unlikely to get better results after 24 hours). -* _RPC APIs_: for the moment, we do not directly support RPC schema definitions. Fuzzing RPC APIs requires to write a driver, using the client library of the API to make the calls. - * _External services_: (e.g., other RESTful APIs) currently there is no support for them (e.g., to automatically mock them). It is work in progress. - -* _NoSQL databases_: (e.g., MongoDB) currently no support. It is work in progress. + +* _NoSQL databases_: (e.g., MongoDB) currently no support. It is work in progress. * _Failing tests_: the tests generated by _EvoMaster_ should all pass, and not fail, even when they detect a fault. In those cases, comments/test-names would point out that a test is revealing a possible fault, while still passing. However, in some cases the generated tests might fail. This is due to the so called _flaky_ tests, e.g., when - a test has assertions based on the time clock (e.g., dates and timestamps). - There is ongoing effort to address this problem, but it is still not fully solved. + a test has assertions based on the time clock (e.g., dates and timestamps). + There is ongoing effort to address this problem, but it is still not fully solved. F} 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≀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

mAQvP6w%!>jGTL$jLe4{))F09rxjEHnoylJG|A z%(c4w$b^Ns0V}~h5oaY?8e8i1Ujlf<{Jy4!KAqps-PDScqZ%?bL^t#K=%CC`gN@{D zSnms16pGZi?aFrUs5}xKF=1R+A-bHM&>9bl!`_6&PTRua41>ivA)pcf3QdqF3+RSD zYxHJrJwTuFiz6N|A7VTt?rC^whGf!1&hctw)GSep5=o^$A&AZtQAHfVE)Fs6@m~%P z>?|;7qZ0&=6Y#PLzu@-(xBpx;rDaRKy`4|ljOo8Ck_ii|r?u{R{q;}Xrq%Qd3bvST9%d)5MYnE{kW zb@m@be?zO|Ohry*UYWt~YEComruGH<`y3M@d4k2PQF7t$xmoB*r25654)6MnIXP;j zT)49EyPhX8lt}%ioT$`$7`V8*4uRsJi$I%jWG3T18RRZ*M|pjJCG%+22Wc{IoL0|$ zEWrBm?vGGMrvW}fp2L|`j*W?23G_>+PO1{T?uhIr(ZQjmn=_jfwdSqE%6yYk>1Za1lZKT?><|rqE zzCZ)n>^q{Bwb|d>nHQl@itH#t;M*E1n9yj&vP zm2N%mnV7*51p?wFQ(NKD!8O#Jh!Wn(;C5&SPSFSL51WS1uR8@+L+TurlB>yyjvVe5 zlJGCt3w+!>y+&$?#Jsk^x#0ZbR9@+xpi`|aOOnI|@fTJ)j=}BM9IWDv+jW%2Ws#8C zNZ%+L0ljass$fQ8Rr*w`IP>0vXj_+nAd=~k1z3`yxzxgag}e}TwV%zw$uZbQT^Q;7 zXH*YFY%H^h3Fg8-2-w*EOSBy9SDqlO=FeUb;Al6Al}W|>f>@L%`sHSuVYBNMim;}> ztU>k7s(Ke{5v2z5K2M$@?{x#oPlJESKd}l;g=S3=)7e@ht?9pwigyxf#$MJ=+ou3{gAh2DvP&D}h?0 z1sM&PQ}9Qkpyxh{x`WoD?YG(u?D%Y>qZlaRQE^VOTqw*2uRXHc$71a>kDUB?^Kgik7|>_#=}_*n?K!f1e+bKy%bUa%*kmw zHF-!r5y-*ih1?K3F6ImAEz`8ARZO92NJy(O{@8H=EUZ0z*DP_?%a0zXN@@-)4cIoA zt#WoPJW4!raa)tQ5*se1y#nQUtEZ}!t6E?ICoT|+7R*RpF(Hf6uiy}$GZIvfpcC$~ zlJCUwP?3#(I*G8=)jh6&zXC3p#k7ecuw67AEV60Xo>e<2FZtTA1LM-OE<=|VjlqbR zzmfG4zrF-pIj%wsT}J@YbQ@((3_)3r$B&LAY({FLj;J7e!FJDCWGq-pIJyJw2jVlpVe@x%^yF|4>YxH^o5y}hkQtKRi z*VUg{d~mFqjPa~^9W8T8`yzx5odX_B#Zh%NoQ8+SY6^ljCosc6rLK(EKG8}K?3qN% z9QXgMd>`b$YPumRii&=CmK?d=;TD%5MNt2@VFb7wu8NDCFIqcGWOf(kd&_siO-^-C z53|oBBp_QLVt_rc%L>N*Jzpq2OJl&yIt>0fXpQ*!oc30OA0}TtT&rym_$|HA*$kN!X;do2Q`**xRyEGz85>X+F}=IoA#Y4NJ+faR7B`{6`V1MsH_y~_H6iE&3>NF z|ERzPG?Gj~jzPXAe8jB`yO_XEgAGJDb?!H?Gve^wjyp}ji(0D#*tnmvPU-3ZF6VwL zo+Z=-%y@et4Ww5|354f}ok93(iTT@4mL|_POJgoFHoay0#k6$la~WS?_nNHQcfcb% zRqAo-(g?Nv$v(h$>l8+1@t7fS7yx5%5qwQxu-5^u-~EpxxMyOP0&>LX3$ZNlfvcim`K(xe}At z(pU0(4it;-=@Gr4uOfV`cfH-H~=7q0YdGpn{{0QWq?v-pDKc6To(_^drX8j`p>xB>M zgWrdbewS9zmG&s6sKa4v_Q_;bc|lR&!HD?;#CyoaXsswkyg~e zLVdxi;X-+8-tiFpbvVd#cfG*T7he&<4&>2xyo3S>{G8R$BEALPT~C=awD z!7CWs8Pg|_hZlz({S(9ML<`wEsUD*2n4gMJ_p}cqX}p58I8}Qt2pUEN%y&A$^xzLJ z!XZIk65OocZsco$v@@_2m~uhTarF%z;=PRhq4~pdGX#$Ys?R4mmHmC%cq0)c zDbVi%eD?@(2*?Z4CXBFRqQk#9Njy6Ke;k(-H*7;fR30%jI~rkF3^HMd1Ze#nxr!?4 z{CDicAc`?S?atv&7fM{YP(LBjqtUfNkjJ~ z{C3q{;4y`2+tgWz8vuaH+H!o}!`aii>C}uq{^)gYfm5XWR6}Z{Mtg9_#{X`LE?hmT zFN2_(#o`tZB5}eL1n6~hF-xnG5(+ zOSKaRe+0k?QA8>~QorK>CcP>PB1EoYSr2%X= z$-jHO#kEWm1MsNj)Ev3G=Dh!df@pLzJ>{(o??_|i4Szb;6FHZixA~P|eDX#+B(p8^ zRq&!9z0|fqGm&2356P%L&DPV*c+UXC?}CRP!7be8XEE zx%mCBavi0tHXz=R4~w=lkQ> z#|j<6{wA}U7oYnN3qW!F(jR=j+f?_YWs0W>T29fOr04<)^kiOtD1eDO>x1o8pWyC9|wX zWf!iYHcE@4+M&-tFE)>8dAPpx)Il6N_Kbh(>`H87P*?9<4%)i6a~JLETC_5#e;Z6z zSaCHARlD14Qn2(V5T6;}Em|ShQ(uW^w!3Zuvgs-)? zO(gFhC&Ph-i^$WIgJeI``v;g$CpA>qEdBR^ze4U|hu-oJt-62(=RRFLMsB-2LAMi) zkmZ}Vru8=uI5}^*blvwtw4`)A8{VU`6weDBia~qKH*zBPtS@uzTV9+eq1#I#-WcY` z@)ZlnxxRs^Y<)9Yzm z?2j+B-#8MEHl$%^=X2Znuu3UsjJwtrn*9k^opW0&5cel|0tv)}D_e4z(&~~0AYh`` z<02{+ihRT!h;qxIoz!eIa!{oSg|GBt;SX(c0LpEpFa@oG8> zP`^0J2?n%ADPFhhep_nzfDlzT!C1tH3yINrFK}~JJ&5zA#@}e)f9==4VGrtm?#r6R zka1i>0{qMR2FXp-G_J-*u^q$25p#Qx_6wISJtZh?37xX z`FyjNp13HwVK}sy#JK^m$yFPnuvXt*D~CP+%Pc-$?d<}ri3DEF;vU*H&|lR6q4!y{ z^7+&lyoy5!(}Hm5D0pG6?+uY>-#iic1P=s7&mIBtRG9lch+NzZc$KmwTZaqMvkPwPV3B?7RVg|S!_aYO-H6f8XZN6 z^C`ti(06j#yQ+PQV~A?UmRV_jk)Cp=KX)g6OlTHMI&vTB7QTevTo|}h3Lt8J#$%p^rO!P+-LIx&Nr;WjqZa<9MmSBK_DkkzePgXqEWu!PGvv z_jK9jMnV!Z>rO@2Zyk$4khDAFVvO;EqqRP{={7naFTV>5wieo8J#~Sb-|`{9ME22^ zpIYQaX|(%qVnDN356Y+ou-taK7MZq3F`ga_GbU5eB3cC>$M1@!plrLQGHO65W6}^k z3_thd9l)kLJ3}aN1eruT=Wc%`hPMPH9g_aAR&;8+N#-7Ti8>kDJ$elSAfZ8L%9n|GcEYf)&K`uO0Tv zoE!*AIrj+3K@e{DUJwEJj1J<*jT?>OOvHrdnGB?<`n9Uy;3H-W<4r2V&f5=AM4a}< z9d|dg#ysl6B|xJ{x9&F$IYW^W(4_E<7pG7>tgVWb3Qg;KlyDh4JeLhI4(>*JZ9;^l zL}Z2bi^~nX!l1TQ#(*!XfCo(|8tHW;Lx*4o&a-p#p0q{)SxM^o|H&0eZ8i^d5WKap zV)C~CgnqJSx#!uBHin_sLCySRLp%Ou1J-2{Y>9Yi1;}>+P&!LH@Xn<0V7KK8)f30Q3oT0Q45IY&3c$?kytEG^lnv=2lJD(^i5TWF>@On^QS}<`H8C+tposN8}!} zX@H<4^)CDRy6B2zv(#wibN(h(CmyXWsM=QZdH#zZ`J`SXl~pd&my@xm!T%Bx5j(_232 zbv)(Bit~$+q5N{(w)M~&OG9i0-<74I_wC-9`8&`($m_D-P^Pee9s=!AYJ2}W_humW z_!s9x8o+L%P#R>G1QhrHbZYnSGI6W3ws#*PkGMt7+c80^H#dSpyNpVEc1mK?6f59B zwB|DbCJz7@v6LO&797_M8&Kf}0AVpDUUp&8Gqg@ha&0bu3kPonQaD=p^CaI&&*~e5 zRp9DVn$Vu!%uv9N0cFbTs*+-CAOa5mo04M zO$=fcuwFy%53WXe2QxIEn?OeiH%r&SV9pz-5Edhk@w$6em9L=&0GZcFF1o9)Xoq-5 zHd0}batRVGca!p9VaFEOy|U6{$0|xSqs5}m&4vqcKOvqb{O>b8O+xRM9zAeh(?7WI zE_(73nkT>h?HXw9-C)?m%S^^nMncg=Fb0^`Ys2y8TiJTr5~7FpGVMhKs2xqki&50q z2am=CfUXFc^aK~ifn$kQt<2ZHzN}?n==&=1k@pybB+nsT| zFh>k$np;EP`B%ox7XZb;6dEG+ zAg03{m)t1b-u{z4>{^kPB{+f;!Q(%GpbPob%i?5D2*mTVRnOx37kzZlyl(}0D{8-A zeaO?%j&z`g4d~&mmq#u8TCh+$=z9{Lt(zmK`4jOnKwf|VK=nBuC%er1kr&K~_B!m)^X%k-Zh>8!>0u72U-Zr4eZHkfvsanE3@Jfz#`{WvwR?zjN66EVfvScbJMtyM z*AEg73~L_FWr#5uc{`OgLl_zyPC_V>LR;Eq`KdIB%5(TjR<&=+EAzR?U3-!7!y(qM zw9vD_Mhc2K&Q2`<5fHI6co@ZN-G+H#vx0j@lV^RApr0eRBD)n9+NQgcjnJZbyH*N9B(xeNbfOi75LDY_PE|MS0# zuuZ^*k<6QlUiGx(jlvp6o@7ZXQ=}1q{!U+2N=`KmIps%E;;zN^-PPYk@6gJx=}9^J zVdN$8)NXowlk4{lB}}hUnpPsBUg4(~Q^lQ;%?b5%A?WUMn=)MK_AtW=^DrO=5RF8| zVSyYUphjvGhkDo(VM6N+OVM2Kt2xA&?`f97nCL9V>nl46td~EBuvOaV<0i^a46LcQ zn2hlUO+bCQjPzUw?PAT)&~nt>MKZ)pQ?Fn3|Dpk1=8dHWW@~fCb6_`g2G269uy!Qy zP163;6t6kx$v>N|r8$?4%ZK6U!Z!?Nskoo&@D|6n^it29_1GsJ+QkgD=}9@p0qHf( zx$bO-2Ak#j^Ef%2jklFkP;p1P)S4_n|1dTOFC3e#EybFN_7i=yQgV$O@0s{47!rMDv4$h%X+=6(@pV| zX0a5(gOw(WS!)+@K|&E}2ltS;_skAw1n%Jg=G#roM#M_P(iB++7L9x62ge3V%HR%x zwRjYY`iOZfrRY`|-1gpbC$j#BV)DDpq0Y=-6RGpEGA^rI;tI<{iN)z;5$hnIp+W+T zZtXGKxLxr>0p`T1GE-k@R~BeVrAomPP2!m`i4&ghlCCFLKcQW$Zp)3$A$gE1yh89V z#pc~#`9k^)w!~Pjg6M4^3Qs}oH9`O1lzTQ4LL)_MUK=?kZ0^duHHg&nZFgUpi3v&^ zOS=YyW>Yv3FkO}Kf439v0#Th{ZBAhvF(`o-$MkPn_B5H^UVN!CAtoa9 zdK;4#96?Qd{oz~^$^@1qLHjpr^<~XLWtE_W0ZZ|)(TzbR&qHA_{ zo7O=0bh|Knj~@0Qx_ZDTBQFHqFU0-^_k!3&V!ZR zcj&bT`ZHH!JL-a6AQh^j)RscurNTGTI<>vrH;S6}i|~Iy!=v;&mbQg~ZTA2ae~IIF&bCmL|8QHq$FA zhUVc;?Mf2UoDZA~nq7;*JR^9YMaHF5^spid`00lz8vOrh1mn0Jw4?nFEvZ+O9*Pd* z;CzM4!5%l&lEe*rzcRm3I35{T@&l1D z4?S6!iT^H=HqtoSdCz)G0u>ThSKZ&0-V}(^K$64(r*Ee0MHZNss*2cd`*8OwbT#uR zLK-r;^VOT&&NZ_1$2*tca6Oh~a+R43#DZ;o(RxHTSTkj913-HP9yW@&!6S;D7WK8X z$i3C{#I(ddoZ}QlUnU;_J3oga(Oofj#fYNxklhO~jn4KPl@WU51f?h9b-k7h53rK_ zwRPbWCxONmoiL{_V~nqn^&Qo{>*`{vT}M3$PFZvFCvlR4*a3RX$E?6gY@U#WjIg^q z=fhm_y41l?Dp(53Z`{5}TiRUb=omA>@>q6p;~(1y$W&B4!!A78m=1T|jEE(3p2*t& z;bCgaKZ*h3Maxhm0Uq7Bm;jf_h(MM_Kt~Vaxq%nWas4HhvZkhA@6B1=<>O{sYTNh> z0wSBogW-WaCfg zpPHjYu0@x*lwwA;`p=?Xg+)>i)j5oK+XI6~c^kZyt{&`fXLXekVAuUhERT%P8rkER z@+^bu^+x0?QY&}G$l?yZjHI@V`-w&+vsLx??)HFj zNIG)1ZH^;NAWnGhmvN~F)uPDTVp89u0a_A+srMPn?x*~h#6CVXYVCHxeO0?^)JR*A zwjUTi$FFXD+4n&L9c{R`s94^^z|2f8s{o|aS_Yk&cx-)ji^SqPsGraBN6$cDBNOi^ zhVN`?s}|4OgI~AEHrMwpm@12;meg$LicLmRAb>m~WylnwvKpxbmfrKQ2EYglSeQ)n z?gJIJF<98v`3^(J-&s;_KsRi)7p~LFdj&p#QXIZ0zl};wvpMif2O)z;uGxaXCnQjF zb-Y6-h?3rtSp&A8tJ6+oifXT@x}6==Fw)REb2j@zS*&B)a&=U`%1bF*grIwys8$Uc ztZ?%{YVdlEeL%F{u|gT}3F0cKAhr~{N)@!|Fgg2uQlSyh7scmbS^T4W7l%tRFE`Po z2ShpxRHHs$dJ2Wx`!c=F74;4z_BU7R3_VG*h-k?Wbi`7 zo&4lUCfaCW9aZHwYDS$`Cg>0=kopUl)mj*QO zk6>g#ZeKNWbO#FH498E|orFjoM1t0$h<+ak_Uv_9q5}_`YpTEZtnt=|!++cjw}prfITsR~F|K{V*@7Vfm={F{3vdQKT#Z}|zg7d#H^OPKnTALR zL2|tz)cGcHg>T_-DQNrTC!d=Nh7c0tUp)s3??jz8ZzJ9dA{?^wVMHc+ZbIbVIOysm z)goo>g~Av~m7I1>ML&bY!K=b*Jl8#L>;#+2vCv8}yqLg_qo4ztVXm!-RdZu$l0s62 zLsUO&=jRNOMsGKJ-htt-L>y3wn3x|k+@(D;s0g~_ZsHx*F)3C%%MD?T`x8ae)2n>P zdscx{&#gA091};^6;V_!u9*XM^}v+t7hZ}PZD(&k^{ziS#Y0?~jRQ0!+BS$eXCe*loP%VO;g8J6u zsHF%d#_L-GW($`oFjn*Zz;;Qg!%F)Nm+sc{b9h)`VX2U8GZv8_TFaB>qSvGIOoCl~ zs`7=nIAm~2qXYesx(asE{qCW)SzmqONT4S3*4aB{@_#bqZcKo)>5I;m@5(&^4g^#k>{mRka=guuXAX>>g zMupQLD1jo2;dD`Wb+p*bFZWNyVkZ+;^t^5^Pv`b3CjU<0O$9nC2M3>yVf(lBh!qwXdTrnOr)ybeAhwnT3*eU%VPxjL}u2JI2C zJ86ypw6w~H18?)F&iIwOz`e~QdQ66obcKn&0bm&)W^vh#DN6Z*ekBGni9E96hPm~1 z-3`8Iv&=BNt5NswV3}c=Ui<2V$J&`t5o7Z|u?;t#yMfPu^fpE)wv*Q4XDA=PaFrI4 zw1M<;08jMNr%E=bQJa9Kw(IarZ{bj2b^c^w$Oet+#kIJB+;~Q;1oA(wgq;-;jU+jo z!h&SS#=TO`#s{ExyXF9Eq#fys&Tj`NX+72`q-tMXw)Po6DIR`Au=|n)}C5 z$f^KXpkgSPUR5^fmy8MCNs@^uYx>a-8cCyN(+Z+a0m^6Td~D+5+4W{WmE^^??i#7t z4&}S(hTl}h833;u0=U!wG$cT~ivv2wK^=1%{Y;={H7w4aiaUVGvUEGYrMwRe!uzD5 ziI}iJG>*PuG~&8^Mmpaoy&lDdFe)eR9-a}01zMpj1hhYrUkG^P-4Ii{~kVHv`wy1kDD=T6p=Ep zP?+$=E@JJ}6HY2Zkguteo6Gzy%<`cP4BSg6#=Q!-S$Mb z*Q?!PM?8@L@b8B^>9f!1S38=-5C5l|gPP{*Dp47mHhD-*$0kETaeTTE!Y83wwnIu2uq zh6RgN_$3WX0Do90?zKo?u~JzP?dvfvfp#-O%p4%lj`owEUj)b8_%+Bx`}Zy(B=F;@E+JG;>ZaXXZ-BUdMZK?kOrOF^yS?7;Td1-K&v zFf9tz(tztL6SINcm{+k~l*PogK1SSDU#Kge5s>o6n_{RDxMKcbpI+>Upb|96GZwT1 zz3G4Ke(k?stV?zaK2X^UJ@CmWS$fLZ$znlFkF2g<%WqWP7r;#FeZ?W5Z`>M_mb#nb zp)ajvl=tWX%*yq4ijVMN$S+>@ZAukR%s%bu4w+S%uB z7DNJAt@4T+r3oN>SkoByWwBbF4WY6jl{cBcY*7R$q%L?Qm&j#S2wF{7c9LisHe+YE zVp&r#6fl}oJ}eaZALT3CFFHlPLXvxTq$IroOV?jB-J#jMN$rfqDg(T_T z>9ar^JG&UmD-*TZ{HLLuS z)A$o&1CO%p_~gKWvYLyhesVpe$-?jFd`l>jAcbD`j#^kIkfKUewW*inxUlMU#SPM4=?RaZgexBOfIv8k=I4CT3Qb%hmCO+) zhPQ#0>g)hh4#U` zvmJq#ZG8Q(idlSpH6-FueMJqHX0U(3}ohVmKi;BHq^7OKdk68%K^ zO3Qe+5_Se3V$6Hs7lc|C#uI!<$Wt_s*z~zZ4M?-pkRvW_r9yclXY;Gdcp$Mi>BPsm|EwXz9cZp8CoB=8%fPIr0mF<^_-x-c+c`MO# z^0Gy^ji(9$Oh)b;2-~tJ_(q0*0AM3&bg5>^u!I2I)MPeXz;)1Eo@c;Bxfn@#Yk=qW z*xE7coxw)Qh$0~#e~`PrKVW>$ywld>;5%Mv`x~Zn?Z#gak(jX5H@km(yrDMwJR zB;V0fdZ|6rwayQ?dc8RwO0^!)&@nS3KJR>*P;6r2KqUiIje|EN%XU{DZyq$-r{4y6 zwx0@jo+f@0I%v@h<|)Z87c+1oY<%_UWGcGz!niiwlCb5dO3pSbx1%CWcxZOgT4tGv z(-KkU{#|gq0#m^J-#0(iLUn416yS#V!}nkipUO4}2xP$&y~agmM{5dtk6#!13KO*h z#(zA>+W!8h?bKtV-4E*ajTu9FVe259Fa;F{lO>1tVnzxk|JaMHgxV+E6nLH0f*Xu_ zBeDfmg9n~0^V}ddnnF;j1Fg*%%7eA|)&ZZBE>e=Evt~5eYL#3DiT6Y^ovEs+^OToZ zoE=6m$O`2nl3;R4n0BJ+6pQK2$ZB>hC07Z1!?L-mwbW4>Sifak_7+KPi0LDoNBcp{ z!I%jbxEi-m9tTDu-BQt_-h0S61h=cHx(QkSNewEV)7ZyUI%ch&%gl~V-nI_%+WVgP zrYm)8YU6O@hIYQ!6?lRhP5aVQLu_jo{CIR}2*>CZV+OABaF-{$4(;Y9z7S#dM@;js z{XFn1E1r&L809|kpeOPHBE_MpNphwZmQuo+dD^8y1B(ZbqiK(AgVnRl-{~lJm?Y z9A2Iuc?a8MowO~{c=+yHLe$ejS-wnx`ptB13{`mi!w<}vlNWM}&X}B%xARt5sp}o# z2d+~5Fj6PiSe7>b_LV@7fW-ixqVUe^voJ&EU*M=$P5q{xeNNIOk%iP-faV5srj;fx zWnxOEKtbAPZevZ+mplcf$_*E4?NSoilbtcGQ$iiqUw@&C7_{*;I{P=pHV5(Wh>GB3 zt^Ig=nDw<_NR@Vd%62x%t3b**ML`_w`n`Y3edvfuII%%{wfFy{|CTkurqR~Y^FQ1- zPNAlXGFwqU6p0NcTAQ*6GC+Ars;P8AS6NP&5KXxMM}C9raqzQlO+J)&#c#5pX!3ia zm3mc(-AqAfMze^6RAT!Y;8gf2a|8Hhg4cL*5gK*17-X^L!|TH7#3lGW#peoOC8BjU zYCT^C9~LEXzCSnYZJ9nzu zR@F}x%wLJLBmao`uY*(Whs|vqZA?rd87q%oge|tH@Qt0@~d)3+sP4Ay*O};}vIbVB1R1>*(})bj;F?28qk{g`qZJ7;(|KD8Dq%hhChrb^a`DI&o0xJ4 zFGCJKz*AWcQH5!?pWm*?sHN}?qHH`iTw4dp!@GsBUb`Jx5_m6@j64#;1Yn*{ttz`r z8p}`EmA~wgKo7dhGnm`CLwqS+=WU`1G1Zp3f*}Vd=%L$hkszPgQd_M^oBompA$Vyp z6gwXQJDQW)lfY|Y@rsQVwk@Eo|B)w$1)a4aY-s|#d5>BR4Sdb!Mv?wz>;*-&^LmL0ym?gMdE+BX5!RRS$ykA^y>4< zxpT)DVUyrp=druRU@D>%imQ>&>IjH=52F??2jS_)L&|l-aD%ZgH(Br@9M#F@HFS#m zBIB1>1li3kmm@EzJyXnZ4z?sf0NLG;k>@*AzFF&q4jlPwmGg=SX)0y=8Uh}!jPuVZ z4R`NV&_~BaB|aYjxMXo0L8N4K*mN=*!WK%~EGdRwOFQHInQnwAPuXpWlq8X`iL@8- z6>+OcDVBph>B6f#1x8a<#+V|@NS^Pq8L1Lt<~GL!f@80&qf}a#64OUMz5<|U3yz*q z4!U0eVd^%~>iTgw&PI)Dcx)wJ+^VCHXs5m7Kh(@;QXqAfFBvPompA9<4ZEF}3>|Z% z$rJ^9SlqE=Kli0I9X&>Fh`2yYCO#;U8#K@E-=c#a1pFkj_ogauLs}4oIgAkOW$5K&qUmsif3OiGwip zq$g@_OT7Oi++lF3(K;?9>PY;X1sR51ROy8_;;;3GC{|^}PN7=Od}GccXpDnLU`A?i zBLSHS5uC)(p+1P~Da>d`V1E9a3s^x$w4%$kS;9tVczpie@OghU#$H+kcOB_m&M6OW z93Cru!~1DQ2d`&Ug3>n4eV%mil*!?+lUh{J*DZsHNYZXAunoje$vM~~H5YPSxkb!0 zAM8G%%>l>nlo0}-$eiMeu5)Y?zjA2ZWTi)HH>aSPl$Q&pEHz(h?WSG1FWcG*yj7))=o(2!QRu6uwYR_*$CpkBW9vgt;_vyLepcj-l(1GzPTy9GgG3tSAD*VC^_D z2Ykq;#??;nQHXBsqxm23*RK$wVv|2lw%T8E#2M|u+KM|oG%L>#713rnnug^5_+GKr zuKTCeqLLtwG6zPq{F|V+kC5}vhHmw429mPYLo@a!ROC`97nF^FP`KXhD)eh>5(8Zm zOS{f1?!HWJ##Y4e`xlL2A{{I)5QM76E@|8~y|Ra{^b<9Qv~P`&X}*>5DPB*Pal#2Q zas{70gsBmni`56qU&AX!zB%B z6#xOpw%y~YuxeV3L`5>vp@9WR{BLnl9X5`5p_MSLeo@kksCENvzwoT@qIO?VIx|_ zDyPKkQjwj+7e4wviGU3t(_$)np!IV4pb9})jQyQJSBSnL5Kt5Vo|3msHg=U=sOtBN zCk@W6e_DozZvEa|=@0ti!#7CU5Z0V-%4Xss)$+bd4@8omQ1JreG0OKd4_$pY7i-Ih zV4F=TUN(3eDHYG12;KlkK)Am}@NEB$4E$h>9-n=}ZLhsqe1>SgOCN`=BPizx2P6He zTZU4ij@3Ll z0i~N7@tSCbvd)~NzY;y!i>hj$;k=dZm8xxuWtEWml`S~jnM3N{Hkl6~73I4-m$xe?kom zW8y*Nj^eM~Y&zK8kvfwVHPbqI!_wDBJ8?k2`M3%W`0zoog#Y zRc9bDM+<3V`68Utq)O@c9A?6630Fasm9&=nj>VR*14dg!I#YPwI=NsMm3}m34(yO^ zj+ENd#&S})3p6A0jK6)!pNS!!pVJ*Lr+EWqml5F(IAMXn_Kq2gdQEv(-W)4?Cyj#w zn@NtV#oJc)7GV8W)(GYls?*Ui`|+%g4*AH19$B)ZQF?K`}bcrWM1*t{8TOZbC-3Ge=B0FhUy#M`zzfPm$ z6VF;nNR&;aWPBa;Ev8F0&`0{hXqxmO2#tUBa#sKTUN}O{E=cs2UWVk~irl_yB|f>Z zw=5<+*F9tY94J$VbYpLr;bNzR_OaBTW7S8S;pbKDmbWpV6B}ki4dzuqE3+tPjnDI} zGL9a0iS&KzNa}R26bf{RIeu-$6dLP@03v^m+#Wmo%wge)-;@?j)FCZPiABVDHTDbTi7e@^9g@+K07zYtINEL2rV8tQV zY3FD1L~#-OyI>k;@KEIyaAl`WNr=Bxq+f(EEghgPZ=Q98lDcXExbkRi54KYuljLn! zt7fmU6pXhV)@;jl7Cs7KO?>Dz2Qig%dJs5Ho1ACtRbObm%dhPTFgRqwvi%Mvigm~S zf-IU@Wy!a_!)yfUe@Z6TTh<~kPru?8=qUkAUZ9M#W#OOOVm($;8X-DMAFJypWGRek z>fjrCp8W+o)y#w0{EhrpV9AE=VtE5Xcq%>{`fr^}e`l26MwEXj4(s?{jzV@TcFp-p z@*J=LK0wkyyEj17>`}Ft2joKGvQ?}(LaKfHAOZIG5zlMB%Z^#^b9DZIQ{u;?2vr;& zffct?MWr=F-vt5L!fx0ys$EbIzU(#zO3St!4xrD_OEdy+{3aDMNRELGf&c($7{IQKt#)1Cuk8YvHJ;}sKw`#i)JUo$u#i91c}3&PO-us`!J+uF z97uqx0@b_{SDYGVxq%&2zM_(ZH}zD-&;1D-w(jeK$YzmIIn@Rf@k(R9+JX1q2Dgkb z+B;kJ=38dyxoeKN$PRTb(0T_$6o&@n)AD#aQ#k34kI&ARAUbr}%_Vmhz$B?$EHWxG zdlNDKt?%-P()j$Dh;5p$(LDHxc%8u)Ke8T$+IRgiit@LOICDQQL#={U9FAw9rC2+z#t&;M80bVAea{xM<2 zWPH)46%?X*3e7M`P`I&mmn`YdsX|eqy8qlUK$|TAIOs>Xhm@dIR$ei+WPDEher$LF#wv{_qlGQzLHcsdG96sd!A=*;p^>75xcLuO~A*qcP|>VagXz zKRClJpqd8`2v>i4`$zcg%eNwiLa7o+BU`p*3S>&S!O*4pHvcMpQU&m;z`CU)4R-6J z4xdMpzD}ho2lTV*1cY}yn$AqJ+;=RtgR)D*L9*R&pGIZ<%gn#kr*wZqUbSQdyQ8d!(Fj`H90uXR8~zvL}xI zg`VmG0ld~7iSyQm2^fc17{8m97(L6Ph3oRC$wNh#uKP6kEPKj1<9v5IZ{XS>B$J^} zj5HvT#7MeSPj{>p{`LmduVjmR`{MGQTFn0HaVh_!guod)$=OytM>oeYVo}DG7h z?O6*1z|=j_`j&+>3ZR{FR=5gT{s}y`;S6oG3k8<6%l@AnKDSN05*Nf=jxgq7tN7r= z+wnWc*6;9SR<5qg!p0_U?ZW*djm%PR)$oFS55aW-ZR||UXcE=8A>ws9t>P&#?|1tA z=N^>kOo4VCP~-=eLer9naq2jsmDS_W1f76VvV}{bwo2Ese^IeBxFO@T>XA!Z7;}jW zC4+$vP_;a+Z{mNbgxMJB3=nLXoAbOi8vpa{Gq_coHMUaS;G9z4Tby=ou0e&=e6K(b zIxRpBaEt}sN}}&fVr?lRs*+oQ1)L1S73THxNe_N7?o2pjF`#j!MSyOZEw&#Gg3r(U z=eVS)&kM@Cwmd14cy=5g)4BJr!Lclxi5ojqP2&nBti<~|GE0mYoC&ihZ7>kYl$dO&|WwIYI&sQ1U*S zMgahT_U_g5+Gk+KIeY0uBMxhUd(^A&j(=)x)W58=<@VnBZ4pL9tG}ddBX$Q`8Ek*D zh7(m`D`e*EjjIy<07&kn-SAqm59S;_)Jp&aU1?mR0?W0;Pay?6;|<^t+a*DRlbO=u zkZs*Ia>rvHDj4Gdnu%=3^B^F4LS+Q|JJcua=2XA##11I1jDCFu;h<+dK^EJcWbi6= z0>@}Pdi^h&<^BqIxIf9Y@1wD?UP%>W9yUAVepq^RAo+;+Yh)tyiT$A~S@hBf_!|=( z`@5019zmnnV6}8SziZ|=&zgLPv(n(h)z;CyVtAdSs^@zlO|^(G$WcTX>W)tE3dAmD zlbPIA05nhZ-w%dr)+I!yctq?yl6gECRYFhoys1g4Kgnq7KSdMfzWy@@aZ%2kYtAH zk)yB1rRlU-7gijJ(gutL^$Z?p6(?fss~VrH8y`b^T6SRO9sV685auiVudNft6G3M%`a%|p(&5Y<`T7d|Itq#tfD?G*W z2v5#XpLeZXygD@!5|G*WfTi2mSQ~s*7XSKTA4!IrQ+jCz(1n!55v|3A;cA=OZh|_uUcPIqk&s%kdAx^aM3hNIwpdcM`(V$1_~#>0 zk&_G`Cs+Z_w_XS2k8Q5gnXm$F(3AGsmfh;!V4v*#z~^tCUfTEBb#1b--&pOyz+2O>3S{!z$4(Y8~*i8 z#wV`IYH z;44``bU}_@X`LK=%}1&2gevT=xyDe0g{V~Gg7cD+3Bm8m)Nb>c1|JAh=(d)$Fm^Il z_V5bYqCv`G(2WQ#uF+AQiQ|HDjN^TIS`SS6A>4uPeCyn{ZQuW`Fr<=@veB%$(2&@0 zSgE}g`+0E}zCS(g>7BHQoz2Le^VQ6&9>K|yt*#Gu0H>_Df>e&Jh`$_Q95I@53!D-N z2-OZ5W8qqDSp6(r{;I~;(bI$7dO`$(Y@urC?=B1qV=4>qdq_J1)b|J1h+$3)VD4Ng zw1b;#MNjQq@P*m50O6gHnLMxxHJoUFQPY+967oj_MTcg`<<=-cu6>>sldaz#ZIVoJ ziJTkNHKPz9m+nP??A66_ITcNQI*rLYP3k0~tOEj5V}3@T0+Wt|eTSrSXDzT~g@Pvv zsy5~Jv!Ad&jL3gL!_@SR&-e6PT1Q{foa&QKHs5_ZZ>AmcGh+*sz}^h+8k-LMk*g{6 z(&(>&fuvf?N!~gcZx9v)Zu=1E=Xt8@N2NN~RVFJ9Tl80=wD@lsKQ#>uy&9fuySwfK~ZSDN}8Q2Ecshg}3BZt&NCqz3qE*%+(w&7RvqKhyyWFuWl%r}kkj+7LtA zGpq)hAs}q?7{@)=Uh{<(4OAwOqjo5ZD_R{o@2le3>0u7t+I0q-4*sBtate`vE=*Kl z2$16c%~|Idg`xG~F<4cevaxfEz5O$lE6yR%X!EPKVxweNZe};p?Ng%L^mSH1QG51` zv3EwRDa3Kc5LrWAFlK>x2@e3ko~&NFZA9o_TAr6%?xV_Dm~x#hP(LL^1?Ld;}7;1 zFu2!HEH(~g6&zVo^nl%2Q}dLuf$)0!*|r*vVwJ+3h?S z8yu4jwf2jxNYl8R6+-K!2S_!h0h0%di^rU%UU5-2+e13G0N2#P}swZ`uD?D^K3APd-@4Pc5KG zi#Oc>{O<@!FB>FDQOmuu`%ltpaMsS9+}B1!F6aI-v7V*M5$>dikbeYi*j;u1&}Zd- zM6uY=1_U^=%MG{aF)_roWkKhtKN@RN-7bjAg4>d-%T)Z1Tg2DwUrmAir>{Tu4az z>=uv+PN5vV4nGL<*t#_|@mh88W>B7mQh|6`U63y-sA%;9W$y?fQ0%onCzdFLrZeDxwMQ1(gj!tpfK^TBaZTR45@^*i{X&Aoo6!8>sv@ z0(f?7OMS>w(WIdC%ucX!Y;Q`(b3A_Rjaz;Vl@qL&OI~U0!kOm5_dmmQ|Jt`2TDuU7 zqM29scj}}%$YA$^Qz1L%T{OdTms#Z?V3YP4`w%9dGESk1!k^-8-Jd3PJPr6h?I0Mi z62W_foiEiMd&iwZ)TYWYLU6PezPJ`3zV`o+ba=X4Fj5NOfY0oS?1_5Xy2e>YWo$HM ziF+F!N7R4yu1mQ2uD#{0I!NQ|P>2-Y1R60!QxK)tGqdT$e^Hyhp8F2y${t7G{9aQz zkgYTwl5JwXQ(!XJYVTMzIrE}6Wlc{9E!X>4&s8zis(;ZzOdL@K6iOZ(hJJ=p1ILy~ zf%+zp68n4ULs{lm1b=(8pV^D}%sQ!mwC@$~^JG!6A6OgCtPQv!o+M<9cUTmnKvN`^ zyX_I{!+|c58TL0E5`lai)+IP5fI>5?dk?_VmJq!S_zW7F?O_4Dz0P#+B^Do67gLLA zbC4Vu{4Cw01Tcr|QJ-L2*DpSB>bKPIJRX%;{*;$uGIKGREXga_PphAWjA56e3~IWr zm4`QV8Lua{BJv*&<5E=0^(Q#k%vhJqT>>qzuV<;4W%aCr+A@r1dZ&Rq{-{9Lj5gwu7lTs zV64Ws%8k)Tu1sFIPQFzIdf$2;FWEbqL0{#jH=srd^0(ZXKD5po62^4P7qjQ7-zD5c zV?HjeF?tmyd2wJF=9h$FXZkiimC;WBZCGHj(Z&{@IoaqxvLG?E@$rBA6@S=Ydekuz z@!j~kJADVmQ{}8I>5c+p8psq6<`kE{NN4%UW*o5c|5z9m+A2~jGl+_pak8C_G9e;# zX)UUd3}?ZHf4kbDpr7v$P5em;nW6j2!c(Bc(!Ml5L`THNvG)hjx}s^N(=+{(ID>1` zQ)4}pAElQL{Y=m4g0ua%R>ahVim!cA1*QLSL1Wi=@Bf3D{b7!F85R*ks_^RN= zR`Q8oE6SrKh5O;nivPMp|4c?sh*^NQ?9)bJJmtnEF%8lN{wO=MnD0vN4P4=5*6FiT zfWB$=wB3rLIi*goga*nAhL3#DJZGM6D;un$q^qNc1IWTPB@tE@_^=Fb4jZeb$ScOa zAbfV(B6rof>O)4llENOblAD{2C+xG=>;`RC4lY9e*&IuIAl2FNkUiE;wa9NrVP{ll zuL&3mo##c`kRGau$=4Z`#*C7zv>x|mUU9yB|4Kki(e539u&b^M%|0iX{q0SS4nntX zkd-}0xOJxX7)GbLbTH4Zu)der6MhS~__;rbxAJ#nG%MsmtQt)ru}^D*v%qlZsqHV= zoJ!Yik5JwknYVWxQJ?#b13|RCUYIt|R!Nm6C^v(TBHy3XJ05TU@mc`Jj%R~S)1^Kb z3dNL}<6U0ey{p~r|3=N$RAzm@?eNixBiipOpZm(NG{ghGK@cd2htgf99LY-@)x297227lyqo;Y}}M{G73##tPH={@_AxJ*(}JRY%gB%hNGg{f!5d z(IgGByq)DULsZBJonPYIh2o9|+;foyFb$sp_0CdEzl0puC+}%ZcWI6_Kx2 zMhk46{VkOOH1643VPdrc6x+PnjY``0>dBsJTneU&fW7Z38z-y4lD^IkhWg&q))bgQ z$K7p!=uD$nB;V>t&ZsHw={PT<{dtoi;W3h$HLGO&vUeibrunFr-XPj_<}fDg9{BoR z-9l`s3Lqypj&Dk_aNBflgPTYkrM*(i>EJiJN)4CjP|;cso$Oi&f$p$?OEXH|j_Iao z|158-MMk;6#RL%Nytje4%s^#SYWW}pEG<%r$fnZ=K-k$7{AVZeK=y|)h*DL}VhX|V zQySc-N00_En(@vO#pky1RF{cQ=UFrI+!tQ7C8cnLti64Bn;7QAj`*6(SZ-MF@M0(4qHjrlFq65#dR8DrE_xGkcF-$} z9YnABDD-^Nwbff2E9}tY08e4PtLceo1nCMPdf(HhO_8Z^#BT$8d3{Xd{=tQ(Vi9^Ir z-?dWzqk2PQ=tGDA28N2Ukacc=cC*ufO@C!r>(9+h4RPF(fVoX7DtU1+#r#k^E|K5y zIcP!kD`_wp>sFh=+A+r@N0d2nxPuA(n z@(`b-rgEr%214yxT0Fj>b|eh5F-`k;&IS6ExUhLmWhW_6zWTiPO57fJ54lUAU!#0u z4!e$^7Aq9|wnEicQfonn_IE-+))#Iqmn5UtsxD)ZO;FOMu>Mzc8C(srb;(=RE}}%y zM4JTUJ4o_BKT@s^2=dOiBKHV9yeZg41WZ-SqVoZz}*^ObPS*;!1jR|WW{D>Q% zvgp7~{)jNPrrE{(VE4LvxpI0KRv>IpAEI**>S?+s|MIYh)YBPA?MFY&NN_Hyi z$iU^88t6$Xp3#cGTC-no@%C_$eh~3o(3kCR!VVN7o=-jYyyx81)o~QvUnU4R#auMs ztSE_~`Os%w1QuDe-l6tB(Hlrd-<8_=y}i!>M-M^SfP==B&qnL5WBYO}2 z=M_I>_4-8n`t!C)?^tXk{jDV}srP<~3yh)x0;sIV>6Xozx~}SCfm_;BLx~#Hn7w{D zqn#?8Vyov%(?+F1m4r6tpldDJ+bvdtPp@LqhHpDtYj`i1`7c!t*VYdb+r8>w?ZwKtgR|MBjoM4&F}@A!WCuFxY#^s)B?q@g_lf2XdS49G|5%%j}0E{^Q~AN zzYEC!JJYLa*h&Yo|JOsg%0vHoZOp3Jg}32t4Fp}x8z;AY!YK4-BDAB_nse4KG*#Fn zW`=h2_!1uJ&Cci&tB<`mrWq9J_33UdcZlS(HZU$!3rxTn$9!_lx)>aAEMbyODBU*` zws8jAKlyY9`^Na0jsv;JGln`PSWE+VgFcPs^@ieTVV&JWoqJwtB?O87Ybaq43$@5X zrh>=^>MOG0gFIF{Z3FKzbBU<6pYqcv;*rD}x^LygOmxY4^K&WcJ zqXlejzU0y|_0=FrBiLUb%G&bAf=7f$Hz}L&2ly0ncv?8-X5D;|J)6)8I6X>#Nos}D z57kjva@uXoo7V$qAA@C3!;DI~jY|6U8b~hmGSOok7u|J`1=2F_F+CcmtpHV5B7MD4 z=BR-P$BR-*2z$zlGz8rvTEejGa4wzjc+$=W1-+SRtKO+K;GD4$^)&scIH9MYgKQ6g zhIPXOEARxiXyx^rt=Md=4G|X`I&G;-9A{ZPYyBeW*ReIbjUkBpJyYtnhZIE^Uojr$ zK)uT*A)4vgBrgcsY3E(!4_9=fQyFbkz_<1eN3MDL_qovQ15{DW1Mb@jIul90yNcJx z1l_u|rpDmP$)q2X&l_;UOtsWy7qza#+g|ZNHH$26%Wq(wO^@3uxidtotg7}kRUhZY z78!Vdz&$@40J1xMZT{}q$aP?>VZziYlbfeQfvCuy5#*kz0y|07azcNTGz zYHn|W|A zOVm0k1Jv>3IH-*EiChJzyyFZG*{)*}1kre@zlSvP7XkBO=UQVdLO>4D5fqIi}O7r-=Q z&@(Pp2W%I-Ay>MifXcjQlY;f$5z9?mIBnC>!nWc$!Br5T7*Mz>O|#Fv{uuOS!8CLt z<9BZKcb#!`8eAV`WvAqKt-#2dF0})nT!$YPBoua44-qByOJqw~KPsFaMP(HY50jWx zZGIj)rFZ5kQx_QcD8jq}k|bl02rlQ{zazuF2$7{!hhaA)G!{ehtCsk66NIm%aeyRCvai(4 zX2_EiZn$u~8T!(a%JLAA_h5WYVVi)&gUTaswh3I1;seC){h^@w(ohpB4Z6+xpfp);dIOxuylag+Cw3recCys- zOtSf8s~0yoGe$HgAUj%uG_P5|k2(TBn5{b%E6wD|y^wbwvG25fNx2C)QA(kt(=(vH znBNOw-hLHyk2sKtt8m|&NXB^r z?t`4mi~Oc7FvcwF@NP?JHG&CC5om$GLp8d%6#}?!MM&FY&9$ucnif;Vp`qIbvD(Y< z_-R&ku;5U-GmM{3zXatxkBinKN>Qo)5r0A8n7lnQ;cd4bI^Gu|VrexYiMaRNZqb!D z^-Fg%*Fa56Vtd@7v6$OtFs{1Dy%kyIZ5G))$A0-fx_!;sw@lU`m*lfOPMbJrf2hj; zB>2-ENfpcKq9DxZRfUsIPe$T2B{LoulakWq{x^l_tXcRwO}JJc+{`eTqz~Z-(R6)9 z@}E%$d&7|jUy6;mw%-Q>ZwvPYMb=+G{*JCyzh_60yB8a--Z2+~G1LZ{OPXr=pD6GY zET@(nNLgYJ2G`msKING9lVGH72&rI>$b9vZE*m*_bNY&2(Y77XpyAnB*Qv5ckViUf zyQoSMm)u78=aRY{;?RgL4CROeJb0p)I}qhfP+pG}l$S&y#P5hLQl|{mWQHY)lETUa z(1FUd^|nnw5SVuz4xeHuu}%C4_z1c~*B-oVUA-NcayC$CD=su-+5!QZ<%mWZu1%Bi zxDdlNa%Y^&){L!(6a6`e+$O3|7|!_E4YGAV*LPn~%+%@~;g0!KzUt)$=+)Z;+Pn`OWJh|UTGdkx(uGO@WMQ>zr)jSS}x0BA<-z^^?75Rs5> zgys6F#g@R6A$cPzlg$9Tziii5611RA3%{$b_hTarGqORSZwQmXI%-}7b-Udr^@Z+R zl7aRA=ll(F@+dH~4ZSx~e#?T3Owh)A7kS$Z14A6FV^aRMD3%Z=F{ z&*7Tk*6`C*`h_B97!8llWS(4kJIry-{=|=ruAR^_DQZ$_kS6SH#Q609p>0)Z)KzT{ zx}J?&IwtaH70!iF5s`z9oMvi;`v&^_SsGtzAhA@SC>d@Q=~<#G+Krlq$`B=XM|9GP zuZ3?pG`~jSAUuO$t|?Rvc?wnHwfWo*wPj;|RFwq@+E9%qJG&w2C`x%PY-&9O-(7!x z#@|PnIa4`W$<~z}jbCQ7jH9-C&~Y1P(}HC)xt4?e!U`Ed6Jb`T%&25UowT_gi&qAy zIC)ZBS69+#ZY*R71}ORU?jjO=+#@XnP}8I0Jf3Gyt%;6McOCS6=Px*L00A`F;#-#d zIOcTDj*bIa&DT+_B^8rwrl7e8I23YgdR=q=43dgVoI_~c6xe42oQAZ)V0BMZ9>cIE zDo;>Z6Bb%!si9{|?|n*$U^1R90$#c<%vF05xdswo`<-hck3{;-&Sw2OKke#h9}q4- zl>^tZU^uLm#>AU;-j|Ffj+=Po3$HrLDEV8aA{Qr-LvAmE!+mIe|MkheYxR`8PHG3h z&K+5BwaQC8?{i7)0&m^$axt+3HRMQ<@18~Yw;K?2&~dWsQCtX+du(9K+V*}nH_i;w z|A9Rf;jIu)=m+;8!o3))J=_C=HPW|8YT1KCle+b7V~gdb=9_H``fG`8RB9kTV=0{&5Q9TW_;L+8Vilm7sr`e>Bxudh%Fe=W(Sh zEd5z=nQA=t^RvEyX%+QrNxNeZt7y$=0Gf@E^!kNj9u)!?!V646Jf6p~SZrsU zRX2b1aX!bJ89rV2BlBE0={8m{ZjoPPns;()9Gak(lqKPn+YKlStTfMfj(egqC}sCT zNty4R;P0XxDUj7dBad5u0X|1%B%ybeZ3lp8Wla~SScR?HRlC7BychGJK{@R>!jAsj z{0r7>R_zKD8i`2#U(k^_n>CU0v@nqBv)aB6vV&_KY&i3N#ezA^S~PvdW=_D+FPkKJ zj8qG-Iv1lz$ecg`1x>g5gV$$eu5A_{x;xJUfR`njpEfSff!#HII8@YW|H~15b>XSQ z0NdZX2?-iP-|Ev!CVdvsyxbTvM~l>-{lpI^z2f z>hvSxEVV}L$y{|?v}TL~Hk9rS*&Y{zs+oOS>5qWXc(SpvIok^x`34u;{!)iIrfqGa zo%q*a)I*_G+nKH3V6N0F;Yt_@!%JDl3?TO3@vP3%pUb)uoKw&_t-pf`dg^1}I(+Vq zbX`m$sh5yV=X_ybCJN?bO#=M28Cu$xmW3^)eD;l?5E&j|+O&>x)n<7LrVLN=#5j7O zGXMD{>RCgJwc)@NAV15b=xXUEEEY#Ab}qJ%_z)IB(z1NvrK7u-27>PIn&Gb&aVDdX z+rQ?ReX}QN-n7Kx4%Lo{=us=7G<^S!9a+y~0QHLC-lo?ONedT-e+6=Q#IIwP+BO@s z@j}l~WOFz=q1=d!u7<+AsxPDJ{+%c^!s5#{5@ioKEw3Wh3U9Cp?OjX)vE*zG1De7z z1EG{%6S3nbZ5=2Mw8T3tS|3vK`po?xb=tr1DD*mqV1*7GWF1KnDKCk zT#T@>G$y`=iUoeLqig}m=uT$p-jd#qoNaiKf4mLs_3pQbIHcB!h%Xl+W%xjLWD!{Z zL?Tl0>*a{BYuapoLSVqjoeM@T!p4@>EV-|2ysGAw(b5~))QSvvEKZe_ONvXLF(|et zyBXei(IFNS893Q43^s71$eGHSi|SuR5{DxPu=WM8Yh`PJ)gb725t*CT?`V&>ur2LL zmL{4$s0&Mj@~g3KX-2}$F;F)$Aazf*V^_!-Xtvw_nsmlEmnI)5;s_noRrvtsX5Vd^ z?+29YFHy)k+~*esG$-AwG}&R+-AXH>g=}bNeiJgT4#<9&_BU6N^0w%M<{3m`$t9LK zeULx=ZOGtTi43_QX^oBSyq$JsY-E@0@A9Qcl}M4M+fCBqsLkIeFZS8U6NzO7z+Q>q zf6N`Yh?2?jy*zJR)S&glDhf%XS(XE#)@TpkI<#jKgk1cSuS*mAS^&6Rsb~m$B=Ob%Hqnyze8UN=?}5ZQq%oC>&37&5NjW<@eQWp# zn1l4`?X76@XNMR2jLiR~4K`=BrVQKO6W6g>@OdNY?__T;Jf;f%n;WJAybq!Ey<&%X z0rASpC+NUr={mb!-EVqVGt3W1GoyeUxELk{iLbWum8fvO0J1)*r5_&YQ;(H^d!TYZ z#WryL?axm7+ZcVpBEg#I>StPd?aTi{YNn;z^Lo?=eSna>&uc2>BCbSfTz!6N2641= zO6VxrzbmN_SHg?vm!uwmC4y@mlxBq&rzyZEV=aVf{c9nhU;Z=8mymH*NUeP11LK=J zvm(zENFi?w7yHCBDyWq7H=JnZlV&uB?7S=`VKFNU0ybo$1ZEpN3QP#CbolZieqkECCNei=57*$j z(czsbvOil!Ct*UQ<@?b%mWf?sZ4_H|=h(f<2VkD_Zz}YrqD{vD-jly<4C7;8y*CzW zp$8{Ydti#sJ90Iq&~}xZCUxsuQJ}_XcQlcX!-W^%tq^fXP^*n0nm3ADgOarD3NWaC?eTb6zA?HBV2&L`fcwB2$T!x4#A!r!-i3zmfjmA=TzbD_^20jh6C z0xF#H=@kTZ^{VvcXU4#Vvtj61uIJalkrded;C?!5!t>KF2h6U z4Y3SPOvv}{!`eE`SQ5WCvi)#2>KeyPIqHMU+1QX&b!0bJ7m#Gm&(UfM1j9ccq;bOiFThjU3nbPME;>?|B&<@ZNW=`ga= zY~^g)MmymnSVgDxoaK^{*fZP+!7`7h2;t$01R4?fAO?ADkn!$`M3oaJl@OpEwQ1Zf zM_7Gxj-&wAtN+~_C&83AJigTWgS3PqSjfdgzWSF2+wMv$2Q+(O6ZW>H1q1#dI6y>s(w#zbooGKf z9$ikFY&1)sBYHk?sB4kR<}%1nLO1}Y!h-yH4nM#L>AVm;dIpN2BqVec=yA*f-2Y@G zio0ev-j%M+#|t#DMR?A9I!jCklRYIH!py`Bp*Z$Jy0RcK?bY!SQTA};4yW~ZxZ9n! zp5T5R3*?B+dsOWJEs^g*{ib{*dh(Yz3g3?_fl?==3(x^REWYY)%bi#=^>AxE=_23)seoh; zg&9Qr(ukNrhp{CnG+%&RJZ13|pAw% zjiPgCEc%TG&RR3~N2dIWe5tmPSV0sotDSkY?PA_fVfU}o8T`Q0A z!8zp&E7JOLR;NgfvP76$1(J>Z9rP1nd*cS2oXKJd;WEg#yhFB1KUvJ?#;0|rBx2r* z@}bEAu-ycMQxGOzj8uT#`w;SILuhc4&_$myqtRv0h(U9TCX+okxjr3o8F{+9w4^<0W7iA!%>=R?xjDIwBK8A{v#(7{j zUnFpiAX%*8%-)zg5*{AQx;=i%9=!mXK8uQY<(R$NmH2Bsbd&jbfV)^SSs|1+zhn)@ zQ|vN^;XK>TPiEGGDz@e$n$xW5pD38RoHIG>%=-%os+jQz5PUWj>1BOTVR}rAK>&A* z8}1)M2P*>UvO_YG!AS&cx^?Jtq;R|}-2}=q9#RU?9wNSb6=M4cm`TtI|G!=TWYwrn zoqwxucb~tK$}SvKQh13iYl=t%jZRqm?GbGO{Lu;BTL93PYD!7N+f=J-SrhozJ>F?a z6GhQsZgWltqpAQR7M^OCmjg4h0?HFekZL;figdx>4+&?N#<)c>?3xjqT~4UsnO5?( zHG%MN)##csDr~jNBUJ>=0HTBwdr3W74KZ45;3I4EUCpFD-$Z^jgJ2phUopeZO8X5X zI$A%ctwrZ2fBiXeXY|q=IXA<<#Gc4`msKcxXCHzgTs*i@EngFrCMGAavti`wyn}?& ziu`eez%CQ<=v+J>5+Us12S%vS^V}L&X^Tv_NqUT$_jJnsAW+AYVzO0{~%Ddy_e=M*`rY&w|A=3MNx;Nc#Cx%N?!W1xG3&zvca`KR~4F9~0gP`u)drQ^0M z|Gl0=g>d|5h@zb}yS^yD*Lc%Z=4)Za>Lv=Ii}wTnW%jpo0fB2-bIMn9`{n`<(uFx}z}zv}fBK zd+}ER-?UN_P`qJi>)`~ejC*H}Yz1Ii$iZ9efyqY$t@%X?!Alqm$ zMd>w3hpc*5AZ1Zw-;P$mI0=|bfo@?J`m$mGa=nR~(q$)cc*)@oD!yNmH4Bq)LD6szY+s#TJb_ zTJ|QdV#{(a(OEK_$U+)KOeailXT1l6#_2SP8$s*74kQ#U-1u+V`M%}Z4#%&C{H!s+ zi117s(5(PDr38Q>U!+@Y`m2XO{F_~ebd6zZr?~ys%&#i&JeqvBIrrXwFInbSW4K|_ z>>_L@uM;3bCu}>gNU$g3W3TfcnN1dO6bkm)1~;(F-|iHCmDSrOM3VFd=WhTYQxYp> z^tqH!#-4jxt@!5LBX-c!M+n@#lj?j82v|!dtJRnt;K|q|%Bg%8`PSpQtQ}A*{7I`G zMxvjQmo+r(8@hy(>z}v3kubo^J3&7)!91?%iiz4PV;vmM+?Rz?45Aa7V)ann;g*n0 z=C$Q0tm$Yx#7VrH7R|%cX@>yZS!?e0yJ0>APxOn#^PlT&>w^}Lq0X;_okSHU%z5cd zZSvrw#_4(8i1*&Kcc@Zz5Wo1~Ux(VG=W*EktYZQNr1A9XW?vSPwhWWRFW{6D)NJB#Vjs#vF++07 z@s@LLWNh@^?;P-tp_@@!Upq76|h}P;$t`4Pr7bO zv~1?p4yPyOjuh67W32UGo@EU{dcfludOW@J@DosBE7~{0NsXP-tr{OBOis^xZ6tA$ zsx!X^d_I;+KBCFbDRlpNPs)cKaN*9Aec^nS0I0x#YUTPdg@W?Ew2C{%!MZ)##{eLG zA~S<%habA;$(7&m=W&ID9Xfu-gEEq(a(+XY44n!>TQriVL=+40v~WA$H8QBk#ID;1 zl2zROMv?uWLkZhnovF?wKTS4PPX{_rwRscP@{jqYCePilDd7Nx$l%8HyCW2#hOxCk zq09%5d7YJ50RyY@@8tm`Nb&`-tAM8zJf$_JU~F?yn2bNi(ir~l)kU$msMg$qUICdc zgedhV#ke3wrb0K*$yK#I2Yy_x!`+#E9WUYsK&^(5D<2kobf3yl?@f2>Zz@Y-Fd(Y@ z?PV{K$0H@b*N|TjEQX=i1QzrZxHv~x%3q${P%6F6?zrwJ8hlu|I=>x4Cw(v5gOrCN zjiJ!JwfzXBVp~o%ONO61`b%oK4RL0%M2}lER?%>`CwV9iUiFCWI6G8tZ(xQ;(+Iil z;%Kqt=jOZNmTayb1*9Vtz?gWu{-mxu$5t|sqL6VX<5Vqm?8&3U0?L}$D(ML*!~?h( zCq|bU?R<``Sfxdn968<7+2`r+Q_mgFr9vxTf7I||hALQR2xq0QK0)jnrB`o!^kmO8 zith|Dsf_{c%Z?mKo(j>Wcv{Bb>?iH?a1&K9Dd`$551M0m!DOe3Xe$2F7PjPoMq}sf^)s)pj(8W`A@G zspEpNE@pk`bmI#P#O+DdM6ugi8(5mQ!m@2oK0D0IoPfelTF}DZsgnXl`rz;(x@xAh zIZyni(Qg@60@n49sgrgB?DB&*vrDjqY(&;0JiI{nSD8NrJ`*R-QMjhk$v@+b)e}+?qDAppz z@q~%tGK6xD5%FhuoCbGX%#!>Znz65kHG2fFaHYLdeF;|&zcfC$-wD=pBG8+K!ZYj@`w&$ufO97>e8(59uy*{Prpc8h`3 zz@Ll=erXwDejM2&B)*`HFcNZ;G)ASUhoj%7Ivxd9@!1VDfi<`HPu(TDvy(?}aZ<<} z3V^wJB$8DIP{9Mn6~#KeSZ#~+1|FIoD8gr^H_KDs224;&oZ(hcyOnVk`qU(YRpH4|j9WMW1z!wGXJ4tE8kV0!lb01$5&F0`#s2Xyan6WU7H z$3qU#56~o7sq8nyl9MKP>u{j`>^})RY=LnEh+CNq_yU;z9(Vg&8T96*LMR}LpepBt zYVPA_=w*Z9UfJRLoJAKoI0nQ;&08b4i&(*eLy+nnJ;YGm7eXkmfJ-%dx*~sdtfVFY z@tkqjfEYJ;$kDV>Ap*n(CH)lnij~Npm;rwn8tY?WE^$)IY>fZJ{viN2K*+x|Q~vg@ zc%}ajk`s9E<1wpK_#iL;{sL@RfU=6wN))JjB6r?C4$?1=;_ru@;qCFbq(h@*cUy>Ku_+ipklcV>}3R#7$3;dP*u@? zV{|=~E~%cuL+hS_Nk8lbC3yn|aXq);Esvx%fAw?$71=hN0-Quu{&_Fsx1yed$UYtJ zd^l-+9c$GM$^^z~Fm`Q|(;6Lx^&X0_N-a^tOrAC`*189UoF38drD0Ld)8k$^FF=ur zc%#p5A5SGJ83kva*)>VX$fVY*ZFhAI}@x8V027rMrB<$5a^ z?J5Zcqk4UAa;T5^^EO}P|SB};+zmmY&d!$8T>qYn$fmi2V+sVy&0C( zmax8*>qA?TVOdw2Zp&P5r0loDSx!YT{nI;e#)rj2PGw0N;G;}Vr&o0R;C%gXbn6SM zQLBNyerFu~aJPbPL!52OIk5*<8wn(bG!LhACmUtUoh^I=0Z60&W`MWhf(R zx1fGYr1dHaVkx+q_>bk1X=%xg;ig7{+9p`q`KLoMollP);~FM_;) z9q{KxII@ImYO@C`Nc=TiQD8v&LQf>>5zjzw_#DFG+1H2=j`_7pS$47S_-WxpaqM>+ z=6vh7zMgfXiH^G@ju?VB;2Nk&gWXy24xK~(CY+O(L{;B)89f)6QfOX_0eTyhXUvH3 z&vL1{!GM%OCv4aA58`6MgT-rohs!s_dfDX^f^kC9egfwyw&`h+K2C1M6~q$TNr_T( zr7!b?af`M+gNmleqe=9BG8=~L$OCs8VkE_DD8?mBP9ysa%T^wb8jO<6!4)j0^H>7t zLAIfztfi|(S5|P-c*|8^|66>F{%b%{f204JQEk13Tn{@{%&KOD$A8g32u4ok);Q!F zT{dS64#*{>CpFZ)Km5uh;0Nv>dty*S3yR^@9Ll5-|NLz0UFi&{kPDLW{~~(^d=Cb= zWNiZyKCR(FJy=ZlinOln~V|Gq;g!l7`c+iOF%9kq^<@ARZ$c@>8J~ zVv2orOX*1Xj?Yz(@!DQYm*@~uAABSF%;N}Z8NZ=)g{mbG6YnfuGo5P$kuHB8O82#0A3|{B#^{h>L!?sB{Mkk0wJ;2hOoZ zebRoVd&NvuB=m%gf~8Q5R->UA$^?3*r+f$Y6n*}IR*5P3{9Ja4c0GTT-V09;XBDzo z6OD1a&<6x0(eEfG(DA?@`07%2i!3VFKYq9_k6qH04utz;E=P^rnav6*fTRX@8%Cto zx2D|n@*Fz2j9+ST1}-|31WU0hy;>sc)fAqgEWxMRU5ITxOIg;Ds|)a8$>#3^E$spV zLV8Eg>(H_eso>saD>fXMrQ4ijE&+CMR^OjV?>5(!r%cb3fe`aG1sY-lg5SMdEXKHR zQL6SCJDz12OU63|;d_=8><+5RyWDo%EmwI90BhxVospve_55nYa{=Y+mVD@Vyt2)i zt}hvk)}?n)f*vm&fT^x}IJIz^Gd7nGuOf`68Cl>U`Wp;wu_%AD=+a^v=~y#N@=IpZ zafCgpCR5##{AO)zcD5-p)3D6Q+uqbd%m0KZ!Y94cS2jVV&gWcX~!2 zHMl)pAKPMqk_vUkITfTfq@?A?ve!#B&UDyf8VR09X8kv{yYTz+Pq(mHRJux3-$ig{#{})r#n}rkLo#{H?CJ#jxF)i6t<`Jzv$LQ%P z(S`l0#UCtz!gxmi9qpii4@p7;JUh4|?lWHu8cXuJVUU$I<1_%t;n3MM z$8>#3Hz^Z|-;>s12t2pSb<;qeN~d}}oi zJE-eqZ%XlC+O`m!UR&jFX9O^VPbn#TJ9FoYhG5{_CYlUlfOX==nTd16@t)#H%eq5N zQ%{@r)%qe3Gz6EO_o6jPi2< z&sd2pT+-HcbOrCQ#>WA%#l?SG&e%)HIQ|KxQog!l=>hNAdYz~_7|t-N2&z7;No;AH zuja6ySXEj24>?l)i9(OX%%M7EBAvo8UrV%5gsV#x$#8K;-&K1sIm~jiZ^KrPNrCY7 z%-Vpr{tvzUK{nMHv+THs2a6;8U)pSClcjMM*H&VHz|x6e?(CNBy!8iz^tin2b4qE; zNVkTvjwXH;Z$TdB(KqPA)PQ z?V{hN>s3!VAWq{6X|-VPNk_sJK{?&)vp!5=#*uwkmXa_3H+YbF-44Kqf~y1NkY?eO zlXL7`HFkM1OdC{*b$vT*A#qjllIyPU`jJr3A%)hmA8QO;((s`Q9^PDfHyjT|O&UOZ z7!!XB&c*jg3>?E=KP>IwARI9UQNQgg%U`#*C8||!! zPuIs<+^L@u^$*?S^}7$BgO!q6zh6^mid*+T5UhBvw;MZd5|>`l$o`AnZ)5mp^LSEs zsZf(JrSAIDnLwFqxC^*^bIm^q4g4zpJ@^G-h>bl#5!Ie68_W6(5aJ%RWtvSZNcs$E zIEG8FACO2Z>r)v)k?P5>&rB)}-$x>t&Y=avJn5l6FG4I9loArv{%Pdz{NgLte@WN+ z1x6HC$Hl~>xc01udccCB^$Adg8O`;kKfvrVcdMc_$1P5n(`d5*cdY;JatN;~KgfZ` z5EA}bm<4xvb+n-jOGV75Z`*8)KRuGea6$lc9C5Je=okA`l2M8X!v+)G1`>j066Iz? zK*oc$d8|$e0R)BANx$yVucmT6uXRe8G|0(-t0g&s%YYj}$+=3w>JJ7dGeCM0)}_MV z%5B+a zS${;Gi`+whOxQ~0*E7C=A`^;}=@0(WT$k1ct1nj{PkH_VyrIv2VTpU>*|s*#(VF=Y z6a2f8MQQ*6;ds@IU;rurj^u100y#|@=vHJEzD2+sK_jk9KQf49#smh`fGqyPmvLjM z8GmSpk-m*j<8Bqi`ihuNo%~P$Em3^cdolDJZG&T5F`htEXQ3Zo3enJBDJJTdcyk)* z5Sc=(CrL9rTX92KMHh=pg59M61kPWM|8Z)zEs?{KnpSSv8W&2gMWMYRwu8XeMCfLN z-(vcj%bRskRJIy^=*qu0BEtNB=dHqTlgt^XA$6n`&waX$EE~EH`1PBgTjat;VY|}O zql&DA&eAB+rwBpuQ89sa7jcPNaBg{l^=KsNC$@cE{`hs*k+Fk` zT8?r1z|`iNSm@q8(Fm%34~=QACE*Zs$SGrPcz;_M=BvR~rOIoCE{pLHX2pIU1hu2< z)I!t!Q`VtTzVZ_D%*nAQCn0v0Y%rvCpK-W?-82wYYL^4S%{2w8viyBc+HG$&>$;ct z*znzk%ogW;?)~O;Eu`#6q?Y0jU4Dm!I-IdbzmnOu;1QzDbhH+T=1^MxyamDSHDFDl?y0; z0ez#f$xz=E5(e8oUCDB)NNIWJJW7e>z}}8}Bcju~qNK;ZmVC*h13;sId4{`kI=>Rl zQ|{I5*S}Us*$JBri8qUMo(^=1&JO}#>1P=}%A2Vx9CQfl-^#0V9Z8mYfqRsbh)QwI z>DSIZWQNv>#K&&QK&}rf}ZI1k&+H_ z8bG9@yQx{ZV{VO4Oo` z%Cjp_enah)3NmZuzJn^5)_48R!{Cbk4so(b9$dUi7-$tzs@U+}==w4iNq_ZfUnzw@ z7w=H;5Wv{Jb-71sKiewH?0jzJ^oUli7YlLP zR>eQNvAv*V#oygK*1sdao=^HTdnT;pCW9Acgq#UngW2u$BY^O!ek%QSjoopWW@Q0A zV`L9zL(l`|J1$6^fVwO*U_BL&d|}yE!9O2N%{MQH=rS?E^WD=`VVA;z-0W4gkNM;2 z5U-;~OIu++_h@bl4y)UbO0pRhohvm!e{}<@eZ*tuP^c^E6iZ1FB5uWnP*g#3oMtha%=&!0e6E~7EQ>l)w@uQW+vPPPHtcgquG3?DOvu-ADH zb|9f()og-CtCUL(7aUkXp6d&Uw6_?omh@)-O(%HsKGy3e`=zNz5R;skQR_YBLu|oQX^Zq zW-u1S3)dz7Zxm}tW_hX8c-|DiFKI-JtLf}isCPpqhtLiy8-B3 z;NmX?yj|}@c7grQJ+@FU%ihrDGmA$-TEZR(Od82C3-Jd8FRZ57wd&N||I%KLnG)CQCXbXJ%Am;LD~J z1Fs{OE+s-`QK=f+eLehVVGP5 zf85TZYvf@fto#j#luq0E7WUG-c>Ok>Tnj z>Mk>>t9&X6DFrK}b3;lF1KGiXcN1>P?y}u|GA@8aY{#{$Q2a^_vBgNrf6cXfrs_D# z`3Jm&h7RMTCx2l=>g6wiR7q7#x0VbYfzY0H-d<<5bJGwT2%_orVvqG*h5BGW(pV)< zJgR-Ir~vcl>|&I0n8_7*PnJ&su#Q5Cjmrdq6kJYF>Hd9cwUV>NE-tef{t=kVyJurO z1pTn&!zIs;WWgTS34KJhvkQ5#X?;F!%VG_|j)~h!UAU$|R8va6@2v-!v?5B7YcR+p z@AwmoO9nUeg^t{QF1oTn-}mQK-T2}1Wx-iEcBq_*%yDM|?28sR#j9wZ>t=gZaN$b0 zy&&r-{I{cFVhNh;KL+SNO*@hoTy<1&>_Snta>(Q@#6#UAhzmME>Y?`P3OiKyjTO5x zd>CsI)w$Xq*?(W%^5jnrGp%W|zD%ujYVj#xZEFpoy-`?Y@O>4yzUKX*MvcCk~ z_iOvt^=5RvDMgt^S5$gV^Ddqa6Y4`X3?ZMD?k*S#AEvY29L~tT*#~gsW2l@Fh~!`D z@Ll}YY_O%k+XGf;CR2ab!OL;rK%w9k}fJ|>-yPf7QX}>ig`#OlEHot3yhVYGZ3|@?~ zZwN`uxB9$+lm=8UD^oT8=X~_5f_Jt+On&lZ>Y{D0Q>mmN&VcglHPR`h z($$^MhhrJ+46`qGyx%vCrfOWI8+I=!_Yf;G9`J(s8n*_GVvsTd%c8;9sf|QluinA7 zu?04-rp=0R?fN29_jg?r{ z%AfL{D-;U0+rR)gHDr>TWe`CWM`L3YFtB$aij`GCDMRb16FVjD2d|&$d7VX2oswW% z$08i>G7=2_?$K8o=f+`JT!OYCdH&s-RnR%J3dufX4P9Oh_!e8VQHkxpWZ6&)w*W^`}2?71s{xJ`;OcIzD~04E{;_b_IOQS178V1J@C5PSmx z$24pWjuAsoI=q%c9``mKlpv|bQO)#lYw))=!;J)Y>QJe(R%HP##G#tzBq9AfE61EU zuP^ZDSPDN$p+-GFKf3swx4e^he;^7V??UI_bldLM{&A)Ct-BMmRPMs2>z}&JwG)^0 za|fn|G=kL=>I3mE)f83bWg;ZbjY$wsYX9!A!aklVXGA3ekF_&^1R4SwwuM{;YSayc zQFR-Sg)~wkZwpY7DVH_1QkHr^_H>e|OdT5YhEl9k2)2B|nejUCb%k=qXAUpt^(05l ziVrrT5U*Ew^3vY`-AC!w_y}uud``-nT-&GL0~kIz?W9=36`?{>D}T%T7n>_N7Na}b z9WsmELcA^fx0FsEnrk>226R7XNJs6dU+FLA7JsYST@NqJ&dLNTL_nVcZ206obM&Mdp?VhM$+H_*X+YJZ}E3Jfyg|=b_w`NXBw#NSUeNZ9>+8#UA8J8@; zXgt-u`pOwIjhQmdfJNxgyRmX(SQM%WuOiAZ>)Z&lwY*c2*i3d8e`NT8N?KMWu%#+3 z5$*i6g+bD%Tc4He{8qAfEJ{(6&?q|M7&Oxru^({a(iGcBqITqSrA#L+b4Jvj0*Pq# zsgN808Bw5kNMRV{)X!K<&53zUDj(A!^5iRDe)%Hht6C$XHzQ+By&OMlhC+};I|C*A zdlwGvzGEpm4tZV(fUO_~5Hoqhu4Yrd#f4(NC{_IafO5@Xy zwSQYR26@nq5FFv`H^#d02xk2`xHPK3fr!G468Y_~dK#>Pq?pe~Z z>|fLQqhV^pM02;G;d4kR1a)Tb7oV6bf(WUm5bG-|*l-h;+I~}B_Ngxd*GT7Ge;#JI zW|I%@lm>qL!7=+kyk=#85I`IC)gyr})K_%R+&-^0{9qAI7#m;G()i)^piNj!7E)+O zEbjGW%6X}+L-gSOUf(m3O-GA+EK$AUzS8A~g}SH2XYJ}+#84isJF6K!|9o@Bb**WJ z)&~e2T9MCBC#76ElW1?n!6N{}e3X+-^(Hx7C2yUBmGg9@P*Vfd1K z*xE;K#SEv%A62i^3y*c>hK?iaLi#Nd2{zy|T2uQ5&qb-_tLoWOydWqkNfAyaEQ#2? zTT!TTF_6u~`)B=1v?R^1S-y05`9F>QMb}TwCX%Mzu~jswpZ!!x7d$f

cDMAm6Gl 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 OjBe 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%0Aac;)IV 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 R5#6Y4yzRF0_TO{k{|_qJV^{zH literal 0 HcmV?d00001 From fc6dfdc52c211483dfe4b0236247293c34dffc64 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 22 Aug 2023 11:05:51 +0200 Subject: [PATCH 211/345] separate init handling and clean --- .../controller/internal/SutController.java | 32 ++++++++++++----- .../internal/db/DbSpecification.java | 35 +++++++++++++++---- 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index 46108211d6..92f04e0953 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -283,6 +283,8 @@ public final void enableComputeSqlHeuristicsOrExtractExecution(boolean enableSql public final void initSqlHandler() { sqlHandler.setConnection(getConnectionIfExist()); sqlHandler.setSchema(getSqlDatabaseSchema()); + + registerInitSqlCommands(getConnectionIfExist(), getDbSpecifications()); } public final void initMongoHandler() { @@ -446,7 +448,7 @@ public final void cleanAccessedTables(){ tableDataToInit = tablesToClean.stream().filter(a-> tableInitSqlMap.keySet().stream().anyMatch(t-> t.equalsIgnoreCase(a))).collect(Collectors.toSet()); } } - handleInitSql(tableDataToInit, emDbClean); + handleInitSqlInDbClean(tableDataToInit, emDbClean); }catch (SQLException e) { throw new RuntimeException("SQL Init Execution Error: fail to execute "+e); @@ -455,10 +457,10 @@ public final void cleanAccessedTables(){ } } - private void handleInitSql(Collection tableDataToInit, DbSpecification spec) throws SQLException { + private void handleInitSqlInDbClean(Collection tableDataToInit, DbSpecification spec) throws SQLException { // init db script - boolean initAll = initSqlScriptAndGetInsertMap(getConnectionIfExist(), spec); - if (!initAll && tableDataToInit!= null &&!tableDataToInit.isEmpty()){ + //boolean initAll = registerInitSqlCommands(getConnectionIfExist(), spec); + if (tableDataToInit!= null &&!tableDataToInit.isEmpty()){ tableDataToInit.forEach(a->{ tableInitSqlMap.keySet().stream().filter(t-> t.equalsIgnoreCase(a)).forEach(t->{ tableInitSqlMap.get(t).forEach(c->{ @@ -509,12 +511,25 @@ private Optional findInCollectionIgnoreCase(String name, Collection x.getKey().equalsIgnoreCase(name)).findFirst(); } + private void registerInitSqlCommands(Connection connection, List dbSpecifications) { + if (dbSpecifications != null && connection != null){ + for (DbSpecification dbSpecification: dbSpecifications){ + try { + registerInitSqlCommands(connection, dbSpecification); + } catch (SQLException e) { + throw new RuntimeException("Fail to register or execute the script for initializing data in SQL database, please check specified `initSqlScript` or initSqlOnResourcePath. Error Msg:", e); + } + } + } + } + + /** * * @param dbSpecification contains info of the db connection * @return whether the init script is executed */ - private boolean initSqlScriptAndGetInsertMap(Connection connection, DbSpecification dbSpecification) throws SQLException { + private boolean registerInitSqlCommands(Connection connection, DbSpecification dbSpecification) throws SQLException { if (dbSpecification.initSqlOnResourcePath == null && dbSpecification.initSqlScript == null) return false; // TODO to handle initSqlMap for multiple connections @@ -529,8 +544,9 @@ private boolean initSqlScriptAndGetInsertMap(Connection connection, DbSpecificat if (!all.isEmpty()){ // collect insert sql commands map, key is table name, and value is a list sql insert commands tableInitSqlMap.putAll(SqlScriptRunner.extractSqlTableMap(all)); - // execute all commands - SqlScriptRunner.runCommands(connection, all); + // execute all init sql commands + if (dbSpecification.executeInitSqlAfterStartup) + SqlScriptRunner.runCommands(connection, all); return true; } } @@ -1312,7 +1328,7 @@ public void resetDatabase(List tablesToClean) { spec.schemaNames.forEach(sp-> DbCleaner.clearDatabase(spec.connection, sp, null, tablesToClean, spec.dbType)); try { - handleInitSql(tablesToClean, spec); + handleInitSqlInDbClean(tablesToClean, spec); } catch (SQLException e) { throw new RuntimeException("Fail to execute the specified initSqlScript "+e); } diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/DbSpecification.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/DbSpecification.java index 2a435c3299..e38ec667d3 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/DbSpecification.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/DbSpecification.java @@ -45,18 +45,25 @@ public class DbSpecification { */ public final boolean employSmartDbClean; + /** + * specify whether to execute init sql script after starting SUT + * Default is false + */ + public final boolean executeInitSqlAfterStartup; - private DbSpecification(DatabaseType dbType, Connection connection, List schemaNames, String initSqlScript, String initSqlOnResourcePath, boolean employSmartDbClean) { + + private DbSpecification(DatabaseType dbType, Connection connection, List schemaNames, String initSqlScript, String initSqlOnResourcePath, boolean employSmartDbClean, boolean executeInitSqlAfterStartup) { this.dbType = Objects.requireNonNull(dbType); this.connection = Objects.requireNonNull(connection); this.schemaNames = schemaNames; this.initSqlScript = initSqlScript; this.initSqlOnResourcePath = initSqlOnResourcePath; this.employSmartDbClean = employSmartDbClean; + this.executeInitSqlAfterStartup = executeInitSqlAfterStartup; } public DbSpecification(DatabaseType dbType, Connection connection) { - this(dbType, connection, null, null, null, true); + this(dbType, connection, null, null, null, true, false); } public DbSpecification withSchemas(String... schemas){ @@ -76,7 +83,8 @@ public DbSpecification withSchemas(String... schemas){ Arrays.asList(schemas), this.initSqlScript, this.initSqlOnResourcePath, - this.employSmartDbClean + this.employSmartDbClean, + this.executeInitSqlAfterStartup ); } @@ -87,7 +95,20 @@ public DbSpecification withDisabledSmartClean(){ this.schemaNames, this.initSqlScript, this.initSqlOnResourcePath, - false + false, + this.executeInitSqlAfterStartup + ); + } + + public DbSpecification executeInitSQLScriptAfterStartup(){ + return new DbSpecification( + this.dbType, + this.connection, + this.schemaNames, + this.initSqlScript, + this.initSqlOnResourcePath, + this.employSmartDbClean, + true ); } @@ -103,7 +124,8 @@ public DbSpecification withInitSqlScript(String script){ this.schemaNames, script, this.initSqlOnResourcePath, - this.employSmartDbClean + this.employSmartDbClean, + this.executeInitSqlAfterStartup ); } @@ -119,7 +141,8 @@ public DbSpecification withInitSqlOnResourcePath(String path){ this.schemaNames, this.initSqlScript, path, - this.employSmartDbClean + this.employSmartDbClean, + this.executeInitSqlAfterStartup ); } } From 0c0656ba28d1208795e9307ab0c4aaca5d219795 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 22 Aug 2023 11:25:08 +0200 Subject: [PATCH 212/345] update TestSuiteWriter for handling initsqlscript --- .../controller/internal/EMController.java | 1 + .../controller/internal/SutController.java | 33 +++++++++++-------- .../core/output/service/TestSuiteWriter.kt | 3 ++ 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java index 63c10c028f..da7d89798a 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/EMController.java @@ -369,6 +369,7 @@ public Response runSut(SutRunDto dto, @Context HttpServletRequest httpServletReq return Response.status(500).entity(WrappedResponseDto.withError(msg)).build(); } noKillSwitch(() -> sutController.initSqlHandler()); + noKillSwitch(() -> sutController.registerOrExecuteInitSqlCommandsIfNeeded()); noKillSwitch(() -> sutController.initMongoHandler()); } else { //TODO as starting should be blocking, need to check diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index 92f04e0953..38992c89e1 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -283,8 +283,6 @@ public final void enableComputeSqlHeuristicsOrExtractExecution(boolean enableSql public final void initSqlHandler() { sqlHandler.setConnection(getConnectionIfExist()); sqlHandler.setSchema(getSqlDatabaseSchema()); - - registerInitSqlCommands(getConnectionIfExist(), getDbSpecifications()); } public final void initMongoHandler() { @@ -421,6 +419,25 @@ public final void computeMongoHeuristics(ExtraHeuristicsDto dto){ } } + + /** + * handle specified init sql script + */ + public final void registerOrExecuteInitSqlCommandsIfNeeded() { + Connection connection = getConnectionIfExist(); + List dbSpecifications = getDbSpecifications(); + + if (dbSpecifications != null && connection != null){ + for (DbSpecification dbSpecification: dbSpecifications){ + try { + registerInitSqlCommands(connection, dbSpecification); + } catch (SQLException e) { + throw new RuntimeException("Fail to register or execute the script for initializing data in SQL database, please check specified `initSqlScript` or initSqlOnResourcePath. Error Msg:", e); + } + } + } + } + /** * perform smart db clean by cleaning the data in accessed table */ @@ -511,17 +528,7 @@ private Optional findInCollectionIgnoreCase(String name, Collection x.getKey().equalsIgnoreCase(name)).findFirst(); } - private void registerInitSqlCommands(Connection connection, List dbSpecifications) { - if (dbSpecifications != null && connection != null){ - for (DbSpecification dbSpecification: dbSpecifications){ - try { - registerInitSqlCommands(connection, dbSpecification); - } catch (SQLException e) { - throw new RuntimeException("Fail to register or execute the script for initializing data in SQL database, please check specified `initSqlScript` or initSqlOnResourcePath. Error Msg:", e); - } - } - } - } + /** diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt index c1691f2ed5..1707ab7155 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt @@ -609,6 +609,9 @@ class TestSuiteWriter { config.outputFormat.isJavaOrKotlin() -> { addStatement("$controller.setupForGeneratedTest()", lines) addStatement("$baseUrlOfSut = $controller.startSut()", lines) + //registerOrExecuteInitSqlCommands + addStatement("$baseUrlOfSut = $controller.registerOrExecuteInitSqlCommandsIfNeeded()", lines) + if(config.problemType == EMConfig.ProblemType.WEBFRONTEND){ val infoDto = remoteController.getSutInfo()!! //TODO refactor. save it in a service addStatement("$baseUrlOfSut = validateAndGetUrlOfStartingPageForDocker($baseUrlOfSut,\"${infoDto.webProblem.urlPathOfStartingPage}\", true)", lines) From 6e4fb1c1e9079ec9c2d636835afbfc176c7391a8 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 22 Aug 2023 12:22:43 +0200 Subject: [PATCH 213/345] fix failing tests --- .../db/InitSqlScriptWithSmartDbCleanTest.java | 11 ++++++----- .../internal/db/h2/DatabaseFakeH2SutController.java | 5 ++++- .../db/mysql/DatabaseFakeMySQLSutController.java | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/InitSqlScriptWithSmartDbCleanTest.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/InitSqlScriptWithSmartDbCleanTest.java index bdcf03b4ce..7ee557465b 100644 --- a/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/InitSqlScriptWithSmartDbCleanTest.java +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/InitSqlScriptWithSmartDbCleanTest.java @@ -37,11 +37,12 @@ default void testAccessedFkClean() throws Exception { .then() .statusCode(200); - // db is empty - QueryResult res = SqlScriptRunner.execCommand(getConnection(), "SELECT * FROM Bar;", true); - assertEquals(0, res.seeRows().size()); - res = SqlScriptRunner.execCommand(getConnection(), "SELECT * FROM Foo;", true); - assertEquals(0, res.seeRows().size()); + QueryResult res; +// // db is empty +// res = SqlScriptRunner.execCommand(getConnection(), "SELECT * FROM Bar;", true); +// assertEquals(0, res.seeRows().size()); +// res = SqlScriptRunner.execCommand(getConnection(), "SELECT * FROM Foo;", true); +// assertEquals(0, res.seeRows().size()); startNewTest(url); diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/h2/DatabaseFakeH2SutController.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/h2/DatabaseFakeH2SutController.java index 2da1041524..6e69fb31fd 100644 --- a/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/h2/DatabaseFakeH2SutController.java +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/h2/DatabaseFakeH2SutController.java @@ -29,7 +29,10 @@ public DatabaseFakeH2SutController(Connection connection, String initScript) { @Override public List getDbSpecifications() { if(initScript != null) - return Arrays.asList(new DbSpecification(DatabaseType.H2, sqlConnection).withInitSqlScript(initScript)); + return Arrays.asList(new DbSpecification(DatabaseType.H2, sqlConnection) + .withInitSqlScript(initScript) + .executeInitSQLScriptAfterStartup() + ); else return Arrays.asList(new DbSpecification(DatabaseType.H2, sqlConnection)); } diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/mysql/DatabaseFakeMySQLSutController.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/mysql/DatabaseFakeMySQLSutController.java index 3a602325e1..cae73a971f 100644 --- a/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/mysql/DatabaseFakeMySQLSutController.java +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/mysql/DatabaseFakeMySQLSutController.java @@ -29,7 +29,7 @@ public DatabaseFakeMySQLSutController(Connection connection, String initScript) @Override public List getDbSpecifications() { if(initScript != null) - return Arrays.asList(new DbSpecification(DatabaseType.MYSQL, sqlConnection).withInitSqlScript(initScript).withSchemas("test")); + return Arrays.asList(new DbSpecification(DatabaseType.MYSQL, sqlConnection).withInitSqlScript(initScript).withSchemas("test").executeInitSQLScriptAfterStartup()); else return Arrays.asList(new DbSpecification(DatabaseType.MYSQL, sqlConnection).withSchemas("test")); } From a6653a5167590e5742f0e7486f7f7c2210be35fa Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 22 Aug 2023 14:17:26 +0200 Subject: [PATCH 214/345] fix test generation --- .../client/java/controller/SutHandler.java | 7 ++++++ .../internal/db/DbSpecification.java | 24 ++++++++++++++++--- .../core/output/service/TestSuiteWriter.kt | 2 +- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/SutHandler.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/SutHandler.java index 8a51180c37..464b74df53 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/SutHandler.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/SutHandler.java @@ -179,6 +179,13 @@ default void extractRPCSchema(){} default Object getMongoConnection() {return null;} + /** + *

+ * register or execute specified SQL script for initalizing data in database + *

+ */ + default void registerOrExecuteInitSqlCommandsIfNeeded(){} + /** *

* reset database if the smart db cleaning is employed diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/DbSpecification.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/DbSpecification.java index e38ec667d3..f743b1a702 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/DbSpecification.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/DbSpecification.java @@ -3,7 +3,6 @@ import org.evomaster.client.java.controller.api.dto.database.schema.DatabaseType; import java.sql.Connection; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -47,7 +46,7 @@ public class DbSpecification { /** * specify whether to execute init sql script after starting SUT - * Default is false + * Default is True */ public final boolean executeInitSqlAfterStartup; @@ -63,7 +62,7 @@ private DbSpecification(DatabaseType dbType, Connection connection, List } public DbSpecification(DatabaseType dbType, Connection connection) { - this(dbType, connection, null, null, null, true, false); + this(dbType, connection, null, null, null, true, true); } public DbSpecification withSchemas(String... schemas){ @@ -101,6 +100,9 @@ public DbSpecification withDisabledSmartClean(){ } public DbSpecification executeInitSQLScriptAfterStartup(){ + if (this.executeInitSqlAfterStartup) + return this; + return new DbSpecification( this.dbType, this.connection, @@ -112,6 +114,22 @@ public DbSpecification executeInitSQLScriptAfterStartup(){ ); } + + public DbSpecification disableExecutionOfInitSQLScriptAfterStartup(){ + if (!this.executeInitSqlAfterStartup) + return this; + + return new DbSpecification( + this.dbType, + this.connection, + this.schemaNames, + this.initSqlScript, + this.initSqlOnResourcePath, + this.employSmartDbClean, + false + ); + } + public DbSpecification withInitSqlScript(String script){ if(script==null || script.isEmpty() || script.trim().isEmpty()){ diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt index 1707ab7155..0bc874b536 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/TestSuiteWriter.kt @@ -610,7 +610,7 @@ class TestSuiteWriter { addStatement("$controller.setupForGeneratedTest()", lines) addStatement("$baseUrlOfSut = $controller.startSut()", lines) //registerOrExecuteInitSqlCommands - addStatement("$baseUrlOfSut = $controller.registerOrExecuteInitSqlCommandsIfNeeded()", lines) + addStatement("$controller.registerOrExecuteInitSqlCommandsIfNeeded()", lines) if(config.problemType == EMConfig.ProblemType.WEBFRONTEND){ val infoDto = remoteController.getSutInfo()!! //TODO refactor. save it in a service From 8905c0e627303629de1c7b9acf1c884b9b702061 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 22 Aug 2023 14:29:15 +0200 Subject: [PATCH 215/345] set default true and add doc --- .../client/java/controller/internal/db/DbSpecification.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/DbSpecification.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/DbSpecification.java index f743b1a702..b8e6b0fe69 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/DbSpecification.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/db/DbSpecification.java @@ -46,7 +46,11 @@ public class DbSpecification { /** * specify whether to execute init sql script after starting SUT - * Default is True + * Default is True. + * In some cases, the script might be handled with the application itself. + * For instance, with H2 database using JPA, there is no need to execute + * the `data.sql` script under resource folder, + * thus, set executeInitSqlAfterStartup False. */ public final boolean executeInitSqlAfterStartup; From a227cc40c9cd059c0effad1d25792ec8f474906a Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 22 Aug 2023 17:49:11 +0200 Subject: [PATCH 216/345] add null check --- .../client/java/controller/internal/SutController.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index 38992c89e1..6ebf80dfaf 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -429,10 +429,12 @@ public final void registerOrExecuteInitSqlCommandsIfNeeded() { if (dbSpecifications != null && connection != null){ for (DbSpecification dbSpecification: dbSpecifications){ - try { - registerInitSqlCommands(connection, dbSpecification); - } catch (SQLException e) { - throw new RuntimeException("Fail to register or execute the script for initializing data in SQL database, please check specified `initSqlScript` or initSqlOnResourcePath. Error Msg:", e); + if (dbSpecification != null) { + try { + registerInitSqlCommands(connection, dbSpecification); + } catch (SQLException e) { + throw new RuntimeException("Fail to register or execute the script for initializing data in SQL database, please check specified `initSqlScript` or initSqlOnResourcePath. Error Msg:", e); + } } } } From 789bc91312ba2a700ce22b55bcfffaa0f387d35a Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 22 Aug 2023 17:52:38 +0200 Subject: [PATCH 217/345] update maven xmx --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 36a0f7f7a6..6e5860472d 100644 --- a/pom.xml +++ b/pom.xml @@ -1004,7 +1004,7 @@ Fucking JDK 17!!! And fuck you JEP 403!!! https://bugs.openjdk.java.net/browse/JDK-8266851 --> - @{argLine} -ea -Xms1024m -Xmx4096m -Xss4m -Djdk.attach.allowAttachSelf=true ${addOpens} + @{argLine} -ea -Xms1024m -Xmx8192m -Xss4m -Djdk.attach.allowAttachSelf=true ${addOpens} @@ -1021,7 +1021,7 @@ 1 true 2 - @{argLine} -ea -Xms1024m -Xmx4096m -Djdk.attach.allowAttachSelf=true ${addOpens} + @{argLine} -ea -Xms1024m -Xmx8192m -Djdk.attach.allowAttachSelf=true ${addOpens} false From f686761c69333d61a4c681a24cbbe97968eac292 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 22 Aug 2023 19:27:12 +0200 Subject: [PATCH 218/345] change back to 4g --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6e5860472d..36a0f7f7a6 100644 --- a/pom.xml +++ b/pom.xml @@ -1004,7 +1004,7 @@ Fucking JDK 17!!! And fuck you JEP 403!!! https://bugs.openjdk.java.net/browse/JDK-8266851 --> - @{argLine} -ea -Xms1024m -Xmx8192m -Xss4m -Djdk.attach.allowAttachSelf=true ${addOpens} + @{argLine} -ea -Xms1024m -Xmx4096m -Xss4m -Djdk.attach.allowAttachSelf=true ${addOpens} @@ -1021,7 +1021,7 @@ 1 true 2 - @{argLine} -ea -Xms1024m -Xmx8192m -Djdk.attach.allowAttachSelf=true ${addOpens} + @{argLine} -ea -Xms1024m -Xmx4096m -Djdk.attach.allowAttachSelf=true ${addOpens} false From 4d20271356d7b9c114c2934665d2098ae71f92e0 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 22 Aug 2023 21:03:13 +0200 Subject: [PATCH 219/345] gc overhead --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 36a0f7f7a6..a9f11d7ac1 100644 --- a/pom.xml +++ b/pom.xml @@ -1004,7 +1004,7 @@ Fucking JDK 17!!! And fuck you JEP 403!!! https://bugs.openjdk.java.net/browse/JDK-8266851 --> - @{argLine} -ea -Xms1024m -Xmx4096m -Xss4m -Djdk.attach.allowAttachSelf=true ${addOpens} + @{argLine} -ea -Xms1024m -Xmx4096m -Xss4m -XX:-UseGCOverheadLimit -Djdk.attach.allowAttachSelf=true ${addOpens} From 3b34cf382eedfe21e7f30a6886bc08304ce52188 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 22 Aug 2023 21:07:48 +0200 Subject: [PATCH 220/345] test 6g --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a9f11d7ac1..1dc3b68e23 100644 --- a/pom.xml +++ b/pom.xml @@ -1004,7 +1004,7 @@ Fucking JDK 17!!! And fuck you JEP 403!!! https://bugs.openjdk.java.net/browse/JDK-8266851 --> - @{argLine} -ea -Xms1024m -Xmx4096m -Xss4m -XX:-UseGCOverheadLimit -Djdk.attach.allowAttachSelf=true ${addOpens} + @{argLine} -ea -Xms1024m -Xmx6144m -Xss4m -Djdk.attach.allowAttachSelf=true ${addOpens} From 148c420af8d56c2e9582005e656d814cc1dd972c Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 22 Aug 2023 22:26:47 +0200 Subject: [PATCH 221/345] clean map --- .../controller/internal/SutController.java | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index 6ebf80dfaf..12c4ed56fa 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -428,6 +428,8 @@ public final void registerOrExecuteInitSqlCommandsIfNeeded() { List dbSpecifications = getDbSpecifications(); if (dbSpecifications != null && connection != null){ + tableInitSqlMap.clear(); + for (DbSpecification dbSpecification: dbSpecifications){ if (dbSpecification != null) { try { @@ -539,25 +541,24 @@ private Optional findInCollectionIgnoreCase(String name, Collection all = new ArrayList<>(); - if (dbSpecification.initSqlOnResourcePath != null){ - all.addAll(SqlScriptRunnerCached.extractSqlScriptFromResourceFile(dbSpecification.initSqlOnResourcePath)); - } - if (dbSpecification.initSqlScript != null){ - all.addAll(SqlScriptRunner.extractSql(dbSpecification.initSqlScript)); - } - if (!all.isEmpty()){ - // collect insert sql commands map, key is table name, and value is a list sql insert commands - tableInitSqlMap.putAll(SqlScriptRunner.extractSqlTableMap(all)); - // execute all init sql commands - if (dbSpecification.executeInitSqlAfterStartup) - SqlScriptRunner.runCommands(connection, all); - return true; - } + + List all = new ArrayList<>(); + if (dbSpecification.initSqlOnResourcePath != null){ + all.addAll(SqlScriptRunnerCached.extractSqlScriptFromResourceFile(dbSpecification.initSqlOnResourcePath)); + } + if (dbSpecification.initSqlScript != null){ + all.addAll(SqlScriptRunner.extractSql(dbSpecification.initSqlScript)); + } + if (!all.isEmpty()){ + // collect insert sql commands map, key is table name, and value is a list sql insert commands + tableInitSqlMap.putAll(SqlScriptRunner.extractSqlTableMap(all)); + // execute all init sql commands + if (dbSpecification.executeInitSqlAfterStartup) + SqlScriptRunner.runCommands(connection, all); + return true; } return false; } From 84a58ca4ad706588f2d9f709d8c72f1bfd48b8bf Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Tue, 22 Aug 2023 22:27:52 +0200 Subject: [PATCH 222/345] 4g --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1dc3b68e23..36a0f7f7a6 100644 --- a/pom.xml +++ b/pom.xml @@ -1004,7 +1004,7 @@ Fucking JDK 17!!! And fuck you JEP 403!!! https://bugs.openjdk.java.net/browse/JDK-8266851 --> - @{argLine} -ea -Xms1024m -Xmx6144m -Xss4m -Djdk.attach.allowAttachSelf=true ${addOpens} + @{argLine} -ea -Xms1024m -Xmx4096m -Xss4m -Djdk.attach.allowAttachSelf=true ${addOpens} From 6329d7ebd702f4767344737fccbad98aeffa80ab Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Wed, 23 Aug 2023 10:28:44 +0200 Subject: [PATCH 223/345] ensure the data is clean if the init data is managed by evomaster --- .../controller/internal/SutController.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index 12c4ed56fa..092aad0bdf 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -421,7 +421,7 @@ public final void computeMongoHeuristics(ExtraHeuristicsDto dto){ /** - * handle specified init sql script + * handle specified init sql script after SUT is started. */ public final void registerOrExecuteInitSqlCommandsIfNeeded() { Connection connection = getConnectionIfExist(); @@ -556,13 +556,28 @@ private boolean registerInitSqlCommands(Connection connection, DbSpecification d // collect insert sql commands map, key is table name, and value is a list sql insert commands tableInitSqlMap.putAll(SqlScriptRunner.extractSqlTableMap(all)); // execute all init sql commands - if (dbSpecification.executeInitSqlAfterStartup) + if (dbSpecification.executeInitSqlAfterStartup){ + /* + ensure that data in connection is cleaned if the data initialization is managed by evomaster + note that such an action is performed only after `startSUT` + likely once if the SUT does not crash during search + */ + cleanDataInDbConnection(connection, dbSpecification); + // insert data SqlScriptRunner.runCommands(connection, all); + } return true; } return false; } + private void cleanDataInDbConnection(Connection connection, DbSpecification dbSpecification){ + if (dbSpecification.schemaNames != null && !dbSpecification.schemaNames.isEmpty()){ + dbSpecification.schemaNames.forEach(sch-> DbCleaner.clearDatabase(connection, sch, null, dbSpecification.dbType)); + }else + DbCleaner.clearDatabase(connection, null, dbSpecification.dbType); + } + /** * Extra information about the SQL Database Schema, if any is present. * Note: this is extracted by querying the database itself. From 93df37361986cc8046c6c87b2b76e178a59dee04 Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Wed, 23 Aug 2023 10:30:32 +0200 Subject: [PATCH 224/345] clean --- .../internal/db/InitSqlScriptWithSmartDbCleanTest.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/InitSqlScriptWithSmartDbCleanTest.java b/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/InitSqlScriptWithSmartDbCleanTest.java index 7ee557465b..aabdbc6886 100644 --- a/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/InitSqlScriptWithSmartDbCleanTest.java +++ b/client-java/controller/src/test/java/org/evomaster/client/java/controller/internal/db/InitSqlScriptWithSmartDbCleanTest.java @@ -38,11 +38,6 @@ default void testAccessedFkClean() throws Exception { .statusCode(200); QueryResult res; -// // db is empty -// res = SqlScriptRunner.execCommand(getConnection(), "SELECT * FROM Bar;", true); -// assertEquals(0, res.seeRows().size()); -// res = SqlScriptRunner.execCommand(getConnection(), "SELECT * FROM Foo;", true); -// assertEquals(0, res.seeRows().size()); startNewTest(url); From bb0843e09c1d921144a6ae02654f1c0a2fb9e0cd Mon Sep 17 00:00:00 2001 From: Man Zhang Date: Wed, 23 Aug 2023 13:15:51 +0200 Subject: [PATCH 225/345] minor --- .../controller/internal/SutController.java | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java index 092aad0bdf..7aebcd6dba 100644 --- a/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java +++ b/client-java/controller/src/main/java/org/evomaster/client/java/controller/internal/SutController.java @@ -425,20 +425,17 @@ public final void computeMongoHeuristics(ExtraHeuristicsDto dto){ */ public final void registerOrExecuteInitSqlCommandsIfNeeded() { Connection connection = getConnectionIfExist(); - List dbSpecifications = getDbSpecifications(); + if (connection == null) return; + DbSpecification dbSpecification = getDbSpecifications().get(0); + if (dbSpecification == null) return; + if (!dbSpecification.employSmartDbClean) return; - if (dbSpecifications != null && connection != null){ - tableInitSqlMap.clear(); + tableInitSqlMap.clear(); - for (DbSpecification dbSpecification: dbSpecifications){ - if (dbSpecification != null) { - try { - registerInitSqlCommands(connection, dbSpecification); - } catch (SQLException e) { - throw new RuntimeException("Fail to register or execute the script for initializing data in SQL database, please check specified `initSqlScript` or initSqlOnResourcePath. Error Msg:", e); - } - } - } + try { + registerInitSqlCommands(connection, dbSpecification); + } catch (SQLException e) { + throw new RuntimeException("Fail to register or execute the script for initializing data in SQL database, please check specified `initSqlScript` or initSqlOnResourcePath. Error Msg:", e); } } From fd5817992034b1c0629fb0eefa7ed9dc4ee4d324 Mon Sep 17 00:00:00 2001 From: arcuri82 Date: Wed, 23 Aug 2023 13:21:25 +0200 Subject: [PATCH 226/345] survey --- docs/publications.md | 4 ++-- docs/publications/2023_tosem_survey.pdf | Bin 0 -> 1131114 bytes 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 docs/publications/2023_tosem_survey.pdf diff --git a/docs/publications.md b/docs/publications.md index 1b894e0bc0..ddca6bdc4e 100644 --- a/docs/publications.md +++ b/docs/publications.md @@ -29,8 +29,8 @@ Also, some of these papers provides full replication packages, which are linked * A. Golmohammadi, M. Zhang, A. Arcuri. *Testing RESTful APIs: A Survey*. - ACM Transactions on Software Engineering and Methodology (TOSEM). (to appear) - [[arxiv](https://arxiv.org/abs/2212.14604)] + ACM Transactions on Software Engineering and Methodology (TOSEM). + [[PDF](publications/2023_tosem_survey.pdf)] diff --git a/docs/publications/2023_tosem_survey.pdf b/docs/publications/2023_tosem_survey.pdf new file mode 100644 index 0000000000000000000000000000000000000000..cb52ab2d84e9290745a7f6a06210fbe464cd5422 GIT binary patch literal 1131114 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�Vezta>Kf0QKcI!ey0vHq$ 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

|rhX=w#&tYHJe2E@~p^U!!be2uqT(Um3I@vX) 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

Ps!$ONe!gd#Js0^u^)b4#i!1f|uYGUq=GaB0? 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

{=gJYgG%V;# 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�d0U9$zJSFzB~-`zTuEs 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

MrR;52s-Y@d4Kvz6W<|xY>*!e+g@Hgr5<0 zcNOPLz%K8WoC|H*h5DCFA!$yd#w)0+01`orCC?gw9W)HDiuPM`#*$jLe~ z-+faI=!Hc-A2FeWYQ;0g(BgvFK|@40<}`Eu;aufpy1~DHIZJ$Tdm}SidnW|vJt@LRl zqLwLuE5tM=DH6P0y{11ADe_W)8}ww}d4vYve?&}9$IYT9xZiu)1QmH9bC^PN7wchh zIQQs}iNA7s$&S+467Lc%#L!qwJ66p4I~2e&)~!xVRv%K5Gquq6PFdb2K~{Dp@i>UY zyVxcgS)9=DU6|_GgDu;RByw^Cr#0ObXI*akkwnTbs*y3d@Pi>WU!%i9uyY;k%ho~Z zMb6!V!x&30b-{k&3CXR}AZ!n>ezRY4oE2?f{sFBhc*J^*WupJmj*RuR&ON-T3%!M3 z3+U1S+V0MDb6}S-1rp_!88aepPkh#PN!$zT3zi1|kwNV)WMDT3OA)b7Y$(iCY z9YmomU_tG~Z1UhZ6@lIiNE!IR`@rCBlFS38o@HLs4D9H-hDafu9kn!uOC{}YnzE4_ z<gZu|G;&Sk|OLP6k3P$q7OR!U$TvdiwdkZQ&5{`_&Sa2@Ch<&4D?YctSOq! zrod)`+ z(lNQ=1KcTV@sNICt5|DxHdOM@=i}rM8K}qDs-`T!2quKXOY7%__FOWAW+n5@MNcY~ znCAGwBZ4AU0HLx+hs_Fy20dF+VldEc#Yw*9zs)*x@skHtw4$RM4j|Kfx>k=I5!W0(HETZ&pVOe(x2(iVjBYp=@~#&!dJ!n~S_2Ugd&DrMH}cR# zczR-^YCA&zqK=*Ru9a&mZL2{xPyuH;iXNbqNrQgtG(aPMCVK9Sb@*W6EB z3LTk@Box@Lo2VK%!Jzhfi+g>nZF^6sLi4_y^tOFLY1P^gJQp{@#YQ=rLw4f&?;6|1 z5T0%xwK>mHJ;+&y7Nw)7`mjh!O_W&d^s{3^9`S9dEE--g-u1GR{y8&vm!wWtDuY%OPEW>ai z0oH9ZDYxbyrj(R;$v`2re_97vmwCbxXwnAIZz`mOgk-a$kw8Xh+(k=l^^6N zw#~3Uje>g^TR z3}zIvemu%)x4eP+uWO8t<3CTDs?dicCOQczIE>u4czkvc-aH{;Wb&^8dA9@a)=I_3 z?HpZY&?~){UM&vsZP2A?n@Y@!paF1Chud_3oQmp^d+LCY3oPzS4zdsxlU`f&NjNgi zE!qH>j|HPaMf}WO(}PD1Cf63Yz#8jaI)M;l&`NUYFNDus# zYa}0KlSP1{cG7p*{N!J@Y|!5+?`}zuEYcG(dzeIi#j$lQ4SEw3WV;gp@SRr&dd5cs zr8q05pAQw*h`4q7*bd)f!57?-xA$0x{!D#Tuoe3%TkxEvVsKsPnPm{f^Ct$V$OuME zX~_HFdhqTMJ76itTW)*L-v)wua~X?X0l?)Vqs9HVJ)X(WN!qTSB3K2Njc7=6jinpA=~R z&JbwHjiuU}1QnX{?P5Q~!u z(=A3aj3?0!&(rX;nwtrzdg8bTxYQh3P8eik=htE`p(^ ze34HyII*uw^6R`aI-j_ruO zM3{!x7&L;rMONL>8uy&7GlbJ119s2=f7pBR$s&pK)H*+*7Mh52rY5}iiG*vzynaw) z?_!XgtvNZRh4EXIe}g=knAbE<=^yo__7aDKTAoMS6BNd3wy1GlTP=kg!-408swUtF zv&9|N9VTf;%>;*pHsSbSsrOnkEwQcRt%%D)Z)a=){+FGi&1&6yMLCLkUa1M^gGfk= zu5Y9#AbDIs?K*f}u8SMp%Kc}ddY&%!_S+uFo>(M>jqN50@XyGmQC4`StIDKNJF)z3 z;!l^Y<8LKZc0>!`LDCi&;4NpilP74wp*ts)Rhp>rmWhh}Bf-~ys_2hTu^JVObNAi; z>5BR{6@XtRm^GJl{agcHb$M_B%_U#Bn*!Ydg5$W4eD^A|G39$xkZpGW!fB(&tZF{R3%$9Wl3?I~cpG|O3S$?}cti8_J&T5}WD|H|+ zrQ2ztoPV9+^XQh0{ks1rIQIR*S>k8u*}c>$92F-hfGVD5tN*r40W zjn^%6Y5kN%BNaKw2%h``xh*Z!p_*ZifnRbi$c)^*sKvbQ7&u@IK>Nk@J}mz%PE|;N z96SKk*D0G+a^>Rs#Gusa9^lP3zTR&kZxPq(D;1TGN|UfkizmPuc2DA8n4-Xl*;k9L z0Mmg2$t)vzDHQV(B8P{&DA?JhV~_T;xnT|99ky?dxz9U`sR2O|9-t~N7C-S~}vaxcxAmLY_mX=l{^VM=nIp$&=B2^BZ{^qU2fy< z64a3n5O4wxgz?9ckSB0$YnC4h;0Z-Y_>2bUJ`sA8EaYH-yBo;Zw=~vr zod1sPImepM))y0|igyxR4A9|~yHeK8*KuFQlWUHmda$;rzGaL?w0G>1;(o#Dt`97x z|3p6+s{lGgS`T~`HvK#qA<9hH06XK()`K$J%n5(f@uEd+;Uk07v1ypT9Z{UR3!Ck3 z*~*$;2iKyC!QqJ&56uffI>J87uBDCnf^3Cu_|}2>2}IP^$-ZhQVL~!=mZugm7~C1J zIpU1#*92us*SVG$$9=qO6^&ufXhJ;TGDa2WGmG_cw>gb0oKIl2_s_b(J3mw>mU#Sk z4OUP_>u8gPEVn&~ppyq%H25bbMLm2Ji~ki(eqaROJ@gGVTA#w9a$yPXmnn$3=Sdc+ zSQMIQ`ukhgkz=BBB}B)W-5>qf1ehV2K5zNuWo~#S{43t7AK^m(q`m#4XPr8i%Fwv2 z;xTaSLTi} zqf@&(eviSu`-ig7<)rFw{DG|}#>yBr= z0rA{ZTapaX<)l;+SbfyceQ6sPs~&0DU!qUZdWt>khz=@|QoZ?W{W{^z9^j<>Y||SY%T;`XG|&N%HC6h}ElOT< zOiK3?ayU^v-YgSp#tDz+*On-sN;cCNX&B8|-`)2;d@sZi*ufBrbsl3D?z@iV3;BU!5Owu*$#hrTr}ZkldXxvubFzXy zJ<;litR!nnKIdav-U$a&>7VIrH_iuuq!fTDww(gQEK*7**@L_A9Pbbk&`?WVPBt2% zRou88+n&H_##@dpsm7#+ik#q=atO*xkGmnTGO0Q6RO$XKqZlrTtSON7r`XXv=^!yg~7Q~1lOXD zWtsdw#PLI{-q}xCW^h~fyz@iOJDRFKgJ{r1SkZP|l;OdotMm?FrUiUPvSPGtEm^@q(t86d0 z5AsN)GIGmNs{gE)rs=ci^p>cM2h(27Nc0|c`l0m7hnKz0g-^iz94$(55iz}Ml7fo6 zh+MSSt5}=s@SQFQN^&oO=QbX=j0m%V7FIs4^sWZnvOEl%#1%X(+`uZjqnt8fadZz6T%JXjmnE?9=jRHZO*uXx&|Z*#qSVCQ(REfIREB_wo8*;@u1UxAbNQ10Hj_*{=HtY#pn&o+o z*!JS~J|*I}Mu?Kl=_geAI1%|BkWw{f6>TW0jOW1V`6%NI>ozR($1M@Zf)$U*N z{2e~NLRI@li(u0w)xs|n^)JTrK#$is_2c*>{?(;E!ci{XN3|{d&q4ZSFQl}!YuI7w zy-4ComRlHf#dNr2NL-cePo{^^ArfWf-VW>wQFiSB+;e@i57jdpwaMHEMzN!RX7q1C zV`wnT`YCQ)mF|Y4xml?4Xm6BX0ZiBDS^UmBctU^?oq-&MGa95^W7q$(GPj<<%py0u zFdRc(T@`IFt2ifzY@7aDyR?OddO>te-$UM=>w)GdiBCbgVp1X;@p;=4l?P5t#iMva z>J*ySoqR1ccZc(z*HS!M+tR0(j-LI;0i|<8enJou`gIvJwN*e!Rev{CNj(8a&c4T& zF23Nf5`)@J?dKI0$tnhB8xpl>K6CPU3cM3>I%c{;}13h`V69jAzaXasQQsEqTCf3*}v?1qQ0ZE zcHTyMJ~}Vlk#s3h9CqP?PYgjBC|n|l;-O%zM+;5iW#a3|bp>5*N=#%ng;sfhBjdBv zvaAi&E1DhGkbp|ZGaYK`oXWCwO<(hQPs#_NY{|I2XyHC#Fk|Y2y6&K6omEF1Fgq@o zg$heX#sf_^-Fw$g(@+d}M4o=i$zO;X==J>7(PyWSX=!o&{=Oq|L32=6zBWZxjZ%g9 zXEq~MpGg89Yul4YHMIO|feQrWfLP6b0=+?XxM#8}JIKP@jQB*gUVJCQw`PSF1ysP6 zQ**xV9PsRHe0KcL*)TYN3Or?$2YDPD}gFtL~uM4>NO2EAVX0wqAQj@ zY!DbkPV#@}zJjWS*2QOdU-rHfXtsJo-6u3?u*kTj{eRAFf@&>HlcHbmVT7{Yy=TgL zbd{HSTyukM#U{B=65UZJ3hk%G#k41NrfA=xFq1|6L`Xype->xal%zg#!I+%-SzNr- zip^LaMD*Q&)VDppW_iHnhsSdj*7uJh!5M%-p$r2`i7q97EtT+wBi<%su1LTD-7MIj zze9I%!wUj(za?;w#a8N6$>Iv2D-7-o#ysrt0LFEI$^3YEY^G2}4j|J$o8# zjrz~}U|>r=yqRlCJ++-A@{+mH#ZJ!7$@rE-wx*y&M^TZ^ZZ;`vGmhqFv^~7rJaxk6 zf}DwEE|)PAfGvHysoG9Cm3Rb5IaU=E>)hD)iDhGInt!Q2Fcy zli|$$vAS7hf3cnF$w5VdH{6~?{J)R%GJ&FT++)05%wJR?pe`vE^79o1xKWbd+AD^6 z|A_9VL9dcXHzC01n__oB&a4?|1y^X8OS|4O5Y#!GE;oP;5s%?r4CF-o)aj@N!jP;q zPmWTuFie+Kk7t967!f_UQOk~$S@&Xuaetv(y^pb|p9BFWsgqzxn8?ZWfR=q1;&ioi z@c)P1_^Q)-O3z}F0I8J<^6L+ABxL}uff3kqb=*}OyUthI1@9Dgqz(7JkB zo-{mtNL8-7{jXpI_NB0-vV(L=ysC@eov`?7x-K32Zc<0!o6lacU4Jko1E(XgMAFm{ zC(E@j&bEtG*KY)|j2uUciAVD24$d{Wqwp4NO-ds%6wV))i9`zoeC~4fhuo*TOJ4R( zIM5=*DpjsD8ME!#Cdjf8PQ7gQAy4@K{-CS!=3L0;s6A>sQ$Hghcufl!i~dCif8&G@ z1@&RltU+29^3_w)+Jx8H#o*s5`XmkjuW{FoV+u#aMkbZ~{8CWQO2}+CS(#~<1KP!p zT0Z4-*p&iF+wG<$x{v2*q6Vm<&tmW~6UDBiq#%?QE)p~P$e<}Dx=I;qhIAC+IQsjx z`nV6Nwg*;EmSeHoxo~YHKu|4?OfH4wwyn^|@w%L({8YX0`)+x?({nZo@?<}_vvMYFvL=<2?5x7VV6nhKu}yA2a>l_hzBfgkGtr&_ z)yvz-Lg2L^FGKm*+>1hqO~o7Ppe>3yE#gnQz9!#e3}4(z&9!jrHO)<9RGNaPJY-_l zSqtYrmqp5-r^n1p;3ng)M79h0^2)Ey0ZgWKi2&1=W(7oITWr`4%%5xf@Ic}xSt_vt z86HEMiXx?cn=HEs8_7{kVaPWln`p9wd;L_brC)5Z_Ag^-Gw+1K6(;*3oF}$M_PvOq zpiscQI)3)uRvzO(04jhwwP5sO+kerc4NcZqWZb{Fk9_HXOu)SMSdtkDu%nS)s2S|+ zy>K=QRXP<0Na?Vfap;sBFyDe7gYxbIKF&9U@zpE&X_DyjoFq@JqO`ewo^!ko;;8Aw zr(PJwoWqR)IK+eY=&kUG znGMd0NZ%w9&{1ZI>?t1L*xHc-ngn{$;p!TFYwX@FN?8{X-Y2IC+ILDnmcF4a)|0Bv zFsve{c0|}Gg2wA_ZeJK?#np*m`oo!qjJGC;S9fFF24l3 zD22jZ%p#i!o4BL}l%5-H%oxbjo|;@2ix(S*Xe1=fogEz?Do77lj+`R@?jPg%{@l5e zOYt)iF;bG*uUy@XGs!mOz`zDToMaQB)oU*S*8^Z&=refrGspZB^+z2&7K1tCuWa^M z&cUuM!zdzf@-hk8yXxZsxuWa7^$ntN3yB>AK^e|tIy=DX+r>r9b@4xr$1o^266j4B zIfyVT>@c~dZU|dNCCk#$s^D*wGMB;b({pTZn}FlyQXbN z#?bOyJ}{rkhmR674p?O&2a9R-5;s6;&qvZpIM8pGRcX&@xjM}_RnjWg9%+>>hhMu- zv^e>PI-xM@AeBX|EeJjhm#I7=Tua#8t`h}2z=-w#d6q!Xr3T-939Snzo)sKksWR1Z zzZ!3Nrg>?`;!T=cMJiBq>BBiQqHyVtucD`+OGm;K_U!Qh^HB^{to6*VsNPaJWGhzt z@6BT`oLhmxK*W2JjESCDmesSvq|K4_fkZ(>%-Jq3$BJJs3{@OH|24Ar9L313%DwV{ zY)~^i+HH$;^#gyQ$Zx2veXpd|nzeed*M)9NkuG~1RSW{fbQF~@{e{;;H#|`#N(OOk7x`vwOABIoe4vl;a zZ43E{q@x9;k!|_0F}Cq`C*B1B?*D3xm}Uu?a9%AA$-yQK2aLBouCzAaypd}?fML+q?(8|?u3!4VpDzATH|-Y;I<#77G7vDYf#gF0ko3!v zUzA!y+#HdI`CNi4{_tSn}ga2&{`EQn}D!`dH{vks;XsQ%>o)Qf=N#={Ti1*QdKKml;! z7!}1~32P9SYV-)9n(}Y$Hadk>lr+c`bBDbjWf3`Y^Mn+MN&{qLtUIdA$Hh#nPSInr zGx4ar(2PfYMV&mm>=}FiDW=Y!b^!%lIoP0eZxLS1SIe6g*vK2gnF%yS*87^*ikK4L&^ZR6BXqw zsvL?}oybL;CV(F7(k<=_y1lSh!Beh;|FOfMs|O#rqwARi8hd)%89`#q3+Bs1jGLB0e;T()&o8BB1&n^8?LADEa+e1;J53 z^pZlv0}0RyU&2X`dv3VASfWhEkwlx)OlxBuLrw)g+RV?y z613BC>-MksR<2MaUngPHYqUmr>dS*W$AsfO_1R66m)iXH2+lD7uIM>||NZqYe*lkF zp8w(mnwEvEkb}O{fN75LL7kOG$RIiRD44m$86Jp|dAuOhDIugFa{Y6Wws{m*9 z#;Vj)U?H=D30dmh=;_c!Uq~m@uxuXd&vyemq;{gpi;yohhXXDi7vZOd_8Lw7vYpc7 zk{u6%01$Z&=*63U2x#i#`KuqAd`Z}PVF5p$Ys}_s3fQ&54MSelfE{V-73s4`O+%|+ zvq>%$Db!1BlllgU&pT_NV_3yPB-=Zq3)a~}Kj5eg}F#F8YYCM z>#p8J0b4+mT|hRJz)`4sEmnpokf0^OA5SN z^zH=}>OSz=0`0{}gqnoj&&_#GNCz>0U9xFmfhsBc@;xF^hDCQid-NOJ-hM>KV1LV+ zitxT9-_Mp3OuxrC$9v}9`2b5dE}AvE0&&oUTshDy)0SX1arIW2U&*34A zJ7H!qY5`JaH%j3*YnpS{@vuvY0d;2;xEMnc@H0<`1U&YWp0*w0(%=`2`&ST&Lni zwU^e$+oGu9x116NRXN%TN0#QZ0(bAp*x~N764VIH=%*_pFt75$$yG^bdrek5#2?+0 z4|p*tBycMw$?tg7l*c zhq!gju%Kf%pNrzYcpd+;Zpm$Ob3wC)RrnbU0P7l}q>g-jU}-sk83+3Y(&g&Ynx0aC zE+As}Q@zy+pF+yJ_3Ic7au7AxOH~6+G&x)r<_Vpr(jFGmT_8nD8b7gWHkj&fyX4rU z6Zl&!UU24I;@UK7=&hr6cy4x>ZUdc!<3BlyIryyEj&q!&9s|G>MLTt{3KOq+80J0`WBj)F^zBKKMx@b3ISC02^uahHT9yXj%nR|at;(UjVwSTIHP7^YJNTcoCq^RD1pVs@Gk|Hw6b?G~c8F*+)`Mpkc zAYaD%#%_!^vDu@{Y&ci>AR$JT{q>NVaHHGviS8B~z*|(&}PBQ;H79tkg2n={sUEDq@Q&VU~ZU&-Z8P9Prjh-;yhQ4$)+Pp~qfRxxs> zujyWt{hsj3lDnfch7oZXLa)ReL$8+Vg{P^9yna|C-Xh_hQ}wZ7doicb-sH6IM~ZBr zH6@%HI>e6Di6I#C9>)6DMC|p>mBmY>_m5k)K}`oP5<3H<{0xVQU*QKD`=O35YNnhp zr1>zJAtA*~p42}mtYS|;??w8Ypg^Z&L(6}7)+a~xgBSp?b3|whs5HijRMvQ)W(gc_ zpcen_z?8s)gTAT-DXJw+)zz!8J;QjPmm)LRV^=WjVdAgXHPK=j05@u(hJ6Ea zImw*lP95mIXkNX@*3bnw-}SRT(@cS4n)acorsdL*FkKl-YM#zcVgr*>>#%$SgR-uq?(z$&!^yy+eumG`&4}J#$Ia?PkHK$WWjw#LqPNu0z40T%f2vFA zIWNhAy$niOGH)l2(J+AZ6Ag<%OFk?)Z`7{cmup%}wvvt9nVxg2DIh#p>c0n zJrhYQLzG_PMYrL&K)z0ov!cmzlILMfx=Y0b+XY!xRlph*K{HRwZ8R2FAPi-T-?eQV zc(GaI>qvA*8En8PZmJeRpcf4Id7nyFIKFDs`lLDoPCkH@p28>+3Id=fI=(i&ni=dL z{r^fwTVEvPh1bAT=t3dRvNSo>Z|5vE5^F;bSENb?qEpK-URN3Bp-$F7XD%mG%Nla6 z1-WRzYflU~H!-tvwcuj(_EJB^lvLhZtH{u*-T<9f@PckYr6wnCHC(7{8w&wy$NxD& z$Gk9rn)1mq@k<=|%>~x6sDnmw3>?X`Tp5&qz3Uk;>k+V5HC9q`@Y^lVZ}W;OnPdm* zq-nyGuH25>$MkJS=46)B+Ce*UFf_uh|GtuwnNmu93t?cvJX~yjeJy=(WgI-^VN6f> zjx_9X#6LejA9N5J_7Cj11!FJj#DuJ9i;1*Et)=qBx5T__X`cEybcaV29{YmUpYNQJ zgrAQ;*}CS#FbHQYxH_&9fQUTsN|ZSG!a-gga*sxqsdyaS4}}=zlv=?G4{11k&^{`+ zHsh`$vQx%JOOe5q`k43WTtfZiV-`1>Z829hjH`e)Du+>?qO9cT9Cxw_-`E3x?H$5G z4&K4}k8qa|tn#ck*6j0Kh>w$TArQ#bH?`Qy2_hX?c<&L%-sGwe%2?RHHBk-9Q(XJL zbn5bGV^ONY%}!E_$LXql8OeBzpKL&eerV$8#67CH|9zW5()dbaNSiI^OJhB}j#U56 zkS6HO&!J-!P*XOz^VUvSV{tI041dk_`BQ-#m0RaoZrJnm;838>*NbV*@mKe6@tKGi3UR!u0ES_{~SPG+A`O zjlgN+U3BU;xXvEyczFr<#RwGlZ#GgTup)Kr%;24Ehq8wfsA@>QHy=!1ilqx4Q3KP( zvV47@7WXv8DPz+U?6mDR-~4_R><}r}5H1WIR!F>2xr~T2oa2dfU&WXY5B85~55e?8 z73GVuLvuZ8Z=(}$E8E$cs{OWm-eLEA4b37a8i1_H;gY>pJ1)48Gpv8JtL$nv7~c0E zDxAd|SPkps+t!2oz)`eed<4fsgbsuwS24KWwVmeIu7Ko*>Sf8|sN1g%e)MbA2VOmz zdoG{%S1w2Nru0f>oA{q~No1x<;=E|74(r!?R0%-KYr{*)QC)%i^u{ad+`CB(4(s=n z`}{5nko4A*5bH&=k&&r_q%#q}dhH-@z>$FgMge6(B75T|`#jCK=wYyCVEJxtyqrBSvqdO}6Vaj_qG7LZ`(Eh;$%d^*%kVlNlwN4*K zRb&7Cd+#mNr%Jm#E=$g_pOvAmp_|KDJ#gcahE?~y(y-~aOAg%xn! zS&yu`PcIpcJcHTXdjDJR$pD)CczC+l(`;Y~s3Jli4;DDmV%8+-ezg|Gnf@~-skq() z5_SX=muYkPYLTG-9u%S9806OtDPjGdu)l5M0)&5j<0R5ULhqL?hfcXG!>Q&&W_C;G zL{eBMF(jd$AR*l7XKy<4*CMy(sA3f}Ajr7s3_6A=ZU%9uy9#`B6->AC*#lv{0}G}q z6(7SBqtPaS*AdLxF1i7vGJEk;)4P!U5_4EtOK{yZ3R$92lk}7O$|d&=@v$QBFZ75|+1Mm|&rk{KtSDggo3G^|E+;J8$?@+C``RQgqB^hk5QwSsN~&R7&Y= z9tyPD=dFBKq~7TOIYSpGU8Qw4Jb+!wC>M--n>(3YZKi)E0rUOq0rGY||E!5`##||q zz$5EX(4?zArl^Lv$&g_l2* zDZkZqo6*B~_`N{HpBi=V=v` zAshyuxCCgGRh&;DjRTQ%HhwrL!-A^%nRMT8A1_aPrVXe;F|?XnOa& zQdU#X!3JK1C#E%W!2I0g>5GNghIlQ^S+DhTFDOHmejpp!ix!b2q(S=2PJ zLs{h2IcgyD^pDDiy=km@1jIY-PKok{nxr?E?t4SYBeHU^pB-ZsI3)3T<*#IIUGLH{ z3YA}svBZfi+kT8|e{Xvw>_H25e*I>w8t@Uz;5_p}bl4hnHx-V2Z@KxvX4I5nk97;B zUf8Z8U0M*E_$%xlL4d2BI<}vB^^(6?=nleSahQx<4vQ%~n-)Kp)&|ue<+3)oh#jF0 z(?|)PKdtp8=|lB{t&#`M1o=sF(xLI=56po+cEM7c?_av1k*v0c!84^tng!|jG>L3d*XU0Ik2t4BVU}^)3K+Dai|F6iK>LW)lw}g2 z{I&W(o0JzW%GwU(@@rLe_X`VvR5Ed?c>i3onANb1U(J)2t4tOhQTGRT&vqFlA z`q^y3Y6gwH8(#xG`S9{Gjy6ggo|NEMqQ8lCv3G-#w#;3|px`x5hck}S6~Ijs6S*nL zWkm|G6mD|~f_(CbgpD>bQt0g%tbP!=6SJ<^D&KfC-0kYQ=-=dvAS(9V!MEUU z6!zaOBo&j+l}yy}uEek>!-`+jUs49w%_W zXT7fLhxaOy`1iFtWU6+OH>NhO34ghe%?{NUDndnPV~D#zgjx34nZx3%4}_EX#-14P zN5~g$3*O?_FmOrhhra`?v{m=4kY9iw_9%VWN%vvhlqDPb9yU4tdu zsLK~dqDyQKiKvDvjiFut(MzLy59{cuUN5(_q+;#)pc~{B@!r3o-ZAwUHUyL+-w3E@ zQBoq|siL_%`&(6kVxSic$Fx&kyM!=41m#Up>{FjHxj@Go&d#*Z1R`YE#(>5_po=gy zmAQa7s2_t9DV|X*_OJk;mXdHLH#v@Iv5G3COvkCbY`Bi%@oflU#Aud)OWJ~I z<7>EP$vvqCj>j+(F>o1&;j7O8O;UDQ)b3>X$^(wj`SFKkFLst8DWa{AF+s_#K&S=N z3)rRMQ;$~L7Au;WOtpE{IDhm&E9MID)yY}`oVj3tUbwtlL1_)}XVViVQLE+p(|*jh zlxII+5N+nMbAO=mQVlf6s7E`eCxYQc-7t)NjvL+d|^ zTXEcQp3K>aW6rF*j=yFkyz8bLVDyk^{b&kOTw(TE09go-H8Yftxvd-#=j!kwwSE96VpLcZ~uqx;o6m@mI%tI$}Cp-haDBYjNFaaYlU zAWrpZTJ9iJ&GXv%W^j(PLV zf>h`Y`rdDj@o#xaU)-26`0^(jX>4ev2U4GtpH$P%*v^cm4};x_+$O8^h)7qQXqsce z%r>Uk#Rf?FOpcyu%?~PTG`-ThDkJX=){}vC`_bR2?HArr zAjvpENz3*@vtY&xAX3HRZRJu)LO^mY(}G$pr47GwxAZWFrizh%V-C8eec*mj%=Z9i zm?HZZ#UOzNmvytMQ1rE^i3D!gM)@gzxHR_)1b6LD(ojkMhpwGj%l#4X|R1 z-RXp!QSYa%*y4ecuheazOWe>_nezRy^IGM~-HlE5D-2z%-dM)JLBZUkh^R-7^k+*F z9VFtbz_U}oNbPy7KI;!sVh@AYXI{H01fEZK57LEm7lZi&OrGa=FoS6S-5tY;K5jtG zkr(2nqGvW*?t(7)*wJ7hc-d=;D{AJB13x=Ao`iV(Q+}>8gHz_W=M>QUq&aC(II`SG z$W9&KshsWFh=tuL#__{Xe?{)?A6GaS7pU78OmkrO9*&OZWW9bK-TGRNWVyd}p_7z2 zHzh*3R>;%RO^?xf`@;$UD~dUR57gMOgSW3rD~M zW|>Sum2libBefpNzbOw=H*+#>Th7HEINZ8{HLrb+=a}hMC<^J)&W>OeVm5wh^TAcb zGf)0FiyGAaq!KT7P_-~`tW0r{dq!}uG*rJpMQ=8~Zv85Rcg!spec(hvX5f!+@>geO zBng2zDcOrcHIG!_4^#>LYTseWn+vFIMoiQzw*>23+*P!Weh#2cCn)Py{#5dyIsA3X zo5WkoaMYK!81dhq6-9*RQ0|F5J_e=TwNir7A+P?LEt=!xJ-o~RCOmTOyU**{!`0K!Iv{ zym|~q$EzI7XUUm;mBi8r%kFi#f#ROZW|EtGsm^NoQNLT=Bf)sg@4m{``oi3>js*|P z%oA7-$vu?g7Fka8X=Q3dV20os8a)Lf07?B{LLGudpu6$Q7G|udQB5SPxA4`vu0$>V z)H5Gzo{=J*`0)4S3^9#9YIp$2vwTPj}ee&qP4%7eGv8Ta* zQ%0L;+?~4hW1GmDGnk?>MEITHm6W__eg0s5Xq!&mprnFv>J>0w}^wkEcF)=i)8+y z6LjHzh2!|7y*)Ckuh^$-0?8Z0t*J+xyKYKz8!Uh;&%w^5an=yejB=bssg1j7{>t6#`6acU`qqo2n9~sbXCdADkHgHOMR09p#-ud(HRu5 z8n-n=q4k({@a8wNJ@VBNMnr9m9k4`C;|V6apn`FoPuW4*zEMr@1-4o>-0gt>7cPWVPFCYk4LW%IRBIdAf)qpTrqe^d`t=dong2((>B5cJrY@^s#w$-DM4nWK{PUo znQKBd!(N^JC2%rl*0&@d^J76DAfJxq)4mlUQlQN+na0i0T;B0O4tm~}arDJ728HDr zo;kY&(euTnmXqO_D^LXxz5d5(#F}O}Kg~9~pPslD^!XyC)6yQ!V#*g1dF* z6b2&tU;Nf-28zZ>vB=%}ljeq$D3g5ZaxaQC@?MAjuxe@-wUx-pk%}B$9+U%EHLMaH zM|*WAuM|IymdQ46L4)HCwDKWl8eh$SdBx&{JHgaynH6J~1?R`1AduX7dSQ;D$f@(K z3utXtSM3(-2KRp!SIqqlTGw6>-ZBXJznlN5XFO zgEpwKGn*_8(1yeh+A8uqxuF?`Z2jebI4}(4PR!MsOU55@e z1sT`V(@Oj`JEwA+e7KG@VD>nEu zamRT^fr~N5-4Tm(!*M+V&0Tw;uC$2z zuTM^=aZ?$j%e%yDH0VqrbFf2W0)Fr*qBppae4G@wAPep0NuqhjSe#K2%F)FkCI^qC zxW|~6wpPqiAoZdyw6ia}+Maf>$t+PibVKJ}%7TgWcVkgN%ZRTk6qJz!iyC$7UfUq{ zXy|6RQK^OWD3EWNG(!*xnliFyC09{kVMe58)AQvH4t#*bWfuztx76k|5FEb<leZ$k(kFs#}ZQIh0` zQi#$H9EqZ62e+v?MP7Q@9aLR+E)605m|Z*-gxr_;Mu)}Bp(R^d;494#F%qpbCF?m% zkvbaPM=%K)=ixH%K|uqlrX@JcVbQN5+Q;4qsrbGbVt|HZC~B%{Zsm1w4ujkzBM8I=oj?iA4Dh#!1SyJT~WlM zAQsyPuQ0-laC3XH{_STAkf7dYh=V7`{RM<7+?Z}7N3?K*&+fyVE3Bt6T_!Lr@_78na~LNRk)Pq> zVUM)650IljgrG+%J7ijnDUA4J)f(p`?GcZ`g24C_;{~T+;h;<6=&vF(;l^a8wCi^? zeDXc%Za_&-?jz;~1l%dfBHBmYdz=ef@#fQs$H`J(wKx}GxTC!AVn*O1MNN-4K|GfT zNFRmC?_Jj3DNR~SWO9k?+Rn`p$tXJCnXO2gts|E-vUv^@rp`OEMmUXK;h)T|y-E;Q zB`ei#^b0%=PO4TpO~t`fU&|czA>9^+m&EdtnKpS73C}2@1YnuU<;U~v$kEv}ZzPs= zr#0l1PI8F<`g>RoLuNGi5-cOYP4igGQ&IMBz?*u@U9os>gg$2TG^Q;0fi-p%gtyr} zQx}CXg#-xe#ac}eXH`)xMHx*@bmt*YCB*1UxhkV98Hi)~)Fok*da3J<@*gIB12A*@By75lcZHXnBN8F3w2+b zO{P<=PaSWGAiav#AbzjIPWQ4?9S&e)yXd z9V`I_%^@z%ZBJc0HYyS5x^Y?%i$fR@Xys%!Uc==EW>!N!?^w^(xLVgHH7S;-1RL)D zhkN-BA)$uPb?2b?`UZ6tlIaRbahJ>i(8D8BN5XKxMD&pj*TUExeP(trF0^)C75h3G5S zQJqaI->kw8PZ>3?wxeD1%c+l80p06t9BIMt>A;(T62;J`t58EAxgwh>fM@Q|MaPq0 zqYiow%vyhjtI5#fpUR&g;l=~nk= zchZ&Y-SzkSr_W@F+>$&sY4^J)4)tu3VP%~gG=TcyZt}tybV1f2tZR%>mWEw_$YE3; z(1IS9bv#)84J?&BbFb09?;-HHws)1*K_rs@?58GhN>8sWnaetd`nH3Dpzu5k4JC{O z1kC963PJ@DuT}Vly87|q=(|re8B)nr+k_Poi^P=T?gvFO9!!gD&^VWVq)EQ&a77o` z3fW#W4#>(yZPSm@tu=}XU5b`GPt5}b7`NQ0Pni+x0kF$~E{uA*( zHaaF|Mi_vki=mCBv7nu~jVU1m3_#h%)K-ml8409k+>Kpvm~Py{FelmRLLRe&MD2w)5_wzsjj1DF6z0sj*hU06YO+rcU-$|ILD!<-ZUAmtFRM*;Rt!C zkdu)e2Jl~niIAC%ftl_9s?7gc{(mk13~Wpcg#Wqz1+lboF?IS^EE_`?QxQ{RdlS?D zg!$j{dThFMfF_XZHnVP?w){UpUfXW(cgyUh=N4P*o1WXwKRF-%{@WE1=&j7owKdZ< zQJaku5ooGsAu_*WW`=)V>7HktVRu5C)?S&`#?0jC)?Ur1gqA{WoMoPI8g@X**2?HW z$Ij5!Tp410N2akiF_V}ep{psTE0w&-Csk&?`HMkAk?Xmn}wwKtM2eX|A=SIuSY8^EKaUYE+i@>vR~BqM1UwQjtwx&E&+|K%?)(*w)H?Z`DeRo*AB4y{Vtr>reB@PUGqRc2)z!sq!@>PjWd0ZzxiglxvpcVp1}VN( z%ZyKtj|+VuHho9i{1AWikKY5FzN2-12vLvv$3;FcGk%(#5AE98eh^N7E^?_08-i=8 z3R9{V_NadVS&6x+wQE0!_x!=E(>1iTy3kZV3m@|NsnH#&<(ZX5snrvEyVx6D8DHsN z{fjUg+Ur@!i;Rpv>5A9+fg9g5g=hOlPxjvD9UMPHrB;@vM#lE1heG3%uk}+`w_x9M zIY!Uq2xULhnj=rQ9x*wxKVx_G+6&{8yQ(Ww3o!}z{`yn?YcfCGY0OTpZ4ac&h9%}E zChxefFYmY9y+0D=hZk1{#$|@4@9x7t1fqt9kn%zVTy7X@LR_c)rVY%F49HB3OpWhf z`97%Z=H|@aYySKZ{jHxbo7)%m=F`(WGcpFN>Uc69Lbc4!O**)YFkKfE zyvAxta%XEQScPMr)F~rM4e@EuQ4B*L2^uHIcY60secHR4MG#|5QHCN6DsY}<4LGWL z)}gE;6CS|PybX70bK|I!p}2Tx`@!vr#PjBeR8rH@_-p#->%eII-pSPzXr#4khA-8HY(WG&n`7^BR}+lQTe(-*iA{nzfpH`~Gbv z1RF|-l)TvT8X(es`e|mX`?k0m3zJJLX_y$tG)n$_ku&;tGoUDZU-nU?-1YIJvB=FQ zg*Ur^+d*E0jLb9IAI2YQyC`N;F46#jQR!V`G40A>Nr#%E||kd5>+j>3kefhMPs=2^|5l|%}sZ|w0|E@ zjq4s&Buu11P!!ytA>o&A@a1@YT=8F=A9x?2Pwv2)Hk1G1sTK__sX(iKzcfmuPm@wy zYVAO$;9tk^gGw@Qpc4#@SaUb(Xv4E5EGK*}h{|nIwQj1cO!2Uj7wji0S$o+^^&D&d zG-L0j_h(7Le4agB5j)!4#K>uEig%t|iPG8H zKmOy_b)Blr7fD99(OaO1n_i)zu0!+%knISMCNN3kbG%MZ99eD)ol-_cV5cOMamEW~ zav(pf{yjTeB)s{Tfhly!6xIBidOV5p?_7-I?xI4A5dSWt3nDCG7*^uU;0Jntq^Zqv zGTV*ro7z=z(4YhD8{X-mM5|u3QDZm>T~I;4*yIZPLZ;G9N*c{%&Fbu}%v5tC>ul-% zmlt-ZJF(mWmGLml1p_L4S&9-cp^45=19Z(XHT58I&3*^888Lf<|P`&D5gnwWghL<*Tz?`m2uh7=FJ~ zN_oT5T#JBJtfZ83ID@;d_A^1)O);+yW-pFa&@^(9l=zH}#)jBbXh_Jzf$ly6m?u{k zW4&AuHk^pO=57WA1?6ZG2u-iF@UC%nvz!RH#!Y^lg+yP08ZU=z93N=tdOZ384WcTf zmZ~vCX}Dy&47mRKeFEt`PkPEn2!ypxT_5T%pd7oBtzE^Y(UIFVTnY z+hLQK*U^1gF+3X_75r^6Xm-nngY=bUxs>p)yxd-TcB1b}K*=q`<72qN5+MS#r!NwQ zdajV%xX4QoG+A8IwvP%6eX&bXr#F3&f(V{%-!eB#*bnQqRqOg3IH9vI=5Z?vGa8Lq zieJtZpt2a7ZxhAf1gkhssLL1NS=`CA&>ycb=e!6 zyC`=uHH4uk-D6NiDvTWe6yhM^cBr?T+7t9Mv9@1i0~tg6_HK_WTqK6U3P}Bx-kjI@ zjm@kEQ`Mm2t-gCt$0u4TR58$S;Qpz!Yj_rbw|JjH4;rUCwfr40@7fT5!Buw)RM~)m zKSs!jgo(v-Jm5p#yx{|p3C|B4cP&GY0f89_IO)%dgL>rBo(B*YKtW4%6_D)-FhyB@ zH#=oUPnrkHp;ChtUjnMd6kfIV?oH?n^T!*2rG=_N)f_q0bC2;MK5T;8?8Za=P_TBX znSX6*%jq*FzdJwX9AD^e?Tjx*ws#a+!)Cr?g!@-NYC|xhje8A={YpC)RkmT#$)&e` z81@J|_=8s5Yf`jJiew~0HX&Ly24|qdeU>y1*F1&guR{sX`Vg~?{kp4)SzFOh--QHL zn$hgiQYpgXjL%<40x54$KT=D^yj4-Ph$G8mTfu;DJh#1ktC1B7*ZTqv8|!AD#(sH- zn})J@S)E(*Sn}5KuyzHN^+q&?1M9WnJq+yh!|ar%{rAOo>uDwpwr0=RuTbTn&BUyjrNQIu%ATbq>W~mOZf;#4bRt1}XbU6%U?=`oL6n zO0^Fef2MW|(Bw9v9+3$xK&1EespwP(0(EUw!N1`czxrx%XHq{-t{wr1X19{DRFdDX z+V&XmKtthlsqkFPY##?`o1-u~i>=u>7)CYRDX+@Be<=FF_g<(!Yn)1?B7yDX9&(K> zd*2uM@kUEaK~j(Wf+FnswK2gvQ7j}JSKx+FUUFUSyviZyN+MoOQtsQGs(^w}?h-Rd z1HR>?2nLUWj}oS?s2qZ$Hu*qTKq7A<$>uICh?$O)rg9G5{+ogIijx{<%@r)pO&$Vn zmA@`d%QSU3=bA^8w851jAo}Tm64D%U1-Up#0OuRryH2Zs3loj=C3D|PW3?^Lv6%u1 zD}1$q>elmgk2ib_iu^?$dsoEEt>@FEXz2*Wm6Q=;K?e-97E>S4e&`*mk80w?OJgBi)R4P zSf=lj5w zb_)GD$UMjn05pMhvgi9uUq@^L~<@;C8%MSmTEb-{y`@(05RRchfON^H;Uqr0BY=!zkxh-{f zuvO=Eza!&jgx4PXp+M1=u~fQSXP~DC-FON@!cd#*bL)MBb`_y%A$wTpu|iJaQ_lm@ z*MT@BCh?1SXmu1Zs`dT!F+Z^E=agfuaKkXb>d~Jiz?+JCyRm|UF*`CYRyRA#v?oyE zGK-NBK0JA-2;|(={}2khz{j=qN!p|(=6ZSDrP0$BNGXh&qtjEZeNOAUMY}F3e5^7S z4D-|!!;7jBEB>af3uP;)!xD~#4FfGiQQ!a7#~B3QqPTLx5NdD&x$}NB(GeJRHA8|Q zeOGkSNI){ZWl#vrk<69&c;j2hC?eX=-O*$Bq2R@p-LHrCorZ5{X% zy3#k%S(AR~ymy5%*GbYoPN#fBr(?t^O$&>qSk!CvBjdI@vA`Fjl>rxZQDeLhw{&Y+ z29pUVJGI1bsm{+2hB^|K!J2Zo6J4FYVSfVE>>48HD=u=5J(`5Y@}nDk7ROdwk;8{g zL#o4P5#na}iTeR%Q`*G&++9-@ze>c}zx~NSzsgl(0V@GWzIdN+^8_y^47731X6P7? zy*-W7VFglLI(_uoCxh0|iwi&5`xaYpj2CLu+=f$+tH*(572ejY)Wb7|3P=0vEtlX4 z`?4hS01pnf8NoCXrnYK!C!X8I?3TIIYg&q&s^k#V+)(&X>JgHU0xOpFMun(H|DX`z z$Sn=5Apom=4CR~iX{LGz=#H1Ys@MxWXuP2|gE3^WkrvTImEEAZD->asx&bF){&$2u z+EEed6>KxQ8Q8|EzT}#on6RVwuwqic{@*mKyb6dF*9`S2;Lb)3SWk45f?{FD z55?{JXDw03L-L)U9V(Nwhm^9DrPp7xml!-%1#!+hyms~<-ueA;YBF9v3jWLm_uPS` zS!P~&Ub~ankacOM|wD;Kx? zF{t*`-_$a*2<<7<*6SlG#^>m~-MTuQMx;|sDD`14+E+>%gJv$z(aI&uJ81?yG*An|K^0hhbm@$2hiESx|0lB=!pp`J}&G$PxK@g6ggz`m|*1bl~;IDXm{5ltEzC37SB#q@-uhI{0(7PK&^pR?iXshn52YTm@D(PA-=ozMQMgh{U>waTKo#ZR8jyR57qrA zMHCqjYF1a}k`cmMh@KM9<(};d*p53Z)B5eFc36rhEF=+971eK{$|9CtkU5=rld78S z6|5}{K0I!E&dvPS;@n+a>bq{grL|P{c?wL3B6qJR;k8nyx0C~_Z9b7pv0)(^WgfDP zZOjcNRx}2z)^y_qx?UHT_{EJ2S7f{hsGF%@A$q+V+$(o8T{<$=QCBAXPY0j2L7gky z+lqKGk8C?W9?$#=uU+Z2%&%o`L#HV1Elgy{VhOg$7|yLghu$%wq@(=w)yLqvK)kaTvprHQwxYrs`mlKpM<}LP+*{Ch%G}= z4}P`YT?r=4qE<@KhFMtkkS9_3>692eaO{kA`x8qCm&@hSYxCydHqE4WoMO%HWixA5 zT+S~13SzHIyBFc9D1440(jr>#t5hWU)UoqC&Pxrn4YG}s#Il>EHTiB-p9Z6uCgTqv z3)_1`C^?*B0j75GD<0Ls9N8Vd9$o`EZX$f(f+g1DVqjmm4a)Iqt8z%u)FfHRB!B6TEh;t%~9 zpFh!biN!Hr+W7Wsk-voRZFtL-7M_8RKTU?=z8>r!NQ1h+wZ_Rb$))_9xO4kW(y#8n z_s7=Hi77I1SU40|mLboOL)qptJT=gL@akuG&m+;kQ4Hlqh6S~#h1C6(BGma-m=ajO zeX)qG(EwUDuluLO({~7;=%s z-6NJ6=t@q>XEc!kS*imB5wAP=I@$N&;2E7fJ8{!7cnzevZlr&L8F{|oY_{J|2TXaZGd`tibdsIEz1KpjfI z-Zuvou~~Fc9G>49P;sA+T2NJ%<&B!?qZ?i=V6z@UFwNK(E(?QG#q#VEeMDL225-k- zev`N}jhWlv=miI=uQ{=1rF$gZ+=nmR(E{h!Jr{}N1SkFD*vB#9m)zwV3Ono=7Hu?b zMJ)Y`F&{Pf`gvF2?K*cUqDLw7cuKe*dH(9 ze$0-X+^m*7Py3Rvf+Y98?Z2U0h{@w)X`ZHk<`B@s5BM4|N_`@*x_h)Y7H*(YYhQN~ zJ!J8Pl}ovGXTXMCD;oTYKZRu9c|9#lKo0Dj$1TjE&}QviRGkD}G7Hrw<#d9PeuJJz zEZaC{l?KmsdfJUvy=}_tbe7kL0olSo5Ac6h4_OyQ{>5s+YTW=&);UJtSdGupT(iFm zM2|fs-RF5%n5ET|f|S|MRy8??Cc9^cR&BbapfJ46U-~1%@6YaX&rA=c{JZMzL$o3* ze&EB=h_zQmsj9t@z2tX>j@Qze&hR`}`vAkZ0ux%g_Y@`r8Jw~S$*N?)vil zkfb5UNwTArt6F4gXuSo0X)zmrh%SX0yMfHHp-6Bw^sZ@iv0=@U60j#j2^Scxh$NZS<;^s z4(a~uP1Y)<-GVx~oc95fi~xu>EH1S8rBOQu?4s!>c+NpwV!UXZe@2b7H4P-8(b9%uz|vk$S6Dz6t8afRm^ zhdq;6J31epMAdTMt_3}RGXFiePC4&+$MzBSTP+TOIV0)DL0IJw+_-w1))=q4nY;SQl~>=c)K9(3>+ zfRTJ!c(No=U@KUlFhY?ZfS|hWby~EGhA??Y$hb0tSa=@<#xabK9F)+f6s@6I2;%of z^M($#<|T}6B8!gi0~s>f=vvnC`fixckZl(VwMdERZA{@j&GDxIkrfX3RW?Vf zC)=JvOF!G~ak*7CG5fr_fCnNgDDO`I*J5SavX;iS5?!O7e!P9~SlcD%@7WXiV5ZET z5WgYq4~Ap})A*D&mX3iB)r3$@%d+C+7u*8RHD@g`kZE~1BHGc0(Nrt60Y>bmL%!Wi z%e^-_pLQ`I_DA8)yDsj)Vh(l}lY9oFrPQVauJbIcH)FwY(ik+DBrNpV+b z1c4>~Y{O0wMapEn zT;9PXG$1{YxMQBg!!SOaDDXdKz`37d6iMCzTC=xs$}DQ?4=8y~eF$iN?zF-etd~K7 z2|KyT3bNPZL3JE)T2Tb@dGTn0l&?j4ej|rH*B$73NQmQE6;FI%?o_DFwB*X_xSIVd zhIz|1xXeG`pL05!IIvq&i)sD{=}1kYW3S-n9`pnb9qqb#Z_@B} zQ;C&BwdAY-UQ2YDPl^F;c+4XCUA3FNES_RWa-@GS#f>(U*d8R?2 zEbvf@k{O`dbEK^=!|OR=R3ozP^pg>h^*QtxUdD+w9wdd+=~ldT;*Y4yO8`y$hzVcq zwBska=M4SKaQnRE=-O{AvmS#gywW5Kjv!KkYf|Du-EyS0x5z_aEI{v#lS*}M=6QQ=s!%S%49K)SQC@JZ1~TT zX9Mux1&x_;#zrVI_J>waTS4E94}vge9%Qo6 z6u56$-Zbx(eW>S|s1bbcVy5;7BIAYr^53=TtA|0Km{JGwa#TEqvW4Brs_j**>hBW9 zo58v&CGiBC3TR0D&hrB4xjd~Oa!_OtxTItBHa(&Xi=Sea)8GCY1b#u|*5WjZfYmjK zPd>ck(8K=@v)%oozP0ga3nsnUI;;T4Fr+_zI$~=)YixBZLMy5YU+G&JS!tpqo>eC^ zFN!unETh5wr!`@8y&HHZJzVwfl8k$g!>*)rrYpp zq~RTI-bQbb@-l6)Q=w%!^4*zZ1WV*mBCW^CVm_^~&FODKugHU5bmjr_8YL3Em`DaMptMJrNGDAnFCR$pN+JXY?FW= z4n{AKr%BK&B_n-co`OgJ#R|9~V*iI{E>Cjn5L9^ny1~%AG@P?=RJBq^j!1qgZikdu zAX5_C@5*&5#E}OUa`N={#1hd31~wc}_~D34QhiG8u@A_GrLvHU>hiWxn;qExtuUFt z8hoEB?nJzqltXuB?*Ry9AzNhe5rWjI#Dj6fZ8H?HmQuMcc!tuTgP}DwtE3Ic!W=Mq zL0pB`GA{J`GT9%Q-m3OSArDebr`_moYRrqSl9TtQuGx__ArThRrQIMl$~>_OCg&ax z#<=?x{QL9mfZJIY0}L#_)rENSC1lQ;RIAGiq594zj1nNPlT@TLOKSk_WqN8@;r^~Y z2R*8uLpGj~Ne&XVXNmjD(6B>3Mjja?23;o=`3+wZ!Xid$BmglnwEaPV`_Z3Vo%- zDxB+?TBH^t=pbVpQbU1i=;9p;N0%wa7Aw4ZaV@ug4k2)t?BDOs{{Af_X>olRoQ_*9 z0MFQG$r=-6uF1ECuj;qf!zVAch*1;TYDf~S2Gmqw8^ z%UD)gac;XU_01V_Y^IJivIPB_yX%Cmmi52u!U|)3H^&fc+zGOC_P5FF3vQOnasZZG zuXZNj-1Q5PZ|=$#c+P)={;loEzBKvgJSxhlS{Yb7;n>z49i&3(=8WuZ$T>*$l5Ls| zr;?G6?m{=6!<0ke6nx>!E7C`bCO`a7N@C(U^g&@XU1!MQ(w+Drw#WslFf&`9>;^S^ z3JU$4cr?L1?4W7VP`@6^k*waW)+jn3IYGvC^b##*_EK&$H(~h`T|tf``t7aNM8+ah z!C0S>XMAku;E4ft0bSkuvc>)m#eez(EA`|0;s3Z9z2m(R`r_m6U-nzIqZLrnOjRs) zX>(R<;E=VQ>mRT#V^wu*F(P!RqCU=%VAaKgdS*Fwb9i2PPnN&7126Qe8m_6srSL4? zTykP$!W%o67x(C_bckA>kGuWK>Z*%E1~a}LJHto`3%y!ZjU+c%jzjeM=UULHX;5 zgZi4Stc$O}t;E}R_u^GI-RLHQN+gbN-j#Z50Rf~IF4js06z51F?qKPddOSH-Dir}9 zDT#F8;~-M}(9BW#@m$FQmCM7`ek(8Rv=@3ih01m|lu2*BZOC^DK0$P-&1$M$3wwcB z7pibIH+gAU2fERIu2%VkW?4z0=q5dG@g8~@Zj&(g_Xv}%P=fb9 zJ^!?+2#S<#+D=}=Rt`?_u zSOolAXd4OP-?0jQ6Lh@=z`q0QopmRKM)4v8q;pr5)VJdRUWYKDn&)8sv`(| zI<}sUtH?;X)5b~Q*X+7u+M@~2>81pW=NP|5lFU;s_C~h-&}qLkrCYwjnU=LhV&_}w zK><^5^|Lq5seAv|ipH{~$@>^=DCLjIzM|6^Bc^BykM8YO813dAO0jSdHIJbFcQbDY zKAY^Vu7exz{{ccky}z|ACdfvzct!BxWS4ygNP&#^9n_dv&@VadRtRlONGP^Lj#cu^ z95VzoaB)E8fNEavHQ@T1_XRio;Qw-&Qcz6-AWixDcddOsd3*V2E39P$r$|$g2)T*r zUgLBWksp%scX6E7?PYxR*u$~aO!*PY+3F49r2drjm7Yj`^<}=KhKrA}*bgYhJPQ*y z)Gu<4K!zLWJ%+Mt`_Q4rz3&f}0L#K+=V6N1Ov8|hPTQtLZ#1e5t?PIfBHI0vRzs6I zqBi9UDbRj`?-Qy<7+9O32UESpP<6RCY6ajl+>UqkI&hv>V;}9!#&jpe*N)KYpJ1C+ z^1}^9f2N>q9{}X``>IZcdInaM`x5ts@+ZLCJb7(o*fdq0c;)KJppmq|Rcq>!*Pg*1 z4vOP-grpc1y=j^QRNOX!QrXeF{U$6=btZvr))0?*zcODH`b^*p zmvf8Q+B4zBMA~QOo^Z3}c|_t+?}K$3JvoaHR~puL3DnXkn_uGE8;wqam1Q-wziL`c zciV`*(3p*2dx5j_~&{C$e{%by}PpR44^CoHA1Sr9jM;MeC?J{er`}h zuL1!zB0UVgrf{lFBGeXX=03p|2b_-T{N4+hQ&cKy&}3K-?ZIBm13I>*pH#+^Vw%@iR^LvRRePr?o%+a?~Oig#aaHzeX=bSmJ)C z3j%!p^2;j{sxES$WKQ5onySy_^XNlaqjy|T$Rea0+_=V$dJNvGahim0cCdaebjvZa zeec(R$G80Po-D~Zq$MsFf$sreg>6qxRvaHF*aU@{F#shIozmaQql0Hrv+bJ~!WZ>j zwp_MO(>2^eKY!hc&mttOyBH&2cOi4LJ}L2C<>733fu5Vr^<$AjDpLdlVyEH<5CQ;4 z5ul0B^Ag5>y-6fv%FSLXYF}m94)?-;j9jJcWlC}EuH%lG-#z$+)n{xjVZz1N*Df-LM#m zT;1|QKh{EUFT;h4_n7Q^TP0T`f-Y#+fxANuP8q$bkP%k;O}sYpcd<@w*FZ_y6o$;k zhbr-D3(VUd?#an7HcRNty|nL-#`;lphs5G)VOjO1_z6|*(oLzAGnaf)RM1D zJdz6VbwN(8fpBX%DH;zD*Tyoj5T2DxyItsbQGwsB<(%Hh*AQ(+d~MOo8;8h=&_(>kmP~Mr80~yv}HS)j)Ob?4PL7_>;Kx3mK`gDLfzb z6;%j<@P1rkk{SREP{>3(naFg~KU&}e3nzP@{PKQiJbBU7*_S1G76!2-`8f_#g(86d z`e+6lUCv|nq(gr~V-b>5cDW?yw@vKHNL00xtDC`~tw|%6^Vsyv)EcD@o8D@t0IBSAc2b~5g0*1I|#eEN5 z2h9wpn72i$WIn4quHu&v_akzqVyjuVaf?39#QU!VxZ2Xt3z<@9wjmxCfkNatDOIRI zztLol#iVk63$Td|ZK_2QM_8|GR<1#)%L465xN4ea&=X?U6hJ9?@%ybIdZ>|_P!&b5 zbGZlKARTdo5g{XOQzHwfo`nEmY@OHjQQ9*rum8DtJ--!lw=!?qjMLv${~|u) zY8zC^5n(f=*Fg;@CIARiWPx>DsHlT+4i^=FM4Xv0MO^uA3kvORt)11-v%Ek; zzWA9n@ehod{^@a*z<79(TwJdgn03=tWFF{30vG1^P|5BM)YW`SoqoFCGSL2qzTl}N zR3k%?KTjVKJWTUkVYp1QQW( zaKLvXpIenQEhjrbS7*z1o!IP@6fk@#k8%MsK$=|4J-rh{VaGo} z<5WPWZAmo`zHWocT2NBhVEo5jmh61zs9AI8(!26Z~c4q=Z^gI^T2@u27BSCF#I1eml+ zr@0{Z_Z6G!A*^FM$b{s7+^)Dxel258XtsC=8PJj?ZR~U}sQzqWn~HNzUtY!A_SWnW z<&iFBoV2;JYoZLW#;)jlOFKxcXGSt@OmTSt#6kEqqpq#qEFP|a7KRVsQLurRk~jIgD@n`kq-U7P*Jgn9h{S zzW}+C#mb_Fnd_GxsS(pw7$Cf7vz3`FWMu%XIdfrEE|6@YsKajyZ|{mvfk0xFj4+!G zux-Rkr$1*U(_-2s{n>-R)H#b!G1%EGdZc4k4Y+cRn`ct#9UTdk$~@wwwM#ea_g$K}E?~9G`W?@zaubXyr44 z66?}&Yk9Za1^$>Tx##TW+P0!cWSAPh83EzhRhB0sqFRQRnrKsPp+o6#(E__mH})q* z?5EwQFtx;e@7R%q8M;d|h&P4#K)l zq7XA{yPdm@{bobk#37XWUQ%B4lV6*AW72tm4MqVy&?&sE&LbC0Zoy$A zv^9t(rlO{I#&jG`6NZB0!oPspr+;PD(og?=Xx{`ManRy7hM$|k9^(9VA4`C%(zq3= z;`Qd#*P6zR9^v~W)Z5W@P3WK$OICKP`9&~h!~!Wlj;7jt{O+B}ylzvsv9P`XQpiLD&bL34CM8vKeUnB)nS#=u$k+Cs#B%e^pt06%xhc62 zSU#}7px}aHa7_gcS+Elb=YTpZecg2}PVKR|5*~y+-cb_RweG~f*Ebz`ZptMa8twJv z9#$F#!qAQd;F5*zeWu-pPn8!(o&|R6+^HXX`lD($GS`PuY_@n65Y@^KIVLEJj2BvA zF?9d6)mjjeX#%^uIx?0&Usl6U9}5o9B64XfBocr>&Q~%S-(Y-;>b#d2LSfrd>mRcu z|A{?Rz30^UBfo9e|HjE`qKB{0h?roH$-ir2?nLKEAa@QHDc}K)iIL%vBq=rL(I}{% zxh1@YBVuCnsr6ZePf1TxTOfr?3r;<+S%PCYy2RC_3Uwy>HC|T z^sXKwF?)dVH%H(gJZ8IG&3oP^*hEDb<)8NsV-k?@@8>}|G}no!1-u`dY6iO;?fI7X z?g;F!rjUjM=I61D@_)jxxFVF+_AQslr_iyL#?w8`#X@o2UGy~S7(_mup;xfZ&bu?(W`z2}p)k}fskbTnTl?Y3Ik|S(R%Kjnxs7wlg4e28Pne- zI*8hiH9HiF2TF&@Bj2Jx(*68!YsPav67N>krBb$oG?WNc;3% zSLIAxcROsU=iNL3xF7j5UJwwz0oYmYm-+YY19P|KkjB=CoY`nNri9b&%W<~`31r0s z0yB>L@G!^jlk$NE=A?Z?`G39-{$#3QJE&Q$3~m-DlbSc7LnU%Q$B&l(^|k?;ILsvVM1D0Vm~46Jj#G z;5L~=ab>frAD?Mk$FOvgF}p+C4d1(D7HC9alrI%?)N^+|DRn#2^?WB_b08h?2x62u zXEbQ@ZCx=YXEWuRX|@#D^WUU@+P zYjuJ4B`{2f1?61wN63D5G=ltTNOPDF(M1J5fR`*ROETOAfKnF0S|U{i7d}HXQBs&Y zV?%bvhJWCDKo!~%Pa~v&c^HiN~{h%hNwSmrZQ;iZlP&e zpECXh340m+9F7Ey&U=t&JG_~ZcxaZKPOo$M(>Y%j(PR|WcOTdsNNqkoBq{l>DDoP& zgsaFXdo1xI{{{f^VN?y~Sq{6L?ad|Z?Yi1_I1#idHF6Qn9|?UUsybBJOdS>w&~M(6 ztTa!L(!JG)Rw%NZZCU4C9~9+Wyt`oogqoOqn{6^0q++YW zs*cu9jJ>VVUc|RT_0+-*H|cEl}MWUFwn&I6S9y}68owE9J-5oJTphG<>L_m zI0Z2Wm#w)HuX|SIkxgRNY@PUHl+APDjy@M9Z4n=`Oin;U8}y&0>4~>O(XLJTooXkVr#w-CdvyH<}8n|`U8)2u|(GCnyfY-(Yev@ z`jPAU@~aXWxq))AfZt~q7X9WfO~WpML1Y$mxfeueV5Xb^5Y--fngEm|I7dgBDPLGa zb1VJy*!wW!3B`9F%!T- zNfKMl-4JirXodqZMdCvaSyjRTe&8LH0T-^k@x)<3V2$P3^{dY*ZHey+13RQf2~aF}2m>h})Q$-2IXb8EB@LZL-VYCY?)LNYzA! zd;=LUMs?FtOL8>A9TMe|LrEhJdwUI`;+8MwvYor-tWn9do^zEiB9dcCMiFl%-CYxg z^8A~t&efaXDvaC5wZ&q+&RDi?g1P5s%5Cl_j~*)qKYyMZ2!0Fe4JwRpLdh{)!qVVw zPEqHK_@dv$n38^7$rp>UIb+$y1B(cIc+e8u_)s(z*0B*&2;eTR)n7s3V^_ZZw_MMX zVB{aCCNM_qMZWP$j#fue2%MH~-F{;e>@4bW_^T5!=3@dFAxYAcr9j|-Q1%M3a+$l_ zqzZjr#;-;SU1JpT+=v&T)xt}aB;IpBjl?v(TB>rnm*!5YG(0Md|2*r1*8BQA`q_ic z6STV_N!KN9j>AgjW^~c~cF#SL)mr#Af29qZ06`{*4b2|C!#Qp!1r zon=SWS^;%h^hY&z>J8d`teEV1-6$uoq_ms*m-!jL#t;9H)9HzXDaX-l&?xK}jkV2? zWN@EBPEzY(9Fkh#2Or0B+&8}Y)t%b3Pr|^7y(j5jov9K-y!@)GugiPcZYi59f5a{) zNuU;pKCK6MMz^(4BGv%Ul7L#_AAVWSvw;mxOUFs#8INUsc1w!#23TBieFEEJZBIqf zYND4M=w0La&|i0nc+n#5{>C6ct+ydpZcP`0AO{H}Lob0qD5NUqLoT95>-hJA7Pb{O zYi1QpmEx6n@Nv_FpTKU_(A+LpTvkv6cDHdMMp^BM>*^37|{BCu-an?_+dao0Ad2Cv}d$;S*-qi$`$4-^=Es|v&czKAUAO~AP&|s z4U4oX3jz-;VENqhLk*y}7f1ysZ$ziZWFItN<3Ey3YaJQnhR7!riWn%Op**)E2C9q> zh=R?c5FzfBH?l`vT0$T`P-~@vj65lKMNyY(V7)h64#cR0oiBUhPAM~0Zpq(pS!YTY z<?Jzk9$V5b@R5l?EP;@UTcqdIaGs5FPo z3OBCS;JO)kDQ=T+Fz5iT_=OH5ydvUwKx$1L!W;~xXO($ZF{nw|hu)Z&Wd7^>2TwsJ z*;i@QD6be`+Ia`;D1mjNUz%bxu;dhp#$se;-IYa8!kDbUEVGI#xG?k#@KNBQ?uY#U zaB&Vgq5#U4ZQHhO+qP}nwr$(CZQHha+IIKcN#0~~-#1iJ**mpX>48S`{#36&)ybq8 z2UzB|r9qZ6$G20oS`$@E%(cSs(R~PV}}aio@HgugLghN9DNt9o}qb?3oR~ zMEL+5TxnhS75j2&Z&F?fD;a7XZUqX%r>7M=_!IZQ?0#s_9GNh1N9LrmhaLU|g4t~a z<0md)bPg1tcc$mqcdFT769`QxYh$7|xX1GUhe| zJdAB%QK46Ok$qf+W7q8~F#OMRM zD=_&r{g>1KB2bKHH~aEl;Nb8H8dI1+FNqUie#AS+iWj{`-HN3Stl46HfPXz3%9ENU2QppzXdPXUuZYt5b0NCXMGEF{ zI`7=z0m}Rn;&J?{o)h)d$w+424QIF^P{V3QFTMFLb$l6@d4XUUuwm8T0fnAy{}eT>IT};nj#h=>5Tzg;%x%Ut9pwL;#`;7w>|ECH7@}r&dEBjhIM$ zglwpd4aOo(T)5G&M*2dK1P;`kwoT&Z;a_f;6KP51*VPqR_cL1H3Ahj$ih&F`` zkB(0o2)nz_G=4pM&rs0-GR_$Cc^jl2X+0^jG=T^J0_y#-4KBkg1)oX!JD?kQCVgha zGgkc&zO<Y%xUk4(x{zh+lxpQO;#*x=mN`11k{aS+GAFzO)v_^vsJfRNmO{oT?ULH0M7 z5U4e&jeF9V7%EhGpEpXPghdIZQCL2vZ)&X1~YLV6!h+|SCCEjQ*GcUne#h{%lkkk0p+Ym!Q@1) zaKXm_>J|LtSEwIqYet-02WiY35otPbj%gKAI7axF6o=vQIoRFH$oF(gRp(SkE}h=% z#595@bqBhf8EFb z$UUMOp#%RMmY-j|FnGzAeAm3sP4=uq!qj4W4D6zx?2Lp(G+Ss+8P5BlBL(C;G@0AG zgMafm-T`7l$)vR1)1U`hQd`j#k!Cj_%j2tczafbJ#}VuSM8jjEV&DUG_7eouNF>PG zdqrU5Ni1OvOdw65V+&Qowi=!{A;l+Uaz&iOzUGQOBAs^UZ3J_;DlNK@ht|cQie6(r z=_)|P{~Ec+PiD_AoMYqU@O4cVgAb&P+8HFt=g}~%QHSY%r{OAM$HSx^bA$SHZ(ZvB zTQcw}>uoPI33=Z5knpS-Shwwkjv>~AOe#S8tg9#H$9c11T zw<`2nMy^I=rGiifqBIvHC8+z{RMj5pn5;dB081 zv(u7wqEh}0Fpb3?ESt`*Eu62?Z>MTB!m|rt`xhXftRxu&4)TNS8`e`8-k+-+&sPa> z87UA%&@Z&3satRR&$89aRHfI2P<8R2INQz1xf$H5s5A{;D|iRpoQ!KIo~Pn*QtG?+ z@1vFd@Y^otNXccjg9vc@SdM2NW)RL+CL0*E!J8pg1An7<`evAHws!m^@lwh~V~lr< zNMEIqy9rf1ZzmLQt%-`S1rw$}tX7(3Ie42p_Q3Sq>Vtu+v^7$N!azqr*hEurL-SlX z@N&moh_CiA4u+{a7>M+O`uIQg+_J;8ly73vTH8SpYku5l4UzcyM2w9(hPVY_ejTz0 z{m0-sq;Kq4xMOnRLj9f;zOJmbL9%kH9ofvmFUW4O zv5aYWJk%Jtg9QDh?JSU4VgttaO4qtTQXi-sZ1{c8=SqEVT3 zztaz(zx_!2rgjVs?0jwBqnb;tu_dGpRpsvjHY!SMA!Z^bp(Ft4#}rrL=LK;z9yB{- zqP<0$s^dJ099)tNQEt;r{IFZy>7tnROX0*5?g9I(xK%4> zAuu2DWg>xXL5O?plh!B5b&1DPz6Hu09Lh)+gx&2Ve}%B)S^=tlA|T9eTV|$dYAD-P zezZC3gj5`mutU+>!gA$t z-=;?%-QP^;WK!pgZ)GCp$hcUzWEn+#9?k36?&?#m3M$z<^Uu-A66m@o@ch^cmcNg} z^azB6=LpW)U9!#@AEwGqFc}N`8BNV$K4a3OK?g}GC6c38k?^o0wc2E14N=mt1W|F^ zZFT6*s9>}VO55O&hq6L|9P%Wg$}HX9!f?iFzVAVKX28{;fafCLEhu<`)l#JM??xA@ zGK%qcv>>1{NtK7n0>1F%iE|N3D zwYw)zIO1^4V&=1m4VN1-D_2853Cxc;;+LhSc|;x4OA*N1JQqz)s{^Dqwcva`-NFGV z;$$*DYOMSJ@6e7wReVz@s@yXWhn+NZBqk-wAp0OwrO3NBW-6I8NRFBU>wPD!5$c4@ zX|XX`>ei>Q%p+!!;ANp0sn1+II;=CL*kl6!llEm&n?I`6Jk%i!hHmfGyrRvHqk?1Z z5^Q_cZ zeqn}i9samoLX$5K{0mY>N`Yv^IX2oCx7qmlYhg~i2_7;!J#RUfN^AHbogR6?$l1io z0z4OlI7J=|^S_d`iQU3E(Knj!WyrxHF{JyguXoDVg!T>C)Y&)vJw#inV#6RF{ykA; z`Q(kp;EVqUUQq$054`tSo-I%7)az4@eRcWMjy8kpF6UTd5Hje?KcISN#E9Fwu6C6O zI$HjO)YV_QA^5jEM^^}Tvc~~ zlmg$cT~E(EkGC@McnEM{%&<|1*;dEzcm~T6er2gWEnf-haidJ{m<^M(DqnB{&$k8( zhv4h;B>z};P6OMD*k!70I~zIyXF|fp)87026lW`Ej5!W-$;!nL4Fg4(l|vUaEbO}W zq-8~_tR{U0W{-x3X{Lq_%uKO|#p+%3O?yym9{BPH;25peA&=?_cL0BgFzD-knIc_b zad|!>?@4GVSo{Rq6c1blOYZ{BkO zatl`EPI}d)_K9~gHFWR$Fe|X<$EHvc#sIW)S+baFpAqX1}ecui|i)NMkdG0h$NM+L-_ zPS75g0s2pJMMl#MB|q0h=R9dGL@#CM&bwmy~~9P@UkFo#_*_9O=I3*r>bA zwGwC_A)ASGc|dAmxxPA$C;xj{Kd?~HzLqH!yEYHL>_e2@t8@;Jxe4(t`nR=UA4iz2 zRV9o-H;_X8k21o5NS=`Yj7~7s*2-Fd2s}R>B_D^5n~W&fiLb?M<)nnXuJa7Kzs4yG z604R$GwSpGz(2C?Om+y)z~5Y;yt?J^q-Wk<_lhS*ei+Y?uMoBh^nkcN!};5RvE@Gr z5G;f$;xD$4;UXiufa}6yH@HnZi)QDD%j@rAA$OGYMLsdqUBvu?I0A`)lar41Ofeet z9Am-r3+|@)_6@FOA1>c!2o0i93K20y;R>i=MOPS4czI5R!nFjpi+n-g+D$2&3;ffH zc0{W(-JHg-`?PstF;^ou5lx7Afa^{RV}%1wx(C9JGlC0_ox+Am@ecfR$OiE~wqjQ_ zv%jQI2M;9B01Zm$28;8i!+>i{)!xJ+%I+832`(?hFE~Y29 z4(@SU3s-@|vgloCPApxvKZ+{iQLJ0I#Tp$?^CpOT;WIG zuR=HKZ|}ss>ZLf2DdNW;%|dSf3DkB{9_)sx<2+UJxX)8(Td6b6st{ax*F>JW8+jnd z0*R|1U6s9MPe6cdKE5bpqCO%+H=gXNwbMh#MB*-W<*j;$gKJ;rBhu@1;!dE4(6@A} zA|OYdlKYkkwn}R?(r-T7TC@`3Qn5Q>KE_y9&dyKm-tk@_kO8r|)#MuV8@q8Qax(fe ziYqO+Ft|&+(JU0b25eF0V!D89{OW_U&ch_l@7M23q!Bs?GHnD~eNmlG;^?ph06pZ}&A_ zAtrN#vFrr^02l}|$EzU`+=l```SxcR6(7msNC!gkZb?i6(HYS|SQ$%J$2&nsDIl+>CaZq~z?&4l-%8M^&VX zi%R5IV;;+U+}I*-o^eTFtvK#bvbJ(No-2h>P%13fakfGzj4Q|Y5$2}?mP`YaOzdc_ ze^GZ;t#t-qOo_s7YPZa2u&v_e9L;(b;_+n*e%Mp%u8@$znl{wDay zlT1}`)_YVw>GAv;mpp`H9%Vil0)ux#u*oXqvm`PlccDPyxuTFh)o)jTw zPAtBHX~lhb>2mNuT5;9{;J2u|)6FroRl0dh5%RDGzqjn(`c@IPv$)xtb^M**Vb%a3 z--LfXYsd%s^43_!UfMvM!86rVLL|amTBQLDwwIg3o^C4mlQrtDxQMErpG=a_Hpj>l zl;J*?u!+gE_?cVsRl4Pkh1yQY3`G5NQJP$ziE~x}N+GGkL5FUFwf<5>+(D~+ScvWG1Y2c0zd55sr$^^=~k*5{JWQpvEN}#A>07L9<@&Z z#01|XXiA0kcV&!>i1NPl2Oj z?vbA)A#w5|#_>w=xL5Q%O@w*(mA(-ht*>2f_=CpvWdq{Pi! zXguK^%;;8w1@q)<7(yI}sSbN9w54b8pj8yo<}4O*EfL-mqGxRAEEmi0f4zsUtr>m^ zWU=Vn_cP~uPVj(}r4?(c=JZ4s>X7twfarY51^PKULyzjA+a~TSv>fay)=jHLozh;p zFZ1Tfu*JE~D}0HF`rLj6StX4^Rdh`CKov*ZG8(^ggZ6+|j?S)Dj-VRXN?=ir)( z+oC{WW}y<;Tp{AwgX-Q`Yj~V_V971V80?xV#m@r?wo35_{9mMOP=VkD^PHfur!(~` zq7YT@)$gi*D^AY`9lv50+kO}@k^+3pTU`HooxU$8*_2i6B8_hq(W=BU4dUi7i&u_m zHukJD|1bihT;G+TR>1*3Ble%q^g!kqrG6iy?o>Z!pF|9q1J{3KHV~Ly$Wa6 zAJU(3q@|5eg|2{W802Ro6u|CttvDugFj!458+;3zW~n4WmBMXQr49P5PSyd$8ZBmY z0(FvtGy-6nZ!qPNgE*ut(^)hZbi1B@aqq#1K-tMckCiQT9V#M*;BOmY5D38vDUK3% z)+w@zX{9tbR9C{NOsea{qR@%>Es8V}_9Yb)c*&pgGS+cAIa)Q2EPlFPv7R9^Pd+y9 z(yV@K-A33v^;Dai0YCrLEf5-Af<}2Y-mF%+gpUElE_}{lW11Rk8I8X&Ecw@G>{8~D z3PQK?=2>}xf^^rdS++Zkwl2Fpaj6h0huW5YXU%DStRdnBq?LdZr0-V8}1xdgfOGUXjV6-*5_c0|fhq6#<20#MZ~YIHC@ zBH)Ne0*Yp#5q#Q^Qt9b{uyVwKj(0gj9wM#vGgNQUYsQD@z)Ck}c%RkT@oP!m;U!46 z54d~8_9J%NVL^HAt=t{JmFXg1r#7G`bQ%8E1P#%}v|TBI!RwsQuFpN@h#%JYh@8(Z zO~4!RcKk}F=T#x1oFLlWZ@XOLvpTm|2jSlzgu$=3#w?|EbnwPdLO_;sY2Za_%*AWg zCe>|NkTU#pQMx3eB6?iiv=eS}Vc0zjm!WT5WuC8C!01b|I-%|W{9XTlWgk`pO(g^Vyi%TA_~3fIO&2J zJ`jvohxaI^3!5j zL$5Cfv{dN~bKz4TT0Z9}9ON@ugElNz9B z>{i}YT*>VT)=+T^Q9r+I7>cv1b9!n8^`%*=r@m|GXPAG(QRLMG3cf`1^kb& zSpg6QR?zd3S-CxovkCpPPTOSo&UlLxcfZxPvTo#8c?RevEfm|IA>>1(OJg%}CYu|{ zyec4EkwY^5HZ)1?9C}T@Ue`I_vair>bjT!n7xgEVKrB&EsJl{Dbi!5JI|B^8#Y+ zg9N-we@LSz8-T6LIXo|7ISYGiMg}Hq(^$Zjx5PyXFdTF(CgAkKfVUVp2$Y;>CFf<{SdNI5dOKu94Izl&Wp+n4=FMbWrUpS zV$6w?SU$25w&&99uqD4$ZNsEZgpT%vaDE{$0%=XH$9e3apC~m-L=)xguSNFjmB>Ed zWnI+@9^1T^=#?NW;D>LMc{R<^l8WT@K~FvKIze~1j0eU}vxQ^Yy9wz}6J-oloaZi` z!MgHpi4Byu>_;Z;i;aj{X2J6EqU$=}P|H1J+y;}&uXjQBeRZZh^{@C;&g@PTM8=hd z4u_3UV}ZaSoj&Z*n_q0xv4Hqfu#9A< z+I|)N)mcCCMz%)!pMT4|s${>Ng)Kw7~X=fT_aZX*JKpC$mL#6nKT2pbxOJ4xj8g z(zE1v&p#{}Bq*F{k#XHT{}rv4gVjvlenMBGPUEzr9N}mvGc^I%lWAUDujPq|Vk?!+ zE$~+XPds+!yUm)%1{b_<&LJaK$DsKKuKk5256}Qb{x}O5I8^LC#IRXZlCG-Nx%M^1 zhWy%nZ(h8Z_q5zt_In#B?$WjOF2sIFgKdDoW7i3B9r#zCD0E}5o2<=vurhlz5<@Ry z&6yy)&=P-_`NTPM(>I9aBfWep;l%vy&HUgvIQj~}QVW55zXyl`eBHSEW0-sKjZc~? zvRq$Y=RF>*AyFy_1WA^UrUwd+dkiu0{%dI|DIYP#XDsG1(vt}1`OFNIHH+V?`XuMw z@QkupR@C3P2A)7a)N;sE<$&H?!p*uEgrks6MKku6j$#MfaU3{K;K>5|W)SnnlI63t zsD2;rQ+>Y}4WjI^rVG9IX4tt$FjGenV1WB>c{gzV8MdE5i7IjK#2-3~mg^>W9}$;u zVB{>cQ3OGS`M~Hp3wh!GG9m?mj@ts$BeWL6Ux$S2!-_1$JcrNx(e#4k8po2c(rfH( z*jWJTo|LKD2bt-_@*-gitkH%=%zB$C^lfb5YP_VtF?267r$EG&2j+?G6`jG;vOpiS z;ryrWdE&UeN^?j+r0TUmsB%$nQ+gTRF#JUVUoaNCfyK#m{L)zR(;;Sac>sXXZY~xA z-1?5WGEO5>Qya%YE#aznHV5p0PtHBv(wwa19#(FG#3^lvDcR`Zm zgW2|7F1dPN+wNoQ8MRn@>;zUDIj?P1C2i;87^j1(L*sw)iQrai9&n5A^i_Q)Xsj7& zHSPw&i2r0))8ca$$l*_!X1O>nc7Y1aqnrQ#O8Uqp@M_R>FLSCNEP_;Zge(M6W*6m=;$qlADNbALS15Cmq)H#4WXS{4>5r=bx!pnwZc zf1qjQ4C)SYnrhBM_1G9hK{f28Bt3jLxo`wuIY15=F_m#9#GxL8rH9Mj1vP<%e|3!c z=7nriTikt#hPnNR(2*yM3Sy8N1wisdI#ks37>oNUOM}iaE4PX@fP;_?*p-#lQXJ5y!H`bX6~&6qutNc-?MOB(S^kv_FtSB29ZW?4Cc!0L_? zu5rSvj5Qq7tn>U9e~?y8sx6`bxL+m1BknyYQvyl5tHi_EsCWT!5#&iY{!(FBdxh&q zat213my!bT)xbVlX}y-o-o;A4LVIi(W~RCzScN(%#TY@fsjkhc=X(^jru+GMa~Jrie-6-bP*SBGESzyMxw(871Xq|1}yZ1^Yhut;`xiO zukicv$WcPbH2~j&VYFUTQ$2cWpIZpQvNiX5;_*)Gmu$YzLyGf5h&slv_Dc;)pG_-9 zYaXG`=^OoPMZNl@MvY;W{Myz)$(GH-%_@%`+36hJkCU}?}?n))n;;3u~5WR~koNF=zWjSZJoToRXKs=7Yf_Z{ZwMZO~ za%^}btRbmr9AW`GM=+;!<^D)StX?R=7JA^q91#?fIO5b%AGZI0)3gh+q9iu@z5^`& z=xTF0Xt=n)>(0vZ*y|D>Zps#iow4Y48$3y-{Z*ovw+$(IrI9O6~?$on~V zPx|wO5N_xg7Na-X+_kKJ8lwmD=!z%1yz=J%W`(WKJvWYetmK8t)Gf<^q_2sHcr!Je zBn7O(V>}43h&lO>W^75dl#9B%hX{!>Kq^zoNwff#((LE|hA=jiV2iqRg^TZc{oT)f z!>kJNw2@cnrwH4|XJwL%DWwDEO`w!WPX_+FY2~>rY^9wcg>BKSjNaA_bFeuD;A)~W z)c`PT3F{lHPJx{My?ruJYK0qrFq?!`tk2Mxf*l3wc=)iFi14J3M-?Ve4p#0Q?|Pkj z477ksK{>-1a%mRdZYhV#{~d3Be0IAIHfcW6DZb}Ot+JxG*3!LP9Wt`$isA9UN0qKw z&$v@r8J#R}5%c*?Ohqvbr2&o)oOEnlLv_E3*DQVCN@K_gN{rY84Zrcj=b#(06YI|B z!tCKDb`jEk3A?aM>K4^CuMtVb7rFST1=9u~E#9{S4=XgI>F1>zQt(f-*Q_hp&##v) zZGia8Nep1z1)OydhnSH%>TkY{r8Szsb7Cg4k-^*c+;N#+kQ~isEQ3Aae&%Xi`NNGL zI%JJgCZ?5FxU^UJJh=$Hy&39QeZ=%R6}OVi zD>08+r0=X$Isye|7-&(d*tGjO8_=uX^774ps%;}cMcdk3f@<;R3C(tqiND+20)DkK z^}o<1N{l}C?Y1+^5yxgoNTO%{)IJU2qv2QKQCw53OubW(C`}WtJGO1xwr$(CZQGtT zwr$&5W81d9*LVJXB6eS8MOH;-WnXkeN9OYms_V>xxqB4~{pqY1j^FZe?=Z(5g;B#W+6&?vhen;9=wh`la&$N$6a>uG@Y zZ-yVgRg(Pc@cxf&>$^%44Hi&Up@C#9dQ-)c{Z7R2fEcgIwaC>*oiyh2IBYU(P3J`a z5EVDD8z0p)=unu`ChHL)xGU^s3hwO^P6?(JSRF)^jMw|8lCW&R@Jx22Vt3#)$lD)3 zEy^{&Xx_6#+aq7JcNWB|xq1<2OC$Dsp0R-3_CrL;^BlSmrb6l$vj@#Du5^5{WQAMA z8$7y^!dDbk*-J4#|AWE}J5n~J1m^i&_?AUP;vgD=uBZy>Ti631br9Ef+f+^Jn)QYXO>kgEwy8Jks) zT9ULisO9j%ri9taA2UC*roG=j)7(=izE zF*Y>qYV62^{R4jeDl}S$X8Av>jgMt6pAW4TSY{I`*B%pU90l8PlK4AvtbJ{{Uw^ks zhpT~Qm5fNPo8GvMev1^U|41D1M`OyKCEOH5gD5L|2Z>DRw~O)XF5Tk!qg8T}f?Xeo|*rvI&J0 z^?b3&^EO#GsLL~j`{15PL?qT-_UAeH9u?MWV#wr>Hi$q%=r;cY)szCrD~2n8Jxc`;D#dGKo~I$A3Go1udaNw4fP&tILQs8^7>Q;`YJXs%FFqw5Rhr zD+44~C>`V8uei2+mf$J=d=HL^0(z;Z^Yc)dr5kmm(MRcrhz%rQV+kvDFk0(tVXVc(B-A%BbJzp*I4H z-P7Jz80`t;bvmXc%cbIOa8Vn?&rsy|HD7Pqu+X=EQAq6qMCe(- z60UAUcO!3Z9zauo&F$b8&?p^kfQ13 z0T|p!E#d>V9ms%aYntfSnD6k*5|n1WqRUJA0%TJrdWRe{xO(n$>#%0zroaoaKI8^J zKq=LB!YD1xl-g>rs*wP~n!34BZ^wRMp15{U8p>b~5v@cju1*?d%;|C5dIX8AQ;qDVFe34&9c;f6 zKbChTH>RXMeW)WAZ~xAhd535I(scrgds%3(?j!!l#1l+nyS=oNG%XNDvih6`Kh=ax zbVFGGK9Cz}st1ZB2k&2JWh+5b@k}ryxQNgyN__sQI`a4XC3pbc;FNed7~#hNw-smf zakZ5Oq!!C$$egT?(0C1MCK$$E$mJcK@TR4hiyewWFOjqdU3Lmh`D6YEWdaWDE#zpL zMQL%88w2qlHN5R)2Qxpq7v)U7ME32y0nVyu5QT*_(l16L&q7_hhmD36 zI_a9m>ZYR1-x?xu{yLC*pP`LsQHgY0XCf+phhBbF%V_L&`|1IW;$&mBTu=~5xANua z4!nS~Z2?WJ2Hi#LfP6i;b=p=3Qe|k#Qi9cNNT30oZ{>xLAyz*7Zwkv8dz>s@tfPB(SS}cve5Bu^vMDExn zWjkG*HnsM!8P4PbZv?NMUUiEuj5jzS-F)iI4k4lmFfKwM0qp7(`QDK!p>Tu5h+IYq zL*}}qs*n6dN$iPa9);?u<^+a#fImLXiU2K9ayI4Cru~Ox{)pWB#>9>C@a$c?bh@8yowh&0nF@=pGMYgYQQ}_I8>H>r70#m0 zK-W(qik3oQGN1AjZ*lSo{)~RFCa1v{lT@MRc&hm`@y}J1BjRTX9@fnhUu6PMYxbU2 zNAu+vm0~vuofo$x?P3W}DO36H)SOh-mwI1+3yLu*+-oSEv>85M`_q?G`>NLP=}^JG zo@zljImOBK#er!vm_+P)w8yoxNI_b3A53vVn_m`o2#Yg~>lOk1fDos#*GoFLatC~K zdowe35LF7YbZv8}u=&qRwA)k|gOmzZ73+m0s9!V_^sQBpIvb~#2y}tT0S4YptI>|O zxf9de1oaNg`dVmRN_xLDp9r2{vxXJ7qbk;g8~N~HDHe0HQb*LS!nOU^gVrc7>(KYw zp?*1ITfDwQZf-Nt_}L!?qW=Q6n*@3zrjXH0WD1}276|qyJ>1g@Dn%~@-BW2^ER#@% zwtOHITh3DWS7yikEO9^`yL8Dh0Y}|a(bG9)1sBAGTWzi|6HQ8Og-rdtZU9UDWX~E_ z^Dz&wJ~MgEB0V~4%(FXETZscfCuP#b0i^2cCBELecc1~eb1sQPt}Q#PQ$jQItw#R@ zWB;chVoj;#xMTYBn6e~7pR(yjL>*-qyl~1N7S&41f-H_b!6d5URqtcYx#Iz#hjegg zg9LJ{{%lv?nY?zhWa5F4&NGxjQMv962yVUjcJq2GV%ii;C3#3C<-BGx{e2;75~}oK z_Nnwa8*?!;5#?pGqft~l?k+RLB}XTVyx+P$6S|0vzzu;O_Q9g<`Bp`-=8!;^YO&;~ zXLVDaFNjTQnhc`%GTKlPo_Dpl_E z>SeEG)LRjqZ?lm7Z`MkF%(2qOuU%?eopasg2*KYPkjGizxT{YAECXVTm{(?P;X13N#IZC*;8O$&c_;$KEjF}%5@#udeIHV_YPcjRxiN@r8MHxKyoCs++ zVO5oHa?wSpOQAnMmjodQt<=GP{X{I!yp54HL>R;*LF}5r5jI(#AfJ&bU!}%r<_!G@ z=gX!>Goe*yuYpXuWlE3Z|1{7-0(|*Zs?OvbiANIcL|WFzG;gOT-&8mB)~EOt$j}dy z4IcJPlEqf#WJT@Vn3MmJcM`JNP;Y8r6QgRQnE39KTE0fjz49D47+ty-N^!v`l2na4 zKhf|@^B!$F58#CjL!>x8o$}VCx4#6vCr0B39(@A6%E<3GgRI*x?p{Z*^{3SksDIu3 zWC40eNzqv52e;DF3s-EzC^w^X34~RhOKyw>>#tBW8`};A7@)qdZ3pgBg6P!lB8eJ` z=Tzuvi~1rguyhNU?w95E*j56L?XS&gav0x>J={!qjI3Qzh<@)e z1=_=n&U#GAH6wI7OZv-u2P&MX35#nfqi>BD0gghE;b zgRLY+xad);pQw-GiS2v+qreYZr%k*H1Z+B3O?v5cWaJH#d`>p~NJ^_j}h67jKJ6(ruBxZhfRm>9u@gP(HrC}|Tsvfxiuh)S2>3W7s=(QPaShl8EX0yK~4|`!qG%ByF7;0MK`s$XR>l6be6p5y@Gh&U(GUd_@z@@q+nEYFZ3pOO59>o{H$@ucB< z72St-G0%jeg1?kv^lXuG9Kpjpow4f}ehWm3rb1?)i{wj|O&bywtjpeGe&nwA83~nd zPH&j6F0ZBG#CvQt7gW4a0Ea{t^gR|PO!@0$;aOTWyrja>Vw`!XG)6MdWJA7D=?z=t z&QEbyN><5R!LbVRG1t$l8o!>K-bd7Oc7G$jDSqzl`L~olie}5Pv>HCwC5#^sD5_mOh@&Jb$}@)$TUx#W!!9`S}l377ab#2hgcX90b#oIewuU z9n3aY1>Wj7uQ1vH2Q|Oc6dfT9ij{14ftN%=BgwH;@hJCIq*x_)O3MIfp7yo`>D+1L zoxCHeW)ZIZ3pSW|__!Xzgdf(V!XE32Pffz#vN7Or*BoF!jG=s8HhTOCU&o)uWJYL& zSq8A<_^?5?b66(5Aw-KgM#houHnt!Ova#W4!WZfBU}|UK%`9Bkx*1bS#~hg`)7m|< zwLf!ET;n;dp@Lp{f*fK+HE&RxkTVv^rF!FG2!?21Qvfo8NVZ!lpVaxJxN=8&;j>tI z8313&W`xdcLJum7*FxwPHPJtAQE^kT;_ejjzVAz7_J;axkj6prpN4CsI-4dU`5j77 zy`pYw?Yui5RcfvWRv}oXsDVQ+jBQTRPSdizg*MRw`iGY@F-7_fIP*%Z#F1w7VGjQ3 zhMTqS3`W+}X!1P6EAde7I8RM=jm}b*^w9&nAg1Sl0z*~*t$+rG4$E9>BjfI21rBvI zqRdRIor$UdjA8X#66PgY9Agyeh40->S9r54mh$n0Z4Ce`)Rd!6cdfyq8ysn?b^>jG z4^zz1?bLZ^=p7S#8%Es!^d0E;?O@&6wW@f=YAtJZv*tb1uWIjrV=A(poW=}m7soI9 z8;RHVL>tQS5zH4j)NC_l;Sxh@Z| zH0yQ%k~)B8_>h+TeUMfSRD2L(_iAX42i3iOG2`+NQ0~W8kwev&ITrLcoo0jj)4AF5>>AJF$2~G-LA_Q#ahB?J#2=@Bx{`nFOhm(-LcF0LWCg)r|0r{ z&u>VAJwRlYGU(&ma09scWEPVvu-iX>64Ukm(jI{cz))H@(zW2U4|tI2krUWx?wz1B zkz|(|;Q-TE&l=P7nN0|&hR(FyZpCiWs?#NJ`v{oTY?PxNO7oGf_ccoihI%|c)*fJ{ zrNqtQIH#?6)BC#4PWz)i!8P+V-Ou;U{c=>{-oIykY}QF#yqmrQ#JYvx{;ysh;mgaW zMghoE$2eNcjEYZl8`np?6T%Acps;z2CFaz%4)(nh;O{Bxq#7xkE8p`qbz`8p%>E@G zM5Oa)Zg%q$p&OlcJ|$Ptjjl*+NtKeneFd$JjgiU6+`pd>Xssl(2amUK&%=g(;vty* z+|J&vGRcuo_lSIg)w`x>fNb8Dr&)IFvk=3b>N@`>cJDbK5rpqpT^DL8ImH_d!T&1I zDt@lmI2N*GOwMYQm*sHD#-BT_*F_Sz1(2x&I4Uy@h3Gl8-l0tGbbZ&`nGVwS<>!|T z5Gmx*_EAzW4Zfd*Lz z%e?!+S!r!fP8RE7HJAs0JD}-(mNaflgc49>b343f(~ei`C42m6r3T z5-q(ZH>>iOo;5>islH2EF<3Q$`%@!`_p$URKEbq|fhQ2Mbaximeh~FLCf>iW+FUv) zZHFfm{P}|Oi_cQ=hyv;2%fI%j6!FMJ{`cx*_SYU?xT(E?bwXM`JbS_^4$VSbbr6q+XikzaCuy@Ft=G9q`Rly5wO5AZ)s(A!C)WGDpx8sNN`1% zDa&G+;6M+1qd7=X2yl)R4Nx2Qs^NJ0N2GujgjhVEr*zI}9A{4HTy_W>Qs0=yNt0UB z=3}YSZ*k#%*`r5*o8Oq`^Sx!ui{9hSM8A4TnmhC~%K^k-oRW}Vk_kxZ=cUjeH$$bX zlh{YYoMC;WNFS%U7B0D^zxWof8`OBSQ~hUnaqW{_BhXbI ziBzJMrSkh4Cx4bgM-QO({iQguX`ghdKUP_2e*6UlBtcuj`@`2U*BWib{@j^{YRpxO zDPOl%pkuynB0R}ykON0e*+ThvImfJ<1yW2%1}7Z$o`*Nz=$4?e#SvZ&ZveX z6TlHr%qARAp_lp4RVaWwX{CzOxK0@y#o*u=wWPTUIfeDMz1<`^RCS{xAfitr+oA`I zZQtH1xjbqke+|PINnp|FPaS}h#j3@O+8?maw@j5w)iQ53uS9;93u|b;389&LY8BdNCOV^Ubd)Or#DHA z4)k3;XIjvHX%Q6bZW@kpw1K?prhp^RMX>SJ`zvK_Fun)~wZ>_6* zg#>?(T$;9=c;w{y@@Ie{pw)JNk>J9*?MK*XwokJ3X*(AwJCHET&8^Zdy9fGVHd-L` zXl=*8gGM~(n#D8#N!zQPUi73Zgy4?W^$8l^j+yxC`A9AI-kTQYbaPCRVs#E}f$ahp zLoH~o8R#KKeVnlF`fN9_QSDc*&ECDr_$I1TJF`&ir}gh2(Y>7^;rKWLBX_FM{$N6q zo^)FHF1jE14|UM3bz7S!{SxHSN!UrEYwmR~Rgz8$nnfkTWG3c9VfO)#Z`}|LK-+#* z3CkD43{o65Pchiat%PZ!+~v*8~7!7iYhSF3r7HX{i=@-XYNz_;^HO(y6Gw6 zE!{?fgc8_{2j_gMPq7ipknWW&bTkF5|@?P5-19^D7vbQHym?l4Q zht3!8;GjV+$$E+XQ$oTeK+o?Q{233yq@|wlnjzvhjY_QWOz{cOHPFwE%rNGIc~S70 z^RPOY3{h_7uwOtN-svZ6kl*(Z;UjlyF>d*KOkS<;xkp-}P%l4V=pTDqk%2vxMv(*$ z;3sr$t{}dg@!a>yp}r;XcE9Rpm~sus&vUkM0eM`iP2HR||Fg$Q*|9-_RD79sRb;^(Y=;Rn-*5`4|S&8AWQaPc*rdXj#>$>iraE2io zONrn!{0-tCU9>QYRQV}J0($9fSyXga?@S|+b(Bl#-==jJ7NFhQZce>ToO6PZSp|M2 zrNbND@Y^L*B|Chp3x%#1a1AyzkUY8E&>)f>`YrEKz-G+$v3VE24&~q9)WtA>Q1y8l z&@g*N8}TWFC2n*Q#F3;fu0fK(x~E6@Vq`9QRf=+YoC(mBt|{0r8LIgbH+(jP3{rKv zt=*n>Ik+2NikvyAe5mzgu4wqBzH89YH#o?z*x!26SR! ze$>+Jh`PZo6|d=DmAsn$uVEU?CSV>qp1b;4#4Gcg5KJ>OkE2>_g(FC`?+1yZ+*sy? z)UK8r4S!I0TCty~fUy9<-y)@2>>Tg|Qx!&5HF1FvPXsIT+ef9Dl9RS7X^9c3`6@x}n1&|1vjs|16 z{M}@bFkMShVK#zGF&ZFrgq`s)gcIZer(^tjiuO%g^pP+qqu;#c_@j|`)?aM|nRhwI z$!yTZGhxY2MT@?J?$^_LQt(bco(Mg{3a&8vtoJR$&b}%FD)`<*gvtG|j|H0!Bz>&%-(V zG~8#%7S;jK5P?GE08BEPR+2;@`JV58FzO3@ujHKK?S%WYKA!{4+<}hxr|Zkz?p%?b zhrfF;il25{Pe>DMZUulvgdS zb*(YW`T@C-UNj(Mgf+lNztzVKVbvjnu`6_UThkj9;eCYUdtZ~Se)WQSKfsyF2I6o1 zdjN%+5V%ZN#gLJ_0*#>vL?(RJ!X1~alJ(%wHKnHqPn0B7rVA?+tT5oLZ|ZkPM%$%q z+xNBZWd2!zLTd|>cX4#YPBZ?!{!V)TmjC|EZDb%|I?(cu&-YL{ zMH@Q1K30Ril|efs*#+$x0@|Xu=j~&#iJb9~be%M-t;|%c^ZJscA^itK^!E=Uue4)! zWjeOA4CN27Vj%$c)@r_tDUR#rN7aTAfv`R_OM$W`Va}!Yp9CFt zvZ2}0^beGmzFqQ2MZUS^KZGP2JEwY6?a;HwM=nsKd_aARRmyIyHD3jFqXyE$%x4sn z2|BwlY%D%1MF1vI7=h-{g%19AE`eBXDcH40*#hXW=R$UADApMv$-NkbrBbLQ*T$CS z8GkA+y0@!<^4-y-P&}k_8_=iM?GwCB+EAMbs9u!zI|=?$+lVpT>1q*~1@$wQV=11r zqC1zZW!=jMSbthYh;L{?$$MfpKxrF&EHX}{+6j5lo(M@#T2;o*fH2n>#dWx0Nq}y! zlOf65SP?r4__6I7*56CAbiz83esxJTPh0sPh<(<;oE-`aJ4nQv^NgNdX7Z1G-=9!L z)`qNnt=VLJMp@5OY{|s1VBcI+tVkfDPWYY}6kmQtsVM;fkV^>eb40irso^j4cB8*& z8J8pCUlClSmz2O^Iy&ExaC8xjxyrN3`VE-H?ze(1cIysLdePfbJH)D%_fE|1wK>es ziy|gyRI($hV?XpA-9i9fyn80)9|%S|{ZIgamA1=izj`Lh0<5-?i-_v0moU0M8}<)c zudx@RzWKZn`&jQ+;53b46)B%LzCVsAeq&nNT&cnu@5zw6CqAy4TaC7_uhBqFUYHax zGawcx0wd%=P#voVWx2t-cH~Jlc=0 z%(1o!ErP{|4t)$$r+u*i%|6Qrw7o0=c|U06?2e(Bs+TVjJ5gn3QF4ro+DHPK6a@C9 zh?W4#`l9CW`!#!RoENt2Un%p$ZBG`D10?Qv2#E%dK*jX_2*&yL4=Lz(^M%fh8FhYs zivBuFmMH&lceGlxl{M7lnC9-905R|duXZ+?X7I!#jNqXL5@%7scl7(|snqSRD^nKf zxCP2U&AsG@ixwC+TuT$!&%OJ_7@^dxGW4U>k^WP4x(BF2gPY7idm?B5Rq_?0m%@?a z;(2%#G-v$98Rb_d_uyVcS#!L^nG{zUL!b87az3h$X z7_(i*`%V>3&ImUSs2R_m%v%SD#r@|m@Jw{=fWFaIBo|u5EuP&iB7l&GyOjnKkd3-= zJrJCS6Y0>gUn1t9&wv^lnYYTYjCy1%x!jY?L16xR#t|24j$=T(aMA%tVOG&PYR`S0 zw7s1$<9Fdiq`EReJ$?w&G}u3_c$8t4Sc^Wo)_X&^THJcdOO$MaO2PR|MGh?hkqX(v zTeRJ6##lld%P=~>8Y*6s7*Ncr8%89QeEaVh|-t>OX7RvhE04ZlGUVk|VO?faCRVT+OxE!{at_YZlZ^H$>GH$~1|njS zg}DZrF?6c6uvs97#{j)XKdai0xWsq1WfTRWL>!sGK8am;zCyAxItvdjysp|I`f}2E zgp{u0E=(d%bE16snXXc?kfbe6ex-kSHo_@mZF#+@CIIuj1C4<>o&s@1VooP=)K(WA zYWa7fIVx<@cZADP&H1tU7AYRBok2vs1=g9v&!q6;B6K0(vofI~NgCXJaQz2N!!MsQ+&*XK4G+BO;`%CaFLzV`(R2 zX=Li;VrgfpA|&~5hmE1RGXcy0wh9T^dk|>TvN5v|&@ypy5U??_GZHW|FtX@E(F-~o zo7%Y$urvPKD{SZ>VQOh^;X=U1#0f?JU&Q}>v`ozZoRThvHkQVMcIGyw1hkAyQ1r?! zrnYJX?5t4q8vpkc%YUbYEexHMOCNcP=`H9j>22t3 z>Fwz4>Fw=I=^f}D44q8vY)s8u{%1M;7oXn2^dI`pgx-nXncjuo#lp$dl-|YNp5B$- zjoy>qi}HWh60`i53L_gU=f8R>LGkhZKP6=Re@ZB0YG>|Z!AQW#@GranEtm+{m{~dh zW&XbVb^9)z)}~ZP#O@bq4i6Frb^74fgA2pYIFj>y>t!*|M^;%j@IQ)eUFWXt|_V zb-oO_iIFu3N?VI#p>d)45!hE_{ky3lh>JsGJz%8-;M!Tn+4_Dv02$h-8~|~CMkp-K zkblrIj6X0Sj8UJ+QA>#B5BL~}#fQG25?g6&!0!mFW9z4VFZA5N{NM_hzPy_|28&!BH538vqcX6kFLG9a!9e-*HS}Tb#hSEe9m1J^-#1 zBmwOF-~v{Gf%Wl?!MDC`E)@WEX!1vYm7jB{xfu+L)2F;s)@DY)@#x?17ypMJdobSY zaeh}0K-r(@5B#$BnqbVkJPT)Xa&9}Yu;22GpD~eJ3n_bGmxY2LN9%VoYPRt+Wz29)dx6Q$g z@89r8{@~?6G_w;q04YOl?W*7NCI^%@2jEN!4GzrTMK)n;@jd_bU+S8K*bG(#fwAE) zeX-@=>B)~g9H`bfv|qQhxadd!c9m(`m>QdaxHtf{QGeZj|IAX zva2HlYfzOohIjMMUwTnPLv8i|a$Ig`>VUZ8JgD^yjz9m}J!p1**|*o$2LLS&pKiMS*H`|Kk2A3n&s3msn|mm!6s2M)#f-R?J1;R!|11S*5zk53 zX9S60-Rxx3!Rc{)0=7kLjU393S~u~^KGh}4=rAzisLu2<;)rK(W9KJfuxbkwAm1!+ z{nWGMmX9odN#B8aot?vI#m)!R@?llyqCILhAX|qC6sE6k3E=Z!{dxZ}r4x7M zGoV!(nW=1yM9b6~R*jv$3>-C&@OIlrOx3>JEvyF}DfsLkqeZ<~YjlP8a9JCq{ZUY+ z9JU6u?GVRA3k*XRIJ5bf37hv}$?cM|z|&h*aNhg*5p?FQel=%mR|ua)}*17aP|qG-Ec`J$)6kV_T*g7+H!UcNxsA>ITEG<6MYs zQB=Nd5TLlS|2r~)24(#1Q#1$lXsHV78(rLCJ+9d-3!CoD&Kgp=fF;I9aSk#|O}JD^g?p%o^bTHgPL zweQLa9)kTXB{tJNBZV6=@3oU_eg_#}JEB*Q{+=M@nh#f!(Ll`W!OgfccD8lQ3v~uP z5;x$b^A=OWc(!Xigop`Q6zA&Cgr#fJ@tV4Lk=Kl~j@5S{J^SY-z)`SV2bC|HOqqon zsdk3uZ8tnN6z>*&7+x&3Q)M}K#%bYgi)MFJR|j#dQsPWyq&wm7ban_y>HgOR_9w9m zt&HwnRcIxXGhEVT0c*1aQ@F&gno9khMc0H|x}{}-2j`>Q2*sP36Xy%lxzkS|`qX&fZ^zgFw9cduE`+XF0N@fb0diujAInai{9G^O~VYYLm(y*ldZ zmY3(%BH++*x*Xa{o||pcV4QRbI<6otsa4Gyy*pc5ap$V+l+M6|g=1zmoPE2dm}I&Q z=m?liFXc*$mJO1^LY^9CJewKsi2=HEr@+E9b|HP;0LD%&N5oQKhiqlp?|XSopmK-^lu2RMpntup6;A(qf*g=THASbf*5x48HoD%{1_bAIV zStnWOw}^15B1|Ntnbbxl>G*Kr8U-%}-WK=BO;2UOdu|snVh5Hpw6!FqH+kj(!!9K%2Rs z&APXWHzxNLj|m5*vp!m7vD#zGIFY3{D*IlS`<&3fZ^4SkkPH5dPDtxe$HGRiPlSq5 zKIj6v9oa;Mk2_byI+{5trvLCCj)YUlU+q9OdcoRBRw2IGESTZ6E+@r^1T%_a z{e$(fmKE03IhLch$03b6)YjwNpydv2kGxiZ>lwmDG`0goP=PjUF=Mg(c;^#}9-!P% ztpeI$8};&m6Ejj0<@jhhSF5@MSMGRziP3_VVy@0f^e9C$-xEDHX3;g$+d(^tNLif| zlyVDcZPhvS4Nm?dBR)wy87XXHdO{urF^?Yq^wU=&`FJc(7%T}6H@X-kM% z2$R@A0!eh|n8qmnwr%R)UlPULwEo$;B!a??D}OEzjKFo2qvMTKl>AhEkh@+h@ypo? zosfN2HEycwBqR8-f&Tb>B*IHg{)+t?dxqtS#0ZB9Jgq_K433T@h1af{(t;rzORM6| zsj`U>G$t8gosICKY&nkrmEN6=!r}Z=(kA>n6`Jf`+@)gYa{?ezm|Vj7leiw;Buy2% zjgAFsrynkS*r;7t3^#Pl_v6}Y2j~KK9ahAY)3ufB5N54i1$)GQE9lgY%m}NLsiaI) zpuV+#;A{jgBIa*GMx%NUk%c#LpB`@ypxZD&d11smX0lj2QCRQL9i3mwHrX6suu+>F zD$lP*5OGA3me4=Ezv)R-J}!hNL*JUa5au>-hWJ5Zb4k^zVOf?XnIoke1OO?jOhP10 zAbMUJO|1!4^G9!R9czu&o50s;*N~h_uMdkyuu;h8-BbFfslqyDB z%hxc~z+D~GfOqCB6%Ow*(w!=UuF-olAc0;_0?<%HA(w5<+EkOqV{(|47D2W?u|fxM zV%|#~s`|$ek%>_#NNMTyOxhlG^ChY+Hva~97VOO_I$Re*$K62hs7<-&kneX#tm6Z# z-*^_jx&4^4pEj=n47!SS1OvwK&qWnQL4McWAt%E2$o3D1_OR?WQK7YMARLr;JO30+ zrC-$rOo2U}w)sil8lIGx&?)xuR$%rU=H!rrIw1Zz_20G|uPSTo5Q2|wb_uAZ1}TlO zEZ9GZHI%_=!~w1ix$d{#Rel*0;CMSuA*E!^y&Gev(L|uM7$U`BpH7pAm*a8F&(x@> z&C%4D2yE~`Xcw5t!E91y&cJj&)Cy&A4+(JV{5YNA-fk~z@)Z>w?eiCtQH}*_t6%2> zFw!5~zB1G_S(znU^UH%aYd>4{nKxW4fzo4x{9Ee61J#U*+3j6__wT<))|}TWzp7Zf zT>rHc=YUI<^m*ylUr|Psg5g(mgO5y&p}Jnmv}dYD>ab;*I&tbfGv1pkhC4t_T!9;p zY$_{1PXcmZm$U-Yig;hi&uD7I6(U%lF8Z)8@+IAjH)$S}hM5qn@@%dr1Z zJg1qw=t_vrgHIV%LF58cWbqv2D2n-Had%ePA`8Sd?WT||tz*GkiLOwUl2>4cztTOn zV;y{l0~d~;xfAlO|D`bwB2*LUFuqb9Fx6#E>c%Wi7|A#0HE1b7d^E2S`Ss3gxfnxg zHr{m`Dc(9us6b&?lH10&XvfS?_{#vWrMrE zh4=n?xD&bW%R<|>8+*%+jJg!+(y|K{ScdABv2H7t@o-$3IyROcpeOBOI+7FH5FIn4 zV|jC_44_QbpsC~;aik#4AHb)6PCHp9z38mjEHhEf|k}=~x$-EEMBah^eYHjK`;W8l6 zYZo~DKx}9BVL2ed6cxN|N3c%FgY(^d^xSQwU1X?cXTo3B5Hz(7Y=YisuGZad8uQAj z4{DG8PWeV68JiugUVIcgPTp^>Wm2I?WZ1V*sS?pN??$#x{LRSMB6FS}SsXbbyDH6V z$be8#>~GaEa9^T;pN+kcF6u#v5van=PZcj2>F5wQiBMy=L?r}QCA;PBXPh14o|XVp z(G?J}`}f>GSZE2HK*|i|bXbqO0`y*dP5jZ z(PlCU%>fFRf=R4Lnwt&z-LgPqXm2|8DfN(~BHk*|McIESLqKovxZVZwsUCi)sLKu! zf))gqd%RP&liR#5ZN}a1X7zZi>&j@*g}$Kno{NJKkqE zPf!b?+bY`Ck6K8&?nzs`XYW z*{dGP>cSsaGS>6!x?!m1Y^MV_UY{G49rGYg<`(9!tGNs4a>?g9n{!6&aK=krcC4K(FpV#O)F$bGb*57chPws$yZw7RCF<|ORi;AzD%^0G~Jg_l#C|<7fcpORiNMjPfWEy+)lDNZTLS!+HB+g{#|8_ z-(r$MT8na40pIJXV8v!ns)(*9~?o1!S`e%Kw1|0@doqee3ll`W%m+v~SWZ_=e`~I; zFmsX7MC26_O$Ar|a(bE7aSyY|DDDE{hF#w?kCY8Ch$HVjtx=+E`?~v6dx7kHnK{*n zb{{ZZd>I^{>dtL>3E@Shd7)+D^dptVK57p><-Wh)>(l*s%Ge5{V)DtcrUP&9O0{%z zJ*03}Em&rigLgJXU5*|txe;?ZOQ*@UlR5dgQ;d-x*MXf^@{8!59j;7p4t~x3afjGW z62TM2;2FHfXyp}-xvoQGnuxjV9kX=QMzhB}f8bQk`n>JcvqO|=#|#qM$fGRy5}~`k`-H}EGO({V3qH2ZmXizdEIjF@Fmi$|A|Qq z_$SkL?Ls75&xW8}Ed$sJ!imlHEeWrwrSTJpA>FT;VCI^_%!_5VY!T+xoM=%(RDq^r zz_VJ<@7zbzmOW|EQavU%XXG*`1F6PAyKph+*~0%jjGJ`Wb+3`_oK09*SRz1~(6%Rm z2Kmm+nCjZ-^&mets6(Kt%ZINB{;+0R|Dp!@+xY@ zCh6wbvTxMZvAK8Br;P#el2v%D?H9tTVo2Gkfji+YwpBr5cKI1A43``Ti1~sF*A(9=;z!!2o19~`Ms zWc&28mKA=+Zj0zJK5VU)h%$;FSW0Vv$wwV1jAB#{x=~6KUo=MGG&r(9+`miXV0{Nz zvQ2u!8~j%hWr!R>fijC-$bYX{q;uOn0{ef zB;sLZBf`vDL%kkVy*l@zvI~;GEW?(LgjEcBn)HbT#g~$YY|)K3A`aw$FX={<`7g+H zsdFmXaDQj*XZW?p7y}s`osof42h6J?il-3|hiLU{8YfLl!p4^dzGn#C-c<4|OK+ot z`1U@xM3(~LUuYUsBn*9sShW%g+RfE(AMfzFl;yJLqM>sQsB4-i;XM`R^TlZw{a?mN zIM2@3YgcA*NLzHV%m zc-@=|gR4#FJqz6eS+KF{rbt3`#L?Oa&-1MaBZtt9@{xvSJ2PX!wfA}ILFbO+v_4gatg?lk0MN@bqtDQTEWW99Lusj8;KNF2oI_C*)Vo0 z%3zQ4@Hfvm@)}6bm*os@NqFWmr+>R{V;YK_lQl7!MbJm?%T1rKJ{tD=o4CyYW=OrJ zFy|$9dgJ7OV4VzIyN_V7J{iv%`W^Tzlpf}>>emjQuNccz*TLkifjh@P(L1%MwKOrl za;fQvQ`Tm2yy|yudEN%9*_h{|W76g*P1Lzedap2zZWv)>|EUpQMqUi$A$CYx zzoU=2T{)mxV|n&5LP+cXOTxERxfuOVRx4M)01B)emIm_+@f^j;sZU4&N`(7YdGS&WF76)u_^Q)|mytfQaHzJJ^WH z`n~E!Bh!spzu^$#$?~*jflx8qv#Mkd1SK2pO?Se1eEM3)AL{Ox8_r*C-gLEi^n!!I z%m^QxFK1srTB;*>!g*(!cY9JY5d&g%xVU^rlyF&)Y0ahd7qxlzy1SuJ$)s4R)L{Z9 z!ek*4RS-rFd<6mMQaROv6Wd95->GVnftVs&uPg1d%+A*4&&!t#_q)DrH2ko|{{c`y zufL~{zNN50<>9dJnFp+izjoZ|mmB?l&7prdQ~l<+`z9JiEY zmWyonv-DAg!VSvP#;mM-`*HYLC1{lio3p}&FrngMW#@%OCYVw{EsOlXy&v%p?HiUA zgl2VC7Fu1I6?|{jxU)kQPA+wD`{^k8YLb^IH^6(qM+h^21&THx26Lg3y0T*@;IY)m z4#P%b$ajBHMd5$>bN9;wOq84A8g4;Dhd1|dNI=|5hDUQj#-i(R$P!#7S zS76o%Ye&6^;M6sm5^u?{F|~im913=jq>Tc%<#y@Kg7HpEqA@{lSLDn+2U9|{`s#{idM_xm$_obCwE3>xms|bA1*@L{=^wW z4a`J`!S3wY5Qu#}#$;|@>NG;(YZvNhi*RMkB2#ECqSPe3iA~d%IS-#oM>?uc1TX)* z$>ZnV``iD9xT4@X?Im~#N>30=Or^ywi$zj|%s;5%i|d8QjRzik$Eu=N*_hUZ;Odc8 zXdCzmE7JkcSn2^Ffrd;3djNLzz}y582OJRM@D$AFdxpP1l!3Uvr#YOuLTuj?+BGQ) zkH^hJMKU>;57u;J6FoP;tNK7ZSU6Lsp_A5ZiRsoOM7VN7wi-SYTaLmN;FFXU9t|9CGYw)_gaUGCk;yFKH;CY0j#t+GynpBkrxA zWP|aljLu1h<92tb-e3{a^>sfvs~Yf>RJQ4fh-~)-;P<(1DG{)wFbP@zInJc$loLIR zsqb{Mnr-RC;L*e9^IH9!JN1019+{jakHrY0!-ArKTj!5oK`|4W-(%s-sNLR9+YAt& z`m7op7E{&pT0lHPt0R}Sc8v=QF)2A4s03eQ)rdaB>vfm(EQsZ`p z9&Z>A$fzEiq{n3PVG4i6q#h(>=igkOwccWJV?p)v;8m92`aPicspqvaAo$a*PylPc$CV^ulW5Yb3&JFSzR0Esu``av|}9mk7IF? zi=xBQDBH7cir+qXX}$)w`IKzG7fGy4&Hjexa@eX9F66puE=tg%Y9~KP zSP;y>M8==_dPD5M`9E2SYAbaQOx^t9yWCNmuP^L#V*PWPM9nTk1Wt9QK?@~6yvdB1 z-S}&PM+Hbq*Et$u+RVa4<}-xl`}Y)1r$|g`3?%8OmYJToEroH}mZFz*wvYzRM6_@; ztr)#cu=CdhjQOy=)B=buB$oh}o*AMQ7m37i^h$V%BgJLnn7ii$QDh;==&|L@U&A^| z%qGOdnkbd^z8j9KH*5T*c3KPqY5LdpPoN|gCjX{br(ULpOCo6XPa90dF{KE z**{cf2B;yy&>ca-$s6|$q(#288iXurJCW#}WjC9X1}DbYZ!1JDq^nM>ML|myklKjG z7hPGK!3RgvRQudHhrCRo;$5fwU|8>1lzzenO(gr+1m*p3U|!$oaM-tl$b#GXTUkvL z-d$?wii;eS2`Q$4i<}epnCqa+j=q_Xi8lI^oL{t2%icF365*7UqUEPi(0oW)=fd9W zo>R|5E!n?XC0MR#b?Mu$WVd9hYzmPsb`GBQct?+@NbT(fH*o;g$uMnJ62 zQw9nGd;F?bZ3#%cxY2?kV+Zk|(pmGTP=p+sx`WFOA3e8G2`gsu8v(JUN(8;J?*Oo?<2NqWi9Mp|YJnf|Gx*)) z3+)GvL%v{<#&fR6>L|+}l(wD~0ssXt-6i^syU&zwR(D;bC)+k1lYGMxD6BmTo^%hr z(qwMMf^4Mtb!ZNGKbDRTdc+Pp_+a9;ueqNvZ2n1TQo&;`txU}~?;o#CrJ7(rSz~k@ zANEMu5lmr;D7+yhlN<;pc2Ldr3-men@ONVD&+6rE(7ShlHsoMkMuzQ3t*x zqnLM6iy%U6?}GJ`w5;N!OTnKSW_$^9!;OLl+Dw9ezQ*Q_v#H{O za-wYcDY;tD+P5_`WdgYcP4viz8v6(TT}}O!pID~GbDwMy=L9mu3CmKvsg4E4L2oo! zF+r2h24WfZ~AOkLOaXeD)Zx(G#;N(e7ZBL9miaOaMS8XrN%bkH1 zrY%aQQ`3gBW!#3w6=MVck`-HxZ1}rhBmd?OMO}RHg3gQDW>?xrEJzbA{L7x+^E(*S z&QKyem))M~4eSxQriQay#=vON>wc-GEN`c#7q@oEeodr>)ipKz6UbeBuztPutDRQq zir(o8HpL+WMKP1X_3}sJ565p96@@L*}#}dcY)O_*@E|>;I>?X zxL0u%C1{hqS1BKG1IiTj`;kmPs3jtYqF1pH1RqtFkMFLHQ+V|KvU($S`o+S9t6eylv_la0y*$(;WTo})I(RKIi zCj7+Ou*fE+2DILaahTylOcBVYbN5Z6$9>DP(-lrG`6p?*l_W4hEUlZ!v37qq~fNfTy(mI<#3jE0y-q6YEt@-<6`{pdD7EQ1Pm1BtQ`ul&)W}nNXN>b zHrQACzIci;RV(oDSN&zgYU6ir)!{Buj5|X{VQU*&niy2;8IOr0PP9J*`$$$}9vT+TE2cSU;HlsLD2ymS zsWi{T{edt28WuA>yuhN{F`)TZl7!WtlEtF@stE#riv~FMT} z=*pS5sJ|nORGDiTqYT-GnlaP*aPgEBI2x9;>UROduiEH}224Wzp&=qdrx^R2`jyvG zJTPs-<3wCBrir?zz3qRPl_2v@MLIVvM6J%YQR%!2tK?64wZ7JKfml_5rfeR7zQGQ9BXAxt?N=(ibUl}*mpCJ@)_ z+Fu}V@fu7fmhQE&+}T&OaF|yYIAX$>KdC{c707WpE_c225jfNo?fojwUcGU=eSHwt z!RBP+^d+TRYsX13`s?zo8&Hc^O)tSpHivJyuF^kFEy1RK{!MY|0tDH+eZAWqF-1y1 zGJjVQ4GxaBekzLQpnb(2PSzf;EiuV`z@ok==)(oHO5^HW5s&veOo>edAQjH1EID@I+W2n z%}X2PYco8ms@rA&@zw)#WH$IV zaqEFk5%2jE&_<+1db*>+7QBM0cq~<1I;$+^$Cs4sZ?9icqJz;KybmJIRv6p(GWptB zmeaL2>Pe9%GE-(>az~-jG3qV1bo)bw{dC@x{-!Baq2XL|1x&%m3ZkAafiQCw$oeBF zo0N^eD*|5P3wywxqFK3cZJlETsje15ABQmgcVH^_kYyv$A$qnm$BIlDNvnpo@m7P3l%t0AJj^8QIX}JCp?SRW0K|sc z1QMyV0>|8{7E^n_xRijuO~gbkG4Tg0AnEAvO%NS|j4uCc4=Y3PUKFIYp5r%?wC?@> z&;F42K?wRH8oaqzQ;s!i7TjU!>91?GaK7)d{SIClHseIp*y1*1_OvUw$4xC5+=Tx= z({Wok7Ugy)+in5mMi+hP)~-HXLKHne)huH8=^T+cJfqnsdkY~Pl~NR`lmY36TG;(1 zCr7wXSo@W!tNdKexyf-E<#oaM>6`&uv+Zr@(huTm}byB7}g(jV!7~L zK-QL5hKveYsFMeyR|!I)5CP|!^6%uKaP^(3lSSB^0@c$V*wAzvZj#I z*&;+KE5+Wm$xVsPy)0FOoI(E7W+A3vlG}{2y@wAsYu0?=?qUep7E$B9+ZhD)Z%hU& zzb`_#&7l-h1S4lfl^Zo-adzRgCkDSZ!U}I(+i@J4LF)ICl@3N8!7$1A)x5vxD@V+y z%Y7f~z*|=1vkkh!oQTiMD?`7fd7*@-F)>;`zn!O;luEj8{cQ)d3rhjH*l$+xiGeac zO17{U&c=P`YkJ1(y!1W%J81HL@_p=){jL#Bu0w4fIV$EFbmO86Qo#v_khRZ-8HRB# z)jF4@N;EN#owZPGEc|S_oB*55LaKDYaoi$^B^9Mf;PzfaBzTFf3-Ng9s>?x^iUf+AV~za`L$=GT|g&u*i3hV3<&FkC1M8p|U2?N4zFyEfo#3nyXT zH3Be6t+acLhG1^Y5+*C(Iq?T-coA?g@MvIXKj3nEfkW`HH;oxzzoOYP+ms=c*}Pk1 zv$+IGDgUP<&xHGLwpHj+^rZW&414_By2T0!x@AOx zAbEFcxE`xlM4|<>{{0WCH{9j6^92}A2E#t3X?4k=hmiMlKyD8e+s|JYUN7Ll591L@vqTAIFjIU?L0-Ffenty@=r{1o)i%k9)s^uX?Yp42)ynpbbl+!N43@UX+chTZ+6UUtF>71SfrUvJ3~ zDg2aA+A@O?q}}W$4NISg=TEG-)nJyO-HK3!H%uv4$aMN;Obq?AsXb>IpZ7v@Ob8r0d2x>rhn>PC9xKv;wtMxVwRb%GHEoKtpSYNe zYuspl7S1-31NJzKq1r8oQ6}+&6`uWj6GhniZGW1&lLiOJIVRQ=l{Z^eLfW`6mso&L z7+ywgP3CP7kgED{d%w7jE(*a}AX|&iOg`mmONip0!r#R?(=A|p?@Kj3^ z5%g<-P=PH}c{HVxTH;-L=(Gu$EXE9NU_V&tZ^u;|-t$Jj&-52fUFIb35~Ygg#vd^? z3cs{!XNcPgPE1-l{5o36#_$0bTHC8Z5c;+&_l$4MWlhkfU4l8N-y}yCT%8cX)|>Vc zihsK6M{pF!>Jjf1FyC;Cm3J=O=23bx%JoLitiv9A$eJdfeyt}Q4T>u`t%Hf)RoQ94 zr(>FD%MV{+8rwS8nlIE-I(dOI#z95vs9h1{t*ybIPc$?qYy1m_ zSaIH4c<&3ncng*=J3LnC{j1I0i&>BMM>5vh1&TrC!nNoziAL;L;8e!WiF`310)5rnDL z$CrGk+%|*BF;(tyY$RRI3ard*rJmKxvQ-@C-{0EXLVn~;(e6n?dzX!-w;bkcP6~=+ z!^k>M1Md*sKUMJuk{*4HtECQfhwYn4Yqo{sW%hmn!5{ClMCLl;B)YgE1d+)y!>vRv z@I;DE|8}Y5Svz5ihoRq(B5JgyV@?Tk#X%jWDAa-Lu`9L_DobmuXvyY)GjFNY zqmB_`fRpFMo#;(QS!YB}N!n0PHI!FX`DAPu@8G+;Y8tA+QHkYMJgbgjHS02$pCIy( zvZ&)xs!VjQv6O<3NcrtUk&tBOB&&ZT305E$Yf3dxG}$U~OYo9F_Jhcx_)cr6E^L1 z$)B&_G=YDHoQDNMn@d~mK0~M9i%K#VNnurb(%8Bv4r|hrtAp$DgP^sq*^Hsk1X;P0 z(s}WP&O7qbL)F3M`=`@io2s%a^U2T_rFj3Z^A?fr0s>1pFqO00@QYYco33-*i5r)k z4GJ<6FB2(DkROz$MIxi^r$L}Rup6{pJ$qxbv#E%s{huU^kt%83kUU$F4G>!{yRnVN z*5?toH}fs*>ePVNNzY$l5|Ww>Jf>`qKy8!p1Ib zdg0mdas{2PisAFhHVsRvOAKQ`7YK?$k+-G-o-9EYTfx3e&+tsPFh!1bE8x z!im32G#`0DUYoK=O~xG=?A4Q>4H70NeMJPq_J2ZWLeD;{u2%r>R(Ju*3`a+>Hu#jo%v~-3p4O}^dZc$KJ>qn4KQPVr% z*bOEL!yxi}khB1AQED-Zs`(`uh6>Kum3;5U9)1NdCA{&v^xzsNm~M&ns&j4fntRm; zKn`Q-Rq2|-7c#S1jyM`H*Fcc9wobedd6u(RYcDi-&{RgKNl4EpZZ32LZw1S4)6tU; z4Bt>mf|`z*FC-EL$}wGt3_i=|T0n`zJ~rlQ>@WsR=j&by(<28IHwWG^>Q|ENNN;b4 zvMqb_V-XR!^Y3J8l$ZbrwtR4;3+*&UR-!VW<((MJ?b207o~fA}jBYgBi6H`08catZ z{diNohzht)pN(!j^VRLxzFGf-V1o5k`z3GPg&r=HPOABA&A?ID0mhrcy|l7|uydO( ztKce!vA{{a(~#CzJ|(l0E=`|7Hdo_RDyANbWo80~B&Lb5@=AzS)!;_2I%Zu6@UYwZ zthw6P7>b?c{wNt8`5m&#B!U~j7n!<&u6a^tR<#X(DMO$&f{u~0SUjO*Tg){SA9+kOGIocK-5yS(B3xOC+k3v6 z&mE{1u6~{9di-%hQ*Z@S{louD=aTtWy%lg2Z5Y^+A_H|w2E1FN6sM@ z8_avD;xdrSm zZmxl%x9HU%wk}q4gg_r*a70P+RoyV@0w+%oS^Zciy(hsOo;9j;y41e66a}$!sQlW? zK7m>GER#D7$k*^`V~@PCgFC)-?iXy;)2@;-hdVK3&W=m`a}^la0LzLELyQ=^kh$%4 z7%ReD8hUYL7$Xzr7c^>pWUr+?lS|v#-K(4koJA#lvM}a9I~-7muCuIOsAqq@y_g`% z#JI+^=ya|a#GQ?vcjNQ&93CKWdAVr4oxl^XE9q?GH{G?QG{l7f`LN)w?lI)X&%~7% z%AWdxPW3ebhq@-=*2^up)_f2lYT*$@zwY%_Y!={#jPe(KL(SZU?`izs#p$kpG^7&qCbQD*$utDD8Xg=5MQvi`OcShfbsjJSYt z$mpOX9hL^U#l4Zevw@PI8s3jr+Z6{BR0efLgIUr`E@1r6-`owz{hFJLUO#oMMX1mYKyQ9ZG))87(m4l(qI;#GJSR4Y@ys_}(V=xt zTVri3*?@iyxuQIoj}(2d72&@RXbRFs;y3kS6^3HOp)k);>P=knqCNNCL#?}-VhnC^2=$z80b-5rXdwhSrhL|d&5+je+M^gb;rr5ALNJ%YgNC#W0YPG1|8 z><|3FXBrXY1i_obJcySP)Iw`~!is+CqX><;6z$G4l4~Ivo{}=7(ezACJEzOwm>C+n z((K45vg*GZ5>sX@&674euu)b6+SJ_j%!oZ&(8VehKj9}$1qTqJvcUn(gD1INzyU99 zN>eDol|47s+Y$Ze-xXTiBMZwHBQM!IlpvF8tVYfzo9RCQ_d%}SU|#l0G;{iG(DMCG zgpvWh%Z3?ka3oi8h@~NQA-^@|wRx2l&-Hx`Csp6dY0^7}4_ggK>cYD*2i}rr1}fDW zDSxuEJtUhg6T?Rerv;@8=0Jqd$kJxdiD#uIV+vjLxGzue{#Bhf+8|)(Z`v5|f(bYl zYZN^ybaXeOklu~0Sz=?Q=0`?DO`5xquPIN1zl}q;%4)1;f&Wf0SmP$|0_kaN-Z_jfHS%+r&qc$!=gP9<> z(;(tZ5ktB12(LTqD3{{#k=_O7sb%e=LD5a%@?6*&SJ7-q@tiyUXy)P14R6eaf(|iD zJl=oJZ;f}KY3v{U;3t24*%vDCbzOqiJ!Q`LpyN0p!x@ZWeKmVMbuY>Jw1RhPyttup z??G{?8vE+^#|?pY*MD5e3iy*CE<*#BA&QB!=REfG;Z;Vv zzE$b_hqySCdIBSdGgNrM>gy)LcH)p)1@Ccs77GK2MxU_^B%dhCJa@uKQ}o81bE_VKm7T6PLH&6(W1!d^rPq~O2~nk0A5 zIXGP!VDJ#Z@pGQ>8JWtnar9We;sfENp4c4~Q>iq4D|6Za}CWrTb?Qseha?!iKlS6wfK`{RCaW z?xiL%&o;Q11M{ks*TVWMjX*u3&P;>D*1#AqVpfPk4~8uyjzt1_LmtVdAqLqLAyO2H@SwQdZ(p^s8E%S?KKzAD_ZbB+90+gX#nT@5iF`rFr zBJ{Vk0r~8Y(eHxUCyVIlcWttRQmrsElf;iXP77HAx+h6-FDsIn(4cTK-HHf2 zB~D;W*SWEo9ft>~&V&}sRt%(z#9jop)cSm*r-<@mz$SzX1C*Ac$y#Bl!XTvYt$BuE zT?472*idGW-_GEMeHOpyJGF+Pl1$h0;!K-W6Ri&MY47@l|vqak(w$3=k%dQEr=KpH1yp>wbndBE8 zy_Bp-q&&((|@d1XMH4ESf!Wr9Pmu?D)m77n5AiCZ#8vWRTSCOYVt7Q5?(8nG9!-96lTONW>SXs3zRY*Z7fq%S^kZ9E#!?)LXk0MtS$X>vPkG z+X|;Q5Obz%m_LHOvIr8(z?qCJ=+=11{1=*T3t`(o=JZu|R8H^{*{JL4~6< zT*IFR-sQc*-TZuX3(Ujx@+SJrFD!hhA_DagQ`_&Q4Ja$@4IY(!E+F(vJ>s69H5$sE z%{SSWbi@PDBUD5{rDr!`>@L;Om91hp$BUgmT`%t7T8qXwh12A39dnyiq;O0 zk0`21B-8H!TS@cuT-{wRK_t)nnGbsqN;IDr(o*XD|8VgRT)F^Df^FHhzOrrGwr!oV zZCj^o+qP}nHcnZud#2~Db^9k|uFQ;xj2-(uIa}jMiH7Ot-_ADvQLv{_|Asc6L3Ogz z#6$M0rosm3&0z$rul4pOnzx#p-P?`>=VJAw|I6e4+{;eCFlq2kJvJnVOMgWsg=IAS zn7$`$4A1jN;pm#?Mm(XtGa~81ia7QgUempRr|^Nij?xp__j}(_LL^* z>n(btWcft2Eq8U!qkLeIs|NY%4L&^@IDUStU;oHNIEA_{0mG=qqf-=}EZR%iNqbjS zjA7&~gxQK2$|||aN$&A8xnYXhyOlfD9d$3DRbvI0U3aC~fhKR7;a`Tgcy569B1rirZim4_WB?Me|qax|zz4B@fKQ^)WnkLV(* zxY#j8LWRUSx5>T$@YkaJ3sQCE|J8PW%Qqf@*v;t$&0hZg2#!H&TOWWmfT<7@ugdK^ zJlW<`NGFHGRVB2;=$atrFWG2AT8JNlwn1#X+D4fEZl@k*zbS>6dbYbizACg@wn%Ii zsmXj~*nc^5$edAz&61Vahr->?iTy4-4hZS6EUt72==?&hH2 zXe05JFff%XZ28PZ+}E?QP^^qSAT6Nmeyo&4jz)^l!HKKooYIusZ9n zGxCR(_Kz(}D)uEjygwH?`b>h2D+v4~*X9gkULla0nqT=|1aCrJHLImnyT-?fy9GJ< zn5EPbJ7d2*z5+RI1h=y)2dkEO272SSda00JFpEr3c5u@K|Aj%Aedvvwv)=wj%SvUQ z@Bj;3pr|~z(!)1;dlG9!8n~Qlr!+_2U^-7 z`NmvhH5g*Y5K}ptKLnFwqat-#&z5cN;l#P>^UTvpV29u3cl)K-kfbO0?RRvLI9 zX-l1Qt(&vUxG7Kh?@q8dK41 zd}lyl&2AQkwrs@n((Z?gTyjLLNvy#q*S#-X&mnx2&iuau<+?>{_O7e}Jx8&Nf*mk= z_gQ0!h&QAZCOnl`h~=NY4)buCHI{_!m1afaK@>8wd=%Rq8PrrQTD&3e8q`>3mOq%7 zvgC)+6idD(jpPx6@-5PcttqSzG`C3*G7< z@pG#%Rqnul2e=P<)69wBz@wqYKGoQ zYi*Z8fUKf(TfhU6+qbdmzeczXb2vL&QgSIH#Ur!k{k8UIZ zix=el4u{iefJ1y=6}5Q4l;=3IE4W|olYuPNN4TGB5|(|+x`xAI--}LHCK<`0J!`ir zQnJAyze&Au|5l4!Y00ZIYka7&tnwXzm_;xFV5~Lijj0E189Nqf&d|UZ@>#EhT<*2u z7D=Hu&41z?lvCZid8#KE6k}(0>9TjH`o72DVs`R$=L_FV$yl|x>>$BNM1CEy)$@0o(}F@sG>ta~XyYZ}Tm>TDYWMj7Z#|E+eQ};&DOVlq?4_ zpN$)6)XJ<>BIq_zB}*@ar9~Q19NvM!?Ah5-BkZCbFK|N$_)Te|=5>As3+a<~g) zS4?TAC>OyB#Fdx4v-HgTYXmoG^r8d&?*gMnWNz@dxH@xCOicRc&rVG^{wTTU&Ha0UpF~fP(No?jPQXOJr(&@y zL?5t@tjSTlzdEN|?0m5GFy-$s`qXgQa-a0Z`NV}f@0xBCB$)=qJl23BJfga{-A@-}`iGBDs0&!$!Ire>?wuZE z7+YN-2!7il(3WF=D?IA3jQs`VjmuddCg`#&vZ36}NG27bVc-#?KyPiVk87$W6q(el zP|1v*riz<7ee*#WQ3)vx{*lw_Mv8R|4BvMbE+SojM_s&Sb2Pm(#9hidAO{$6jSJVt zK*P+g_d9u9^cj&$=619-)MNn*W3eK?=t?|kTm?^@KuI0b$0cM zMhJN1sz;4XpXr#2g7sTDq=35Ll6ora`n_WM?J;_1;1&5mJ$F}9W&tyiD6k+e^N3_Q6e8ZVGjJr*jW2h-Q*;_ z9DBDvsya(sF;WHJ8Lza;KyuN6QwGsk0;X>x{)?HvL`iT4h`lXN&(PINs1%u6 z6J39f&y((Y>Of86Zj2=Ex(t520l8nu*0_oKy*j8cD2`mi(#hr4^}sisRD2YK^e)&Bi;UoAen~J zg|0~x8F6s>*Cy&!3DO0Se}&@^GLiP7?>UzkI+Qcs@b9u=feEg+qzNA|aCWeET5>Se z*sO=-Tr9{hu*p>pvlz2{kX^v%=4#Eag_%|OuMj@7wf_eFnp35}mOEiIljU}~Ts(lS zf$rpyOzOw-@|7iss%PBby1U1`F=uwRgs;NCEtCpLTS1JOOuI2nTko-htAS%OfY3K0 z%uA$V`Fr<#w7{Auam}Qz|8KW_?2cuDKyz$g=mJ8*PbJ~7gz*nHv#Q_L2cGetb=&IAYba3K|}+hMA<`20_=Vcxckp4DdEnF(o0!3(F6EdlHXV80)wrfOH-_j zObLbZ8hdiHhhD#)r>}Z^pR(l4i&&p)kujHR@_8HUdKuFsm4ey!6aL~YR|?tykj8KS z^f^rX9lCFlqFRU{PbbxW;nwkHO^nI!cT0xKy-40s!&!}Eqh?WRdfr8QW^x43nR5qr z)}2#afdoe&^-|aHLLO_og~?cinf|uDe4-$p{JAZPZBbA4N>6qo8TCz@?swEPJYQ=; zW<3mb#fZGwjGr7pcdb(PqiU2B{$VlH=M5I?u8o5vp?vpocyR+cN|tHgRBA_x(;6V4 zzh2arb{I}8`&4nF@N$-QXmWcYl`b%gr|5GiE*t`_jQU*Xl=Yj8ko3(0H{cV2R}qL!1) zt5zHan?$Q+fRsRGN(GX7Ax(Dh`yzn5@9Ur6In#0ESybhY*JQ0~M(z;XIS%q`Jzy+9 z*(M=NzIlg_k!QEWAkLsgwCCrH0O$mWA;tqS|Ll9LOcWmVxDUa_fY`cF&|N6(Lf(14 zeTF*({U6WCihT{=9l4Q09_ZfJFov>c`LpiUxX|K;yLveRK8_=ksviuoDH_^e63I=k z=SZ|?5!43+N4xT_ip=W$7ShQ-?8vV@s`f7s9Z0G{?iSq#KDQUYC36}73yq#2F+&-c zz${{dpKX7~>)`Ff)6kroSmC&!({|j{E2T|1%1s1+Tcm2>C-?zKR4d=dHepRD5F^WM z$e3-1S;3;-fvicZpfc6NL+BvM_Jg7NIQ)Sdb-5+U-bltvV)DUt-k=d0(*(!o?07=l z-L7b1Hk=-T+tD9*7@<3^J7?J8-nm9G7(Z5jjT8NMzg=q9QF{gXTZly0$`qf15<+EX z5iAlB6aCfPDGBQ%D`;iI`R&|n4s7033*oj2l-i`^QFIANJZrs3R8DhHJc}WrJ_{uA z$RFbM#0AGt2j^zVeb1)|pH=YHP%xtbW-zPmC1HJc$esBRKT~_>)Dnb4s%Y?Gu@R3CO zW59IL?U`aRFc$>rH8G4PZkREBUOd%h`E7vLWypHzuyUn?aP}|tOQ>ZQ`(kVDE_d^J z#vk7K6daSeO=Hc|O~J^PF?=j6u2+CNBJwa8dzX#Q3NLxHbg-12tpNDX~rRLQcI3^Uu=Hj9(GgN#9S2`UM6pB5MKh zuHWNPf~U<lEpkT(8qcM5fwaG`DXIk2=o_a;9>`R44Kw|D zw|R8Pl=}w#x<%2TXOW18j{hl7;-+rm$RKKk(k!kyIv(sgOf|m7AYJj9tIGQ<->@1i zDF$}U7A`G(6ie6@Dll3qR_pAHg`IA%4RzuOB^E!yT^g)=`(d_V7(6O(F!VCNewp-c z)oFFb<%Z4U0It$l#i<=MPo)X->nwnr^$+xy$$8xxUJijc+%1{-s1-&90uzok_d##- zN-qQ1GIZjChXt^U{P30_d!FlM>}Lhf?sTYIdF$&U_1~n0a>+Wu$o=ugB(PFHb(-+s zC=iu?7Pp=hYk*I!qN_lx<;3$dc<>HboV%!^HnfI*Q^YDU&xiiNnJ;riH5IO07x5;rG#dzWh6wqltf4r~*I0e93R+aZ>y~ScZ7g<{wu&7-NjUR#XExH7lv_Z>$&XlKb|mgrh5p1{q03A%Aq{@!(w>% zVwzlHEy{ci_4$0Bh%zbp(m@H*pLnZOaB)O8uP=-K0Oe1#-dYj>FjrOzNy6>KNWx09 ze``tB>(EwdqFB#;im4UjrPHYUn_bfIHZE|jL#VWt`|=FvLV5T)r=U%{Knv)vC}3FX3% z*^xS>u+SkJ(A3R5WEQbJ7U|*cGL%g1hYQ>2gn3M38?;3o3Eqwy{uyNqX0RU^lSwgUtBH+Ik)8DlkV~n1hO392-?`2JKD;ruvXdFr&1c2 z9(7#cZM+zlM^=kMZBj*H5)&(E+Uku2C0_Z`Vbkn;i|VPLY^L+pmQ4$A)!S?ZnfeZM zJ~5&3=^tugTxIO_Uu0&oUSuvVu~dG*qA4_dmVXa}F|m&!lC+m8gA?VLy}qTk`kB0b z*9{5^<|2c%ESH{c0ugs(ueZVE9j#&2fU8foO&a ze`v)gy)3@VDs~s$UEOwf<*NO$hg_vf$xjGn%GhB&=nY@K%kMFpn|&^>-eGy^+!RHG zIR>7(Qp2bEVX(#Z6c*NgS67~k=5xpVkqd`SSM_^&Eb$y1b4n~xO%C`ery>heJ_2_? za8(M7!mXsP!$(Gw5d>3E@^7_cVdSOPhm2tDP%8c;R~3!GTcV~vsmJa9tTb_(6(6?` zr=A;@5*|WM{)dXvXwlrYVaZs4FJBf*N^1yk6#o?EnLBntL5h|Y8Q8Sq{Ctb)QBNcE zuhlymYeQICq!VXH>lL@)fdYb>_f}T+rs!b*fh1)cKXO6rGtdT=f{GgIy1)DP{NGhH zBh5sZ)LxuD!9)WaNcsr#6mV-ZyQCFH;}cmR=CWP6ebKbt7@(NnNN{~9+XEwd?nrCD z5W53xbr(hmWeVbj_Sd^t4R^&i|W_NMDcB&Id| zX&n)FT4U^lpBjRMu9U;^=E`07GN4b|k6}EBKWuScT;YV~c!AEgH(ul>;ebI|^Rr4KFz@ z-Zd#_3}iKm4W+);EXj16Coz8v0^BmA`Mbn2j=EWZsd)V2QmH>%F0{!>dH^Gz@`_+> zV|B=00QXWDBcw(e^J(C&oK6TctD;2+xKm!K3{yR*Q$poO8BhGAZ9^f4GrXq4J4YWU6Nj{yX2_v%_?XhI4j~nN|xT^H>#n| z94Y`U#$c*V+!ZEO4?s>@An-3eOIKGN2u7%9`E1q950hJF!vH<2jtsQO0@WT4No{2~ zhfX*(ZF;3&J_hc7TeJQTJp1wvGc5m3{LE`7qEeZmQa~J-R2}c_z zs^^DopO3OggNr6K`7!VC5GX%0ywbXNSbvJ_F}$z}HVvdCJ@7PS_~0Th=lstY#m|qu zY`1n*C?K0bgE9!^-z0_ERo)j^RV8HASkQmayS;RBRRH++V(j!E8m%cs)Bo51<}LnP7}lGM5*a zRw(7C)LAe97986LyHKfFHtZD?=vT27`Fhni76Y~6l5&##kS}S~kzN56z#OK?4*za9 z)m*odw-WTUaMT!%+q&lFW$*xX%|dJI=&CqX&y8zQy2t+O3ZqLel#dTaHE*Npj?Tt8_?G3-ZY|k7(Z1+Gd-C*%4;USsddxo{8vom;Vkg{A2%G zTie3!!AVy=n8=hf9?DoxCl1&7qQwe=UZM+;^d?@m2_!PxGy0ZLH7;k+bUcdcly@XKS~vj)eZSc89+R}QLl|wc}rNNfYK`{?Cn5h~0&xe{C zw=5MyC-(5bdJ7@dGH!q=M3nVi@wR2C)c0D%-KA@t(-42SQLifz%_Y(bLcBl^Fca_6 z%564&H+O1({i8|s8FjR==UJ^S**OyHfWxGp(exA&BL$i`C&AmbIHY{SJ(psXngJIZ z7B8kN1K^x0#23ShbSQD~G`@oCgI96RAb&!#UB|Z&p-bR;i@cQSEkN2nR2?PT>kl-! z?Y-p2y1D*JDP`;6ykH**`t}~DTmbl zO0(D<3jCLWGUHDWAJ$26^g({I#Ia->Xu*bU*LOL!GiSQg6M$|epZU(Nfydts{9~6U zL#IQb`a8$Q0lg9PchWvG>q*tBV&}Oqv-)jTa+8FVBZKYKZn4kxFZcMOhgXmB?wM5vmV$c$_K>|`{$XiZAT z{%FWvU3^KR7c3;g!Hm7`GC>$xwNUH2hksK}G)#ySs#_c$Bz=W)1MK841%Y-SJP;rm zAGFd=%|X{gU2^=@JGp`kq-PNZQz#pok#Q*)n#b?#?aE%6g{t1&YuJw;WbQh*_+($b zU4rOQ9vKU6psw(P|GtJJIy6cMpjY;}?xw(#2)iRq4Wl059@-0#N#gN9Q^~cZ^6jA& zXE3eNRgQ>9!Uiq z+`m!H&80n*e!7_OO0~+x7=-v@v@`3Xc=%g)wmbl%%h;x15V@vmo%ghGz4KKJtr3dA z#QiJta_h@MHRf1==WdeN{vpbFL4AAQoeHNyOhsR}@J)+%0^C0R06Y=dgr3}mHE1TX zx)5lKWK%ocFuB+;>sa;H-b)x9J6AWeuF`*u?r0YZco>Q-5Ojs~1y#m6x>UR(v_^aL z`P`un{<*7!V~7XVZT3pqZ^Qckn;f0K8;d%kg0Ww|x{u5ZRbU?2YvGBcmH{NqEloTZ z)SyT^M$}aLKgIXJI!G~97;gRi4J~nUqO8{|xE!j=r$2g{5t%|@-Bd+ZGjs+CbCQl^ znt|rsVzualmaa;;U?=3UgAZ*iuRuUEW%2;sBv_$3;?F3o633<|&oQ_-Q3IU> zj!DUSG5w$D+T4ou-t5axTAV7I23@*)MfbnKldNV~V=Ryuy9aN?7$}3nWMInqV4Qye z+ZYWnze8TSKleHK8AwGu(`cjCaI)lBe*Ixp1>Ox7)9Y!iQYjkf$YK{qC2HAL$KoRw z7ZNJ+F)vnTpQT|^D5Ja5rew=w)eN{P|NCADv-q$8(MNTr)aV~~k*_4@YDR+Afw1sP zH25yEg11;4q>+Ta;gsNHv(Z-~@+MP68&QWm8nY=R1iFG!hSHdsLhx;s9sG0r2RhH# z0gneFW@)x~J2jnT)PLOP+pjw+@k@He6TiR1=BwtA0hRzo>od%e@z@Sjl$9#MUDIcO?p&sn7rMf{h>` za1z(7eyX5}(29k+z|1P`(FR?8lA>i*P3D4hu^F(fCMUhP4)suq)`PudGj z*{Si|(1nOIoeVlB^#k@Lz(d6!Uv>gNj8eKZEJCG5K(zE*zScf<5mmTyVo{ZI?mx-G zYoQ_q4)~Z%lx(VvZ!YQa;bar>-UYpjv@{@Ge z#&Esk*90D0SRYrVKHRpo%kojKlgo3fA#Cj9s?g|O9+P`I&*y-=xcglF)b5azU-%Zu zF%yfjkUE&p{*_Ml*0!~;n0DRRQnoca6ouS z6$z#?YQ<(8kti>aNa5C5}pjoZZHr35N*L{CRA`q59O-?{EUWUJBqQg2h z%#^bxJnV!({#2YXltV!EY^4Cw&>EO}M$zPo)M9;O4LX*8T%W6#EGngg%fv;*AM9x8 z`rog=&qhdpqmTu*_|_AkI5o5;Jif|d{D4qdkp&jZ-=<5-_)zVKaGd*ET>9%5u*fsomwg_)o$1(9pA5l2cdilPp?yQ`7d51PEoJ?y}$9E4zs$TH@tLu z=_fOWK2d;)yG|OnW)q_Pu>#rGZfE=qdl5XQ2-2T)yn(DevQna>2?-SO^9X4V_Dn&u zoN~fJ@-%=a;2~B-9ecBXrZ$|OHjo<$UD#`dgTcD|K<9;a5#LOvsySFY&ZR4#4PP=_ zNcle*4OBy@ZP4o45l%@w1_64T=I@lJ`Oibc0}@{jV3H(oO5O`ZQ{u=S?X4~+@Jec- zqFHGH4t*chj+#$mXj0@5UOJd=V`0$l4E8Ui2PPX9&1wI!*7|X6H?Mi4%yz9XGdm|< zo2mJt@2I?pI`IKE(L}nxauspm^EC-sFS_*!9w7;OyBC(TC8FbpMd|NZ-I{?=M*Xk5 z^#CzM$fy2hp567_Gfe!{}9s%0re`7v2rv3M;H@WA)f&prThKcHo@MS5NMRFX= z2ik!x#v`6e|3hc3Yx{$IbK_N*uyV>eb_n|K!XsQL&_f$eIGmY%VTJ~kmBspX4icuF z_8KrLo*Y#kRnknwSNb0><@+a^qF)R?l zRl;^i*-D@gMhD7p{6`w@Lpfis0Sl^ihY=R%V+zrkUd``U{9_u|FS^h_xKgz*k>s;t z9t|WTOpv|ToYUFBsj#Ieac`MQ!CaR$O;D|SkmA#Ga(4wB*t!w17}P6QJ49FBoCv|eg&dk zIdS;bW1bKTa-tS`<8<2RDyNNkXN)A+4^}qbq&Ji4RxJLIl^6%$uXK1JnfPty z)a9YmGh@y_YVlc`oQ2c#X*tuE)G!P4O5Y0W-?k|=9Sof$vonka=4dAGx*zj0pZp$;v=%kswx0ec5<}>{>bC7TcG}el zSc=Db^0RN09kBUTq| z{LSuHyh^sOp#6+hKMdqnx?-HyMe`#ztqSk7%T1vw8TTbIinvB-Cv{){?QkfF9?B(e zk2O$qHuIR0SOEupM;OUmUX*yN(9Id}r4S5>;Q{HFmp}7CwbAhrsIO0*xrv}_l1B<9 zde?=Djj?i!)P-;XnRYMcdYPPypGNltJ$xA z!kf_oMTcQMq}W&9e@wM;t5VZ;0BOq%Bi<-7+e(+k#Fc)|FDk+zLR?GDSlMvfqa#E> ztn>A!ZtE*sH>%7{%sA_T&{->9{$Sz@)$~Q(6Id^;z)pT>?m_kdunqh|i67f7{*OCO zgWMsO3Sz^GYgO%J5igL6&TmrMmC}s=VnK8aqUYka;@Rrq5Gv-Pa&`OA{6dK1YbPRt zZst*+zTBNpj`<&(U>rhcC-e%3mrlaGMRVl5ulG}DMmYJ`M^JV6Zm*`DB;oAo zo|!WN?%7VCrF4BJ2*#!wJyVpx2{47-eR6jk-sL{WKi z+SRr*YG8~bj?AivTkKI&?D=1X-pZz_Qm!wCo`tpTa zTc*+hh4tCNQVvE0Za*7KY_CQH?MpRZhMkeUt0AN>!UtZ`~0(@*o&_jSobyTz^`F7(wIUdgUJg&DK?_min2UG0=l{Q1dme@-m$ zpNoK7p9b&rrXI7jw6+{X;Jp=A)gMS&6nLH}ZXO%!{f$WA6gh$Hy=-6^KL%V&=obAV zL%XlmwtK~B@*WvTohTyOF-m$syU!(NuS2yBLCt}!1)+^ECassKOE@TMCdLSokm6im zbgiY_bpm^XpTbpizr0$hnD*p4(+6)k7rGwzf90WiG>|e!RjQg*Ns0!X%^`F_pJ4s; z8eN##t(h7k8S^~1Tzt-U!>ntl`izgKit-plVRL-dWY`f@@AJ*hEKk;1)2Qu&s-`mH z$^`P2)Gg=nnA}UdCYn9n_&{?^kgED&tKqzE<77s&{@#&I<-Z7b&*8-jAh)&HD(|;aI-_L3t3^$z%m***-ppLjPVG# zc?qaNBN|~xD+x%;Qo^kOy<77^N~KDiR8j>*avxmAF9Q>EYBVoHHC}?^dgNOUcVikj zI7$EDs$TEB8FIgkW|s8IOLLJ-+7MC6FxF)4n9aW}M%I9onY?o5T_mn@hskAP}O=lOoPSYLdKldKfLP^2gko&kZGQH)?_C= zE1gicwe_><`{S~0b&6Mb^rTMC#C)HWy>6@Z=f6AM6n4ZFJK&JG>oeKrP`t!bp`9&X z+LKFxWCYYlPh-r#;r!Wp^1ho{`8@d)>j?zg_8ohbh9{boD{obN1=41nOn~!!F<&}7 zvgMK@ds@}O=i|Or0?0CqQ843#r+T0!mzhF3Z{6qb$3poiTwz`eXmW#CFTkZX_}g%9soZy@yiik5-;?` zE(r|5^D?X2hGGzjZZa4az~;Y!c3mHhzH(zK&8sNJnrcVgdxv5l(H_ee8{-QtyHeT| z>NuDo(%lv#n4-uAL``e`$ETAr36UQU0!%e1)B8v95aB?Fz_vLbX??$%pDfAl%EBz_ z1l=|d<7b{|2h=Eidx&U#`TC?IWc!Iym3f^gKDldUhzq$KoG>GG+v|y!$Qf@(tEOSI zP*=#}qj;D>5y0)l0JSQxW7oxuNr929CxTR<1wJR?3my%L=;A!ng@0srWz39m&=_}) z{F&WuQru^>PP5SxJqMB4XEMbBfoQ}6pD<=k(5RkwTz8S9PEXj6A!?e4?aNHY>6ie# z%KRgr9j=lZUh+i*1E)mgQ;QunXzo1O+J>w6)s8qfb}ha9{0&EV)A|Hgm2nMaN0u9V zZhx&`#N(L|E{99WQ`%8rc8Ec?6GMaf z@4q$Ua4^e6elrflKBQd&0OsURl8IV_4D6qWpRjyjMISI_ToHs+oVWIo06dQZo3Iz} zBuZKxNM=NONErPnPD6n4GO0~Iy0!)rSmUwd^G{%Qw8DigSnI_S;J1_7fsqLh?Cms+ zHlpb@_x_a8kytVGwXKN8xbwr6am76XGvniv8$Xdt!Lu-WZ5-GZvA0e-sN39q8vuL7 zm32^s1UJGgNK9$l(K0HaXevJ?K9Hzm?I0h<+E#8G08%03n_G)ADLAWOeIFh)+w+i< zVCJJFVX$5Bc-T@lMxJo& zrRFNk2PGXv@yPF37u+VKo?RLKui&su48yhkez)7@Z>g@38*NUIj{?#^f(=blkFREQ zMp7jlRxHpihwn#$A~wROzDGv$N0&cRBDq<@5HEJ8ufMhfm=@q{sw!Z$^_ z_uEsF6`U0Rgi2exHjHM0NT@G{??9vG&IHNr67aG`DtN0Dsv<93G*s|~;qEi1W#ZqC zLc@m-b)J*nyTAzCjGq*cBY-5tszngQIj2i3iEsW%zT;8(N=Qm>G)%uvM3ZlPE(41` z)OI4IPn2CT>YKk?%?ky$y}#i?ol1!DtZE;J$_K7fl!?Zo?WM(ShJaABnmxPbDVuIe zHk%XAoVPB@`mTxwoZuCp?baEs>Fs%LEDduc5M1vaSrqU#Op+9_cF_P|tx9(2ce7A@ z=hXgoC<#ehKHNJ#rOJ^*|7$-*Vy_`cNtQw2TKhEcRwIB|qFNe4| z(ALv=&#CwO$ghYV=R(ytqDj6V-tS!&Gf2+Fv&!dpl2VuVKX?a$p)Yrl)mQ8i;M)}b zIGwkpO--n*z10-L{0fRx>IylE2+MH_u`q-yi%Vy(0;sfOw%ke35SN*IEm7)TS9vZX z^4i>cZf4v4a&HQrbF~>u`f!O@3C*R!)ykwShVok2#qW3^vcUaD?<+KSU_1 z)5t)4XwO9k6!J%w5(&@EZ{qixD#|UE^UWStR9FlxL>9EC1dv67i;UhV;8d32{Y<(L zcEcF)5mwmx?Nz(pbS%MzfzTuBS>sy_ZC(UV|Pt=OjF`k@WM9Z#cKN4 z3~LB;MJ`sY)f_tF7JMT<^-Jtp}u_x`K7<0V0z zcRye$k@d#|1O+wY9?fKX400akJek^5$?XEa53@4QB`r;78tjlvuV+B(Tq>6m)Tz*$ zp_7AAo(1EEfPxLKl5_EsDO36O=KlGg<}<{m9i9%Nf{geqqcxv~GB$@tKpq>~b5{VX zX;g5rU{C70gPt-QJmwg2BMYj1MkFI^U<=jJ$$S(J2l5e*AZCeETAemOUUp0Bz#9~8 z9M#}rsnl8d+lU>-u`D?5K|yBjvE)2rb0Nj93y#Sg8i%6L+2p{3=w3=#)7(dLMyBFi zy!s;*f~vYgFDUt#f$=M99pLs{w>@-YW{8*adoNlI9*RKBQC-OCBp2v{j5iAdp4gy} zBC%S@Z;fmdK^#sMb)?G=jp79lOwuQqYlwB66q8{8B4(MR^9+{qSyql1vSe7r^ z60M_E3aku$8SZ`YW)Ai3B&C(4uib^{`%=ZSQP^l{doi$&yy&^ydes|<8(m#a!>sCGhdX_lQ((s zrFy4PH?rf$&Kxo5IRrX=<3V|)gE`1G5Ko<7NcB?A(r^q^Ijps{F@JljTdpX7o>AE? z9J4ti?0ONsVAquYOq*FeXFmG03&c>(`;Af$m6F%;XOf}LIp6s9L7|Q9%nmdSC~iyB zn@f6grL;we+HXLTwXzg3y_9~AwhameI?T}oXsqbx2axU*3TU~3)GFeT0GL%Y3dLEE zXO$TDbCUFTVZ2nC|LYV&K!G$fr;74v5J@qr%EK!MkByDGVKsxw3jbE0J_C24$8`3z z3SlXa$t;w@&a_=s|P%ybB(7}RWB*KRn73<7F=nNrdG68 zD%0J1+dD~37k)jBRKQ9N+J-l`Tt{SiZROVcZasg@d=#UB-1acjG1OXG3~6Apwt={$ z5~&F!H8&xK?oHrw@Z9L7bHzlL=NBX4ETT=ca^ltG_#Knh!|GLdz3B3|jTMFD`UR4WSi zMFc`!lSCfHkZeps9+*U@jqLMf>O?m2T<-D=(E#2N!@-hVBEo-AIX`9=({+Gsx-$qut2Sz-R2pYy`znjyzadmE@Sj3&ZWo zKFPFQiN34Ry1QD?@B&ePOG^DG+m!%z=E-pkIj=U5>4!f(V)kpzTYsPx zvRE(L-5v1NlWNqW!;toUuCM1aS!YYMsHF`XmT$m*s;D(jl;?-Uv8I(k84{C+yS2jj z3P_Q=n;(1pE`TV`WoYy4KwJ~8v0{NEEj}8h{IxNw?tC3+pYD_JI(4-rKtw9Gycwq8 zAYUU47B-Br13ncf!Q*}+5($b~x=$v6g|3(YVi%=}c+R@V<~EJOFgsWcrvdmYEbYZV za}?1D6|&u`Ln=+OmOvI2= z()UrySn|a=-e|xtKhkqWd>yb4u@e{f)JY$ASsBXa8rCL>kS;{%VP2u#AnP}axi79&JV35KUW&-I%vrA0q?~LNCg25M&_Ry%0 z`>B%JHt2Yc&wQ>+Ka>m@WSi3oeKmsGni1-JYZG3>$Xi5IVA+rJzZx77*>Zamv^1?_ zy(y`H+1LItI@f4Kk+CB2|A=IuwYjAXJ(UW{b^~jw5aDEJ_{uvkH#ON!KBL^0+j+tP zJtbTYIj*qZiT`b#)L>Uvh!gJhBj=|vsC%?NG->Rt5BFGC2^=$>EyVm@sRvX5Jd{(8 zPS?E0AVr2?pK~|$jUhfAr5Wyb+AI})3+FPM@P37V{>VNajG)M`>~u%Xb`!IVoRou$ zqid)1wgiZ`^J&3~^UMNeYJ$Ir*xZF(CHs`vN-6jXazb?Pv5jTPNaPrqy)-x#p~gyC zw)2_6uya4Z z%Sb4}_c`{uD!^_rzl%UtoT$m?s@=}Q{O`Q(jQJO0IaCVf_f)^}W6O)|UU);;R@AHo z5)9R9tPJW`D+MyaDvmw-qp+8g7D+~I>XR%@R)=z7B;ZVR8tNhvtN^7 zk?(R0F`97di8u-&x41j!@V z?UQOY!pWVc6X&aifcqzMNp1+DsZzkM%2l(~i`Hx7)C_G(-Ax60`#(Sa)0J2$rv2iX zfS3L=jiD+1Oitd)jKym)1S~q8ttq|+dQmS5PK5a;x1;<3y*Prcwf*<3zKW0r&W2&o zd-7949}8*cJ_cyt4*2gyzt~^y&6#C`*Wy?h*+J61gs2mB#KF-fe3})~XTEd#dZzcu z6eI9jYRlmNkE?T95(PklY}>YN+qP}nwr$()+qP}nwrzW7Huh!qD=IVM)XA@)u2kur zwm&eBN4OT6sS|T>JW}Khxi#3V%+e0CPkr%Pcu@|cNrnTK+~c0F@WJuEaIuY?ChqF8 zI~@7=_byy1UUEzr>+DNcUe?41k%}!%Jo?+?n<@2cc`SbszvKNZ%6sLg zj@Utxe^VU=1@HRx(>%P63P_~PgCeYJSX-UP2v>sF(j}nzS&_l(8cLkXe>s}xjRaOG zR~FM6DksS$@0dJ`Rdlet)Yzx$Qp*s;wsre;>CtGBJ15Q#!<}f*a%eaLJDCj|^Z^){0-S|^J?TacPFbq#}sX$|^HI?`+FaKKApbFUGirS2> zl#@SJ?|rnR11(rd-($2x_JyV2ZM}T+(&*tDNV3_25Z~Zy_1zQ59|B zqf|-AJv?x~p$sX~Zpe3^O-1_kw(B^j3}`hJ!Es@%2{)EME%XYI4NGPZ{s<;c#ss(9 zl|xWP%+llLWlPd3=$h0wMVLjedoIL2fd{?n1^6O+6$V)yN%jh*R>BiwUx)n>Xh-ES z77tl|d@PIQD7D7CdcPTd;{(0*0>F-s*{nUbsCRjeY~OFsn2xzMuO9nrp$c|HCfhsl z#>)2AQYSD-6agG-4vew3u-A2Dc5*HcH8$ffH8Fe^Wfim7L4XZi1R+qDkOiDBlCF7t zS+zbf^`Rr$Vu9sGN`hxt$NKtNx&-UhdKjDsgw1d!4&(&~s}zi-_B+4G%z=v{1K@AH z1ELj*#>(@3b|DQ&qO%`a`BrW#hUPP_mD2q;tEzf^M;IOnrN1Fe@e9_>12u}s5bRGp z@juBG_?4to>jwwLxs~0HmOh3RDDn;YCL>~6KB07Cc}GQE0O_TTWMuGm-l*d{Uz$9NT69NgnVX1 z3c@Q%LeO>`kM~0WndSgZc=z7KcS|=^gEUXRSD0~2JKlY9))1$l6a*Y`qm4WB=Pow% zG|*^nfLRFgBI!)~+%mMmezU`L62uolr!Q~Q)=JFu`J3L&$*iy z?#M2L&E0SAAzc2doU2dln^s}BlSLuX;^-Us+E%hBBKY^>@m%G>7k;FhG5Afs!-kvj z${vo~lH77HcTGq%$R%tRD6*1-mcf1b;a1|mTfols?e{a*RQ47WI*Xd_1^Uicri1IrID6mRaP9&;y0hQ(;zvCgwSs|-+X_@d zc-;>=bc3|&%56s&pqul;Q&k9m#E0BL?t}t=_Io49jHo%S$t}Ye#{SjGNo9^=0qPd_ z>hPLm=9q;`Q=idw+%EV9|IjZmHAi+cfP{ZEEJS)CGtcDe%x8|9$7( zTS1kr3K=h0MsS|`V(;!{Jv(bOz3&Qo-?nB(if z)B)78Ll4&N;h;?L3@r=?dAaXPk^y|ijeq~}A3yi#pGuaOCa_9q7}7z?Lr<* z&vHNxE}p`rPMZxmtLgK?(qbxkZzMn75tFCZ-mjKwn?G4nbo1#+KRUAstp|gsG%dOv zoGx%x@!po)*zWt>HzdNX__8hN|8+Up_Uwq|z&XYmf zQxVjvd`A=XKA1+?bT2>fnsO>sV(Mwyk+r&>p^^dTjhUlMI3PYL3EAfsjy)Pz zk~B4mHYf|~KlFWJcZYJ_|J9QoCp5_N=VwgiH}Tjp^e4Gsm%T%dBjUYGt8f7lXs2$O zE4~e3s}zIk3P;B6yUxD1r-7`p_UjtLQjU=q0W3Kn`Zl0GUMrLDll6{BvXOT9;z3&) z1ry}s&m45efDT;J6j$umpHb6r3taA*-8a^IjbsnT9IUc}EI0~FvC9PdEmI$9b^ur* z3&TTIJepesuu}G@7-v4DykP+ve?9g;#THVjo~GKW4raAbfPQex zgu@Lan*l4OXT(*PaK$@2eEoqDC<$t(Xkxya_I@$Q+-M7U8#>!WT#!%-BxdzxvC zD4>>VHcQ3G^X9$61^N^|7Gq`EwUrgfOj*knhU&H+eIEKkOZ1X)+!nGFujycQ9tl|ujr6^wnU0~4Z%E3S z)52nqNH4`e`#3cAeNnXBU?uyNkjt2k5#HoCejrqSh7B_W-&KhXU&i-3)}yO44rQI6 zM^%Gzo?uvG>}kwByB#hR`c1@75GRC+*KjQIjk?N8HTn{bb*cL?{haVg6Ft+aP)xuyNINr8$m^W30 zM=O4h&C1xx0J^ei$m)4}H?ht2G?h z6FL0}>#-gA9OH;6Me97wu=bm(ObG*~O-{zCmB!&AbR9`8XR%x$J;v`%{gw||w7)S& zbpZsCdX268f?Bm%itGv{4q%XF4u?KXis9{y-yb?TSbcdLl%$DX2SK>eX4QlTGd(>7 zYRx8L)m;GEOO?`R)(sK^H%5J$pTgPjC07-C5E-YJZ_6?vrg0!pK{NgkQ`N}d(_>Ay z>N!N~BQ@=`605#mdY2O<8vp5o{ldWhYU%FddT8jGL`s7m2aTnB4c>?BV5XK-+rdOB z(8|1Yq^Z2 zwX1YuLnDgDiVLn4c}O00_?d9LO3Ie2MH&iUngbSdd+KR+Nx#gq>W#;G2;)w*X@iZm zmG@WPjKV>gCV$fHQp-_kxC5Dgw8irRGEoz%t4ssa3VI-5t!xiO`HM-`9o`XXNnL#K z-b!Gg_1J%$IXUXkM&iSNhtk+=ojs$8lQJ)`?%URwRzqefu!EzEa+Z^Qnsvxg=Z|VA z>ZGBn&zPcLhM?&H=N7(q*7jU;r?kV_I@X11Te3dWy-28(e^~G_k6%cFr&mI?v!H4^ zb@#v=TxC9qPW9JZk2ulLlY4UX>ur{pdYODEZVb;4!PyM{%fDys8p^0g#WJsAunzVbG=5k z`sU32aUR7~RK&`~_$TR0KLdSqzJAz1G8LbVwQ`;fKN6^)p89~g!>ysyxDg6l?VKf) zHmUm`LoI>D)QW(ym#!KE^iSetHNRO3>u;d=zQ1#g>tajj$gqi8NAjPn+z^a~>W*x3 zo7O9NKdbE)8p7Ol5iHOG$|*M*h+ColPHYQ`$#^;5_pu~mWonkH!5R^cIHR2%6cq$Fp}jmH%r!g zUQs$rJS(GX~4@i#!O9Df+?Vb7`(;Kr=uLCG7R4lYcAaDy~=SuhUw;J;a%y zfdj1C4~i=9U)>sM?_P%kRK^229QL`Lxe%DQcX3BRn>5uUNfb}vn2||th&ZuuY?eBZatKZLOQI)(^H%^5Iuk0?%VqSRxn?G#m?pE&sd?j|H z;@inno8XjCxXVQI^QfEgdGFqboFPaSIDQ&$_`osg?j0flZ-wUC=v!Nytsj(E$0N|8e^( z#yVGV8K^EN9F`4WW|=o}oje{G>turp81ue}1Z)kvk`%huChVgD+Sp+lr zu0th=yz_cI>qS!$Mm@i*uhr+}BCEj~zyQ-^DkT>F%?u#G5@@8g`b~gwaaZ&rCHD#R z`+0A5EC~F0yu-j@KL%&blMPeZO-o!cYcdL^U^n}9BA9q2z1Vwr>j^7N;co8@QOCBM zSRp}Mk<0yQzOe=U8A>8$eP`z^5i-cZBxXUp>(lMk4q8-_Sub&T+oYY95s`q@?*c!z zP4k`G*6DsyooB<{FV)4N!^DK#6Kws0_|2Z@5xBEbW>42-4u^nEdoDG_F!UAil2UR3 ze`rEeJr9ZK-7GWi(V%`kltVihVQ3rw`_P)?TC|j1a%R9$PLTr|jVO6Tkm%Rp|2PwK zTqmQ4;Ot09`4A5`b(y~xFtPt4)vY~MY=E0raZ$VsABw4OB)}lBqu8xy~lf?5%)<+TRibOZ>!T`OGXs*FEU75nebEtyV;4n*I~X}-5U2I zw7fqx&K$uR^yT*rrbuvp=1zdA5~g0=?VigdW{O!j>~HwC)jo}UJa#1(qCPSZb^!7Ia1*F5h3~a z*sS>=SaZ!n+qnO5guZzLCElg$aaOI8s)e$jJBQJfUw@if0q;MdrOTDM!=)?*M0#q zGVR37;Cq|dBeW`y8#(#s`90D8<~2qHB|1{9e-$+7vM+!mdg;M^%6Y7(UIt^@`~EFh zLw0Jk17VSc2Dvn7;8f%CHs3FUGC+~AwH0YVQH{WA1&?*cc!_k<^^((%?RUj11iNCk z(xUR)b-8h_2flfi8J0Hlg*I^shCVwNG7301)O z4y$VZ9T)uzMPX)I{QX~@o>OVGqqS;0Ixyp@KqFRB5v5>5aU)uUaI!J@a{|!&8U72C1S(1Oop|TAEskLXJo}28 zs6O4k1U&}1Gq^m5(RDfmF4G^1lNlMWVfcaQ`YkN7t%5ZU&jh&{pD=)9VhQuZvB>+c z3@x7#cn?V6MK3@Y;fSe|yNezU^bFILH1Iyy3ZSn+bJWw+^sDm0(HQrP-X zB4xn(VJ3P!I}a48PBZP!GEm^qM%_Rl8~pv5e3hklfUhe}K4``)G?Qf74x}~5PvG`C z5qgw}ZOpaSU}RT@in+8r61M((Q%&Lakw-T15EW~?bD$<1lFf}1>4dzhE6qIu9_ybW zEbN_bQ*1DX^rX!-LUYFJISF|=te1)jAD=xDc0~;8P>N!t3CTkyjtE>yOx5D$WiTUi$QghpwD<7S0xx&NW zi!fGfDIh&Gm~}NX4Y)D2VrnO6uA0N|U#P^(PL`YU$2z0A%?-vT(`EWF%>dJ{>eqFF z_vwQoLt-^!IPh|gLMr|8_-OtB`H!nc8p;4x!_viFgey#TgF#@uS%C`nz3^3Jn77@o zauBubp%f0wFNv6(*GOCTv}qua%$a?<5@`fOySn+yCfCywm*!wKmN+k7Ox2Q~8t#nG z?R%GasN`75lQxY?iB_q`DVqO}8gNH7Lwi-o0kDNOMyB(X7D1(H>3WxI0h0!F6S&=8 z<+YzQQkj>wlUCAo{ZUn6NPv?7G(gM0RH7acqJx8AY-!sceZ}CF`|51#Baqnx0`6Y& zm$}(wraLc7jmWHaLm6BFjRh7dBcvYR@}nLAFCyYyqN)AJy={pt=jI*xy75->i2 zc@s7+@1e^j7cE6DeL39)oFJ;`(53SzDFU7`syjdeBViTa>X9=Ko@_Rp)P}UTnU^`w z@h@-9RW2F}iQ9Hu@N!g!Ck0`nJ1D>XUrnSvX88n^*%#t@81Ak+#a-ASBeQL*Zh@Be z8d3BV3z-3Xypw|j zXMWaANXCqc0hziW^ewu>GM+_-kfqT-oifgSXbgtGdK}y9aJ%XymOgB@e+~Ho>;aRT zD9P>b`PPHJQY+KjsLks~sd41L@h6;K9}UT`?qf)7oH!TbSaPoKXSl~zQF?yK{X^+- zp7fVMvu3C5tDcyKoYfZ)VV~h8!8hU>0S7>9x~(0d zL^g%|a4@vDHNl7>=viT-4|QR(UCV$!ySRNP&OOG#<^BEz+XMqEFNy+27q=<~q4;v$ zAGhdLm*S%n=DA$KzQ+=J?Han~Mx3)zAE}*iPL2BI74WugO>9rVv81eD|(( znY<$V5e1_rmJgzHbT_HZ!n?EELo7*%^AM_+ffH{ zkkictcs+mxR(EgSlMuhf$7A;tK|OfSso7s!WmZPdK4)Jxa7|&G{+KrZELEQ$O_WVL zN>>>Rq%zl0|AtvTG}BVjoV7OMq&x*BREo6OmkK(Sz;2w>5k0!j?~a@a^(rPtNt@10 z{SbVW0yIep&YSv#5q8#T*>LISs>5e(8<*;)2enGGR|G)HyKN~%XAWuS zrPj!JC}^1#ugZkh)F(8LQ*zWIENW)#xg6sVNeV{K>Ag3{N1c|U5#!g#T*U$q_jOxYMX)YU!E`D2*>Hoo?z|H>mD+|v5znE) z88D@lyP(uHF{K9#ss%p!Juy{4v>QW=&>Ve1+Io%ANGam>oVk-}keUOE%h1FwLkU{` z|&AS5^vo}m2pW*4GKu*bOCD1H>Ose81^*sBohIi z!EX4nl10?c<3I!Uw33+xA`bFAc43#ozt3?El`r{?;g1LwFM1labKRYXs4 zMBP1(UlR~bNN1CORQ!&|a>iL@KXtW|;WPIO`7qD2U+xyCxlzl5uY*$1ICyngN5QAJo)em?fVZ8VZMnL zkwJ_tZ5;(Ptu>NU*9J}hg%m5I@DiT*YD}bU`;H$-Wzbi5IL=_T&2WY8UbPRcy-ub| zU7&1YUt3EH4p`@@PJ*>D zNOjm~&99a(aO)$8D*^1ZuQXbTSG2(GQWEVX`>@C^;O!WW-b3D$6?`}G4}PffE^53} zVicK^=Q;kcy5RPp%4H&po!G9z#v+R>S#XtYhw~0HrO7glh7-f;2l0}uPn}}=R*rY@ zO~{>T(yEj3mKjRlyAsHHJekG|Evddi$U+o~3i#1sh`y#QnmFUMWRxeg))|S=>!Shb zkB5vFS9lLk)Y})K|Dx{TDJ9uQwXf{CVC!?K5b|t<-M7P5mv4Bn^@SJkmM2rNHICuI zk@{FnG8LN$+Qskzo|I=DHf)@~i!zrex&A-#vRRltTRkMizT%>lPkkw2UK9}2$j#cq z*wWST@!$~Z`3;nCT!<9BisAy076j=0wUcY*F{BbO0vP|7xdeWeA0;K4yPCeurj*L> z!O@P_iAR4ksBle(nOEU|mHTdz4{(rAlf~FJpm~qn%SQ}i%M~8g#06c|9$f{6Zmk;v zdME%{UnMX=UH!RKMwlhue9)M3C&Y!&_~y~oJJ7nM@$+ar$>N{y3F<&ThY@>u}`7JB1q?Cil{(@%04U!{1*O@gg`~!d3s&lVcH4|@P1%MYRWQuBc%V` zK|6}sW_sNIO}=YKcF82reSuTiEu0hX*(HT<<|y%Yn40a1tKOyy2SYf1uPBL);k0rJ z+K5seOZ9ZDNzDmFVHfyMt+O_^zM*(`90^DN5z2rBhTD`bTDU+)?0Y^!szsh^Myv3< zh~)Pf_TI??CaR19avyoO%SoqXZLUhW%F{tq9%C!5;wL>I&3(5W_)Gm`d?36`U|_m( zbHE9dX$qG<;n8k0Gu<$p%9_h!Hy6JeVE?;>Se_T9;3xl2QtB#S!$2lyLQv4G5Pcm0 zzy41>&Yt}RM4^+QodGpeR#qpE5dLz6vHj|8wKbH_uj>-!Hoo#{y`Bme-%C3r| zQUts9kl-try6Fx8sYSNlxVoJ=!4&)LjDt0BXpvK5XwTScI?;Hjd4ePwMtOdV|6Cjv z6${#VqdGRhh71rwuWusdfGYp&h*%ISx7@e3d7sFxA#9MWeES>*2I+`^`7VC?MF~GJ z`Zb~+yMRE{$~La6Mydju%b6C0u@Y^&ad7M`jG$sTS=~gfb;RES^ciErv%}d+NRq@B z{TxWD4tU8zF=Yx1v|NYT|iWbkeFU z#YRdqQvliL&KU7|{#3|jy&pE%KfINR$3y4|NEhi2@NHloZAMB-EAs#Fm#i}#H8^BQ zG!Q4011&k@NAW26Y|iq%>ic&QR(Z#0pWq7D@Z9=@?m6I%h%~kTWX40gfzQEF$1u>@ z)e!6WM;mc|RcCU|B#GyhqZT}EPnj5X$C_Zpwv3d13Yrv#qf|L!uSJrzr_&oQUH{B8 zh`{b7jRK({5I|oA_y_Dqi(b+kiQ)k(MnIJman5RSKq5rK79B?@UVb@A(%8*#^@|dk zDGle7hlLpPjLBA_wfFvne8L6IQ>&7y1kp!R6tu<>E(p1ar*;nz7qviSA6Mn&uSRrc zDGJV^YV%Bv=OtFES7uqKnpyXh1Co00c$xW(I$Yk5}hUR(8&faY}1} z#BqBasv&0?v%=ahEoDx$p@&=-D&s%00dpRHsD=cF8YbCQgtM2SmF;qnVp-4Yu99eb zBIvC7bb!ks=IqVi0K2%VO(S9NG3Ci56pa@k7_5~65|6lK^#!U!gl&w&8Ei|9$;3*5 zzq}p4Mz7J z?uxB&A04zAKx&TGZ2#&VTR@(Z!R%-x!_aEUu*Gi@)Sv_McMda~zF1cQ-J0mFE4X9| zEX6yY0Cs3_G+4}TZ1*FnB!YCn{Ti1^{5n`$s-|0&JlQY2=dW{>v93HZz*z-q=5@ZG z8*RoSAkSo@LtY6i%QgjY4Eww29BM$6cYFQU)Da4+zGBYS$+*mT-O;$H~W%#70YZS}B7eYL8<)keoB`DxP~MYf;__YtB1KV{Tf*j!kyL$Rhdi9pY5?5FX7!pRt$8Ha$@lp44`wfm(VQ zGJqvs!toRV75~@PPS<%~>JLt;F_T=05cN_XIP;lL+s6hTgy4t8asAlH)UO%hGIXUD zX1J=u%uCXE1~D74xHT0!3E)+06jx5hF2SdPE{=hUF{piM;n4;)& za0mrB*S>3(&Q!rZj{=PuK4~l~VT$^}qJdKMaL~jM9z;cewVjhw{jgpYVBp(v;h`Q` zb<4>8QOJNa*3l4ZA|YRMsHcUrPbn>2?agI4Y5zDfETk}bl?_%l(0VB^J5nx2p2CJ} zQa!l^xCZu6VRV5O23cKtI=%OgZ7nNKu``Jbh`SSLa^T%dnE@TtyK-})E( zXAzCE5FPJKl}dKGh7nEK*$R*nEUib#$D~9BFYs)<=W1`{9j)gx^bpx_JbH4Hoa7#j4TY7PJ2H1nI8)m-d?i3 zFdu$Kl$;<@eg#3vYdIq^#yMlIAhF#jys^;w=32ptwP}3xML$?=tuv`=Ok@rde3IeG zSeSzP3c4aT74iDtCophv{TX>x1;YTZ+ydny9BB-;AYls{Ep6!lFZK^^m{wFOsgW}| zl#XjzbWvao{xLLiWlFjiBn!z>-@oQLGu-BTT52g!gP8{t5p`<}# z3pOxuCdEqne~v8X&@Tu#CokJ0414w*no6KWwOg8)9P?6;b;5nKp96{psGag_HqKwk zi9XcH&wnSipz~?Mor7jRDz@cKxphvPqv9xW=kVClhWA@dL?RTuE%C8WNHDh|V^8*y zd$b$k8G3#PcmzjIP`Zy#AV0e1OLy5#rs|^pghEABBROaJEZyy9Dyqi_sd$04H&y=u zO6D6;C#9&v?PX8P38CNxpy(Y5``!&wuFiBrFu{E@SPXGrj19knIIA$9jb5$UoyB}b zO$E@5W%jWD9j|w>xrL7J>MJ=~%&pb9OTw+22MR!!iF(y;@{S!qIJ7|xB}kMTYo^mb zxI>A}q#@TJ^eoiON-)|MVK@5S_H#CC7|cW#9ws0YEj*3{7g2FSOGzA({~Ju%P=1wXU8)3>ZzNOPeWDAt1*k*&G(3`Q2VQSGNDPG9w92NeTUMG*I=+ z*e_UPa-4*0gbG&;J-6{Zm!ZpFuVvRzls& zUlAa{Y*)*qwdRCrIH>AAR!cbGy>}lf=X!zUo^v>8PcXvg)`6IHt5+eB>h&g8b@4I< zu-gF@pFsE3%PLjEJUyIax31_EeJdE^n2D_--HVt&XXJq^mCJOYD5-Z!v$U7tRJmW4 z)1`io6i6AV*h;+Xy!CuuK+p?wm2IrUWJz{`@t#CQiYtz%k)S^L%ptZ>>la8!K@MU8 zXw@C}`Pt=v8d|xv!MQ`h^Tb*@iEUHpbm`9LOa}0_L5eObc{lFtJ9>i)9_bV1fvBxG zr^&aL&7H8aa^MUWj_lYk=yiWzpz2An4UP<&Eq!vf9WYwrUHz8&7D6=@#4;N~$1-TE zV6XsTaML94x+%w-;6c^{p{!RC4ZK>^YLiMmj|&%?2o7C*i>@QtTo_o_KM_N6?GS_pR9|cSX`MZ8qp)ao@iAdji$Bu zswltNyc?-DpQ7-Rj=MPy_|lkS0W-nI!_Xz5x13b`1l^nr5NA$sK8xk9=H*o0TXX{l zLHZ;#&i+HBdugz0CimX~=d%~+Bi%T*n^13;&sH1k^SzIWeru?y9}BD)evO?+7wNc_ z-4azHHbjn3=M5<4r3Z;6>kXFsj{ituizBZFW=l>A0lFSm@ZSAXm&E@?N3*d#R8>#++>{uY87bnb8~}f%iYyMLgtwpoNMn^&Vx-(go5+$W z#l)u^bGV5C=KaPc_Ctix(AUWiKBhdWKGE=G@XQks@~*} zLP_(qX1sb@??>Oo%7lB-_m3j|Jv<33imNS-*+fp=t%~~nIAdKd=tq0{7kADKfN;*+ z&mp(#EVj_CE=wbZP*;ABdqv=g8{HYszY}7eBRxBHL+f`f_Anp$sJ5LU|8l3(X}BB4 zhCW-5NopibDywGD0J(J2bjyN5gf~27zNxz?t7#%b0Yc4#hh%0jhfST4dgXvv9;@}) zG5zWYX|C*%Lo8>QwIF^Vs!h()HUhmKy%)29{jBH(T%$fCc626B*HwpC^!*~R9dmzP z52vxPsJ+)!4M33_0`%2`M%Fw89yNL^Brgl~kF#K=JJ|=HE3JlLYH4SsRk=(2$<32A z`Of@Ni?M09L5n;B&f`?P02aChT;X^9|kZAmeu>7*va|md%Zfm4B56zMZm9QK=B@ z?ef7eB&8b?AnX@mRq_RwBY`$83fF2Gi^j46jf`Ux6s@$oLn-Fj1oSMO%&mD+P4t7- z14;Iq$92S-q0$n?H zoDe@}LpOKO^xW;L%{fJenU%uIj?mJ?M5wm~RPx7|o^GcvXa=na6XNY>y zpMP!rFHTWjnGf_%!1;tg7MGy=Ibwa<(~*->HkL;286d?KfRiCUnxeQ6&A1y+OLf4f z*#~zHTG=ttU^BHA02Lq6JYXZeY<@PLU*d*6O!^;+&p-vm#+K1yHSINu90gF^s_*x~ zY>AC3OGdFk(!4u%+u+77Dq1W*NPUif0#R6J-(Pfh$N?m`S^*qwX48dvndIEIc1z@hY)JQB+ zqfV0evRIf{J`Cdn#LhKGB@?hKIV5(MRTugR0}72`=T&Y!&zYg-Bc@PZT(|eq2}9gr zBo7p{(;X0~qu(4*(|re{^ST0D z#H^o6{HE?!(Kx!cMd0$AL?Ms?`>j7Jj=ZjHBTG(Kc`2N13OOAz z%+Pr*5Sr)E@wRP>ijkVFW%n!FeN;^;RMO!X5+?3PPzv0XVCf(BZFGwM(GeF0DSuVL3 zj&C-vQlh-d==p7o! zT9~hU9u^Nl9af=4jh8j@f^F?XWn__S<@*aFL2EXrX%Eea-A4tq+3v7CieQb z?@mltmk}ELP>_{cKe7zwQ@Ws3+_zgS$my=e4FO=A*TMdQb}xvPDzQyJ4;GK6pAaa` z)zCX#Od@76aKgCEPrs$zjTt*a7J#36x4XcWsZ$Q9 zg_+^r59;5nvTsluZFojY;-WTJcHv+3rlWYZnf$@1#gqQprzt7B4#xJw0GS%1M7<9yml?nBBb>z zu>wX!3%mp7;{i8Z{)9Hxc>ZgQkb1f2jlzY8nND}03J~(bqaT`^ zv}_q+0z7KI(t>fwjK0UT6Zkc(dqj;4S5-nsniwm{31^mri6b79^6hK2Fl-?m)XKW! zJsmB+KiF!;mllUyd7W_E7P7{avRB9+P*x3?zortJ6)7DtU;L@=)BEy#@k0b0!U4!HdY)Ya?R~cJ2TlV$G!WF#Q$Os9lxR% zx2<}+BUp6D@Uerno4`Jh2mkdHZf}aD?TT)F2+-K<19yy35f@+c zMpa2s$DI0^aA>F(_BvaL5W*dv6W^)`cFBYI)>NMeH|t*~pID z)zOnqnAhL3Z~M+K&WwbjqVzqE;WrzSjh+qkSivQGIkK}_UFYc0fBrD#7pO)C z0f&7Wwv5lRNHkI`zMpEG#{_=2RSn4!+RHH3A@6U&XQy;%aXFPq&(=+=Yg*u`6^-YD z;kzRN)~e{=PCE>%L^8Qn$6ob~&ooGq`8IEhkp^KrXxaUo(xONmy9# ztd+Pac-{h`!pN#o0r4%Q6#ooTwL87Iv-|a`S5}|}C0LV0p z6Vzaxkz5r6yZAw3waQLCmHp&*=e7z77r+$pe_TNd!El+{5Bdz}t9tI`q5r2#Po_}~ z!l>T|EG6Rf6a*C%ym2h4K&wKaqRs8UoZ@V8P{qm~Ul{6(RiUPIP+q

`=W+nXhXPDNww3fJh+o5-~X`!Ag2IMPEz{>)9>gJIO7- zZebjWAOp+m41+IMgsGo2Fb*sZD`qlSeo%rqRAor;X1VQ^pVhhb4)6mO@~H9@hD0W7 zs2#b9`v0lno26jTiXOMkT&i+*jt!de z-CxHZYW;g+6YOFq^*%k#yS$bv9P}|=ow z(XAcf+MfxW_uThKGP=!9VDKjTxt%*M3vWd%%X}p5i*FZiYR1T$L%eBQfc&{4Bf3fg zJl4*9yXaY>GOPr$vKI$k%Ta7mZ`H^HH5BV)DNXF0AoL|a$aCpm7#SD7I>DP14;9!3 zyu10u-ikkXQ0rAJuu!Lwux?4&AkWQKG?WVi&9-;SY9e9hG4e?7*lzdkGkH^^YW*2A zcBO-4W%^Mw6;@#huRZqfae1~l1sM=|kE`QLj$-4q^<_U>Iwf3%C`Uf{bHEo&=e9SLun7$3(wcq|!E@MgF?5Ez70@AV6q@8B{h4K;F#YUE25-{B?7^U zIU4rXvynbGu{fc5dnhS#Iz#aMk0}H5$*lZqh1yJL?ViY#4C)bO_5F4rM$)yU?;>;l zjBT|U@A3=m!g%-^OaFbcXxvFMyx0ubiD^=NK`fDxaD9)Y|IBf^XE}L~X;D7|q^)?r zE7_pb6LwUMrg^%(8+;`Aw-BH{Ik}EQpyOwnn$k;qU>{wO zoBu9EKZ#Gg>A>-C4~M4C^sKMFqKTZ)i_{&qaQ#n8Ii?A`Ld`i{A5U4Lgo=HzqcR-v zQ+BLV>JW6*KZ)9(%JWyCPkiZ7$dorr|8z+;m{DWA&&VDJuUqJ4;pyvOmRy;`REzqNp+0B><@kPjguI#1}r1A-Fm%Nimpp~H& zmoz9qyjI$)a4eB=MIy|~$4;Iz9+eus>>Q2$6J#nYi zU>Di*8YYA*MjI4%#1j*=lJTKR+u>`pzy~9(3q`m~o$^)JVcg7J>Q+yGg_0`C=|RM= zu+UKq`V%DiM!Ey1s#7I7@-^LLX_`QkYyeWE|L=rO7H}g}QTz?x>`!Kq^`FgXZg~ zwP40IxQ*z&4mS?Anxs;ZsidMGj4w0;--iO&k#@~`h4^78qL;uCN*;$B6$WY6f=ZTA z)UQ?g3VH&uKpohhLaBeSIwevIJ>NHP(95OI5E~ndU(>)qA3cKK`RXQjExg05{!`w4 z!8=8;FA72BJhW7)#N+FoAL>wMB6he>=SMAb_7O$7y5jq;S=?GKIZm3VGfRjJt`$b2 zeD3N*+Q|bxe#l-Dt+m_`qAzOKW3bCA3#yU{g*WxdPBD~0qaZwp^J=p8_ves@Yyz#N zFxJb~LV5X+rrATL?-xblm?CLB@rPw#bc&-leRrlPHqWy4M~a1j;*iMZ@;b!pF7~nh zqVJ7>In$VE&KCthz8v$vMH7x2Xe_{Crh}4hm~BBA1mQV#v)+<%J(g+ zvl3PV{%1Gh>w|Lq&)5xBRrjzQMrfDl z$isirskqiyHbJ{EWmU`ojL;z9rd|{_i+daC42AbeSQ~1D0rR0`RC&B4a72l(=$l;{ z3*GhKT#B*d!p`Q&I;x@Rx*O9eduD4MKn%!<(cB|gnAVC-&a?{&E_N^w2r_W@2k14z z^?GbQf9L!xw`{H#&^;S4MOWRdVPLw>u(V>0KD$Y9U!dJGM@W7`zqS!Wnd?h_Ljpc# z;#bUBv^!KlPT5ShSO^02jX)0|C3l^g@PH=#4c=~Lgn6FUi@hSpWDWL^6taD}bKNRm za@ErB!hgOB)c2&+C*Gwg>k8I${X~HD(cuKB|tw(H@`;RpX zjzg?QqwDvy2FHv@rb`Z~4*B&nPSQO4Gy(nG3G2NfGbffO=iBuEkqyerGIKhugyauO zzC$2!k*K=yI8uZTABDCnZjRb{_LDdb>vX`-2qKdmR^BQ=6^@H}!htOuvQ)<3Nw6?f zYh14Tg7f?knMH}vQq8mu*us4v)*k&H=LnAE>rK>HW(k0cQk4q3JVZ;HU_E%%vX3WriQQDMkDVVxy{S4i1n#~0&wgSr-_9PK6>P>H)^a)ph=z5F`?5FE$yZ(HUFEN-Tw>^J5l?xx81bmeEB=z6x{F zBeafq?m)b;mXT(i_AOnSGKgD2>x2l4cLebgO8v1UMfPgvqZ^DPmVjs;Vcjhb zVmT)OU=yj7(>lQsrW)ECfbCyqUg}^ET$GOT0A`?xv8TR@9Dl$0l^zAzwMxuLM=B?gm|)Wwdswf zT>2?>pd_{!BfU>!cAQmb&;XuMlXH7R+jY=FAm6LvJrtOxjWrjGs6wHQzq-_C?%O<@ zNt>lM$3CDn{uTt7IGtyjNF_MOA|Kc=&W#t$>STLt3Ya=z{@1(b=&vS-c zvrdSM0u=f*FZrG9{sO5g^eLYUGM|9)8bMtG`aK7{no>N53emMbgHj{gZdJ@?cWev8F(&HdB!WyMQkW3fjO!OeAds@bU77$#+(& zF{xTpfbaWRmQqaME+$}ljT(V*Ok#Uj4>EI8gZiTNTeBKf zUim|qp4A7ua3+RoK0a2sO7rQKk@b4u1}@KmTgfy~>%#RUE#a^)LYanplwGwg!+c7s zzlXFNNN(#`ILA~lBVtrIQgbdN{?P;~UQ~w6i$OmbBQ1(0@wkTyOXl*dBuvC=VlYZb z3lq?4m#dR+s5ik-mY`;Sx7T2Z%_NMHVI(Z=0CXnTO~w(IjfkOj&i#?3GB_(|nW~uA zXzc}`xdyI2;O}@HVA#P2tamvRGrQpuoBUe5<~6k~k0WL}Uvly}Pd9#cuqQuBq2sAW=h5Ra^3agb?I zQxTJ%VL;1|B!Q=tSu;j~X}3HX_fzzJSB(f_D{*>y)?Qs+>Y%Fg`qdG9=#C>KP(Y(S z_K%`+=-$AGw|{#OtlqaIyiChcC7sJ~+r@hw!Yy zT#N+;1tEXmN&}7%2vZTuDUh(EtN{4h`G1&qz2=rvC!ffs%^7MR&K;uFPi8Pqs(RAG zdq>@wj=Ahx=l)%hd41Z&1BqDH5%Ue`rwW#&!NAq>=RztVo9eY_qD=NM6Z6QZ6&Ya7CV0DLy$gd zGt2L+8rgk0t8J1Ylr+dNKs(?nqR7#nVF6p=JU?-Et^jb?hV8+J6q7h{f>}2Nwpgiu zh2=Ou6uTm@GZ^ z&~05{?L^bBbfz;}$E@jgAgysuuvI+9xKsg0!6h?H zf@|N(`Q?U!=Y{}MuyiYVTv5<&K#Y@%1!n#yNiDsgIjPS zkjP?h^zRn;8;QYk_pB7IIS17uTq2yGYvkUsYu;iG!%s-GUwOy$AT}y|T~hOWS8Y_? z77yY-R9}gd0arXP!GHZC$Hsqqt=z#)Kv%;)_n{d5(*=kB=yDqeFrsyflh-mORIa){ zhL#8qSqyYBKDM=r(y-FvqlrJ=U#NcTDkax1d^xl2Ha2W)&6aPH86Cv#-c$I6>RYbF zt$X^r5B&aaXI7k>7NQpYxT#9XH&pUz+U1bBpj=pYKtHsb6KHF&4p6^SU=XoFM8D_5IfV%#jaW+v9l} z*5*4ra_?dY<6mk6z-7guhq=socSI=ai0ir`k-9NFkg7F?w=c8!SHxt8Z>N0e5T&3Q z$fzb4>y~KfYRKyT=5hkNZO%;)%Nzs@f`Cg09WURKe2IkIjYmA2_@47O6GN)$>>qf} z=)@meJ{oTu=f$ys+U5*kFHets^%Fo1xqFBhI{K=(rlgB8T0eRYi}9^nA{V=KnHzt4 z09@`S?~cC?DE4M|k4z5ahFgFTf(=1hjO@AX$Fi7Z{zb{V( zexTR(Vw>ls*Gk3e?^9+SDJ$j@a8UZazEM&imjL!D8ey-t?@g@io>04$xhl zXttW`smy$~U}gkQhBGHHnp@Vd!}xCXC6f}72OB&71&6wgdnQI$$6l#7B|I{0a8E3< zO!{~S%e@iG5d6vA6*6#JI|VC*Fp;hdSMya;8^YW)nQuCK9OGxy5Qjgk3a&bcA|Urq zs$7C8@*M*N6Ox>lV=Sz}j+3KSCLmui6sv0U!-M&a_&1!TMR)s*q-B1^@t(u2^X@Eo zZX3%MBYJVY@&V%4l*p};fk5_kB8 zxXW9M5!~0Jmj-#QyPhmrtL(9+1f1TNEyK*F&NvQhHZ+4uwxHHV)lX(s`L?M-TcZ(B)2DeBf|vwaU4~m$SD`5s2z_@49+2By%Df3kL`yXA z7b4^93b11ESuA`+X-L50RPQsbQfaT;vfe3ZiQ`n{Tj&9@+ z!;xL0p(i}Zr`2p*>#`(qVZx=gu2V?I4F^jmBbI3lW{z^bfrM6rU-Gh?L!T0jGi2Z$ zgOX=CrRPaht!;Uu8z`Ip&j<1I-V;1p|2R#3-Qsn?XA=z{qGF3DkEAX1P6a6}oElCC zA6v|8hJCO9?I3??h8Ua7#`P$m?eP1qS@gbdzS$o2i4LtjcAdx~$&)JGowudcxz0T} z0tLu#Q~yx&s+N%H^o^AIT>Hb1C-f?9k1 zv=(PGsjwRC8VS$T`MptiSb|-iLb|f+1sR?I#c$zHlb|Bu4KL`iZ~dA6-BS^9tARJq zeDZZF>|>83^Lc+T;3H9q^U%G|5&d<|YdRHt>~Q6J6Sj zE{49u;;5~hD+in-xy`yA*7`a@XUuwM@FyH0?v}o2Y;09!i#iKS{5d)Cq;@!6xzvh@ zi+8O41@X|!6H5Ji04U7^EHb36h z*-?4+Dln73gOZ-%yv5mpjMG$F>0r?KOj3BWQzHALIkDM?p@yQ2X@OJ>Swf*%vtow# z#%{KQaPKk@@ETBAxNm{~fPqKw zfARN%K5+_}H9CqKu~YQ}rpj8))bunFA>SV)_ow4;9!@2;#R!i+m|%pa`_PrWIXx(G zmwv*k5$%$b!M+(LXsy{5fIG|6H{euQL;;qw54B=$YP{99{5+dFOOkF#%K>=6a&l~` z+LLgX=Z~EBIrU-_klVrANC%+Vrm4_Ji=I9l`wZzGiP05`Jj*@v!iw)!cw>Ob7CX!K zToJUMS$K-bp(iMTY@1nopyb*6gRhs-s5h5mBjPzIQa{6ff{YW(b%vGI?L+~`!+pTx z!#u6>Bg47>0cs`3=lv!Kq0{5kf7`bob3|qF_85B7nDn z%hf^u3*jj6vx_EwV#Ca71w-@OFN8P}Dp2D@8C9%CQ(!JdgxUycJ3;Vi8`Ew1l zN>xHCUC%dujL}Q%WnU`{s@eDDMsq$XA(aFUdSQ|EKK|V|y^unMKn2tEwoup>&1m;5 z%CYyc6h$YVRgwpE|7xasjQKGx%M#x(%<2!yOsx-)CL7KbnyYczv|vXioI6HnkB3qB zLm{?dNN%u&4wze$+P>m%?jmFcknZ_^hvSc{0qmoFcoAqISF^c@-l1zgZ6t~4V{1@^xTOvlO?;S^q$e$?6+rkTx z2Z_+i1mW8!4~OoFT_A%GtD~-2P>vR2at{I~h%hu(tO?mAo0fTDkAy@W-90seGmx~X zM^ozotBya~KpJxd@nGf|HklFuzp-5ekS!5SU+apVg-M8(jaI#WQk)6{f=fT>lyBvT zR}Ws*l7_fG>!M|ix4U}=%794zflJU+j}I+$2A6JGSn)FdUN+57C{?sfyK}zM+a`>X zyB*5QdDxMiZP%ULhzFsN*vuV|2Ulp%1Pc2*0)4I+eZ#400%K>Kk#1^adZ@2w3HxwYXD7`)?a(om_5eD0j(J zGp}y%{H)u+jET`u{+bU!V<#9H^KN+G8BoL0pvIjEQ1j%s`zpnc%k&*@y1o1}Dw!_8 zTp0lQ2YULk+7&iiM4O;QU`42m@RQxM4^1gElaZGJye>4{quegbKjFJy zqDV2t9~QV$cvz}jeH!ST&w+v+Em_Yn=I?GH1m&DOo0iTW%RZQgp09dX?ln%*SN2b$ z`Z&cb#vbP`<49m)B-h28c0s-*@@N~Sa_B5x<0$z7s*3mqs=cEbbhXQ`hwg8kYOQwX zRC3A6egOdTd) z!;KU(Bzr!myqx+|P*t#k4=bH)aG@QFZZo8=V$BTS>j*8rzS;SMb2Yd5rTbgdS~095 zn|KWC83L+ATr;wDA8n*Rhx7AHBH2M~Wp-l0cWTy*VDJH-sC{a-hY_K>6!``ARCfKG zNuLZVBT7`o6CgaM4#^fkh=d#wzin1rCGFm7AM@P>S>yt0rtCR7dAHGJXvC1TCc=}c z1B%!^4?A}t6Qe#zT5d3=pvjz1El;>_Fy>Qku^e|I^-KSuJ&52(0if|Rluyw=X}1eT zkeBZ5NuUDy-Ra7OkkmH|0HwK2;F$Y`Fa!YnwgQ*&=uDp1wtBMj+y}GQLE54l0Nj0W z?HUIK?yu+9+jUMz&ni{!E+?A$ctC6Y#98m<;U`RGF;I!$0SL(`jQk)60`O!XJ*=oU z4Z;d2i_#cWL%0^c`+*yQ$3ZcU8sj(F2105e8nBHX4LSKk()ooi-5x}U)Su1J_BKPNJkxx344iZm$((!^PL{?{CH8XFN1b!1?(Av z<}xLyKUy@?Kn`Q=|ot|{0d z1&^_uh)S2j%EotV+|#ouC?|SJv{L?60yS&|XJy<%L+j z#2Sq|$aU!#8St&c(o29gBb;>)`{t26xL^<0$W5y8_M`poW)jiP$E2xE|IT6VLzdC) z@X289*XT-_V0gQ$`fUbkGSWi z;yYV6{ygKUT6&J^-*v*IqIDJB^9WU2*L!@oAx%Eo*%!96G`3W1vI*fbUN|7(!Ung{ zYBf_MD*{_q6C}qrU5a=&3K_i#l>4lenEwT_9Yap8P}ADe+6C{oIH(md$y-@ku7pi# zbdK&xq;uO|2_)K%QYLK*j5{msqUXB-Dm`B3ceY(ZKOW|lz;qPebZ${){S%YKUYFuWD_P?FNUSJjn8-W5$#b3OQ69DU|pB)V*OxtafU*aeBywt4;a zCqTR9H3E&OSYDCX9an`;2NE>$|GSBXg#M78?%(^`<%_OeslN#|Z~V_j)() z;Np-ttbf+o>t1PV@wOI!HcSyp7n|=XQ<(~uY~s8qn#jm_-#;WM{IpZehx?T0ZNQAr zkZrVa6mQLGF8U9n`6Ut853VChd|^CVC?Rx2nLIOm2^Xw#GZNWz^%OWxGEpB3J9P|iH<=qrD_ctwU)~jtw-}()==U!4i*u3 zh`nPNO{xBHl8feKA%vAK{UI4H;P#3mhO8YXO>H#lN;3M%vu|mPR0T^&j~KKa9GFW( z>WI3XTX6jG{?xEzZw9u)&elN#A+|A}VQokuM4{4&;~9RwrHgN|Ds?~v8Ym4E;e6ix z#OSj#=Y*J>s%M$>wLyLYOuL@x%Vh9_l9Ngpc1n=>5FIa}^lp2lN7uHxG8wCa@JWX! zPyw%F!kpJkVd11@lEDW#S(`Q0C-x7EIIG{`sg|w%2{{)dilKz6XR(h7omvWHfK#*Hpc+rl5-3cAfI)$kf3+nA805om0xo5LA{m_0VmHSH?vf? zfkFXSNvR(vo?CHnhcZdOSm-Bb1g39}*>SJ1w ziQ_!`y3&!$U{s|8TFWP(pi^t%VU$l;Ki1lmQXinCXSAdGR5U%w#f=gbHBxPiq(Y~IZe46J8vsSv$ar`9AXCkD%$=!Z2t zBP%*VqO|dHB5z$QHZq}afHvqNlr^t2)4|bOVn|U-toZv=fD|qMPt=+jJl$Nc>9Mq* zR19w@$J>=>_WSU8;AsvlQR_-KiEcxI0m1ae*9HLzBDFi}Hnk@5Ma%6pOUBg5D&8_iz&$20DCqC2&hTvZ?GPbmr;5FX_p=VPU4Ndq0R)ZJXu zH&L3HX%E@G@ziH(BZ$QbqG*1_zfoDqJ+P+3;~+S(ju?BgRNPWon#fYVvB^y-!;q#f=xy^ zKY9C|AN4Z~IdK%=jwlB+*xYi!0veT2v@4VkKzN58Cr^;1!Pe?J>F$K&3q(9-zT;%aSl)(V6^KU}^rFz@f1LJMI`u<`_$hQ9YiU6Y4u{W2 z$KnyHuyr5b>LrGGd@kg_HKaa|O|XWgvX}{~$T(NaSd3d#o~NlRV#P8A9qz&)DAY{cWFpAsmkAT6lqF>% z){ePyEYDO5v4+|_SVXCbI75R{T6OI}a<7dq!=p#PUA8nbIAsSqqm1hVn3I2L;=3$M zk;EKLOnTc+6x*R4aSwtu@(wDppdRKqem|VdL!|`z%d-TiW$3pGA8t#-V}4$d;OLa# zwyp{!e_e2Fib(i(JKhwiuOvI^z_4ElAs=!-s&RLEIAU5&WxEPe**0>iYFP;}AS7;d zQZujuCBgac7$jO&$OW<0pG?KG5mD)f!>7s2 zLG(sn{ZvTJqG7Jt7YIPN$0wPH13CAHrEHY71s@_xwS*Jtc!o}S>~dE>qpmGzX?C;D zm@{-UcK?(B#T|;_*YKjjOofwgI@g5&0`Y})o&u4K}r(+e>@D{?FOEk{~G_THx>MhWNOViEsybC*$c(ZWtTCX71yRq zgQb6g&V*JT{|(xr7VVRWor;ShCT_$r zo32N%q+e^eB({ON9yEo?6wuRE%&S6K)Au0gdO}e@&@Y(B3&r$YB zbO0Y;;~uaZHlttOrKJ8mU{fc*aNgS`z)w90Hbk`6OMK)9PDB(MAc4q^yrKtt1+(7R zO&%}6N+S=2I#)b35Ds3w&@#PQYGgIYKWCQmft& zcJ4@NeCK;EXfC>oy3G!!P+$^BGm@Holk<5Gj4>n^ol6KXgVb)G809-y2Yn-5gTaHy(^~{q5|F;dqGGL;O_ci$g-*31u4l; zfIuf#zTwP>*xN6yNI*3*oFo3DhN%@kLWGmxsrEY>!aD69RUu%EkNX8NPwU7;>RD^= z*rmANacE)gA#+6cl}5Oq;OoTUGq^=FNNHddPD6@@K{1UvO%cyL@C-8ixd7*)hDx7s zDm3Y+y-XU$n@V(68RHKe_wYs^Vbz|0`F-yD!nTta06Oezg)n-@7=yBCWD_@==T|fB z%emye)B1PT)QFGb?M-gN3#gcBK=dQZ^vZm@ABF0^5Fz`j_hIp_hU z>!eHg`*7KUUl1h_LE%$n$c3s?xsIW-D|SI(1JXW9Pn0SO`(554wz~6+5@|D-N84;Z zM&=St>Dx-rt*qcB)aMFmv`WKF-IoX7a|Zh_3un{_2o1!f6`wT+Q5=p>?hYfc*GtqG zqA1jagAO;X23e&WiZ+1#!p1%48S5~0DKT4@-@VxG(-Jh%AvUm6KnzL%+wO}<C_=#%_ z*uF>8$B#E5SS`ekF8wJBPBkT|`z@Tu{*?gzjn}%&L!2 z0Ng?%sp@0gWKNf@jQ#vkgCf>ZPyth&n3~^E9ucFemf&B+0I5WN1!K0fNbV zZJT63>|GnT_kLA0O{CDJ0r~aQjwQ}#A|QzlEu{^fQNr#RXiM$<L`iYce(b?|4?s z2F@hWlBrayFxlV>{knwtj_>U)G(^UU#e2U$LT;ezy9dcIgzK5HY zP^N#yK_gNC{bF*7dHak_*@D9-l2irYXqCpc);)_*}?LA-;-vewuS>uZ;U(!>He{N=^oZxWF~o)M0I6cduAqRBeJ zm;FX3K9GLbx$;qISAl#jYFhw+g}3lJG(_B5YClWdstXJ12$?+Cv1-htjsI(LGN4z7 zN4ja_9l)Hop44FKpo43%fZn9#`fF5I2wyjTW z+sVeZH?}vnZQHhO+jg=^Hs-tg{XOSC?>|%3S6BCRUp;50Pxn-H12jn#Kmvj$AV+nt z02#VQi+>qts;pz^U;ripl1yv_(;P>O9NGR@3kXNB{|`NibHkf9&=t-;Etz!!EdrJ~ zVN0Z0yumP0{~bJ>%FpnoT!Q8K9tL?SQjN^Kubvz5#R8?}LRP9-iibO^KT*V3E8oyRtxFO}T(so`y#v*JAH6juL zq~c2v1qBW8pYW(7wC){k1nRu0J}uL9!sb_$v}f=>=1X;%y#Nsgn}7V4`gK~~gvv4&xPh*j|6H+kgr+oje&DyE4|M8u zbh9J7MZDv~Nq^3-f1I&PuKcc+zJFCi9cVodJuC^S2^;K76ar*m@eJ;T*=delnX!6R zHMy;djnEuYpDyR2;gK%5s(dhR%q@8{l2b5En%GJ)^&KVkDTuBG!RK?GhW{Ug-TS5^=^U{=)Z5E~LdcP|L4=Gy zt;(*q-%dIC79jIj{)h;TFe_3|ma$&5t8?Vh&6(JR75C}eD{v$e0kd>{X$c$X0;GWF z*wok_VHX;c_0_i!maibU$f4G8)OFotGJk0)F_(e~{!%ZHk4pvhd{rQ>Wa01F%KS^X z+hr?0+H$4L+nkGObtcd$A_huiGBqZ8pMeCCr-~`%Su_Y8(jPRq)f00^SOy`*)O5+s zr}~dqfgK?v+Ao%jE?bv_k7^!&iZM&78fW_#{BtsU_;3q|ne$T*zv|Ce+lIvGLFSTk z*9d$YCCORQgNn(qNtX3Q&*NQ%Z&XIF4iiBYl3;ne`mi?AAvwRIvT|63B6BHz^)K^H z@6Vg##Ljj$LGY!6{axnac85WGUB{m^(_TB{mg(P zHd4Ew0wxvq~Z%2gZRiZ+>mQ#%msH8Ev?yaLmr4;G{q_$o)vJ)xr6Fg}h}pW1JS8X8s`bJ>0VZv>TydiE+aYv}_jnkaCNC&$)jv%6+VWf)ap2JUD zkrtqUB4bd6_GwTfbX;`!`2 z;qHM{uS}Qrgk45x7|{6#p{>N(f)|*p$ruk6B%Jy_n2v{PO5taA=6d6iMko&)>=D*; zc`SNuOh2WJDBfJ9yQbV=$w1bh3Vz4ePZODQKX5JL*k!9QNzJ zBKPS4OK#ZwZzF+r++VE*i56&k>S_sUlI1L%%6h)JFJW+}A#r*8k1HlmFR#5~e1=yw z)f;*f00m%)TYH=s9VV)ax;Yo^_+58!R|}H>whF@gm`f(z-yqE0&8(V>R$!<;Q*e|n zFpH6` zT_S`|>$_KSuHQ#TkDsbXJ8M513O@Lw{f1rJbrJElT(-eg9;(K{!Vn2UfINcpePBWZ z@+a3l(Wc0;fpVF-DR0Sj(`lXb3SZT`ghe9n=n3vKqu{XV${q3y#D7(D1o`P|07NM3 zp*IFJ>gU7OO&!K}#PdA^D?*@IgQ>)$3~wj;ug1T~OyvKpaPnt}3I6zoGqa2UKn-^# zfrXiF9FsaYI5HU9JGJ4P5-Z~t={E1eFtlIsLC`s!2U!zbVEJF-h=XS+64E3Pv_EkcMX}@jTG3m zb0R}$d^f0^Y&XgatXw6kI;7^xzx9-QO|bLL63obW-d9BV)7Fc{dz9LAHGjhc*;#|p zQY(;l?Z;O}UYLG9dZ)30su{{&dHlpo)d+J|Tq%u*@$AF&!@MEB<+NuPnTx}=i1ddN z$V@ejvLl{pdpx;>e}(N6wS?zaMP$B7G0vI4FZVr&vt`ZQ&p4;7);4^dg?<)m`IF<- z2xw|1pnyC?t}m(d;99&{Zx9+hfoUz%q&>>^Pg{0%{!4IA? za2N5ZK^GME=lFd+b#>(t0wX(ViR&Y}=}V3w(slQ=rg={wR-9}V0PG@9;z-383sk@9 zgGorZJ3nDDyV+FvfNaa!IpQ=tzo8)x0tTkVtp{CYf;6;>h!45!{04En9+72OmAQMt z``g#5hp!^Pp;(=f7o3-=`IOpCN|3*8q^@`M8uO|oKJW&gP(v4c&szdm^pT!)H0*73 zvQUM27+C zftYA!2A2b8u(N>oWS=c1PR7a@hUbiy*N3cgLx{er<-PO&6#u^B){0oYmjXxocF-*l z8Xn&e;8NPi4px>su0Zq@C&69+HvHQs{8zlw}0lhRGe8?k~QOg@)h#R+9S zV7h4RTbhb6QY59Hg{PknaN$9Sx^K{}&iDPm|My$~Nn_D$u(sfdvv#{2#~6}?>|U>hkI6JT*0*Uf2al4Igz-7aq|a!H zp<~VaLq0yukAs5Kv9UKFjR!$`TjLS|?}*%Ia8b;dPFBgJLlPdiYLj(vD#EtM@q^&5 z;wDO8ool2E$-PI@Pj3S^u|<2kmvyxHC*Ncb?j!V^yC%=^UMldOh0FyMRbA6Zf?E7xwK<&= z#6ZdJ_-7{u49v%p$H91uG;~H>V({S=0q58ItUT@aOW5zZ>ncXRAxLTb~=Q5ji#qCN)whN#O_&0>>StKNj%bKj(*~2N(AIST+fN zE~`JgLXSxhBV`2$qO%zDW2JS#l2$zmmIO~7I>>t%6dnt?P=2(gA5SAQ_imnbPQ|Xp zC(Yy`CI2PY7rJE-g+lgY&WB`ep~5ZvcKxUaDw>M~y=N+9SOJusGLlA-c>}L|8}^U| z(Snm-hx$CQZiP66Is!aDHv;~p8fy-<*XTBKry|C=Fq%6KYFx{5w(4Luhsy5KCeMqs z))1*Oa8RJig|=G8!1RU=`kJz>bjh(bEEX~O43EUi*(0=Dj|59-A51H{ z#b_bEwFju}nV~#5W5YT`#Hz8N=3dAsI5w=G+H=FHb6soy9EFMh>)g(ZcQTwA2XS?- zguxQkJ4Y$!8@W=`7SM(iyJq+mQ79c%gnl~d0Rp-Jv_)K&U%Wsw%jm;-=*2jSJY~#; z5#`1dGeLziY_(hBFWtqDO8g?3?`!L~!XThjy}7dPcU?`*l|1qcj7x7-Lo_qtDaLHO z^f5t$gsZQiQXugSwvDlIFt&BqHyJnf)}M{rYo4MAMR%cL=@)*tZ)w~7B=DO;SP|DK zv#YgCeA5ydFpV=d>nQCt!(FlEIm?IM!EfInxd4MXziZ+w&hVZVSi=raNNHHX7E=P5 z${WduQEkCIxEnsj>Zqkue=9d+`KI1o)8faEn0$OU4;w0}40G9NQ%d?l8%Y~n0q_}r ze%bIBj+(a=jzVav<&^5&Y#kyD;V9faL!d|L`V~V8XGgsmo4N~(yjXOUExc#KUp~&d z9l@t%Y$e$yzkcbb2&J<_H1dN!3)gdfvNF&cm<(MlDk}3{hr$zs)}Mm%fcnHqJ8&pc zUftCLT2u*1sG7}*5aHe->NEhrkK*@U&`OXe2T9ds?BP8Hu)3hkQb(88!OVO4XUpgU zc>}{if7~^IEUJK!qA&*y+HkK{`>zoy3NLe8Yu3{8Uh%zy<-p9O+bxoa4yLXUl}i_W zm|oHJ$~pJeom`lpmezurRZ-XNjQ|Z7>07w%V+~rp@j$c;4g~WgM90Sd&}?3?u(Ua`t_x}6w?TCd z8Ov1pQ(!dDi7>+Qv5K)_!?uL?FqNYpR->fv-mYe;ivrLtFL5AHf62~Z9;MQAN`OU7 z#lzEA9G}?pu^W_idTc!U+sr<=5U0#i6>(_x;g$=+)z~W*e;#NI_d;!ih2LLctSzjR z%lS)ZRP>l2lGbr>Po|UIwtUlwaAc{UKS1rYl8bdl9 z$U5ys7<}Z|dpH7FU~n!#(^jDyQP~asAjrAARWF4!G#EMOt$EOk6FqVRWL%}A{R*l^ zH&!u!(0bWi%G5up^`e#Hs|vSvgx6BxyCjiB=l%Ax+|+Ze>Y^5;U5@=ayn157S`G6L z^hS=uCExGo?5#PXDGG=D%V?NGuU|H=@eO23BzcT*?aj~MG(X#Y;%=zCx_P^Itw3o| zM1^$KZ~ZLYnbmuR?rz_TL*Zbob$Om`!OPf`D4SYf_YsT>1w2Kd9vJflFgy;aBpThc ziMzwyLJ8=EIjsB5{A9Cgx?njcRi40&c7;+{Xfzokb_V0bSLT+SP7^Fe*aSWzt4 z9bq0%Ln^eN$##Cz!jrQ~pDGNh1gkz@N%OBs7m{Vf+!*CO%=Txt66^HC3y;s<4&w)2 z+%RTpaV{`~p`|HaaWlc)ZSfmW;abW77aB@L5ZwMmX+|f+ziL}CJzdFx)XlS@h9m+4 zYE&h_9AuVGNJ>{q*cR&MZ69OcsC|*GJwX=S9BdiITNo6W1Bx^LGyM)uD*vQ6G0D+S zpGX?g(Y8nC%IgvTY%vbdC1(;(fJu*?!fN6F4YuYtS(^oQ3iZkrwq^F1zH?Yv=p8w^ z0&!)8ne1IrO3j$FNm-g`knMQHPNdnyIq4eSWQbk;846;Vh53u%1ZSw&yCiH~Jg3gYh^entuqBtkqj{BE{gqxIx}?d(zw7| zV-OF-#4Ybk3AJ8Bv-=gU`EG?&qrDJ{mhKHFGQUy@l?&6*BhVV2b!&R) zE53(h4Er%wn-b~!`|1-{pnZS|#8A{klR!f2Q52zVTpi#9ux0egvbz(T#fVSd&(W<; z+D%LF*PgOk*5ae==tXrZfUzUs;}W%L!s-w}4BYPd!%rL0sO_1YSUpQ7_IvM!ts&x4 zPjT~;cT1z6p_dRdBZCShP~xJs+-Nv#xMR10kMEw#&~XKw3$abw#jHv2qXQ^_lsy*b z#$$Jxc9F2M{g#On?9di11UevZ&ng#UxOx^zz^6I$hyaby8FvZy8#Wz@=FoXvg9V?F z1nBIAKP%uOA1;>Gx1I+e_5Aa(`}x-ay06FNZK})uA`?xz2QTG*3Q}>--(B?pfyDB0 zOkkP;bafBbB@7-}b-Mta1TuCl`XqeuOWioWom`%#C}(&7zn;EbO$iJ91xK@B5+wps zD@UR^M~3#pdDd33IUG_iI|nF+SAxunceq@H!hEZGXg=QstOrE|#Sq+aeqL zZv^DyfMPrUZWhc9{&`5{f#=#&{)+H?a;>Y7ElXN&knLO6=(dx8u!g;nWj-yY<@3s~ z61|g{Ud01yaq)C15%<2?NWCVQ%&3Ah?I-@x=FSLBijwSTe~BVx=uByrT39;8e3au8 zfYJ>?V4@Nlkz$e3H4L7x5$;FKD&eYG0P1z1m*xD$sTEcMm6;O6nAW7SZSCar)Yq%G zzU2*FG^m_FId0{rQwm|DSIZWIvrW<~j0fzsJN8cCz3kBRvUC$p$sGX+Z4DB;L70SA zqP!CGz2w^OWfp>cTf(qrE8|5`+sW%2GgE&`f%T>j!7e7uSQsGI>0QIn;J^-+@CKp# z*nPL{Pc3Std?356Aqw*&Al9N^zB<*9T?%U4Qj!a zyoSw+aTi2Mj(Z?b>?l83sX-hRxni<}-OBmQ0}Z5p zj7nZirXUXFEnx`(Pb+7CbX)N1iF0l^Re0Cm3J+c(SADQ~dEn z$``@1)@PQc;1;QRyreQ5(I0q!7bU7>-~L#kf4<9BoQs~9brqk!y=I5>rffFJ;1y-= zblJRHRuTlsdM+|DP;ay&OFBTW4XuRO1HU6W_^U^o=*gE!H`zY_DDjw8AlU@Lkwk3{ zgiA^bS01>zh(S-FzIN_snT-8q8u_Z8&!0=l9qvu7Vmx8?VlGtjc;esHc*dM{1EJGD zIdk91b`K|#_!)Jt8oD~J;)9U>a|Lv_|X*TdclRd)il zw!A$Lr!Gpv}%`&v-Uyn6(3UNP@KLU8V)w!^Nt<9;h$Q~Epx9%b2 z!8Q~A1717NZzMh?Y^~Y7`}K<9hG7-Gfb$+xeYeVARSF3wMhmA#_NdS_9*+RA?zd^N zo2fZT0Woq>?f)Z^NmJ5D9Z2ns5)=J+U|qCnx43VeC`y#(o;8UCyr^F7P2bBtUfe!h z8ziq{D7Md(_sKpe(&44U4Bk~n7-e}}@ zI~)MEXbi${pRekE7x{PlvVmv8WPc>%?%h8ANx2w=|Itm)vBoBa|uGe|fI?!%=*xO z@fy0AaSvFOcX{TL=_D=6U^z4Amo<%;hC^UZPtE4|z7`N{CHNu?OQ>;4W!iA+oiNOi zHm2|AIt8~yvY4x;5BwKPeJQ=#(dqk;>Jj`_uHOq z%IDFj0mDXnWJzL07aE-F3cFH=Y#zDDV?!^wyvmm)rpC{{F6E!5dAE96AVZ(mY`=tg zDj(9jle$=RB>IOk>#j$t|N7t8k1Ntlt`+u@YsVtfQdNsA`Or2T?SP(3(R8F?EVJ?_ zlbV7T&3o=V#nEh6q-gRhQ7s#0g}xURoKI(%?NfytySTz#QAR8K)>ywTsJ(_`<~gD* zJghG`E%;CX9*#MRy>O)BL>T>R)5>o)lg#eg$g22$6f7HV!N~W%GM}_`^)hWz`-HD5R#zC?h1saSP|G2uDMJGMZLcdP#qI>n5|o z>585(fMNDPt_(O=Vl#oGjdO_aBQ63iqxyJ%I0;AZC`LK{_t#%Po(|OutLL8g{yW)j zOm?W+4Gx8#1TG2{b=AT7?Y{2b4N0-EVEh7;_YonATCh~aNbhMLDQ~msu-hCa?RpF| z3Tw*iR}R57w*BCTgBaAb;wzpdk~P8#S4S?&MQD~w4v1@UFQMum7yCFEu9=Wq=J^v1 zE&PMeH=mxrD)Bn&0Si7wd77;-W?0{dk-kH~G7-O|J2Lw{3tijOYtDvGW8P?j*(0Rh z4I#1xa<8*s&rqZHhwijj2{rnJt}0WnC&Io+6BO+1dAsycJvvSR^kp z{Q|1&ybd{|J&VQ1;h`dbry=NM#E823P6eKG&W4)o-zxQ#F?-<( zT#vZ|5BVk^&)_MWC`L7t53-n%KF}~OzaEL|R`C&&@yWQwa{VU?Zmu9rE473Elt$Mq zRXvGs#93{Ams_VxmxYoGw=MTW>(}TOSY}ukdzoN^MZt_%C$FPpIC|NtvC13cL<%Li zcQr(>hfp^XOy<>bQkIUi%kn4tZ2-6uEB2CYVi` zo9BfjLy2POcIFHA6Jf-->B+&ZQo5vr_+1z2u+}aAMH5$eGi*92p%Fq7*!D;vv6@w= zNX#6xs*NE0KmxZLYR*=Mt|Ls-oq@E#@21bU>Bs^RQL!BV;0~)eT$mNNuk3Rt+g5ew zKdWWEnb_rlXT<9A9a}zTBc7Ke_EoKin@BPIIQI&01=chlH7xlUtgvn?i$Q^c(XizQ z(_37u_g%gLzgddcT@;i>bFeCsCE32&birPLin9^)nS4^^E*;xF87u@8D{oRP80|bML737v&i7<6cIOvora$%+&R zGyh6mpy+mt4C6ts`sh%dXi8KuY)zy9wr{sH@$Obv@GExcumEO{kfa33z5hJL?TC#?L;}Y@6v)wmlRO27G{e~wBcy_b05a6 zE{(I5vW{bABXHY+{7N)!rg77UqX;j+Bh}C8Y?Q^9)8HxN8u7dT+&`(!T(U)$%O$_# zcX}t8v`u~poHGicCu|cH+=0&cyEccP)7D;&*#PnLSed7+RC2F1lu9tBcUVJ_5RNUo z(Mc43(Bh?dFpr%+t!GdH}3#6^_j@v1hJ~uO)OcQAN zi_D|wwm8WHb^oznX(C0+QWQ?(kL7D3^B*f$1l>MrK<$_xL`nrgVx_l$K#_yXKZ*$1 zQ__-Wz?>#z4FASeMv94xazyWX5qg!3^!a^9&rp#R^2XJZ2s9=7B0 z3{GN?*BB}=|Ct9p0u2aAu#3yl;=ZJAE)e@%k6OAl6NXVf=bvvHz}%0y8Z4z@Ac1>A zV1?45*-J!!lHj&BG`k<$@Gt#AiV;ChVWpovWyw`}i%Cu1S_;VQGo5ew?KFjLge7@> zq;C^^?eO2+q8PQ7t}zum}la_cV+-8#$cp@lfD`hms+^jq;S21DQ-EvgZDD_gDtV#Zh?3n_!|lz z$Kv=4kqREpb99Ej(%+LhaqDaV`$H`CeaNy`wlsk-q2p;P(O z0e!UZ^69%gx(r)|n|v;B0wX)nLWhWh~G7~I_HVNWEb{5X7iM$+-KPoj;fo5sKFA8bwqKylVo3%)R&~j z6$nBgNsJ5JbcDcXyCC(#{8_PvA3)SMm%a_xk{n-=g6XS@d}l-{-;RM*3$SZ%K}$+L z)@5m(r{=J3pADt2UlmqzZGw#aNdmK)Gtxh56*S&wX~3on$gUO_XbSP4`4nmEfGLFW z&j(v0vgM3-cN;BmTdf}~BSDjPfKex_x*>Ijp4YSPJF4dFWx@C7e_8AGZQl=qZTmZ& z4V0?XR=)o|RC%%nK0tpJtpPoF>*ooQ6zP)!`)seLP!tAuX(&&;(udIQ61M@%-@v}? z&s$Pv{AHU*iMznRl!_Q5Tu56^S=E1&W7yC*@jI)qNYM1}79#c&^m3pje-Ef` z2KV2C@ko|B&m(;&Vmx`fF|wNNW8_NPa7-$BJy3;&E(!rQ)*pDB@k+f&XLKElb|p|% zD5o(viAXiy?k;K0f7YMC_UnO-@cBOGa-*!R8+MOh5LSBxa&C81*Nq8QHpsZcyx$pGSDBT=BhDW`GzCq@>CfftwXW4;{wUDb% zsnDJW*KT^3IOjvpwfgLTJz`hDWSYl^U;oj=NZ=njK+NSO(|UB<9jG)CGo16DFz-t=3M2&>h~JoI02%+0)N{6^!~Y$vmeoV|fV zWfOkUU^Nc3gl^29daU^E!mT$`kI~b%@Ak7vIAK#kk}{nk`Ts^r1ZWgs>-xjh*1)MS z=^bg2U3M5+tA^!v`qI~*p08tj4AV2{FWHkwV+wN=E&OTVB(7t>ALF;*cBEvt^)>y; zoA!nC+2(t#t&4V5#{{Nt^s#%@Gj#Rki8T(134RYhGp!M3Kpv&}JY!yk9IjSeB_Ms@ zEin`v;5Zn8CpbFf9!fosL%o8&Jkph|pf;tX8;dP4J91*|XUF3gO2MfJ+O8h}>c_j`jY4*8M7E(Z3**LI1%_|u zrh_5Y`1L8+&&NsJ`mHZoX&?weN05%3pQ=!a(!S?W5SdP!=lvAZlbNyx?8oaXP7;6Q z9Y$A~NY(BH(vf`^m<6Xk4znY5=jpjJ0)*O4^<62Z1a`dD5u|Rh;e5 zispVJUU(N3!Dhj?`8>IYIYwx*I62CRK2m!oBj3mb-hVe+5;ufH=k!bxPeTooN}54O zf}h<~9{zQN+$|6(2lyTp1uQ(L^R0Km>n@d#?0hb*mxvGY#+y(#DsKHE`XX?#nh9*t z3#YAe?i(@dz=({qSMc;7S}YBQn~oR}k<$5*@TlG0Q7AG@D^M{h0M8#`M?VcoI{vij zD6Y-4w8zzqj*Dz@+U~1m78uckj z5=9tLOa;j3-s>{(2u}rX6K0jHtsMHEPQ4(W%tmt_gSww~cf@5ZxPKCL>N8R&SD-r| zlti|%sXcJlf(b%Ln9@)W79KCQKJJl=?N-c~vAwDZ*o?ep^_^@I9$)BSTRDgHGdn-{ z4_7u!v7cg+Q0e%ykHogM;zZTvNBLY;OU2~7&~P;!flpaK6cIR3`OB(E6WwODj`4pS z%Zi#B9snIFW-lRl@NY3|m_2_F<8&}$zPx&-YD!vy$G>LPSqKFm-$7Gg^8~uJ(s{!g zi8tEkz1xS4#V8gt0Y#h>&xb$x{2ba&s}__b;YuTx(hRn)R1{WaT8^dcdjX*%PEUj> zo~tB|=FfH#@qK^Am>@U}n>*D*~T5j+ue%?9NV6CM6e_U(0utN3`y$zJOdH!Vs8%0vhu$7 zAq!1MY%XZ73frY-Nsp*Dd$$c_y*8I5!37^w4KUU;DR=r96g_;TT!Wxs^V@I7286I3s)$k3!RTKrk(%bmavaVQ4cFpH~q_~+)h{%>>^$~bDCEFkCNm8zZ;$r(159j&2w;N|9CN-V4L^^7zHgt95IaMB*7 zAZaa*ga9rFyZ0<(^1!>%vVHt7@F`3sKgm|ka+kv%D0K`sUh9gu3Q5~-MmTenQ1~+&R|J&Fq$mi#pF$?rRV9C0uw}aGS%2V1ksap#Kt63NyPoAz*v}rm zjPkmmP+UIT61Qze;|LL317wLBTOs0WExq<1En8S(F9%>PosNHa*%_IE{6q!s{$VaJ zeJ@GK&obD!3V%qs$N0NvL3N&XH3Z3=_QZm6*T8+n_*!#U?rh?FGWJAb+hj3VOpPmt zTTh6QX8TZfT14|$PMG)}IK+BfXQ<%ZyXEqke#5aajR||ImH3d=>~+m`>wUnwy+ohq#+>D-00gwZgNpwBF7qP zRtJD`L|~~FbT25p=NGEsmqRg5?*xN{5QZ6X{)t~HFZx}uTo$=@Kk)cfI4K-0Wib{Z zN-%6)rOcCAV99O|=DsRu*c_IR&0fozytOzS35t)ReT~RHVPvee@RAYm7W{h46ng1~ z55>xmw$U5qc{hCgrT&5^at2^1zb6VF~vv$|6L8HndiL$CM+E~#BuzOpx) z7prp)Jn2Cf;#0PL-3d_E=y-Mkh^?7f*xCZ@Z6n5nB${rpBGpb0wx6Sk$(DCk_Y zC<|#?9C&U;5i?|DZ{PY-1sNVG(cs$A`Q%x*gmJXL;mzjF85TO%7DN1?7#zIML+5Qb z?gflnNvWRGrc%t22JV-tZsct@4ziwT8K2%6?<{w$k3p^eeLbp36uF1@>CYax{j_9O zl}vG%U;vHeJ1(!kH5IspsS{=ejZD_sc(4)sjBmJN942h42%6Xv>7sx+kn4lu2-kB= z{-}O25l^-&c*|uC} zLryleR@62go7zrD-Oo~y3(M4`p7;=ny_bjh@F_)$&60%~uA0{Tok~un8^qKy0X5WP zS7|XdV&;#`UB`Tr;chV4ElN%AK@}OQU&s5Q!Sj5?-kmDf#ug^_I<>LeuEqVl=u|Uu% z&dX9Tquh*rED5<%%pWjh1$VJXL~@w+?J(d!?_va_EF2=Sz`YIVCn<}vfr^2~I|#YL zDRje!yqetO5~6%1#h@aqKELbcH0lg6+eT>$2$OoJ*ttdGgHCV)7mm`aBA?D^?vfAk z9f;8!FduygM^;)Zl8uPale^XFmdy(17t%vUx%$n&uEg9OHbpJu*mlV`RqmUhMxtS- zQPFt}yv){ofcEW8V{RKbxSmoV?QJKCS7FtUOtuB^+_s7mAdhyx-MZeu~{C5tSXFbAKTT7;5 ziEjvNc53#@S=vELaF4N}XgHzeJdz-gQtN4;B8jo7M&};Nm3d0IwIXc6nuPoL<46fj7 zCPsJ^{2VfXXO+qrTq2d8vIrAH~eI zA5elN_bv7O^$D!h8{=zex3%;}HA(K+TCv7w%PZ_gBVm8WU#k4DpO^DqKuX@LSQ9vY z;$7MJOQh^Gp;&?HFbbvgXfj}#3Os8v4Bbtnu%Y@Ri|De0*r8;hxCy<2Kel5iY+S(ekreS6m z%^%xq(X7RhGn6#5CkgE4&Nj`j#>Z2T1!Krm)z|!Mmt&_I{SyHTBm0b`-g^zz67ZgL z2uR*}ioZ}>UJMfkXPO`;KP)i_LcE6k&R_vsi4FTOwWyvH;gP??Vh$g}3MEArmr9X0 zRis717ZsZ!SBIzwf2R=Ru&1aDjCI;B4bad%<6xu!vEoG$#WSxoDg8wWA|e#3jrSxvx~wKpi? zpwp1^RXDL25yoGY?gueMj&Xp%HJ@`C>NE%6=qzBZx>aOnnm~-oP`!Q~KGu?(2(q^c zODN(v)CX<#RF~FN2X5?xI&o1z&0ryZNT*PSt4R^I4|O zsT;}RW`kDP^U%)EFmKQ~>QowcTi!HA9!OQp1>+CUA%FEWg#SJpDQRh>UiG{1iEqxi z)er`mFtDxl4nlK2S17=#kq=EO(i%GIMf^b&;B^xeKh!i#{#pn0ef21Qv+!F9%J&Zm zxsua`V~Z%&$&3q|I1ty>Q1t-h-mR8$$i-edy|m1oUVOW8Lnh6~7_6;*r#Ui@ zD27x6XVl%cdOX(=Osb#_jhv9$2BgjfmzQ9#t(?UQsP$^UA9nAsWLS+j6(z0K+*ZPk zOS)JzYfZ|b@Q&{24c4TX0ZALnd(vDH)~*=|76u*x3i9)KY@XODMuX$-2&Ndi{M$sU zrLEL56LBYj#qspl&;)ajRGku+`Xzzb3C8nA$=lpNm~MtN8ll0dl-Zl_2hjU`COM&= z<&WGQ+P<211-lYC4#EewYP859)5*k2t&to1!0qNZ1jXENIs7e!RP1F*pg(6WO=?40 zuG9=%uuXb^7|=;WpvWr0$DDs#;Ea%p;a%8~=L*_UP>!mAdAvRwF`Qi6@vG(KZlwQS z?ErcTlwtRR#G_bMX}o)IK@4eF7pf;~maa%uYL`N{V4l$p8wR`%XOr!;lT^GVuZnZd z#3ESlQrP);Fm*U?4rZLsUV<&{F#K3KsMFtHtjtwW9%6ZrdXJ0ebEAxaT&8^=u<8AwZ$q-RFdbb`lN^fYLnu@z+brJVGjW`6lUaDMtTQ$el3oq@PnvrO zZ8}iJt8Ro54(VK{GdE<6Yzn2*>n~;9cstykPxxDd$_Ro=PmVJe8^l(anRv~P(YCxIM2sl}B2PGqc{|SQItNWz*>h4ls+rB2*zs=yp@F%#sF#*}PFyE;y z;`jQ4^b#MZdR|VL)EJ6A+aC;%%!4FP@zZYP?QNJo>UAGh+SGjOK3`VjXwQtNxv2#0rK7Vf0IbB-Iv;+bdyBDew0COV@_Q?TScq zXyGAF39_Tg>I^$EsUCV)F=c4|>5y)7u_D7~r&%{x)~(tT6o-kLswTwAFPS=JtiST# zzzu2Q8*1Hn6{^ET&(R8t!Jw2t}zUBXAAlb#SIj%S{LRzO|5NQCP_5D-P2Z%`JAbDg8ej#4mlRQe6KGv z7=STU?!8?O>zM9)I&&KAN}mE<4<|iD( z-d7CdrOZ8oB`|qM+O*#d1G!FYz~I>*4{Y8WN%|}&1AO1C$9xstk>@i&T%ul*n}Oc! ze^rupA5HhY^b?bS4kor#pHjYiKCXp;WY$F)QcdKw=R%O(%m*!cI)Jfa zn1E8mCqR0de_+SnCr~wZ2ph(b+AfrB6T+CVDX?a7Nzd4d>;vzySw6d1AvX(}sj?R|0rYe8) ztqskch*Kw#LT7(0}ygDHnnvoVr6540f-pd zOPX4mTR0Q3v$DVd{@w6j9z8P*K+4(B+R|9a*4)~Zh!F;$>}+bIM#RAd1JL+?C0LpN zCPXX@9hFU;iKqZV03kzT7iUv|kfWuoIY1a70uTj=0mK0k07-xpKpG$ekOjyAwm%qVDnGd0&M>ie}^1k>uhOl@?W!TU2Oh72Tqpewg5YT z-9H1n|37nfe~s+{_Wy8uLq}8F|H%1I`(Ic8^7?Pk0e_{Xoe99++QkXr0C4zc@8DwU z;F^!SM)#lzxw{u{s%e&9RHPd0{k~_e;=s-quR;T#`1r3JDIxv zWx>hP9pGgBk2`08^S?6A7LKN-0B1KlfD6FoAK+qZ^0#Nkc8;b1SAgq3#0}u~Pq+iz z|33hYKytq>b7L=gFL^F=V=sCydMil2?Ck*Z_HF``ormD>kP0r1DO4R+PhoYnFA!8 zfIxtXy_t)o!D-6zN40b>l>VI=q`4jn18JSq={<3U;TlT*#$KRIoZ_D+!<^IbuGyQw1{;V+5 z{i6?ne;oC%T>cdJzcTp?!oaQ`E7Q&)$7X7Bb7T?458)-^TY-;#|7phm z$CLfvO!NO>h5`R?Mf;;zR~wtZr{aH;p6Z`_)W-Bbg(_iWYiZ;8-+25dSOfUi?Es+q zKM_f}82u@iu${Thzoy61S;Eo-XsT%GV)CbQMmEmCf2^zh)ft;V5fts6E&r|~JvY~X zY^hpUnpoQbot*(}|6l|DDfPcc6t^?6H~p)jDlUKeVB}=_uZ6$1Ocvr^Z=T`t}WY}@P=45gmgXC3*))dA5MXb?)mz%$+|CuQ>O{=J%5hwqWX;! zONl{9X7#vrB!^2aL767AJXRZya^EpRl7@6w>g8bRuG~H(oD8PGg9z*4C?gEKbvm18 zRxlvzY)ZyT7EW-?rBW@5(=h^nf%s)gh8{+J$=7>MJhRR0v}8oSZMTElepApOnW*!EX1Kg@%&`9=Hzov- z0J@1O2*dJHiclbf|MnEN@Z-e*zob2|b<qOSI6&4s(1g3N1*};X+b7E~UZRD8 z-DSPlY=Z!`dx#?6L|d?e{@rC=HJ(Adp)63E?gt!dHDRIpvWM@eADjmb^{)&Vi~(cw zIE(6^+lNOqT@){-jY++DDVXraIKzgQ}I+vpN;7`dyn8f$ifjvGMze0d;-kO zr$7^kevHzDrg!Y-J8?g|P%AM>5O!wd#+HPfG}0Z08(YL+uwEg1Phk7loWhLCB=~w# zitfsfT$X5(;+{YVSE9u zc%d?x+342A_H3Snwt|54&;EpmQ;1PMZAjEvVRf}{zBP6D^T5e3DA8wT6LSZ2a9 zc0q3?nheXnt9*08>1Uih5lO~x)GUq-p8+p>^cu0U6ofjl9xS0xZO{-FT&W-`E3wnp zLwMYCl|f0c)#yGLOg1%armfZ>3%A2Hu+_G;j%SeNIxS5j815M&!eGN&?+{Hta+Nig zzx0du$NX}nii&mB@@s*b7{_6H%6E4!4vH-X#+N3jDb=C-G)c{s${qeIRo{c}ljtPT z_!r4Tg)*b9l3o|#2Rj^|6@|GLM5(iTP}pX=aRjya7RAWLH7d763+TP8Nx$(6BLxxC z2N2$I7wC_7thA3%Dj8-MY$?hwTbR3A<6>5paC;d8uvHa5OV4LF1U{o+k@OlwN;UU- z_w2iM$-XvF_f)dPs1E2LTtE4`C1*bpsv@02)?X?dh{W*y${Hl}O+qK;Iu7x>lE4Jt zkX8r{HF}?v^Gbg=mTo#tn`~|#Bu#l(^LWA)3N)v|>PgUdl)7()r0>sS4>~?f+Uj3l z5@;o*np5R-ic2DMu?iTZVPMCq3Y0Y$2THl^4viz7?piRRlmmT>O zPO(B{zB$y>c}y#|uy&;+bk+Voh7Y2-pIs1`)?!W((sK>1CC=jyht6zt8j=ue!6!u~ zqh;0*y*JNNQSY&o<^9Q3Bqz7M{B3z-hXmoUMB-U}>wc3#`o!YF(G82W_DUiW)ujH5 z_%|W&lkFeug={&bN%=d7DzOu9r8@gK#zKS&Tjp}DAY|c{W33PXp6hEY_Cy&T7jLM?$V!7+6TrhFXLDj>I>?ybIeL!