]> source.dussan.org Git - sonarqube.git/blob
dba4a9c613309ce454ec997c9a70a231dcdd87a5
[sonarqube.git] /
1 /*
2  * SonarQube, open source software quality management tool.
3  * Copyright (C) 2008-2013 SonarSource
4  * mailto:contact AT sonarsource DOT com
5  *
6  * SonarQube is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * SonarQube is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.plugins.core.issue;
21
22 import org.junit.Before;
23 import org.junit.Test;
24 import org.mockito.ArgumentMatcher;
25 import org.sonar.api.batch.DecoratorContext;
26 import org.sonar.api.component.ResourcePerspectives;
27 import org.sonar.api.resources.File;
28 import org.sonar.api.resources.Project;
29 import org.sonar.api.resources.Resource;
30 import org.sonar.api.rules.RuleFinder;
31 import org.sonar.batch.issue.IssueCache;
32 import org.sonar.core.issue.DefaultIssue;
33 import org.sonar.core.issue.IssueChangeContext;
34 import org.sonar.core.issue.db.IssueDto;
35 import org.sonar.core.issue.workflow.IssueWorkflow;
36 import org.sonar.core.persistence.AbstractDaoTestCase;
37 import org.sonar.java.api.JavaClass;
38
39 import java.util.*;
40
41 import static org.fest.assertions.Assertions.assertThat;
42 import static org.mockito.Mockito.*;
43
44 public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
45
46   IssueTrackingDecorator decorator;
47   IssueCache issueCache = mock(IssueCache.class);
48   InitialOpenIssuesStack initialOpenIssues = mock(InitialOpenIssuesStack.class);
49   IssueTracking tracking = mock(IssueTracking.class, RETURNS_MOCKS);
50   IssueFilters filters = mock(IssueFilters.class);
51   IssueHandlers handlers = mock(IssueHandlers.class);
52   IssueWorkflow workflow = mock(IssueWorkflow.class);
53   ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
54   Date loadedDate = new Date();
55   RuleFinder ruleFinder = mock(RuleFinder.class);
56
57   @Before
58   public void init() {
59     when(initialOpenIssues.getLoadedDate()).thenReturn(loadedDate);
60     decorator = new IssueTrackingDecorator(
61       issueCache,
62       initialOpenIssues,
63       tracking,
64       filters, handlers, workflow,
65       mock(Project.class),
66       perspectives,
67       ruleFinder);
68   }
69
70   @Test
71   public void should_execute_on_project() {
72     Project project = mock(Project.class);
73     when(project.isLatestAnalysis()).thenReturn(true);
74     assertThat(decorator.shouldExecuteOnProject(project)).isTrue();
75   }
76
77   @Test
78   public void should_not_execute_on_project_if_past_scan() {
79     Project project = mock(Project.class);
80     when(project.isLatestAnalysis()).thenReturn(false);
81     assertThat(decorator.shouldExecuteOnProject(project)).isFalse();
82   }
83
84   @Test
85   public void should_not_be_executed_on_classes_not_methods() throws Exception {
86     DecoratorContext context = mock(DecoratorContext.class);
87     decorator.decorate(JavaClass.create("org.foo.Bar"), context);
88     verifyZeroInteractions(context, issueCache, tracking, filters, handlers, workflow);
89   }
90
91   @Test
92   public void should_process_open_issues() throws Exception {
93     Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
94     final DefaultIssue issue = new DefaultIssue();
95
96     // INPUT : one issue, no open issues during previous scan, no filtering
97     when(issueCache.byComponent("struts:Action.java")).thenReturn(Arrays.asList(issue));
98     when(filters.accept(issue)).thenReturn(true);
99     List<IssueDto> dbIssues = Collections.emptyList();
100     when(initialOpenIssues.selectAndRemove(123)).thenReturn(dbIssues);
101
102     decorator.doDecorate(file);
103
104     // Apply filters, track, apply transitions, notify extensions then update cache
105     verify(filters).accept(issue);
106     verify(tracking).track(eq(file), eq(dbIssues), argThat(new ArgumentMatcher<Collection<DefaultIssue>>() {
107       @Override
108       public boolean matches(Object o) {
109         List<DefaultIssue> issues = (List<DefaultIssue>) o;
110         return issues.size() == 1 && issues.get(0) == issue;
111       }
112     }));
113     verify(workflow).doAutomaticTransition(eq(issue), any(IssueChangeContext.class));
114     verify(handlers).execute(eq(issue), any(IssueChangeContext.class));
115     verify(issueCache).put(issue);
116   }
117
118   @Test
119   public void should_register_unmatched_issues() throws Exception {
120     // "Unmatched" issues existed in previous scan but not in current one -> they have to be closed
121     Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
122     DefaultIssue openIssue = new DefaultIssue();
123
124     // INPUT : one issue, one open issue during previous scan, no filtering
125     when(issueCache.byComponent("struts:Action.java")).thenReturn(Arrays.asList(openIssue));
126     when(filters.accept(openIssue)).thenReturn(true);
127     IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setResolution("OPEN").setStatus("OPEN").setRuleKey_unit_test_only("squid", "AvoidCycle");
128
129     IssueTrackingResult trackingResult = new IssueTrackingResult();
130     trackingResult.addUnmatched(unmatchedIssue);
131
132     when(tracking.track(eq(file), anyCollection(), anyCollection())).thenReturn(trackingResult);
133
134     decorator.doDecorate(file);
135
136     verify(workflow, times(2)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class));
137     verify(handlers, times(2)).execute(any(DefaultIssue.class), any(IssueChangeContext.class));
138     verify(issueCache, times(2)).put(any(DefaultIssue.class));
139
140     verify(issueCache).put(argThat(new ArgumentMatcher<DefaultIssue>() {
141       @Override
142       public boolean matches(Object o) {
143         DefaultIssue issue = (DefaultIssue) o;
144         return "ABCDE".equals(issue.key());
145       }
146     }));
147   }
148
149   @Test
150   public void should_register_issues_on_deleted_components() throws Exception {
151     Project project = new Project("struts");
152     DefaultIssue openIssue = new DefaultIssue();
153     when(issueCache.byComponent("struts")).thenReturn(Arrays.asList(openIssue));
154     when(filters.accept(openIssue)).thenReturn(true);
155     IssueDto deadIssue = new IssueDto().setKee("ABCDE").setResolution("OPEN").setStatus("OPEN").setRuleKey_unit_test_only("squid", "AvoidCycle");
156     when(initialOpenIssues.getAllIssues()).thenReturn(Arrays.asList(deadIssue));
157
158     decorator.doDecorate(project);
159
160     // the dead issue must be closed -> apply automatic transition, notify handlers and add to cache
161     verify(workflow, times(2)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class));
162     verify(handlers, times(2)).execute(any(DefaultIssue.class), any(IssueChangeContext.class));
163     verify(issueCache, times(2)).put(any(DefaultIssue.class));
164
165     verify(issueCache).put(argThat(new ArgumentMatcher<DefaultIssue>() {
166       @Override
167       public boolean matches(Object o) {
168         DefaultIssue dead = (DefaultIssue) o;
169         return "ABCDE".equals(dead.key()) && !dead.isNew() && !dead.isAlive();
170       }
171     }));
172   }
173 }