diff options
author | Belen Pruvost <belen.pruvost@sonarsource.com> | 2021-11-11 14:16:24 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2021-11-19 20:03:27 +0000 |
commit | 92f482f2aa43e4aa36e0fda377d13b9dc3282ff9 (patch) | |
tree | b3ab8cf7b11a56c604bfc2a105c5d8ad634d6521 | |
parent | e63a5e9b07290d49a790b807af8915a48b6c28ca (diff) | |
download | sonarqube-92f482f2aa43e4aa36e0fda377d13b9dc3282ff9.tar.gz sonarqube-92f482f2aa43e4aa36e0fda377d13b9dc3282ff9.zip |
SONAR-15631 - New UT Monitoring Module
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> |