简体   繁体   English

SonarQube测试自定义Java规则无法显示语义信息

[英]SonarQube Test Custom Java Rule fails to display semantic information

I am learning the API of SonarQube, trying to extend the java plugin rules. 我正在学习SonarQube的API,试图扩展java插件规则。 I followed this tutorial successfully. 我成功地遵循了本教程

Now I want to build a simple analysis that checks if a toString() method is used in a unit test. 现在我想构建一个简单的分析,检查单元测试中是否使用了toString()方法。

public class TS_SensitiveEqualityCheck extends BaseTreeVisitor implements JavaFileScanner {

  private final Deque<Boolean> methodContainsToStringInAssert = new ArrayDeque<Boolean>();
  private final Deque<Boolean> inUnitTest = new ArrayDeque<Boolean>();

  private JavaFileScannerContext context;

  @Override
  public void scanFile(final JavaFileScannerContext context) {
    this.context = context;
    scan(context.getTree());
  }

  @Override
  public void visitMethod(MethodTree methodTree) {
    if (ModifiersUtils.hasModifier(methodTree.modifiers(), Modifier.ABSTRACT)) {
      return;
    }
    boolean isUnitTest = isUnitTest(methodTree);
    inUnitTest.push(isUnitTest);
    System.out.println("For method " + methodTree.simpleName() 
    + " found [isUnitTest | isViolation] :  " 
    + String.valueOf(isUnitTest));
    methodContainsToStringInAssert.push(false);
    super.visitMethod(methodTree);
    inUnitTest.pop();
    Boolean isViolation = methodContainsToStringInAssert.pop();
    System.out.println("For method " + methodTree.simpleName() 
            + " found [isUnitTest | isViolation] :  " 
            + String.valueOf(isUnitTest) + " " 
            + String.valueOf(isViolation) );
    if (isUnitTest && isViolation) { 
      context.reportIssue(this, methodTree.simpleName(), "This test method uses unsafe equality checking!");
    }
  }

  @Override
  public void visitMethodInvocation(MethodInvocationTree mit) {
    if (!inUnitTest()) {
      return;
    }
    Symbol mis = mit.symbol();
    System.out.println(mis.name()); // null when encountering an assertion.
    if (mis.name() != null && mis.name().equals("toString")) {
        setTrue(methodContainsToStringInAssert);
    }
    super.visitMethodInvocation(mit);
  }

  private boolean inUnitTest() {
    return !inUnitTest.isEmpty() && inUnitTest.peek();
  }

  private static void setTrue(Deque<Boolean> collection) {
    if (collection != null && !collection.peek()) {
      collection.pop();
      collection.push(true);
    }
  }

  private static boolean isUnitTest(MethodTree methodTree) {
    JavaSymbol.MethodJavaSymbol symbol = (JavaSymbol.MethodJavaSymbol) methodTree.symbol();
    while (symbol != null) {
      if (symbol.metadata().isAnnotatedWith("org.junit.Test")) {
        return true;
      }
      symbol = symbol.overriddenSymbol();
    }
    Symbol.TypeSymbol enclosingClass = methodTree.symbol().enclosingClass();
    return (enclosingClass != null 
            // && enclosingClass.type().isSubtypeOf("junit.framework.TestCase")  // errors!!! does not get the package name of the class!!!
            && methodTree.simpleName().name().startsWith("test"));
  }

} }

For the given test file SonarQube cannot find any assertion method invocation! 对于给定的测试文件,SonarQube找不到任何断言方法调用! Only the method B.is() gives a result, ie mit.symbol().name != null . 只有方法B.is()给出一个结果,即mit.symbol().name != null Can anyone explain why this goes wrong? 谁能解释为什么会出错? Here is the file that is used as test: 这是用作测试的文件:

import junit.framework.TestCase;
    import javax.annotation.Nullable;

