]> source.dussan.org Git - sonarqube.git/blob
c93f7365d70bc35b9b3c98a0cffedeb49f2ee273
[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.ArgumentCaptor;
25 import org.mockito.ArgumentMatcher;
26 import org.sonar.api.batch.DecoratorContext;
27 import org.sonar.api.component.ResourcePerspectives;
28 import org.sonar.api.profiles.RulesProfile;
29 import org.sonar.api.resources.File;
30 import org.sonar.api.resources.Project;
31 import org.sonar.api.resources.Resource;
32 import org.sonar.api.rule.RuleKey;
33 import org.sonar.api.rules.Rule;
34 import org.sonar.api.rules.RuleFinder;
35 import org.sonar.batch.issue.IssueCache;
36 import org.sonar.core.issue.DefaultIssue;
37 import org.sonar.core.issue.IssueChangeContext;
38 import org.sonar.core.issue.IssueUpdater;
39 import org.sonar.core.issue.db.IssueDto;
40 import org.sonar.core.issue.workflow.IssueWorkflow;
41 import org.sonar.core.persistence.AbstractDaoTestCase;
42 import org.sonar.java.api.JavaClass;
43
44 import java.util.*;
45
46 import static org.fest.assertions.Assertions.assertThat;
47 import static org.mockito.Mockito.*;
48
49 public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
50
51   IssueTrackingDecorator decorator;
52   IssueCache issueCache = mock(IssueCache.class, RETURNS_MOCKS);
53   InitialOpenIssuesStack initialOpenIssues = mock(InitialOpenIssuesStack.class);
54   IssueTracking tracking = mock(IssueTracking.class, RETURNS_MOCKS);
55   IssueHandlers handlers = mock(IssueHandlers.class);
56   IssueWorkflow workflow = mock(IssueWorkflow.class);
57   IssueUpdater updater = mock(IssueUpdater.class);
58   ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
59   Date loadedDate = new Date();
60   RulesProfile profile = mock(RulesProfile.class);
61   RuleFinder ruleFinder = mock(RuleFinder.class);
62
63   @Before
64   public void init() {
65     when(initialOpenIssues.getLoadedDate()).thenReturn(loadedDate);
66     decorator = new IssueTrackingDecorator(
67       issueCache,
68       initialOpenIssues,
69       tracking,
70       handlers,
71       workflow,
72       updater,
73       mock(Project.class),
74       perspectives,
75       profile,
76       ruleFinder);
77   }
78
79   @Test
80   public void should_execute_on_project() {
81     Project project = mock(Project.class);
82     assertThat(decorator.shouldExecuteOnProject(project)).isTrue();
83   }
84
85   @Test
86   public void should_not_be_executed_on_classes_not_methods() throws Exception {
87     DecoratorContext context = mock(DecoratorContext.class);
88     decorator.decorate(JavaClass.create("org.foo.Bar"), context);
89     verifyZeroInteractions(context, issueCache, tracking, handlers, workflow);
90   }
91
92   @Test
93   public void should_process_open_issues() throws Exception {
94     Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
95     final DefaultIssue issue = new DefaultIssue();
96
97     // INPUT : one issue, no open issues during previous scan, no filtering
98     when(issueCache.byComponent("struts:Action.java")).thenReturn(Arrays.asList(issue));
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(tracking).track(eq(file), eq(dbIssues), argThat(new ArgumentMatcher<Collection<DefaultIssue>>() {
106       @Override
107       public boolean matches(Object o) {
108         List<DefaultIssue> issues = (List<DefaultIssue>) o;
109         return issues.size() == 1 && issues.get(0) == issue;
110       }
111     }));
112     verify(workflow).doAutomaticTransition(eq(issue), any(IssueChangeContext.class));
113     verify(handlers).execute(eq(issue), any(IssueChangeContext.class));
114     verify(issueCache).put(issue);
115   }
116
117   @Test
118   public void should_register_unmatched_issues_as_end_of_life() throws Exception {
119     // "Unmatched" issues existed in previous scan but not in current one -> they have to be closed
120     Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
121
122     // INPUT : one issue existing during previous scan
123     IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey_unit_test_only("squid", "AvoidCycle");
124
125     IssueTrackingResult trackingResult = new IssueTrackingResult();
126     trackingResult.addUnmatched(unmatchedIssue);
127
128     when(tracking.track(eq(file), anyCollection(), anyCollection())).thenReturn(trackingResult);
129
130     decorator.doDecorate(file);
131
132     verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class));
133     verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class));
134
135     ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
136     verify(issueCache).put(argument.capture());
137
138     DefaultIssue issue = argument.getValue();
139     assertThat(issue.key()).isEqualTo("ABCDE");
140     assertThat(issue.isNew()).isFalse();
141     assertThat(issue.isEndOfLife()).isTrue();
142   }
143
144   @Test
145   public void manual_issues_should_be_kept_open() throws Exception {
146     // "Unmatched" issues existed in previous scan but not in current one -> they have to be closed
147     Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
148
149     // INPUT : one issue existing during previous scan
150     IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setStatus("OPEN").setRuleKey_unit_test_only("manual", "Performance");
151     when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance"));
152
153     IssueTrackingResult trackingResult = new IssueTrackingResult();
154     trackingResult.addUnmatched(unmatchedIssue);
155
156     when(tracking.track(eq(file), anyCollection(), anyCollection())).thenReturn(trackingResult);
157
158     decorator.doDecorate(file);
159
160     verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class));
161     verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class));
162
163     ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
164     verify(issueCache).put(argument.capture());
165
166     DefaultIssue issue = argument.getValue();
167     assertThat(issue.key()).isEqualTo("ABCDE");
168     assertThat(issue.isNew()).isFalse();
169     assertThat(issue.isEndOfLife()).isFalse();
170     assertThat(issue.isOnDisabledRule()).isFalse();
171   }
172
173   @Test
174   public void manual_issues_should_be_closed_if_manual_rule_is_removed() throws Exception {
175     // "Unmatched" issues existed in previous scan but not in current one -> they have to be closed
176     Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
177
178     // INPUT : one issue existing during previous scan
179     IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setStatus("OPEN").setRuleKey_unit_test_only("manual", "Performance");
180     when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance").setStatus(Rule.STATUS_REMOVED));
181
182     IssueTrackingResult trackingResult = new IssueTrackingResult();
183     trackingResult.addUnmatched(unmatchedIssue);
184
185     when(tracking.track(eq(file), anyCollection(), anyCollection())).thenReturn(trackingResult);
186
187     decorator.doDecorate(file);
188
189     verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class));
190     verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class));
191
192     ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
193     verify(issueCache).put(argument.capture());
194
195     DefaultIssue issue = argument.getValue();
196     assertThat(issue.key()).isEqualTo("ABCDE");
197     assertThat(issue.isNew()).isFalse();
198     assertThat(issue.isEndOfLife()).isTrue();
199     assertThat(issue.isOnDisabledRule()).isTrue();
200   }
201
202   @Test
203   public void should_register_issues_on_deleted_components() throws Exception {
204     Project project = new Project("struts");
205     DefaultIssue openIssue = new DefaultIssue();
206     when(issueCache.byComponent("struts")).thenReturn(Arrays.asList(openIssue));
207     IssueDto deadIssue = new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey_unit_test_only("squid", "AvoidCycle");
208     when(initialOpenIssues.getAllIssues()).thenReturn(Arrays.asList(deadIssue));
209
210     decorator.doDecorate(project);
211
212     // the dead issue must be closed -> apply automatic transition, notify handlers and add to cache
213     verify(workflow, times(2)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class));
214     verify(handlers, times(2)).execute(any(DefaultIssue.class), any(IssueChangeContext.class));
215     verify(issueCache, times(2)).put(any(DefaultIssue.class));
216
217     verify(issueCache).put(argThat(new ArgumentMatcher<DefaultIssue>() {
218       @Override
219       public boolean matches(Object o) {
220         DefaultIssue dead = (DefaultIssue) o;
221         return "ABCDE".equals(dead.key()) && !dead.isNew() && dead.isEndOfLife();
222       }
223     }));
224   }
225 }