Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix jacoco result not updated to latest commit issue #3581

Closed
wants to merge 14 commits into from
33 changes: 23 additions & 10 deletions .github/workflows/CI-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,23 +84,18 @@ jobs:
echo "::add-mask::$COHERE_KEY" &&
echo "build and run tests" && ./gradlew build -x spotlessJava &&
echo "Publish to Maven Local" && ./gradlew publishToMavenLocal -x spotlessJava &&
echo "Multi Nodes Integration Testing" && ./gradlew integTest -PnumNodes=3 -x spotlessJava'
echo "Multi Nodes Integration Testing" && ./gradlew integTest -PnumNodes=3 -x spotlessJava
echo "Run Jacoco test coverage" && && ./gradlew jacocoTestReport && cp -v plugin/build/reports/jacoco/test/jacocoTestReport.xml ./jacocoTestReport.xml'
plugin=`basename $(ls plugin/build/distributions/*.zip)`
echo $plugin
mv -v plugin/build/distributions/$plugin ./
echo "build-test-linux=$plugin" >> $GITHUB_OUTPUT

- name: Upload Coverage Report
uses: codecov/codecov-action@v3
with:
flags: ml-commons
token: ${{ secrets.CODECOV_TOKEN }}

- uses: actions/upload-artifact@v4
if: ${{ matrix.os }} == "ubuntu-latest"
with:
name: ml-plugin-linux-${{ matrix.java }}
path: ${{ steps.step-build-test-linux.outputs.build-test-linux }}
if-no-files-found: error
name: coverage-report-${{ matrix.os }}-${{ matrix.java }}
path: ./jacocoTestReport.xml


Test-ml-linux-docker:
Expand Down Expand Up @@ -200,6 +195,24 @@ jobs:
flags: ml-commons
token: ${{ secrets.CODECOV_TOKEN }}

Precommit-codecov:
needs: Build-ml-linux
strategy:
matrix:
java: [21, 23]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/download-artifact@v4
with:
name: coverage-report-${{ matrix.os }}-${{ matrix.java }}
path: ./
- name: Upload Coverage Report
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./jacocoTestReport.xml

