Skip to content

Commit

Permalink
Merge pull request #4 from telekom/fix/end-time
Browse files Browse the repository at this point in the history
Changed event type of synchronizer
  • Loading branch information
martingrossmann authored Dec 2, 2021
2 parents 089c3ab + 932e62d commit ba58d52
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 100 deletions.
10 changes: 5 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ apply from: rootProject.file('publish.gradle')

dependencies {
compileOnly 'io.testerra:core:' + testerraCompileVersion
implementation 'io.testerra:surefire-connector:' + testerraCompileVersion
// implementation 'io.testerra:surefire-connector:' + testerraCompileVersion

implementation 'org.apache.commons:commons-lang3:3.9'
implementation 'org.apache.httpcomponents:httpcore:4.4.13'
implementation 'org.apache.commons:commons-lang3:3.12.0'
implementation 'org.apache.httpcomponents:httpcore:4.4.14'

implementation 'org.glassfish.jersey.core:jersey-client:2.32'
implementation 'org.glassfish.jersey.inject:jersey-hk2:2.32'
implementation 'org.glassfish.jersey.core:jersey-client:2.35'
implementation 'org.glassfish.jersey.inject:jersey-hk2:2.35'
// implementation 'org.glassfish.jersey.media:jersey-media-json-jackson:2.32'
// Better JSON mapper for Jersey
implementation 'com.owlike:genson:1.6'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,22 @@

import eu.tsystems.mms.tic.testerra.plugins.azuredevops.synchronize.AzureDevOpsResultSynchronizer;
import eu.tsystems.mms.tic.testframework.hooks.ModuleHook;
import eu.tsystems.mms.tic.testframework.logging.Loggable;
import eu.tsystems.mms.tic.testframework.report.TesterraListener;

/**
* Created on 17.11.2020
*
* @author mgn
*/
public class AzureDevOpsConnectorHook implements ModuleHook, Loggable {
public class AzureDevOpsConnectorHook implements ModuleHook {

private static AzureDevOpsResultSynchronizer synchronizer;

@Override
public void init() {
synchronizer = new AzureDevOpsResultSynchronizer();
TesterraListener.getEventBus().register(synchronizer);

}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,8 @@

package eu.tsystems.mms.tic.testerra.plugins.azuredevops.mapper;

import com.owlike.genson.annotation.JsonDateFormat;
import eu.tsystems.mms.tic.testframework.logging.Loggable;

import java.util.Date;

/**
* Created on 20.11.2020
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* Testerra
*
* (C) 2020, Martin Großmann, T-Systems Multimedia Solutions GmbH, Deutsche Telekom AG
* (C) 2021, Martin Großmann, T-Systems Multimedia Solutions GmbH, Deutsche Telekom AG
*
* Deutsche Telekom AG and all other contributors /
* copyright owners license this file to you under the Apache
Expand All @@ -22,6 +22,7 @@

package eu.tsystems.mms.tic.testerra.plugins.azuredevops.synchronize;

import com.google.common.eventbus.Subscribe;
import eu.tsystems.mms.tic.testerra.plugins.azuredevops.annotation.AzureTest;
import eu.tsystems.mms.tic.testerra.plugins.azuredevops.config.AzureDevOpsConfig;
import eu.tsystems.mms.tic.testerra.plugins.azuredevops.mapper.FailureType;
Expand All @@ -34,11 +35,11 @@
import eu.tsystems.mms.tic.testerra.plugins.azuredevops.mapper.Testplan;
import eu.tsystems.mms.tic.testerra.plugins.azuredevops.restclient.AzureDevOpsClient;
import eu.tsystems.mms.tic.testframework.annotations.Fails;
import eu.tsystems.mms.tic.testframework.connectors.util.AbstractCommonSynchronizer;
import eu.tsystems.mms.tic.testframework.events.MethodEndEvent;
import eu.tsystems.mms.tic.testframework.events.TestStatusUpdateEvent;
import eu.tsystems.mms.tic.testframework.logging.Loggable;
import eu.tsystems.mms.tic.testframework.report.FailureCorridor;
import eu.tsystems.mms.tic.testframework.report.Status;
import eu.tsystems.mms.tic.testframework.report.model.context.ErrorContext;
import eu.tsystems.mms.tic.testframework.report.model.context.MethodContext;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;

Expand All @@ -53,7 +54,7 @@
*
* @author mgn
*/
public class AzureDevOpsResultSynchronizer extends AbstractCommonSynchronizer implements Loggable {
public class AzureDevOpsResultSynchronizer implements TestStatusUpdateEvent.Listener, Loggable {

private AzureDevOpsClient client = null;

Expand All @@ -66,24 +67,34 @@ public AzureDevOpsResultSynchronizer() {
}

@Override
protected void pOnTestSuccess(MethodEndEvent event) {
log().info("Method " + event.getTestMethod().getMethodName() + " passed");
this.syncTestresult(event, Outcome.PASSED);
}

@Override
protected void pOnTestFailure(MethodEndEvent event) {
log().info("Method " + event.getTestMethod().getMethodName() + " failed");
// Only the last execution of a failed tests is synced
// Otherwise the run contains more results of the same test method.
this.syncTestresult(event, Outcome.FAILED);

}
@Subscribe
public void onTestStatusUpdate(TestStatusUpdateEvent event) {

MethodContext methodContext = event.getMethodContext();
Status status = methodContext.getStatus();
switch (status) {
case PASSED:
case REPAIRED:
case RECOVERED:
log().info("Method " + methodContext.getName() + " passed.");
this.syncTestresult(event, Outcome.PASSED);
break;
case FAILED:
case FAILED_EXPECTED:
log().info("Method " + methodContext.getName() + " failed.");
this.syncTestresult(event, Outcome.FAILED);
break;
case RETRIED:
log().info("Method " + methodContext.getName() + " was retried and will not sync.");
break;
case SKIPPED:
log().info("Method " + methodContext.getName() + " skipped.");
this.syncTestresult(event, Outcome.NOT_EXECUTED);
break;
default:
log().info(String.format("Method state %s of %s cannot handle.", status.toString(), methodContext.getName()));
}

@Override
protected void pOnTestSkip(MethodEndEvent event) {
log().info("Method " + event.getTestMethod().getMethodName() + " skipped");
this.syncTestresult(event, Outcome.NOT_EXECUTED);
}

private void init() {
Expand Down Expand Up @@ -122,70 +133,83 @@ private void init() {
* @param event
* @param outcome
*/
private synchronized void syncTestresult(MethodEndEvent event, Outcome outcome) {
AzureTest annotation = this.getAnnotation(event);
if (this.config.isAzureSyncEnabled() && annotation != null && annotation.enabled()) {

Points points = this.client.getPointsByTestCaseFilter(annotation.id());
if (points.getPoints().size() > 0) {

// Find the point with the current test plan id
Optional<Point> optionalPoint = points.getPoints().stream().filter(point -> this.config.getAzureTestPlanId() == point.getTestPlan().getId()).findFirst();
if (optionalPoint.isPresent()) {

// Create a new test result based on current test method
Result result = new Result();
result.setTestPoint(optionalPoint.get());
if (!outcome.equals(Outcome.NOT_EXECUTED)) {
result.setStartedDate(event.getMethodContext().getStartTime().toInstant().toString());
result.setCompletedDate(event.getMethodContext().getEndTime().toInstant().toString());
}
result.setOutcome(outcome.toString());
// Priority is taken from test case, cannot change with test result
// result.setPriority(this.getPriorityByFailureCorridor(event.getMethodContext().failureCorridorValue));

if (outcome.equals(Outcome.FAILED)) {
event.getMethodContext()
.readErrors()
.filter(ErrorContext::isNotOptional)
.findFirst()
.ifPresent(errorContext -> {
Throwable throwable = errorContext.getThrowable();
final String errorMessage = throwable.getMessage();
result.setErrorMessage(errorMessage);
result.setFailureType(this.getFailureType(event).toString());
final String stackTrace = ExceptionUtils.getStackTrace(throwable);
result.setStackTrace(stackTrace);
});
private synchronized void syncTestresult(TestStatusUpdateEvent event, Outcome outcome) {
Optional<Method> method = this.getMethodFromEvent(event);

if (method.isPresent()) {

Optional<AzureTest> annotation = this.getAnnotation(event);
if (this.config.isAzureSyncEnabled() && annotation.isPresent()) {

if (annotation.get().enabled()) {

Points points = this.client.getPointsByTestCaseFilter(annotation.get().id());
if (points.getPoints().size() > 0) {

// Find the point with the current test plan id
Optional<Point> optionalPoint = points.getPoints().stream().filter(point -> this.config.getAzureTestPlanId() == point.getTestPlan().getId()).findFirst();
if (optionalPoint.isPresent()) {

// Create a new test result based on current test method
Result result = new Result();
result.setTestPoint(optionalPoint.get());
if (!outcome.equals(Outcome.NOT_EXECUTED)) {
result.setStartedDate(event.getMethodContext().getStartTime().toInstant().toString());
result.setCompletedDate(event.getMethodContext().getEndTime().toInstant().toString());
}
result.setOutcome(outcome.toString());
// Priority is taken from test case, cannot change with test result
// result.setPriority(this.getPriorityByFailureCorridor(event.getMethodContext().failureCorridorValue));

if (outcome.equals(Outcome.FAILED)) {
event.getMethodContext()
.readErrors()
.filter(ErrorContext::isNotOptional)
.findFirst()
.ifPresent(errorContext -> {
Throwable throwable = errorContext.getThrowable();
String errorMessage = throwable.getMessage();
Optional<Fails> fails = event.getMethodContext().getFailsAnnotation();
if (fails.isPresent() && StringUtils.isNotBlank(fails.get().description())) {
errorMessage += "\n\nKnown issue: " + fails.get().description();
}
result.setErrorMessage(errorMessage);
result.setFailureType(this.getFailureType(event).toString());
event.getMethodContext().getFailsAnnotation().get().description();
final String stackTrace = ExceptionUtils.getStackTrace(throwable);
result.setStackTrace(stackTrace);
});
}

List<Result> resultList = new ArrayList<>();
resultList.add(result);
this.client.addResult(resultList, this.currentRunId);

} else {
log().warn(String.format(
"Cannot sync %s: Testcase with ID %s is not mapped to test plan %s",
method.get().getName(),
annotation.get().id(),
this.config.getAzureTestPlanId()
));
}

} else {
log().warn(String.format(
"Cannot sync %s: Testcase with ID %s was not added to a test plan.",
method.get().getName(),
annotation.get().id()));
}

List<Result> resultList = new ArrayList<>();
resultList.add(result);
this.client.addResult(resultList, this.currentRunId);

} else {
log().warn(String.format(
"Cannot sync %s: Testcase with ID %s is not mapped to test plan %s",
event.getTestMethod().getMethodName(),
annotation.id(),
this.config.getAzureTestPlanId()
));
} // end method enabled sync
else {
log().info(String.format("Sync of %s is deactivated.", method.get().getName()));
}

} else {
log().warn(String.format(
"Cannot sync %s: Testcase with ID %s was not added to a test plan.",
event.getTestMethod().getMethodName(),
annotation.id()));
}
} // end active sync and annotation found

} // end method.isPresent

}
if (annotation != null && !annotation.enabled()) {
log().info(String.format(
"Sync of %s is deactivated.",
event.getTestMethod().getMethodName()
));
}
}

public void shutdown() {
Expand All @@ -205,19 +229,23 @@ public void shutdown() {
}
}

private AzureTest getAnnotation(MethodEndEvent event) {
final Method method = event.getTestResult().getMethod().getConstructorOrMethod().getMethod();
if (method.isAnnotationPresent(AzureTest.class)) {
return method.getAnnotation(AzureTest.class);
private Optional<AzureTest> getAnnotation(TestStatusUpdateEvent event) {
Optional<Method> methodFromEvent = this.getMethodFromEvent(event);
if (methodFromEvent.isPresent() && methodFromEvent.get().isAnnotationPresent(AzureTest.class)) {
return Optional.of(methodFromEvent.get().getAnnotation(AzureTest.class));
} else {
log().info("No annoation found for sync results with Azure DevOps");
return null;
return Optional.empty();
}
}

private FailureType getFailureType(MethodEndEvent event) {
final Method method = event.getTestResult().getMethod().getConstructorOrMethod().getMethod();
if (method.isAnnotationPresent(Fails.class)) {
private Optional<Method> getMethodFromEvent(TestStatusUpdateEvent event) {
return event.getMethodContext().getTestNgResult()
.map(iTestResult -> iTestResult.getMethod().getConstructorOrMethod().getMethod());
}

private FailureType getFailureType(TestStatusUpdateEvent event) {
if (event.getMethodContext().getStatus() == Status.FAILED_EXPECTED) {
return FailureType.KNOWN_ISSUE;
} else {
return FailureType.NEW_ISSUE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@

import eu.tsystems.mms.tic.testerra.plugins.azuredevops.annotation.AzureTest;
import eu.tsystems.mms.tic.testframework.annotations.Fails;
import eu.tsystems.mms.tic.testframework.execution.testng.RetryAnalyzer;
import eu.tsystems.mms.tic.testframework.testing.TesterraTest;
import eu.tsystems.mms.tic.testframework.utils.TimerUtils;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicInteger;

/**
* Created on 26.11.2020
Expand All @@ -55,10 +57,25 @@ public void test_Passed01() {

@Test
@AzureTest(id = 2284)
// @Fails(description = "Das ist halt ein alter Bug.")
@Fails(description = "Das ist halt ein alter Bug.")
public void test_Passed02() {
TimerUtils.sleep(5555, "Wait some time...");
Assert.assertTrue(false, "Mega exception");
}

AtomicInteger counter = new AtomicInteger(0);

@AzureTest(id = 2284)
@Test(priority = 6)
public void test_03RetriedTest() {
this.counter.incrementAndGet();
if (counter.get() == 1) {
// Message is already defined in test.properties
Assert.assertTrue(false, "retry_for_azuredevops_connector");
} else {
Assert.assertTrue(true);
}

}

}
2 changes: 2 additions & 0 deletions src/test/resources/test.properties
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
tt.report.name=DemoAzureDevops

tt.failed.tests.if.throwable.messages=retry_for_azuredevops_connector

0 comments on commit ba58d52

Please sign in to comment.