aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBelen Pruvost <belen.pruvost@sonarsource.com>2021-11-11 14:16:24 +0100
committersonartech <sonartech@sonarsource.com>2021-11-19 20:03:27 +0000
commit92f482f2aa43e4aa36e0fda377d13b9dc3282ff9 (patch)
treeb3ab8cf7b11a56c604bfc2a105c5d8ad634d6521
parente63a5e9b07290d49a790b807af8915a48b6c28ca (diff)
downloadsonarqube-92f482f2aa43e4aa36e0fda377d13b9dc3282ff9.tar.gz
sonarqube-92f482f2aa43e4aa36e0fda377d13b9dc3282ff9.zip
SONAR-15631 - New UT Monitoring Module
-rw-r--r--build.gradle26
-rw-r--r--settings.gradle1
-rw-r--r--sonar-check-api/build.gradle2
-rw-r--r--sonar-plugin-api/build.gradle1
-rw-r--r--ut-monitoring/build.gradle19
-rw-r--r--ut-monitoring/src/main/java/org/sonarqube/ut/aspects/AspectAssistant.java86
-rw-r--r--ut-monitoring/src/main/java/org/sonarqube/ut/aspects/ClassRuleAspect.java80
-rw-r--r--ut-monitoring/src/main/java/org/sonarqube/ut/aspects/Measure.java124
-rw-r--r--ut-monitoring/src/main/java/org/sonarqube/ut/aspects/MeasureKind.java42
-rw-r--r--ut-monitoring/src/main/java/org/sonarqube/ut/aspects/RuleAspect.java71
-rw-r--r--ut-monitoring/src/main/java/org/sonarqube/ut/aspects/RunAftersAspect.java104
-rw-r--r--ut-monitoring/src/main/java/org/sonarqube/ut/aspects/RunBeforesAspect.java99
-rw-r--r--ut-monitoring/src/main/java/org/sonarqube/ut/aspects/TestCaseAspect.java55
-rw-r--r--ut-monitoring/src/main/resources/META-INF/aop.xml19
14 files changed, 728 insertions, 1 deletions
diff --git a/build.gradle b/build.gradle
index 275229f72f5..3cb1467fffb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,7 +2,7 @@ plugins {
// Ordered alphabeticly
id 'com.github.ben-manes.versions' version '0.33.0'
id 'com.github.hierynomus.license' version '0.15.0'
- id "com.github.hierynomus.license-report" version"0.15.0" apply false
+ id "com.github.hierynomus.license-report" version "0.15.0" apply false
id 'com.github.johnrengelman.shadow' version '5.2.0' apply false
id 'com.google.protobuf' version '0.8.13' apply false
id 'com.jfrog.artifactory' version '4.21.0'
@@ -337,6 +337,7 @@ subprojects {
// Documentation must be updated if mssql-jdbc is updated: https://github.com/SonarSource/sonarqube/commit/03e4773ebf6cba854cdcf57a600095f65f4f53e7
dependency 'com.microsoft.sqlserver:mssql-jdbc:9.2.0.jre11'
dependency 'com.oracle.database.jdbc:ojdbc8:19.3.0.0'
+ dependency 'org.aspectj:aspectjtools:1.9.6'
// upgrade okhttp3 dependency kotlin to get rid of not exploitable CVE-2020-29582
dependency 'org.jetbrains.kotlin:kotlin-stdlib-common:1.4.21'
dependency 'org.jetbrains.kotlin:kotlin-stdlib:1.4.21'
@@ -608,6 +609,29 @@ subprojects {
}
}
+ if (System.getenv('CIRRUS_BRANCH') == "branch-nightly-build") {
+ tasks.withType(Test) {
+
+ configurations {
+ utMonitoring
+ }
+
+ dependencies {
+ testCompile project(":ut-monitoring")
+
+ utMonitoring 'org.aspectj:aspectjweaver:1.9.6'
+ }
+
+ doFirst {
+ ext {
+ aspectJWeaver = configurations.utMonitoring.resolvedConfiguration.resolvedArtifacts.find { it.name == 'aspectjweaver' }
+ }
+ jvmArgs "-javaagent:${aspectJWeaver.file}"
+ }
+ }
+ }
+
+
signing {
def signingKeyId = findProperty("signingKeyId")
def signingKey = findProperty("signingKey")
diff --git a/settings.gradle b/settings.gradle
index 9852b8d959a..fb4ee883d96 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -55,6 +55,7 @@ include 'sonar-testing-harness'
include 'sonar-testing-ldap'
include 'sonar-ws'
include 'sonar-ws-generator'
+include 'ut-monitoring'
ext.isCiServer = System.getenv().containsKey("CIRRUS_CI")
diff --git a/sonar-check-api/build.gradle b/sonar-check-api/build.gradle
index bf0f33e4f29..910647a60f4 100644
--- a/sonar-check-api/build.gradle
+++ b/sonar-check-api/build.gradle
@@ -6,6 +6,8 @@ sonarqube {
configureCompileJavaToVersion 8
+
dependencies {
compileOnly 'com.google.code.findbugs:jsr305'
+
}
diff --git a/sonar-plugin-api/build.gradle b/sonar-plugin-api/build.gradle
index 6226e1a77a1..59b49a673d7 100644
--- a/sonar-plugin-api/build.gradle
+++ b/sonar-plugin-api/build.gradle
@@ -31,6 +31,7 @@ dependencies {
testCompile 'org.assertj:assertj-core'
testCompile 'org.mockito:mockito-core'
testCompile project(':sonar-plugin-api-impl')
+ testCompile project(':ut-monitoring')
}
configurations {
diff --git a/ut-monitoring/build.gradle b/ut-monitoring/build.gradle
new file mode 100644
index 00000000000..84f4b1ac066
--- /dev/null
+++ b/ut-monitoring/build.gradle
@@ -0,0 +1,19 @@
+sonarqube {
+ properties {
+ property 'sonar.projectName', "${projectTitle} :: Java UT Monitoring"
+ }
+}
+
+dependencies {
+ // please keep the list grouped by configuration and ordered by name
+
+ compile 'com.google.code.gson:gson'
+ compile 'junit:junit'
+ compile project(path: ':sonar-plugin-api', configuration: 'shadow')
+
+ compileOnly 'org.aspectj:aspectjtools'
+}
+
+sonarqube {
+ skipProject = true
+}
diff --git a/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/AspectAssistant.java b/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/AspectAssistant.java
new file mode 100644
index 00000000000..26007ee0599
--- /dev/null
+++ b/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/AspectAssistant.java
@@ -0,0 +1,86 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonarqube.ut.aspects;
+
+import com.google.gson.Gson;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.function.Consumer;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+
+public class AspectAssistant {
+ public static final String COMMIT_HASH = System.getenv("GIT_SHA1");
+ public static final String BUILD_NUMBER = System.getenv("BUILD_NUMBER");
+
+ private static final Logger LOGGER = Loggers.get(AspectAssistant.class);
+ private static final Path PATH = Paths.get("/tmp/ut-backend-monitoring.log");
+ private static final Gson GSON = new Gson();
+
+
+ static {
+ try {
+ if (!Files.exists(PATH, LinkOption.NOFOLLOW_LINKS)) {
+ Files.createFile(PATH);
+ }
+ } catch (IOException e) {
+ LOGGER.error("error creating log file");
+ }
+ }
+
+ public static void persistMeasure(Measure measure) {
+ try {
+ Files.write(PATH, (GSON.toJson(measure) + "\n").getBytes(), StandardOpenOption.APPEND);
+ } catch (IOException e) {
+ LOGGER.error("Error in persisting data of ut monitoring", e);
+ }
+ }
+
+ public static Object measure(ProceedingJoinPoint jp, Consumer<Measure> populator) throws Throwable {
+ try {
+ long start = System.currentTimeMillis();
+ Object proceed = jp.proceed();
+ long executionTime = System.currentTimeMillis() - start;
+
+ if (executionTime > 0) {
+ Measure measure = new Measure()
+ .setDuration(executionTime)
+ .setCommit(COMMIT_HASH)
+ .setBuild(BUILD_NUMBER)
+ .setTimestamp(LocalDateTime.now(ZoneId.of("UTC")).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
+
+ populator.accept(measure);
+ AspectAssistant.persistMeasure(measure);
+ }
+ return proceed; }
+ catch (Throwable t) {
+ System.out.println(t.getMessage());
+ return null;
+ }
+ }
+}
diff --git a/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/ClassRuleAspect.java b/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/ClassRuleAspect.java
new file mode 100644
index 00000000000..9bb149e07db
--- /dev/null
+++ b/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/ClassRuleAspect.java
@@ -0,0 +1,80 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonarqube.ut.aspects;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.runtime.reflect.FieldSignatureImpl;
+
+@Aspect("percflow(execution(void org.junit.runners.ParentRunner+.run(..)))")
+public class ClassRuleAspect {
+ private String testClass = "";
+ private Set<String> classRules = new HashSet<>();
+
+ @Pointcut("@annotation(org.junit.ClassRule)")
+ void targetAnnotation() {
+ }
+
+ @Pointcut("target(org.junit.rules.ExternalResource+) && execution(void before())"
+ + " && !cflow(execution(void org.junit.runners.ParentRunner+.runLeaf(..)))")
+ void classRuleBefore() {
+ }
+
+ @Pointcut("target(org.junit.rules.ExternalResource+) && execution(void after())"
+ + " && !cflow(execution(void org.junit.runners.ParentRunner+.runLeaf(..)))")
+ void classRuleAfter() {
+ }
+
+ @Before("targetAnnotation()")
+ public void targetAnnotationCall(JoinPoint jp) {
+ testClass = jp.getStaticPart().getSourceLocation().getWithinType().getName();
+ if (jp.getStaticPart().getSignature() instanceof FieldSignatureImpl) {
+ FieldSignatureImpl fieldSignature = (FieldSignatureImpl) jp.getStaticPart().getSignature();
+ classRules.add(fieldSignature.getFieldType().getName());
+ }
+ }
+
+ @Around("classRuleBefore()")
+ public Object classRuleBeforeCall(ProceedingJoinPoint jp) throws Throwable {
+ return measure(jp, MeasureKind.CLASS_RULE_BEFORE);
+ }
+
+ @Around("classRuleAfter()")
+ public Object classRuleAfterCall(ProceedingJoinPoint jp) throws Throwable {
+ return measure(jp, MeasureKind.CLASS_RULE_AFTER);
+ }
+
+ private Object measure(ProceedingJoinPoint jp, MeasureKind measureKind) throws Throwable {
+ String measureClass = jp.getTarget().getClass().getName();
+ if (classRules.contains(measureClass)) {
+ return AspectAssistant.measure(jp, measure -> measure
+ .setTestClass(testClass)
+ .setKind(measureKind));
+ }
+ return jp.proceed();
+ }
+
+}
diff --git a/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/Measure.java b/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/Measure.java
new file mode 100644
index 00000000000..bb014561476
--- /dev/null
+++ b/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/Measure.java
@@ -0,0 +1,124 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonarqube.ut.aspects;
+
+public class Measure {
+ private final String category = "Validate-UT-Backend";
+ private final String measureClass = "";
+ private final String measureMethod ="";
+ private final String operation = "total";
+ private final String suite = "Standalone";
+
+ private String testClass;
+ private String testMethod;
+ private Long duration;
+ private String build;
+ private String commit;
+ private String timestamp;
+ private String kind;
+
+
+ public Measure() {
+ // http://stackoverflow.com/a/18645370/229031
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public String getSuite() {
+ return suite;
+ }
+
+ public String getTestClass() {
+ return testClass;
+ }
+
+ public Measure setTestClass(String testClass) {
+ this.testClass = testClass;
+ return this;
+ }
+
+ public String getTestMethod() {
+ return testMethod;
+ }
+
+ public Measure setTestMethod(String testMethod) {
+ this.testMethod = testMethod;
+ return this;
+ }
+
+ public String getMeasureClass() {
+ return measureClass;
+ }
+
+ public String getMeasureMethod() {
+ return measureMethod;
+ }
+
+ public Long getDuration() {
+ return duration;
+ }
+
+ public Measure setDuration(Long duration) {
+ this.duration = duration;
+ return this;
+ }
+
+ public String getOperation() {
+ return operation;
+ }
+
+ public String getKind() {
+ return kind;
+ }
+
+ public Measure setKind(MeasureKind measureKind) {
+ this.kind = measureKind.getName();
+ return this;
+ }
+
+ public String getBuild() {
+ return build;
+ }
+
+ public Measure setBuild(String build) {
+ this.build = build;
+ return this;
+ }
+
+ public String getCommit() {
+ return commit;
+ }
+
+ public Measure setCommit(String commit) {
+ this.commit = commit;
+ return this;
+ }
+
+ public String getTimestamp() {
+ return timestamp;
+ }
+
+ public Measure setTimestamp(String timestamp) {
+ this.timestamp = timestamp;
+ return this;
+ }
+}
diff --git a/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/MeasureKind.java b/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/MeasureKind.java
new file mode 100644
index 00000000000..d70a8e19791
--- /dev/null
+++ b/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/MeasureKind.java
@@ -0,0 +1,42 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonarqube.ut.aspects;
+
+public enum MeasureKind {
+ AFTERCLASS("afterClass"),
+ AFTER("after"),
+ BEFORECLASS("beforeClass"),
+ BEFORE("before"),
+ CLASS_RULE_BEFORE("classRuleBefore"),
+ CLASS_RULE_AFTER("classRuleAfter"),
+ RULE_BEFORE("ruleBefore"),
+ RULE_AFTER("ruleAfter"),
+ TESTCASE("testcase");
+
+ private final String name;
+
+ MeasureKind(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/RuleAspect.java b/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/RuleAspect.java
new file mode 100644
index 00000000000..a81c32776e0
--- /dev/null
+++ b/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/RuleAspect.java
@@ -0,0 +1,71 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonarqube.ut.aspects;
+
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.junit.runner.Description;
+
+@Aspect("percflow(execution(void org.junit.runners.ParentRunner+.runLeaf(..)))")
+public class RuleAspect {
+ private String testClass = "";
+ private String testMethod = "";
+
+ @Pointcut("target(org.junit.rules.ExternalResource+) && execution(void before())")
+ void ruleBefore() {
+ }
+
+ @Pointcut("target(org.junit.rules.ExternalResource+) && execution(void after())")
+ void ruleAfter() {
+ }
+
+ @Pointcut("execution(void org.junit.runners.ParentRunner+.runLeaf(..))")
+ void runLeaf() {
+ }
+
+ @Before("runLeaf()")
+ public void runLeafCall(JoinPoint jp) {
+ Description description = (Description) jp.getArgs()[1];
+ testClass = description.getClassName();
+ testMethod = description.getMethodName();
+ }
+
+ @Around("ruleBefore()")
+ public Object ruleBeforeCall(ProceedingJoinPoint jp) throws Throwable {
+ return measure(jp, MeasureKind.RULE_BEFORE);
+ }
+
+ @Around("ruleAfter()")
+ public Object ruleAfterCall(ProceedingJoinPoint jp) throws Throwable {
+ return measure(jp, MeasureKind.RULE_AFTER);
+ }
+
+ private Object measure(ProceedingJoinPoint jp, MeasureKind measureKind) throws Throwable {
+ return AspectAssistant.measure(jp, measure -> measure
+ .setTestClass(testClass)
+ .setTestMethod(testMethod)
+ .setKind(measureKind));
+ }
+
+}
diff --git a/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/RunAftersAspect.java b/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/RunAftersAspect.java
new file mode 100644
index 00000000000..6b49d8a170b
--- /dev/null
+++ b/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/RunAftersAspect.java
@@ -0,0 +1,104 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonarqube.ut.aspects;
+
+import java.lang.reflect.Field;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.junit.internal.runners.statements.InvokeMethod;
+import org.junit.internal.runners.statements.RunAfters;
+import org.junit.internal.runners.statements.RunBefores;
+import org.junit.runners.model.FrameworkMethod;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+
+@Aspect("percflow(execution(void org.junit.internal.runners.statements.RunAfters.evaluate()))")
+public class RunAftersAspect {
+ private static final Logger logger = Loggers.get(RunAftersAspect.class);
+ private String testClass = "";
+ private String testMethod = "";
+
+ @Pointcut("execution(void org.junit.internal.runners.statements.RunAfters.evaluate())")
+ void runAfters() {
+ }
+
+ @Pointcut("@annotation(org.junit.After)")
+ void anyAfter() {
+ }
+
+ @Pointcut("@annotation(org.junit.AfterClass)")
+ void anyAfterClass() {
+ }
+
+ @Before("runAfters()")
+ public void prepareAfter(JoinPoint jp) {
+ try {
+ Field nextField = RunAfters.class.getDeclaredField("next");
+ nextField.setAccessible(true);
+ Object next = nextField.get(jp.getTarget());
+ if (next instanceof RunBefores) {
+ nextField = RunBefores.class.getDeclaredField("next");
+ nextField.setAccessible(true);
+ next = nextField.get(next);
+ }
+ if (next instanceof InvokeMethod) {
+ Field testMethodField = InvokeMethod.class.getDeclaredField("testMethod");
+ testMethodField.setAccessible(true);
+ FrameworkMethod frameworkMethod = (FrameworkMethod) testMethodField.get(next);
+ testClass = frameworkMethod.getDeclaringClass().getName();
+ testMethod = frameworkMethod.getName();
+ }
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ logger.error("Error in getting reflection information", e);
+ }
+ }
+
+ @Before("anyAfterClass()")
+ public void prepareAfterClass(JoinPoint jp) {
+ testClass = jp.getStaticPart().getSignature().getDeclaringType().getName();
+ }
+
+ @Around("anyAfter()")
+ public Object measureTotalAfter(ProceedingJoinPoint jp) throws Throwable {
+ return measureAfter(jp);
+ }
+
+ @Around("anyAfterClass()")
+ public Object measureTotalAfterClass(ProceedingJoinPoint jp) throws Throwable {
+ return measureAfterClass(jp);
+ }
+
+ private Object measureAfter(ProceedingJoinPoint jp) throws Throwable {
+ return AspectAssistant.measure(jp, measure -> measure
+ .setTestClass(testClass)
+ .setTestMethod(testMethod)
+ .setKind(MeasureKind.AFTER));
+ }
+
+ private Object measureAfterClass(ProceedingJoinPoint jp) throws Throwable {
+ return AspectAssistant.measure(jp, measure -> measure
+ .setTestClass(testClass)
+ .setKind(MeasureKind.AFTERCLASS));
+ }
+}
diff --git a/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/RunBeforesAspect.java b/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/RunBeforesAspect.java
new file mode 100644
index 00000000000..f900a84b997
--- /dev/null
+++ b/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/RunBeforesAspect.java
@@ -0,0 +1,99 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonarqube.ut.aspects;
+
+import java.lang.reflect.Field;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.junit.internal.runners.statements.InvokeMethod;
+import org.junit.internal.runners.statements.RunBefores;
+import org.junit.runners.model.FrameworkMethod;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+
+@Aspect("percflow(execution(void org.junit.internal.runners.statements.RunBefores.evaluate()))")
+public class RunBeforesAspect {
+ private static final Logger logger = Loggers.get(RunBeforesAspect.class);
+ private String testClass = "";
+ private String testMethod = "";
+
+ @Pointcut("execution(void org.junit.internal.runners.statements.RunBefores.evaluate())")
+ void runBefores() {
+ }
+
+ @Pointcut("@annotation(org.junit.Before)")
+ void anyBefore() {
+ }
+
+ @Pointcut("@annotation(org.junit.BeforeClass)")
+ void anyBeforeClass() {
+ }
+
+ @Before("runBefores()")
+ public void prepareBefore(JoinPoint jp) {
+ try {
+ Field nextField = RunBefores.class.getDeclaredField("next");
+ nextField.setAccessible(true);
+ Object invokeMethod = nextField.get(jp.getTarget());
+ if (invokeMethod instanceof InvokeMethod) {
+ Field testMethodField = InvokeMethod.class.getDeclaredField("testMethod");
+ testMethodField.setAccessible(true);
+ FrameworkMethod frameworkMethod = (FrameworkMethod) testMethodField.get(invokeMethod);
+ testClass = frameworkMethod.getDeclaringClass().getName();
+ testMethod = frameworkMethod.getName();
+ }
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ logger.error("Error in getting reflection information", e);
+ }
+ }
+
+ @Before("anyBeforeClass()")
+ public void prepareBeforeClass(JoinPoint jp) {
+ testClass = jp.getStaticPart().getSignature().getDeclaringType().getName();
+ }
+
+ @Around("anyBefore()")
+ public Object measureTotalBefore(ProceedingJoinPoint jp) throws Throwable {
+ return measureBefore(jp);
+ }
+
+ @Around("anyBeforeClass()")
+ public Object measureTotalBeforeClass(ProceedingJoinPoint jp) throws Throwable {
+ return measureBeforeClass(jp);
+ }
+
+ private Object measureBefore(ProceedingJoinPoint jp) throws Throwable {
+ return AspectAssistant.measure(jp, measure -> measure
+ .setTestClass(testClass)
+ .setTestMethod(testMethod)
+ .setKind(MeasureKind.BEFORE));
+ }
+
+ private Object measureBeforeClass(ProceedingJoinPoint jp) throws Throwable {
+ return AspectAssistant.measure(jp, measure -> measure
+ .setTestClass(testClass)
+ .setKind(MeasureKind.BEFORECLASS));
+ }
+
+}
diff --git a/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/TestCaseAspect.java b/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/TestCaseAspect.java
new file mode 100644
index 00000000000..4959de41b7d
--- /dev/null
+++ b/ut-monitoring/src/main/java/org/sonarqube/ut/aspects/TestCaseAspect.java
@@ -0,0 +1,55 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonarqube.ut.aspects;
+
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+
+@Aspect("percflow(execution(@org.junit.Test * *(..)))")
+public class TestCaseAspect {
+ private String testClass = "";
+ private String testMethod = "";
+
+ @Pointcut("execution(@org.junit.Test * *(..))")
+ void anyTest() {
+ }
+
+ @Around("anyTest()")
+ public Object measureTestCase(ProceedingJoinPoint jp) throws Throwable {
+ return measure(jp);
+ }
+
+ @Before("anyTest()")
+ public void beforeCall(JoinPoint jp) {
+ testClass = jp.getTarget().getClass().getName();
+ testMethod = jp.getSignature().getName();
+ }
+
+ private Object measure(ProceedingJoinPoint jp) throws Throwable {
+ return AspectAssistant.measure(jp, measure -> measure
+ .setTestClass(testClass)
+ .setTestMethod(testMethod)
+ .setKind(MeasureKind.TESTCASE));
+ }
+}
diff --git a/ut-monitoring/src/main/resources/META-INF/aop.xml b/ut-monitoring/src/main/resources/META-INF/aop.xml
new file mode 100644
index 00000000000..2a50c10be4b
--- /dev/null
+++ b/ut-monitoring/src/main/resources/META-INF/aop.xml
@@ -0,0 +1,19 @@
+<aspectj>
+
+ <aspects>
+ <aspect name="org.sonarqube.ut.aspects.TestCaseAspect"/>
+ <aspect name="org.sonarqube.ut.aspects.RunBeforesAspect"/>
+ <aspect name="org.sonarqube.ut.aspects.RunAftersAspect"/>
+ <aspect name="org.sonarqube.ut.aspects.ClassRuleAspect"/>
+ <aspect name="org.sonarqube.ut.aspects.RuleAspect"/>
+ </aspects>
+ <weaver options="-warn:none -Xlint:ignore">
+ <include within="com.sonarsource..*"/>
+ <include within="org.sonarqube..*"/>
+ <include within="org.sonar..*"/>
+ <include within="com.sonar..*"/>
+ <include within="org.junit..*"/>
+ <include within="net.sourceforge..*"/>
+ </weaver>
+
+</aspectj>