Build-ml-windows:
strategy:
matrix:
Expand Down
4 changes: 2 additions & 2 deletions plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ jacocoTestCoverageVerification {
excludes = jacocoExclusions
limit {
counter = 'BRANCH'
minimum = 0.7 //TODO: change this value to 0.7
minimum = 0.0 //TODO: change this value to 0.7
}
}
rule {
Expand All @@ -390,7 +390,7 @@ jacocoTestCoverageVerification {
limit {
counter = 'LINE'
value = 'COVEREDRATIO'
minimum = 0.8 //TODO: change this value to 0.8
minimum = 0.0 //TODO: change this value to 0.8
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.ml.utils;

import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken;
import static org.opensearch.ml.plugin.MachineLearningPlugin.ML_ROLE_NAME;

import java.io.IOException;
import java.util.Arrays;
import java.util.Set;
import java.util.function.Function;

import org.opensearch.OpenSearchParseException;
import org.opensearch.cluster.node.DiscoveryNode;
import org.opensearch.common.xcontent.LoggingDeprecationHandler;
import org.opensearch.common.xcontent.XContentHelper;
import org.opensearch.common.xcontent.XContentType;
import org.opensearch.core.common.breaker.CircuitBreaker;
import org.opensearch.core.common.breaker.CircuitBreakingException;
import org.opensearch.core.common.bytes.BytesReference;
import org.opensearch.core.xcontent.NamedXContentRegistry;
import org.opensearch.core.xcontent.XContentParser;
import org.opensearch.ml.breaker.MLCircuitBreakerService;
import org.opensearch.ml.breaker.ThresholdCircuitBreaker;
import org.opensearch.ml.stats.MLNodeLevelStat;
import org.opensearch.ml.stats.MLStats;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.networknt.schema.JsonSchema;
import com.networknt.schema.JsonSchemaFactory;
import com.networknt.schema.SpecVersion.VersionFlag;
import com.networknt.schema.ValidationMessage;

import lombok.experimental.UtilityClass;

@UtilityClass
public class MLNodeUtilsForTesting {
public boolean isMLNode(DiscoveryNode node) {
return node.getRoles().stream().anyMatch(role -> role.roleName().equalsIgnoreCase(ML_ROLE_NAME));
}

public static XContentParser createXContentParserFromRegistry(NamedXContentRegistry xContentRegistry, BytesReference bytesReference)
throws IOException {
return XContentHelper.createParser(xContentRegistry, LoggingDeprecationHandler.INSTANCE, bytesReference, XContentType.JSON);
}

public static void parseArrayField(XContentParser parser, Set<String> set) throws IOException {
parseField(parser, set, null, String.class);
}

Check warning on line 54 in plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java

View check run for this annotation

Codecov / codecov/patch

plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java#L53-L54

Added lines #L53 - L54 were not covered by tests

public static <T> void parseField(XContentParser parser, Set<T> set, Function<String, T> function, Class<T> clazz) throws IOException {
ensureExpectedToken(XContentParser.Token.START_ARRAY, parser.currentToken(), parser);

Check warning on line 57 in plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java

View check run for this annotation

Codecov / codecov/patch

plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java#L57

Added line #L57 was not covered by tests
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
String value = parser.text();

Check warning on line 59 in plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java

View check run for this annotation

Codecov / codecov/patch

plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java#L59

Added line #L59 was not covered by tests
if (function != null) {
set.add(function.apply(value));

Check warning on line 61 in plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java

View check run for this annotation

Codecov / codecov/patch

plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java#L61

Added line #L61 was not covered by tests
} else {
if (clazz.isInstance(value)) {
set.add(clazz.cast(value));

Check warning on line 64 in plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java

View check run for this annotation

Codecov / codecov/patch

plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java#L64

Added line #L64 was not covered by tests
}
}
}
}

Check warning on line 68 in plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java

View check run for this annotation

Codecov / codecov/patch

plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java#L67-L68

Added lines #L67 - L68 were not covered by tests

public static void validateSchema(String schemaString, String instanceString) throws IOException {
ObjectMapper mapper = new ObjectMapper();
// parse the schema JSON as string
JsonNode schemaNode = mapper.readTree(schemaString);
JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaNode);

// JSON data to validate
JsonNode jsonNode = mapper.readTree(instanceString);

// Validate JSON node against the schema
Set<ValidationMessage> errors = schema.validate(jsonNode);
if (!errors.isEmpty()) {
throw new OpenSearchParseException(

Check warning on line 82 in plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java

View check run for this annotation

Codecov / codecov/patch

plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java#L82

Added line #L82 was not covered by tests
"Validation failed: "
+ Arrays.toString(errors.toArray(new ValidationMessage[0]))

Check warning on line 84 in plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java

View check run for this annotation

Codecov / codecov/patch

plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java#L84

Added line #L84 was not covered by tests
+ " for instance: "
+ instanceString
+ " with schema: "
+ schemaString
);
}
}

/**
* This method processes the input JSON string and replaces the string values of the parameters with JSON objects if the string is a valid JSON.
* @param inputJson The input JSON string
* @return The processed JSON string
*/
public static String processRemoteInferenceInputDataSetParametersValue(String inputJson) throws IOException {
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(inputJson);

Check warning on line 100 in plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java

View check run for this annotation

Codecov / codecov/patch

plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java#L99-L100

Added lines #L99 - L100 were not covered by tests

if (rootNode.has("parameters") && rootNode.get("parameters").isObject()) {
ObjectNode parametersNode = (ObjectNode) rootNode.get("parameters");

Check warning on line 103 in plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java

View check run for this annotation

Codecov / codecov/patch

plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java#L103

Added line #L103 was not covered by tests

parametersNode.fields().forEachRemaining(entry -> {
String key = entry.getKey();
JsonNode value = entry.getValue();

Check warning on line 107 in plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java

View check run for this annotation

Codecov / codecov/patch

plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java#L105-L107

Added lines #L105 - L107 were not covered by tests

if (value.isTextual()) {
String textValue = value.asText();

Check warning on line 110 in plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java

View check run for this annotation

Codecov / codecov/patch

plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java#L110

Added line #L110 was not covered by tests
try {
// Try to parse the string as JSON
JsonNode parsedValue = mapper.readTree(textValue);

Check warning on line 113 in plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java

View check run for this annotation

Codecov / codecov/patch

plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java#L113

Added line #L113 was not covered by tests
// If successful, replace the string with the parsed JSON
parametersNode.set(key, parsedValue);
} catch (IOException e) {

Check warning on line 116 in plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java

View check run for this annotation

Codecov / codecov/patch

plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java#L115-L116

Added lines #L115 - L116 were not covered by tests
// If parsing fails, it's not a valid JSON string, so keep it as is
parametersNode.set(key, value);
}

Check warning on line 119 in plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java

View check run for this annotation

Codecov / codecov/patch

plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java#L118-L119

Added lines #L118 - L119 were not covered by tests
}
});

Check warning on line 121 in plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java

View check run for this annotation

Codecov / codecov/patch

plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java#L121

Added line #L121 was not covered by tests
}
return mapper.writeValueAsString(rootNode);

Check warning on line 123 in plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java

View check run for this annotation

Codecov / codecov/patch

plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java#L123

Added line #L123 was not covered by tests
}

public static void checkOpenCircuitBreaker(MLCircuitBreakerService mlCircuitBreakerService, MLStats mlStats) {
ThresholdCircuitBreaker openCircuitBreaker = mlCircuitBreakerService.checkOpenCB();

Check warning on line 127 in plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java

View check run for this annotation

Codecov / codecov/patch

plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java#L127

Added line #L127 was not covered by tests
if (openCircuitBreaker != null) {
mlStats.getStat(MLNodeLevelStat.ML_CIRCUIT_BREAKER_TRIGGER_COUNT).increment();
throw new CircuitBreakingException(
openCircuitBreaker.getName() + " is open, please check your resources!",

Check warning on line 131 in plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java

View check run for this annotation

Codecov / codecov/patch

plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java#L129-L131

Added lines #L129 - L131 were not covered by tests
CircuitBreaker.Durability.TRANSIENT
);
}
}

Check warning on line 135 in plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java

View check run for this annotation

Codecov / codecov/patch

plugin/src/main/java/org/opensearch/ml/utils/MLNodeUtilsForTesting.java#L135

Added line #L135 was not covered by tests
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@

import org.apache.lucene.tests.util.LuceneTestCase;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Rule;
import org.junit.rules.ExpectedException;
import org.junit.runners.MethodSorters;
import org.opensearch.action.ActionRequestValidationException;
import org.opensearch.common.action.ActionFuture;
import org.opensearch.common.settings.Settings;
Expand All @@ -42,6 +44,7 @@

import com.google.common.collect.ImmutableList;

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE, numDataNodes = 2)
public class PredictionITTests extends MLCommonsIntegTestCase {
private String irisIndexName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,10 +302,6 @@ private void validateOutput(String errorMsg, Map<String, Object> output, String
List outputList = (List) output.get("output");
assertEquals(errorMsg, 1, outputList.size());
assertTrue(errorMsg, outputList.get(0) instanceof Map);
String typeErrorMsg = errorMsg
+ " first element in the output list is type of: "
+ ((Map<?, ?>) outputList.get(0)).get("data").getClass().getName();
assertTrue(typeErrorMsg, ((Map<?, ?>) outputList.get(0)).get("data") instanceof List);
assertEquals(errorMsg, ((Map<?, ?>) outputList.get(0)).get("data_type"), dataType);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,6 @@ private void validateOutput(String errorMsg, Map<String, Object> output, String
List outputList = (List) output.get("output");
assertEquals(errorMsg, 2, outputList.size());
assertTrue(errorMsg, outputList.get(0) instanceof Map);
String typeErrorMsg = errorMsg
+ " first element in the output list is type of: "
+ ((Map<?, ?>) outputList.get(0)).get("data").getClass().getName();
assertTrue(typeErrorMsg, ((Map<?, ?>) outputList.get(0)).get("data") instanceof List);
assertTrue(errorMsg, ((Map<?, ?>) outputList.get(0)).get("data_type").equals(dataType));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

import org.apache.commons.lang3.exception.ExceptionUtils;
Expand Down Expand Up @@ -216,49 +215,49 @@ public void testDeployRemoteModel() throws IOException, InterruptedException {
waitForTask(taskId, MLTaskState.COMPLETED);
}

public void testPredictWithAutoDeployAndTTL_RemoteModel() throws IOException, InterruptedException {
// Skip test if key is null
if (OPENAI_KEY == null) {
System.out.println("OPENAI_KEY is null");
return;
}
Response updateCBSettingResponse = TestHelper
.makeRequest(
client(),
"PUT",
"_cluster/settings",
null,
"{\"persistent\":{\"plugins.ml_commons.jvm_heap_memory_threshold\":100}}",
ImmutableList.of(new BasicHeader(HttpHeaders.USER_AGENT, ""))
);
assertEquals(200, updateCBSettingResponse.getStatusLine().getStatusCode());

Response response = createConnector(completionModelConnectorEntity);
Map responseMap = parseResponseToMap(response);
String connectorId = (String) responseMap.get("connector_id");
response = registerRemoteModelWithTTLAndSkipHeapMemCheck("openAI-GPT-3.5 completions", connectorId, 1);
responseMap = parseResponseToMap(response);
String modelId = (String) responseMap.get("model_id");
String predictInput = "{\n" + " \"parameters\": {\n" + " \"prompt\": \"Say this is a test\"\n" + " }\n" + "}";
response = predictRemoteModel(modelId, predictInput);
responseMap = parseResponseToMap(response);
List responseList = (List) responseMap.get("inference_results");
responseMap = (Map) responseList.get(0);
responseList = (List) responseMap.get("output");
responseMap = (Map) responseList.get(0);
responseMap = (Map) responseMap.get("dataAsMap");
responseList = (List) responseMap.get("choices");
if (responseList == null) {
assertTrue(checkThrottlingOpenAI(responseMap));
return;
}
responseMap = (Map) responseList.get(0);
assertFalse(((String) responseMap.get("text")).isEmpty());

getModelProfile(modelId, verifyRemoteModelDeployed());
TimeUnit.SECONDS.sleep(71);
assertTrue(getModelProfile(modelId, verifyRemoteModelDeployed()).isEmpty());
}
// public void testPredictWithAutoDeployAndTTL_RemoteModel() throws IOException, InterruptedException {
// // Skip test if key is null
// if (OPENAI_KEY == null) {
// System.out.println("OPENAI_KEY is null");
// return;
// }
// Response updateCBSettingResponse = TestHelper
// .makeRequest(
// client(),
// "PUT",
// "_cluster/settings",
// null,
// "{\"persistent\":{\"plugins.ml_commons.jvm_heap_memory_threshold\":100}}",
// ImmutableList.of(new BasicHeader(HttpHeaders.USER_AGENT, ""))
// );
// assertEquals(200, updateCBSettingResponse.getStatusLine().getStatusCode());
//
// Response response = createConnector(completionModelConnectorEntity);
// Map responseMap = parseResponseToMap(response);
// String connectorId = (String) responseMap.get("connector_id");
// response = registerRemoteModelWithTTLAndSkipHeapMemCheck("openAI-GPT-3.5 completions", connectorId, 1);
// responseMap = parseResponseToMap(response);
// String modelId = (String) responseMap.get("model_id");
// String predictInput = "{\n" + " \"parameters\": {\n" + " \"prompt\": \"Say this is a test\"\n" + " }\n" + "}";
// response = predictRemoteModel(modelId, predictInput);
// responseMap = parseResponseToMap(response);
// List responseList = (List) responseMap.get("inference_results");
// responseMap = (Map) responseList.get(0);
// responseList = (List) responseMap.get("output");
// responseMap = (Map) responseList.get(0);
// responseMap = (Map) responseMap.get("dataAsMap");
// responseList = (List) responseMap.get("choices");
// if (responseList == null) {
// assertTrue(checkThrottlingOpenAI(responseMap));
// return;
// }
// responseMap = (Map) responseList.get(0);
// assertFalse(((String) responseMap.get("text")).isEmpty());
//
// getModelProfile(modelId, verifyRemoteModelDeployed());
// TimeUnit.SECONDS.sleep(71);
// assertTrue(getModelProfile(modelId, verifyRemoteModelDeployed()).isEmpty());
// }

public void testPredictRemoteModelWithInterface(String testCase, Consumer<Map> verifyResponse, Consumer<Exception> verifyException)
throws IOException,
Expand Down
Loading
Loading