org.sonarlint.eclipse.jdt.internal.JdtUtilsTest.java Source code

Java tutorial

Introduction

Here is the source code for org.sonarlint.eclipse.jdt.internal.JdtUtilsTest.java

Source

/*
 * SonarLint for Eclipse
 * Copyright (C) 2015-2017 SonarSource SA
 * sonarlint@sonarsource.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.sonarlint.eclipse.jdt.internal;

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaModel;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import org.sonarlint.eclipse.core.analysis.IPreAnalysisContext;
import org.sonarlint.eclipse.tests.common.SonarTestCase;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class JdtUtilsTest extends SonarTestCase {

    private JdtUtils jdtUtils = new JdtUtils();

    @Rule
    public TemporaryFolder temp = new TemporaryFolder();

    @Test
    public void shouldConfigureJavaSourceAndTarget() throws JavaModelException, IOException {
        IJavaProject project = mock(IJavaProject.class);
        IPreAnalysisContext context = mock(IPreAnalysisContext.class);

        when(project.getOption(JavaCore.COMPILER_SOURCE, true)).thenReturn("1.6");
        when(project.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true)).thenReturn("1.6");
        when(project.getResolvedClasspath(true)).thenReturn(new IClasspathEntry[] {});
        when(project.getOutputLocation()).thenReturn(new Path(temp.newFolder("output").getAbsolutePath()));

        jdtUtils.configureJavaProject(project, context);

        verify(context).setAnalysisProperty("sonar.java.source", "1.6");
        verify(context).setAnalysisProperty("sonar.java.target", "1.6");
    }

    @Test
    public void shouldConfigureSimpleProject() throws JavaModelException, IOException {
        IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
        File workspaceRoot = root.getLocation().toFile();
        File projectRoot = new File(workspaceRoot, "myProject");
        projectRoot.mkdir();
        File sourceFolder = new File(projectRoot, "src");
        sourceFolder.mkdir();
        File testFolder = new File(projectRoot, "test");
        testFolder.mkdir();
        File outputFolder = new File(projectRoot, "bin");
        outputFolder.mkdir();

        IJavaProject project = mock(IJavaProject.class);
        IPreAnalysisContext context = mock(IPreAnalysisContext.class);

        when(project.getOption(JavaCore.COMPILER_SOURCE, true)).thenReturn("1.6");
        when(project.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true)).thenReturn("1.6");
        when(project.getPath()).thenReturn(new Path(projectRoot.getAbsolutePath()));

        IClasspathEntry[] cpes = new IClasspathEntry[] {
                createCPE(IClasspathEntry.CPE_SOURCE, sourceFolder, outputFolder),
                createCPE(IClasspathEntry.CPE_SOURCE, testFolder, outputFolder) };

        when(project.getResolvedClasspath(true)).thenReturn(cpes);
        when(project.getOutputLocation()).thenReturn(new Path(outputFolder.getAbsolutePath()));

        jdtUtils.configureJavaProject(project, context);

        ArgumentCaptor<Collection<String>> captor = ArgumentCaptor.forClass(Collection.class);
        verify(context).setAnalysisProperty(ArgumentMatchers.eq("sonar.java.binaries"), captor.capture());

        assertThat(captor.getValue())
                .containsExactlyInAnyOrder(outputFolder.getAbsolutePath().replaceAll(Pattern.quote("\\"), "/"));
    }

    // SLE-159
    @Test
    public void doNotAddNonExistingPaths() throws JavaModelException, IOException {
        IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
        File workspaceRoot = root.getLocation().toFile();
        File projectRoot = new File(workspaceRoot, "myProjectMissingOut");
        projectRoot.mkdir();
        File sourceFolder = new File(projectRoot, "src");
        sourceFolder.mkdir();
        File outputFolder = new File(projectRoot, "bin");

        IJavaProject project = mock(IJavaProject.class);
        IPreAnalysisContext context = mock(IPreAnalysisContext.class);

        when(project.getOption(JavaCore.COMPILER_SOURCE, true)).thenReturn("1.6");
        when(project.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true)).thenReturn("1.6");
        when(project.getPath()).thenReturn(new Path(projectRoot.getAbsolutePath()));

        IClasspathEntry[] cpes = new IClasspathEntry[] {
                createCPE(IClasspathEntry.CPE_SOURCE, sourceFolder, outputFolder) };

        when(project.getResolvedClasspath(true)).thenReturn(cpes);
        when(project.getOutputLocation()).thenReturn(new Path(outputFolder.getAbsolutePath()));

        jdtUtils.configureJavaProject(project, context);

        ArgumentCaptor<Collection<String>> captor = ArgumentCaptor.forClass(Collection.class);
        verify(context).setAnalysisProperty(ArgumentMatchers.eq("sonar.java.binaries"), captor.capture());

        assertThat(captor.getValue()).isEmpty();
    }

    @Test
    public void shouldConfigureProjectsWithCircularDependencies() throws CoreException, IOException {
        // the bug appeared when at least 3 projects were involved: the first project depends on the second one which has a circular dependency
        // towards the second one
        IPreAnalysisContext context = mock(IPreAnalysisContext.class);
        // mock three projects that depend on each other
        final String project1Name = "project1";
        final String project2Name = "project2";
        final String project3Name = "project3";
        IJavaProject project1 = mock(IJavaProject.class, Mockito.RETURNS_DEEP_STUBS);
        IJavaProject project2 = mock(IJavaProject.class, Mockito.RETURNS_DEEP_STUBS);
        IJavaProject project3 = mock(IJavaProject.class, Mockito.RETURNS_DEEP_STUBS);
        // these are required during the call to configureJavaProject
        when(project1.getOption(JavaCore.COMPILER_SOURCE, true)).thenReturn("1.6");
        when(project1.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true)).thenReturn("1.6");
        when(project1.getProject().getName()).thenReturn(project1Name);
        when(project2.getProject().getName()).thenReturn(project2Name);
        when(project3.getProject().getName()).thenReturn(project3Name);

        // create three classpathEntries, one for each Project
        IClasspathEntry entryProject1 = mock(IClasspathEntry.class, Mockito.RETURNS_DEEP_STUBS);
        IClasspathEntry entryProject2 = mock(IClasspathEntry.class, Mockito.RETURNS_DEEP_STUBS);
        IClasspathEntry entryProject3 = mock(IClasspathEntry.class, Mockito.RETURNS_DEEP_STUBS);
        when(entryProject1.getEntryKind()).thenReturn(IClasspathEntry.CPE_PROJECT);
        when(entryProject1.getPath().segment(0)).thenReturn(project1Name);
        when(entryProject2.getEntryKind()).thenReturn(IClasspathEntry.CPE_PROJECT);
        when(entryProject2.getPath().segment(0)).thenReturn(project2Name);
        when(entryProject3.getEntryKind()).thenReturn(IClasspathEntry.CPE_PROJECT);
        when(entryProject3.getPath().segment(0)).thenReturn(project3Name);
        // project1 depends on project2, which depends on project3, which depends on project2
        IClasspathEntry[] classpath1 = new IClasspathEntry[] { entryProject2 };
        IClasspathEntry[] classpath2 = new IClasspathEntry[] { entryProject3 };
        IClasspathEntry[] classpath3 = new IClasspathEntry[] { entryProject2 };
        when(project1.getResolvedClasspath(true)).thenReturn(classpath1);
        when(project2.getResolvedClasspath(true)).thenReturn(classpath2);
        when(project3.getResolvedClasspath(true)).thenReturn(classpath3);

        // mock the JavaModel
        IJavaModel javaModel = mock(IJavaModel.class);
        when(javaModel.getJavaProject(project1Name)).thenReturn(project1);
        when(javaModel.getJavaProject(project2Name)).thenReturn(project2);
        when(javaModel.getJavaProject(project3Name)).thenReturn(project3);

        when(project1.getJavaModel()).thenReturn(javaModel);
        when(project2.getJavaModel()).thenReturn(javaModel);
        when(project3.getJavaModel()).thenReturn(javaModel);

        // this call should not fail (StackOverFlowError before patch)
        jdtUtils.configureJavaProject(project1, context);

    }

    private IClasspathEntry createCPE(int kind, File path, @Nullable File outputLocation) {
        IClasspathEntry cpe = mock(IClasspathEntry.class);
        when(cpe.getEntryKind()).thenReturn(kind);
        when(cpe.getPath()).thenReturn(new Path(path.getAbsolutePath()));
        when(cpe.getOutputLocation()).thenReturn(new Path(outputLocation.getAbsolutePath()));
        return cpe;
    }

    @Test
    public void keepOnlyJavaFilesOnClasspathForJdtProject() throws Exception {
        IProject jdtProject = importEclipseProject("SimpleJdtProject");
        IFile onClassPath = (IFile) jdtProject.findMember("src/main/java/ClassOnDefaultPackage.java");
        IFile compileError = (IFile) jdtProject.findMember("src/main/java/ClassWithCompileError.java");
        IFile outsideClassPath = (IFile) jdtProject.findMember("ClassOutsideSourceFolder.java");
        IFile nonJava = (IFile) jdtProject.findMember("src/main/sample.js");

        assertThat(JdtUtils.isValidJavaFile(onClassPath)).isTrue();
        assertThat(JdtUtils.isValidJavaFile(compileError)).isFalse();
        assertThat(JdtUtils.isValidJavaFile(outsideClassPath)).isFalse();
        assertThat(JdtUtils.isValidJavaFile(nonJava)).isTrue();
    }

    @Test
    public void ignoreJavaFilesOnNonJdtProject() throws Exception {
        IProject nonJdtProject = importEclipseProject("SimpleNonJdtProject");
        IFile java = (IFile) nonJdtProject.findMember("src/main/ClassOnDefaultPackage.java");
        IFile nonJava = (IFile) nonJdtProject.findMember("src/main/sample.js");

        assertThat(JdtUtils.isValidJavaFile(java)).isFalse();
        assertThat(JdtUtils.isValidJavaFile(nonJava)).isTrue();
    }
}