org.sonar.batch.index.SourcePersisterTest.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.batch.index.SourcePersisterTest.java

Source

/*
 * SonarQube, open source software quality management tool.
 * Copyright (C) 2008-2014 SonarSource
 * mailto:contact AT sonarsource DOT com
 *
 * SonarQube 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.
 *
 * SonarQube 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.sonar.batch.index;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.sonar.api.batch.fs.InputPath;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.sensor.duplication.DuplicationGroup;
import org.sonar.api.batch.sensor.highlighting.TypeOfText;
import org.sonar.api.database.model.Snapshot;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Measure;
import org.sonar.api.resources.File;
import org.sonar.api.resources.Project;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.System2;
import org.sonar.batch.ProjectTree;
import org.sonar.batch.duplication.DuplicationCache;
import org.sonar.batch.highlighting.SyntaxHighlightingData;
import org.sonar.batch.highlighting.SyntaxHighlightingDataBuilder;
import org.sonar.batch.scan.filesystem.InputPathCache;
import org.sonar.batch.scan.measure.MeasureCache;
import org.sonar.batch.source.CodeColorizers;
import org.sonar.batch.symbol.DefaultSymbolTableBuilder;
import org.sonar.core.persistence.AbstractDaoTestCase;
import org.sonar.core.source.SnapshotDataTypes;
import org.sonar.core.source.db.FileSourceDao;
import org.sonar.core.source.db.FileSourceDto;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;

import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class SourcePersisterTest extends AbstractDaoTestCase {

    @Rule
    public TemporaryFolder temp = new TemporaryFolder();

    private SourcePersister sourcePersister;
    private InputPathCache inputPathCache;
    private ResourceCache resourceCache;
    private ProjectTree projectTree;
    private System2 system2;
    private MeasureCache measureCache;
    private ComponentDataCache componentDataCache;
    private DuplicationCache duplicationCache;

    private static final String PROJECT_KEY = "foo";

    private java.io.File basedir;

    @Before
    public void before() throws IOException {
        Snapshot snapshot = new Snapshot();
        snapshot.setId(1000);
        inputPathCache = mock(InputPathCache.class);
        resourceCache = mock(ResourceCache.class);
        projectTree = mock(ProjectTree.class);
        system2 = mock(System2.class);
        measureCache = mock(MeasureCache.class);
        when(measureCache.byMetric(anyString(), anyString()))
                .thenReturn(Collections.<org.sonar.api.measures.Measure>emptyList());
        componentDataCache = mock(ComponentDataCache.class);
        duplicationCache = mock(DuplicationCache.class);
        sourcePersister = new SourcePersister(inputPathCache, getMyBatis(), measureCache, componentDataCache,
                projectTree, system2, resourceCache, mock(CodeColorizers.class), duplicationCache);
        Project project = new Project(PROJECT_KEY);
        project.setUuid("projectUuid");
        when(projectTree.getRootProject()).thenReturn(project);
        basedir = temp.newFolder();
    }

    @Test
    public void testPersistDontTouchUnchanged() throws Exception {
        setupData("file_sources");
        when(system2.newDate()).thenReturn(DateUtils.parseDateTime("2014-10-29T16:44:02+0100"));

        String relativePathSame = "src/same.java";
        java.io.File sameFile = new java.io.File(basedir, relativePathSame);
        FileUtils.write(sameFile, "unchanged\ncontent");
        DefaultInputFile inputFileNew = new DefaultInputFile(PROJECT_KEY, relativePathSame).setLines(2)
                .setAbsolutePath(sameFile.getAbsolutePath())
                .setLineHashes(new byte[][] { md5("unchanged"), md5("ncontent") });
        when(inputPathCache.all()).thenReturn(Arrays.<InputPath>asList(inputFileNew));

        mockResourceCache(relativePathSame, PROJECT_KEY, "uuidsame");

        sourcePersister.persist();
        checkTables("testPersistDontTouchUnchanged", "file_sources");
    }

    @Test
    public void testPersistUpdateChanged() throws Exception {
        setupData("file_sources");
        Date now = DateUtils.parseDateTime("2014-10-29T16:44:02+0100");
        when(system2.newDate()).thenReturn(now);

        String relativePathSame = "src/changed.java";
        java.io.File sameFile = new java.io.File(basedir, relativePathSame);
        FileUtils.write(sameFile, "changed\ncontent");
        DefaultInputFile inputFileNew = new DefaultInputFile(PROJECT_KEY, relativePathSame).setLines(2)
                .setAbsolutePath(sameFile.getAbsolutePath())
                .setLineHashes(new byte[][] { md5("changed"), md5("content") });
        when(inputPathCache.all()).thenReturn(Arrays.<InputPath>asList(inputFileNew));

        mockResourceCache(relativePathSame, PROJECT_KEY, "uuidsame");

        sourcePersister.persist();

        FileSourceDto fileSourceDto = new FileSourceDao(getMyBatis()).select("uuidsame");
        assertThat(fileSourceDto.getCreatedAt())
                .isEqualTo(DateUtils.parseDateTime("2014-10-10T16:44:02+0200").getTime());
        assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(now.getTime());
        assertThat(fileSourceDto.getData()).isEqualTo(",,,,,,,,,,,,,,,changed\r\n,,,,,,,,,,,,,,,content\r\n");
        assertThat(fileSourceDto.getLineHashes()).isEqualTo(md5Hex("changed") + "\n" + md5Hex("content"));
        assertThat(fileSourceDto.getDataHash()).isEqualTo("d1a4dd62422639f665a8d80b37c59f8d");
    }

    @Test
    public void testPersistEmptyFile() throws Exception {
        setupData("file_sources");
        when(system2.newDate()).thenReturn(DateUtils.parseDateTime("2014-10-29T16:44:02+0100"));

        String relativePathEmpty = "src/empty.java";
        DefaultInputFile inputFileEmpty = new DefaultInputFile(PROJECT_KEY, relativePathEmpty).setLines(0)
                .setLineHashes(new byte[][] {});
        when(inputPathCache.all()).thenReturn(Arrays.<InputPath>asList(inputFileEmpty));

        mockResourceCache(relativePathEmpty, PROJECT_KEY, "uuidempty");

        sourcePersister.persist();
        checkTables("testPersistEmptyFile", "file_sources");
    }

    @Test
    public void testPersistNewFileNoScmNoHighlighting() throws Exception {
        setupData("file_sources");
        Date now = DateUtils.parseDateTime("2014-10-29T16:44:02+0100");
        when(system2.newDate()).thenReturn(now);

        String relativePathNew = "src/new.java";
        java.io.File newFile = new java.io.File(basedir, relativePathNew);
        FileUtils.write(newFile, "foo\nbar\nbiz");
        DefaultInputFile inputFileNew = new DefaultInputFile(PROJECT_KEY, relativePathNew).setLines(3)
                .setAbsolutePath(newFile.getAbsolutePath())
                .setLineHashes(new byte[][] { md5("foo"), md5("bar"), md5("biz") });
        when(inputPathCache.all()).thenReturn(Arrays.<InputPath>asList(inputFileNew));

        mockResourceCache(relativePathNew, PROJECT_KEY, "uuidnew");

        sourcePersister.persist();
        FileSourceDto fileSourceDto = new FileSourceDao(getMyBatis()).select("uuidnew");
        assertThat(fileSourceDto.getCreatedAt()).isEqualTo(now.getTime());
        assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(now.getTime());
        assertThat(fileSourceDto.getData())
                .isEqualTo(",,,,,,,,,,,,,,,foo\r\n,,,,,,,,,,,,,,,bar\r\n,,,,,,,,,,,,,,,biz\r\n");
        assertThat(fileSourceDto.getLineHashes())
                .isEqualTo(md5Hex("foo") + "\n" + md5Hex("bar") + "\n" + md5Hex("biz"));
        assertThat(fileSourceDto.getDataHash()).isEqualTo("a34ed99cc7d27150c82f5cba2b22b665");

    }

    @Test
    public void testPersistNewFileWithScmAndCoverageAndHighlighting() throws Exception {
        setupData("file_sources");
        Date now = DateUtils.parseDateTime("2014-10-29T16:44:02+0100");
        when(system2.newDate()).thenReturn(now);

        String relativePathNew = "src/new.java";
        java.io.File newFile = new java.io.File(basedir, relativePathNew);
        FileUtils.write(newFile, "foo\nbar\nbiz");
        DefaultInputFile inputFileNew = new DefaultInputFile(PROJECT_KEY, relativePathNew).setLines(3)
                .setAbsolutePath(newFile.getAbsolutePath()).setOriginalLineOffsets(new long[] { 0, 4, 7 })
                .setLineHashes(new byte[][] { md5("foo"), md5("bar"), md5("biz") });
        when(inputPathCache.all()).thenReturn(Arrays.<InputPath>asList(inputFileNew));

        mockResourceCache(relativePathNew, PROJECT_KEY, "uuidnew");

        String fileKey = PROJECT_KEY + ":" + relativePathNew;
        when(measureCache.byMetric(fileKey, CoreMetrics.SCM_AUTHORS_BY_LINE_KEY)).thenReturn(
                Arrays.asList(new Measure(CoreMetrics.SCM_AUTHORS_BY_LINE, "1=julien;2=simon;3=julien")));
        when(measureCache.byMetric(fileKey, CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE_KEY))
                .thenReturn(Arrays.asList(new Measure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE,
                        "1=2014-10-11T16:44:02+0100;2=2014-10-12T16:44:02+0100;3=2014-10-13T16:44:02+0100")));
        when(measureCache.byMetric(fileKey, CoreMetrics.SCM_REVISIONS_BY_LINE_KEY))
                .thenReturn(Arrays.asList(new Measure(CoreMetrics.SCM_REVISIONS_BY_LINE, "1=123;2=234;3=345")));
        when(measureCache.byMetric(fileKey, CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY))
                .thenReturn(Arrays.asList(new Measure(CoreMetrics.COVERAGE_LINE_HITS_DATA, "1=1;3=0")));
        when(measureCache.byMetric(fileKey, CoreMetrics.CONDITIONS_BY_LINE_KEY))
                .thenReturn(Arrays.asList(new Measure(CoreMetrics.CONDITIONS_BY_LINE, "1=4")));
        when(measureCache.byMetric(fileKey, CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY))
                .thenReturn(Arrays.asList(new Measure(CoreMetrics.COVERED_CONDITIONS_BY_LINE, "1=2")));
        when(measureCache.byMetric(fileKey, CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY))
                .thenReturn(Arrays.asList(new Measure(CoreMetrics.IT_COVERAGE_LINE_HITS_DATA, "1=2;3=0")));
        when(measureCache.byMetric(fileKey, CoreMetrics.IT_CONDITIONS_BY_LINE_KEY))
                .thenReturn(Arrays.asList(new Measure(CoreMetrics.IT_CONDITIONS_BY_LINE, "1=5")));
        when(measureCache.byMetric(fileKey, CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY))
                .thenReturn(Arrays.asList(new Measure(CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE, "1=3")));
        when(measureCache.byMetric(fileKey, CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA_KEY))
                .thenReturn(Arrays.asList(new Measure(CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA, "1=3;3=0")));
        when(measureCache.byMetric(fileKey, CoreMetrics.OVERALL_CONDITIONS_BY_LINE_KEY))
                .thenReturn(Arrays.asList(new Measure(CoreMetrics.OVERALL_CONDITIONS_BY_LINE, "1=6")));
        when(measureCache.byMetric(fileKey, CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY))
                .thenReturn(Arrays.asList(new Measure(CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE, "1=4")));

        SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder()
                .registerHighlightingRule(0, 3, TypeOfText.ANNOTATION)
                .registerHighlightingRule(4, 5, TypeOfText.COMMENT)
                .registerHighlightingRule(7, 16, TypeOfText.CONSTANT).build();
        when(componentDataCache.getData(fileKey, SnapshotDataTypes.SYNTAX_HIGHLIGHTING)).thenReturn(highlighting);

        DefaultSymbolTableBuilder symbolBuilder = new DefaultSymbolTableBuilder(fileKey, null);
        org.sonar.api.batch.sensor.symbol.Symbol s1 = symbolBuilder.newSymbol(1, 2);
        symbolBuilder.newReference(s1, 4);
        symbolBuilder.newReference(s1, 11);
        org.sonar.api.batch.sensor.symbol.Symbol s2 = symbolBuilder.newSymbol(4, 6);
        symbolBuilder.newReference(s2, 0);
        symbolBuilder.newReference(s2, 7);
        when(componentDataCache.getData(fileKey, SnapshotDataTypes.SYMBOL_HIGHLIGHTING))
                .thenReturn(symbolBuilder.build());

        DuplicationGroup group1 = new DuplicationGroup(new DuplicationGroup.Block(fileKey, 1, 1))
                .addDuplicate(new DuplicationGroup.Block(fileKey, 3, 1))
                .addDuplicate(new DuplicationGroup.Block("anotherFile1", 12, 1))
                .addDuplicate(new DuplicationGroup.Block("anotherFile2", 13, 1));

        DuplicationGroup group2 = new DuplicationGroup(new DuplicationGroup.Block(fileKey, 1, 2))
                .addDuplicate(new DuplicationGroup.Block("anotherFile1", 12, 2))
                .addDuplicate(new DuplicationGroup.Block("anotherFile2", 13, 2));
        when(duplicationCache.byComponent(fileKey)).thenReturn(Arrays.asList(group1, group2));

        sourcePersister.persist();

        FileSourceDto fileSourceDto = new FileSourceDao(getMyBatis()).select("uuidnew");
        assertThat(fileSourceDto.getCreatedAt()).isEqualTo(now.getTime());
        assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(now.getTime());
        assertThat(fileSourceDto.getLineHashes())
                .isEqualTo(md5Hex("foo") + "\n" + md5Hex("bar") + "\n" + md5Hex("biz"));
        assertThat(fileSourceDto.getData()).isEqualTo(
                "123,julien,2014-10-11T16:44:02+0100,1,4,2,2,5,3,3,6,4,\"0,3,a\",\"1,2,1;0,2,2\",\"1,3\",foo\r\n"
                        + "234,simon,2014-10-12T16:44:02+0100,,,,,,,,,,\"0,1,cd\",\"0,1,1;0,2,2\",3,bar\r\n"
                        + "345,julien,2014-10-13T16:44:02+0100,0,,,0,,,0,,,\"0,9,c\",\"4,5,1;0,2,2\",2,biz\r\n");
        assertThat(fileSourceDto.getDataHash()).isEqualTo("26930cf0250d525b04083185ff24a046");
    }

    @Test
    public void testSimpleConversionOfHighlightingOffset() {
        DefaultInputFile file = new DefaultInputFile(PROJECT_KEY, "src/foo.java").setLines(3)
                .setOriginalLineOffsets(new long[] { 0, 4, 7 });

        SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder()
                .registerHighlightingRule(0, 4, TypeOfText.ANNOTATION)
                .registerHighlightingRule(4, 5, TypeOfText.COMMENT)
                .registerHighlightingRule(7, 16, TypeOfText.CONSTANT).build();

        String[] highlightingPerLine = sourcePersister.computeHighlightingPerLine(file, highlighting);

        assertThat(highlightingPerLine).containsOnly("0,4,a", "0,1,cd", "0,9,c");
    }

    @Test
    public void testConversionOfHighlightingOffsetMultiLine() {
        DefaultInputFile file = new DefaultInputFile(PROJECT_KEY, "src/foo.java").setLines(3)
                .setOriginalLineOffsets(new long[] { 0, 4, 7 });

        SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder()
                .registerHighlightingRule(0, 3, TypeOfText.ANNOTATION)
                .registerHighlightingRule(4, 9, TypeOfText.COMMENT)
                .registerHighlightingRule(10, 16, TypeOfText.CONSTANT).build();

        String[] highlightingPerLine = sourcePersister.computeHighlightingPerLine(file, highlighting);

        assertThat(highlightingPerLine).containsOnly("0,3,a", "0,3,cd", "0,2,cd;3,9,c");
    }

    @Test
    public void testConversionOfHighlightingNestedRules() {
        DefaultInputFile file = new DefaultInputFile(PROJECT_KEY, "src/foo.java").setLines(3)
                .setOriginalLineOffsets(new long[] { 0, 4, 7 });

        SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder()
                .registerHighlightingRule(0, 3, TypeOfText.ANNOTATION)
                .registerHighlightingRule(4, 6, TypeOfText.COMMENT)
                .registerHighlightingRule(7, 16, TypeOfText.CONSTANT)
                .registerHighlightingRule(8, 15, TypeOfText.KEYWORD).build();

        String[] highlightingPerLine = sourcePersister.computeHighlightingPerLine(file, highlighting);

        assertThat(highlightingPerLine).containsOnly("0,3,a", "0,2,cd", "0,9,c;1,8,k");
    }

    @Test
    public void testConversionOfHighlightingNestedRulesMultiLine() {
        DefaultInputFile file = new DefaultInputFile(PROJECT_KEY, "src/foo.java").setLines(3)
                .setOriginalLineOffsets(new long[] { 0, 4, 7 });

        SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder()
                .registerHighlightingRule(0, 3, TypeOfText.ANNOTATION)
                .registerHighlightingRule(4, 6, TypeOfText.COMMENT)
                .registerHighlightingRule(4, 16, TypeOfText.CONSTANT)
                .registerHighlightingRule(8, 15, TypeOfText.KEYWORD).build();

        String[] highlightingPerLine = sourcePersister.computeHighlightingPerLine(file, highlighting);

        assertThat(highlightingPerLine).containsOnly("0,3,a", "0,3,c;0,2,cd", "0,9,c;1,8,k");
    }

    @Test
    public void testSimpleConversionOfSymbolOffset() {
        DefaultInputFile file = new DefaultInputFile(PROJECT_KEY, "src/foo.java").setLines(3)
                .setOriginalLineOffsets(new long[] { 0, 4, 7 });

        DefaultSymbolTableBuilder symbolBuilder = new DefaultSymbolTableBuilder(PROJECT_KEY + ":" + "src/foo.java",
                null);
        org.sonar.api.batch.sensor.symbol.Symbol s1 = symbolBuilder.newSymbol(1, 2);
        symbolBuilder.newReference(s1, 4);
        symbolBuilder.newReference(s1, 11);
        org.sonar.api.batch.sensor.symbol.Symbol s2 = symbolBuilder.newSymbol(4, 6);
        symbolBuilder.newReference(s2, 0);
        symbolBuilder.newReference(s2, 7);

        String[] symbolsPerLine = sourcePersister.computeSymbolReferencesPerLine(file, symbolBuilder.build());

        assertThat(symbolsPerLine).containsOnly("1,2,1;0,2,2", "0,1,1;0,2,2", "4,5,1;0,2,2");
    }

    @Test
    public void verifyDeclarationOrderOfSymbolHasNoImpact() {
        DefaultInputFile file = new DefaultInputFile(PROJECT_KEY, "src/foo.java").setLines(3)
                .setOriginalLineOffsets(new long[] { 0, 4, 7 });

        DefaultSymbolTableBuilder symbolBuilder = new DefaultSymbolTableBuilder(PROJECT_KEY + ":" + "src/foo.java",
                null);
        org.sonar.api.batch.sensor.symbol.Symbol s2 = symbolBuilder.newSymbol(4, 6);
        symbolBuilder.newReference(s2, 7);
        symbolBuilder.newReference(s2, 0);
        org.sonar.api.batch.sensor.symbol.Symbol s1 = symbolBuilder.newSymbol(1, 2);
        symbolBuilder.newReference(s1, 11);
        symbolBuilder.newReference(s1, 4);

        String[] symbolsPerLine = sourcePersister.computeSymbolReferencesPerLine(file, symbolBuilder.build());

        assertThat(symbolsPerLine).containsOnly("1,2,1;0,2,2", "0,1,1;0,2,2", "4,5,1;0,2,2");
    }

    private void mockResourceCache(String relativePathEmpty, String projectKey, String uuid) {
        File sonarFile = File.create(relativePathEmpty);
        sonarFile.setUuid(uuid);
        when(resourceCache.get(projectKey + ":" + relativePathEmpty))
                .thenReturn(new BatchResource(1, sonarFile, new Snapshot(), null));
    }

    private byte[] md5(String string) {
        return DigestUtils.md5(string);
    }

    private String md5Hex(String string) {
        return DigestUtils.md5Hex(string);
    }

}