1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
|
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT 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 util;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableMap;
import com.sonar.orchestrator.Orchestrator;
import com.sonar.orchestrator.build.SonarRunner;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import org.junit.rules.ExternalResource;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.Objects.requireNonNull;
import static util.ItUtils.resetSettings;
/**
* Rule wrapping around an {@link Orchestrator} instance which handle:
* <ul>
* <li>automatic reset of Orchestrator data after each method when used as a {@link org.junit.Rule},
* after each class when used as a {@link org.junit.ClassRule}</li>
* <li>automatic reset of server properties after each method when used as a {@link org.junit.Rule},
* after each class when used as a {@link org.junit.ClassRule}</li>
* <li>associating project with a specific Quality Profile before running an analysis</li>
* <li>provisioning a project before its first analysis so that a Quality Profile can be associated to it</li>
* <li>"restoring" a Quality Profile before an analysis with a specific Quality Profile</li>
* </ul>
*
* This Rule has preparatory methods ({@link #registerProfile(String)} and {@link #registerProject(String)}) which
* will allow consequent calls to the rule methods to be based solely on Quality Profile and Project keys. In addition,
* these methods returns the Quality Profile and Project key to avoid information duplication (the only magic string
* the IT developer has to know is the relative path of the Project or the Quality Profile).
*
* To run an analysis, use method {@link #newProjectAnalysis(String)} to create a {@link ProjectAnalysis}
* object. This object has a {@link ProjectAnalysis#run()} method which will start the analysis.
* {@link ProjectAnalysis} can safely be reused to run the same analysis multiple times. In addition, these objects are
* immutable. Any call to one of their method which would modify their state will create a new instance which can also
* be reused at will.
*/
public class ProjectAnalysisRule extends ExternalResource {
private final Orchestrator orchestrator;
private final LoadedProfiles loadedProfiles = new LoadedProfiles();
private final LoadedProjects loadedProjects = new LoadedProjects();
private final Set<String> serverProperties = new HashSet<>();
private ProjectAnalysisRule(Orchestrator orchestrator) {
this.orchestrator = orchestrator;
}
public static ProjectAnalysisRule from(Orchestrator orchestrator) {
return new ProjectAnalysisRule(requireNonNull(orchestrator, "Orchestrator instance can not be null"));
}
/**
* @param relativePathToProfile eg.: "/issue/suite/IssueFilterExtensionTest/xoo-with-many-rules.xml"
*
* @return the quality profile key
*/
public String registerProfile(String relativePathToProfile) {
return this.loadedProfiles.loadProfile(relativePathToProfile);
}
/**
* @param projectRelativePath path relative to it/projects, eg. "shared/xoo-multi-modules-sample"
*
* @return the project key
*/
public String registerProject(String projectRelativePath) {
return this.loadedProjects.load(projectRelativePath);
}
public ProjectAnalysis newProjectAnalysis(String projectKey) {
ProjectState projectState = this.loadedProjects.getProjectState(projectKey);
return new ProjectAnalysisImpl(projectState, null, false);
}
@Override
protected void before() throws Throwable {
orchestrator.resetData();
}
@Override
protected void after() {
resetServerProperties();
resetRuleState();
}
private void resetServerProperties() {
resetSettings(orchestrator, null, serverProperties.toArray(new String[] {}));
}
public void setServerPropertyImpl(String key, @Nullable String value) {
ItUtils.setServerProperty(orchestrator, key, value);
}
public ProjectAnalysisRule setServerProperty(String key, String value) {
setServerPropertyImpl(key, value);
this.serverProperties.add(key);
return this;
}
@Immutable
private final class ProjectAnalysisImpl implements ProjectAnalysis {
private final ProjectState projectState;
@CheckForNull
private final Profile qualityProfile;
private final boolean debugLogs;
@CheckForNull
private final String[] properties;
private ProjectAnalysisImpl(ProjectState projectState, @Nullable Profile qualityProfile, boolean debugLogs, String... properties) {
this.projectState = projectState;
this.qualityProfile = qualityProfile;
this.debugLogs = debugLogs;
this.properties = properties;
}
@Override
public ProjectAnalysis withQualityProfile(String qualityProfileKey) {
checkNotNull(qualityProfileKey, "Specified Quality Profile Key can not be null");
if (this.qualityProfile != null && this.qualityProfile.getProfileKey().equals(qualityProfileKey)) {
return this;
}
return new ProjectAnalysisImpl(this.projectState, loadedProfiles.getState(qualityProfileKey), this.debugLogs, this.properties);
}
@Override
public ProjectAnalysis withXooEmptyProfile() {
if (this.qualityProfile == Profile.XOO_EMPTY_PROFILE) {
return this;
}
return new ProjectAnalysisImpl(this.projectState, Profile.XOO_EMPTY_PROFILE, this.debugLogs, this.properties);
}
@Override
public ProjectAnalysis withDebugLogs(boolean enabled) {
if (this.debugLogs == enabled) {
return this;
}
return new ProjectAnalysisImpl(this.projectState, this.qualityProfile, enabled, this.properties);
}
@Override
public ProjectAnalysis withProperties(String... properties) {
checkArgument(
properties == null || properties.length % 2 == 0,
"there must be an even number of String parameters (got %s): key/value pairs must be complete", properties == null ? 0 : properties.length);
return new ProjectAnalysisImpl(this.projectState, this.qualityProfile, this.debugLogs, properties);
}
@Override
public void run() {
provisionIfNecessary();
setQualityProfileIfNecessary();
runAnalysis();
}
private void setQualityProfileIfNecessary() {
if (this.qualityProfile != null) {
if (this.qualityProfile != Profile.XOO_EMPTY_PROFILE) {
ItUtils.restoreProfile(orchestrator, getClass().getResource(this.qualityProfile.getRelativePath()));
}
orchestrator.getServer().associateProjectToQualityProfile(
this.projectState.getProjectKey(),
this.qualityProfile.getLanguageKey(),
this.qualityProfile.getProfileKey());
}
}
private void provisionIfNecessary() {
if (this.qualityProfile != null && !projectState.isProvisioned()) {
String projectKey = projectState.getProjectKey();
orchestrator.getServer().provisionProject(projectKey, MoreObjects.firstNonNull(projectState.getProjectName(), projectKey));
projectState.setProvisioned(true);
}
}
private SonarRunner runAnalysis() {
SonarRunner sonarRunner = SonarRunner.create(projectState.getProjectDir());
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
for (int i = 0; i < this.properties.length; i += 2) {
builder.put(this.properties[i], this.properties[i + 1]);
}
SonarRunner scan = sonarRunner.setDebugLogs(this.debugLogs).setProperties(builder.build());
orchestrator.executeBuild(scan);
return scan;
}
}
private void resetRuleState() {
this.loadedProjects.reset();
this.loadedProfiles.reset();
this.serverProperties.clear();
}
}
|