public class AssertionsInTestsCheckTestJunit3 extends TestCase { public void testCompliant() { B b = new B(); b.is(); org.junit.Assert.assertTrue(b.is()); } public void testNoncompliant() { // Noncompliant org.junit.Assert.assertTrue(this.toString().equals("")); } public void testNoncompliant2() { // Noncompliant org.junit.Assert.assertEquals(this.toString(), ""); } public void testNoncompliant3() { // Noncompliant org.junit.Fail.fail(this.toString()); doWork(); } @Nullable public Test notAtest() { compliant1(); } } public class B { public boolean is() { return true; } } </pre></code>

Note that it is not important what this code does! 请注意,此代码的作用并不重要!

The Java Analyzer requires the byte code of the libraries used in source files in order to complete the semantic model. Java Analyzer需要源文件中使用的库的字节代码才能完成语义模型。 Without it, most of the semantic information which could be retrieved is missing. 没有它,大多数可以检索的语义信息都会丢失。

In your test file, you are using junit . 在您的测试文件中,您使用的是junit However, binaries related to junit are missing, as you are most probably not providing the library to the check verifier. 但是,缺少与junit相关的二进制文件,因为您很可能不会向检查验证程序提供库。 This part has not been described in the tutorial yet. 该部分尚未在教程中描述。

By default, no external libraries are provided to the check verifier, as the tutorial, in its current state, doesn't requires external libraries. 默认情况下,没有为检查验证程序提供外部库,因为教程在当前状态下不需要外部库。 The part explaining how to use external sources has not yet been written, but once done, it should be available following this link: How to test sources requiring external binaries . 解释如何使用外部源的部分尚未编写,但一旦完成,它应该可以通过以下链接获得: 如何测试需要外部二进制文件的源

Now, to solve your issue and sum up what is going to be presented in the tutorial, here is what you have to do: 现在,为了解决您的问题并总结教程中将要呈现的内容,您需要执行以下操作:

In order to use the adequate byte code when analyzing the file, you have to provide the junit binaries to the check verifier. 为了在分析文件时使用足够的字节代码,您必须向check checkifier提供junit二进制文件 There is multiple approach to do so, but the simplest one is probably to provide the jar by placing it at a dedicated location in your project: target/test-jars . 有多种方法可以做到这一点,但最简单的方法可能是将jar放在项目的专用位置: target/test-jars By default, the check verifier is going to look in this folder. 默认情况下,检查验证程序将查找此文件夹。

You can add any required library automatically, by changing the pom.xml file at the root of the project: 您可以通过更改项目根目录下的pom.xml文件来自动添加任何所需的库:

  1. In the pom.xml file at the root of your plugin, you should see a commented part: pom.xml#L108 (from the template used in the tutorial) 在插件根目录的pom.xml文件中,您应该看到一个注释部分: pom.xml#L108 (来自教程中使用的模板)
  2. Un-comment this part of code. 取消评论此部分代码。 Note that for the example, it already contains the binaries of apache commons-collections. 请注意,对于该示例,它已包含apache commons-collections的二进制文件。
 <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <version>2.10</version>
    <executions>
      <execution>
        <id>copy</id>
        <phase>test-compile</phase>
        <goals>
          <goal>copy</goal>
        </goals>
        <configuration>
          <artifactItems>
            <artifactItem>
              <groupId>org.apache.commons</groupId>
              <artifactId>commons-collections4</artifactId>
              <version>4.0</version>
              <type>jar</type>
            </artifactItem>
          </artifactItems>
          <outputDirectory>${project.build.directory}/test-jars</outputDirectory>
        </configuration>
      </execution>
    </executions>
  </plugin>
  1. Replace the <artifactItem> to use junit instead, or simply add a new artifact: 替换<artifactItem>代替使用junit,或者只是添加一个新工件:
  <artifactItem>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <type>jar</type>
  </artifactItem>
  1. Re-build the project with mvn clean install -DskipTests . 使用mvn clean install -DskipTests重新构建项目。 The junit jar with the required version will be downloaded by maven, and placed in your target/test-jars folder. 具有所需版本的junit jar将由maven下载,并放在target/test-jars文件夹中。

  2. Re-run your test. 重新运行测试。 Issues should be detected. 应该检测问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM