sortedDifferences, final PrintStream out) {
190 | String currentClassName = "";
191 | for (final Difference difference : sortedDifferences) {
192 | if (!currentClassName.equals(difference.getClassName())) {
193 | out.println("Class "+difference.getClassName());
194 | }
195 | out.println(" "+extractActionType(difference)+" "+extractInfoType(difference.getInfo())+" "+extractDetails(difference));
196 | currentClassName = difference.getClassName();
197 | }
198 | }
199 |
200 | /**
201 | * Dumps on out
all differences separated by its type in the order
202 | * remove
, change
, deprecate
and add
.
203 | *
204 | * Prepends statistics per class regarding difference type.
205 | *
206 | * @param delta the delta to be dumped
207 | * @param iwidth the integer width for formated integer counter
208 | * @param out the print output stream
209 | */
210 | public static void dumpFullStats(final Delta delta, final int iwidth, final PrintStream out) {
211 | final Set diffs = delta.getDifferences();
212 |
213 | final List diffsAdd = new ArrayList();
214 | final List diffsChange = new ArrayList();
215 | final List diffsDeprecate = new ArrayList();
216 | final List diffsRemove = new ArrayList();
217 | final Map className2DiffCount = new HashMap();
218 |
219 | int maxClassNameLen = 0;
220 |
221 | for(final Iterator iter = diffs.iterator(); iter.hasNext(); ) {
222 | final Difference diff = iter.next();
223 | final String className = diff.getClassName();
224 | maxClassNameLen = Math.max(maxClassNameLen, className.length());
225 |
226 | DiffCount dc = className2DiffCount.get(className);
227 | if( null == dc ) {
228 | dc = new DiffCount(className);
229 | className2DiffCount.put(className, dc);
230 | }
231 |
232 | if( diff instanceof Delta.Add ) {
233 | diffsAdd.add(diff);
234 | dc.additions++;
235 | } else if( diff instanceof Delta.Change ) {
236 | diffsChange.add(diff);
237 | dc.changes++;
238 | } else if( diff instanceof Delta.Deprecate ) {
239 | diffsDeprecate.add(diff);
240 | dc.deprecates++;
241 | } else if( diff instanceof Delta.Remove ) {
242 | diffsRemove.add(diff);
243 | dc.removes++;
244 | }
245 | }
246 | Collections.sort(diffsAdd);
247 | Collections.sort(diffsChange);
248 | Collections.sort(diffsDeprecate);
249 | Collections.sort(diffsRemove);
250 |
251 | final List classNames = new ArrayList(className2DiffCount.keySet());
252 | Collections.sort(classNames);
253 |
254 | System.err.println("Summary: "+diffs.size()+" differences in "+classNames.size()+" classes:");
255 | System.err.println(" Remove "+diffsRemove.size()+
256 | ", Change "+diffsChange.size()+
257 | ", Deprecate "+diffsDeprecate.size()+
258 | ", Add "+diffsAdd.size());
259 | System.err.printf("%n");
260 |
261 | int iterI = 0;
262 | for(final Iterator iter = classNames.iterator(); iter.hasNext(); iterI++) {
263 | final String className = iter.next();
264 | final DiffCount dc = className2DiffCount.get(className);
265 | System.err.printf("%"+iwidth+"d/%"+iwidth+"d: %-"+maxClassNameLen+"s: %s%n", iterI, classNames.size(), className, dc.format(iwidth));
266 | }
267 |
268 | System.err.printf("%n%nRemoves%n%n");
269 | dump(diffsRemove, System.err);
270 |
271 | System.err.printf("%n%nChanges%n%n");
272 | dump(diffsChange, System.err);
273 |
274 | System.err.printf("%n%nDeprecates%n%n");
275 | dump(diffsDeprecate, System.err);
276 |
277 | System.err.printf("%n%nAdditions%n%n");
278 | dump(diffsAdd, System.err);
279 | System.err.printf("%n%n");
280 | }
281 |
282 | static class DiffCount {
283 | public DiffCount(String name) { this.name = name; }
284 | public final String name;
285 | public int removes;
286 | public int changes;
287 | public int deprecates;
288 | public int additions;
289 | public String toString() { return name+": Remove "+removes+", Change "+changes+", Deprecate "+deprecates+", Add "+additions; }
290 | public String format(final int iwidth) {
291 | return String.format("Remove %"+iwidth+"d, Change %"+iwidth+"d, Deprecate %"+iwidth+"d, Add %"+iwidth+"d",
292 | removes, changes, deprecates, additions);
293 | }
294 | }
295 |
296 | }
297 |
--------------------------------------------------------------------------------
/api/src/main/java/org/semver/Main.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2012-2014 Julien Eluard and contributors
3 | * This project includes software developed by Julien Eluard: https://github.com/jeluard/
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.semver;
18 |
19 | import java.io.File;
20 | import java.io.IOException;
21 | import java.util.Arrays;
22 | import java.util.LinkedHashSet;
23 | import java.util.Set;
24 |
25 | import de.tototec.cmdoption.CmdOption;
26 | import de.tototec.cmdoption.CmdlineParser;
27 | import de.tototec.cmdoption.CmdlineParserException;
28 | import org.osjava.jardiff.DiffCriteria;
29 | import org.osjava.jardiff.PublicDiffCriteria;
30 | import org.osjava.jardiff.SimpleDiffCriteria;
31 |
32 | /**
33 | *
34 | * CLI interface.
35 | *
36 | */
37 | public class Main {
38 |
39 | static class Config {
40 | @CmdOption(names = { "--help", "-h" }, description = "Show this help and exit.", isHelp = true)
41 | boolean help;
42 |
43 | @CmdOption(names = { "--diff", "-d" }, conflictsWith = { "--check", "--infer", "--validate" }, description = "Show the differences between two jars.")
44 | public boolean diff;
45 |
46 | @CmdOption(names = { "--check", "-c" }, conflictsWith = { "--diff", "--infer", "--validate" }, description = "Check the compatibility of two jars.")
47 | public boolean check;
48 |
49 | @CmdOption(names = {"--publicOnly", "-p"}, description = "Checks public members only")
50 | public boolean publicOnly;
51 |
52 | @CmdOption(names = { "--infer", "-i" }, requires = { "--base-version" }, conflictsWith = { "--diff", "--check",
53 | "--validate" }, description = "Infer the version of the new jar based on the previous jar.")
54 | public boolean infer;
55 |
56 | @CmdOption(names = { "--validate", "-v" }, requires = { "--base-version", "--new-version" }, conflictsWith = {
57 | "--diff", "--check", "--infer" }, description = "Validate that the versions of two jars fulfil the semver specification.")
58 | public boolean validate;
59 |
60 | @CmdOption(names = { "--base-jar" }, args = { "JAR" }, minCount = 1, description = "The base jar.")
61 | public String baseJar;
62 |
63 | @CmdOption(names = { "--new-jar" }, args = { "JAR" }, minCount = 1, description = "The new jar.")
64 | public String newJar;
65 |
66 | final Set includes = new LinkedHashSet();
67 |
68 | @CmdOption(names = { "--includes" }, args = { "INCLUDE;..." }, description = "Semicolon separated list of full qualified class names to be included.")
69 | public void setIncludes(String includes) {
70 | if (includes != null) {
71 | this.includes.addAll(Arrays.asList(includes.split(";")));
72 | }
73 | }
74 |
75 | final Set excludes = new LinkedHashSet();
76 |
77 | @CmdOption(names = { "--excludes" }, args = { "EXCLUDE;..." }, description = "Semicolon separated list of full qualified class names to be excluded.")
78 | public void setExcludes(String excludes) {
79 | if (excludes != null) {
80 | this.excludes.addAll(Arrays.asList(excludes.split(";")));
81 | }
82 | }
83 |
84 | @CmdOption(names = { "--base-version" }, args = { "VERSION" }, description = "Version of the base jar (given with --base-jar).")
85 | public String baseVersion;
86 |
87 | @CmdOption(names = { "--new-version" }, args = { "VERSION" }, description = "Version of the new jar (given with --new-jar).")
88 | public String newVersion;
89 | }
90 |
91 | public static void main(final String[] args) throws IOException {
92 | Config config = new Config();
93 | CmdlineParser cmdlineParser = new CmdlineParser(config);
94 | // Load translations of command line descriptions
95 | cmdlineParser.setResourceBundle(Main.class.getPackage().getName() + ".Messages", Main.class.getClassLoader());
96 | cmdlineParser.setProgramName("semver");
97 | cmdlineParser.setAboutLine("Semantic Version validator.");
98 | try {
99 | cmdlineParser.parse(args);
100 | } catch (CmdlineParserException e) {
101 | System.err.println("Error: " + e.getLocalizedMessage() + "\nRun semver --help for help.");
102 | System.exit(1);
103 | }
104 |
105 | if (config.help) {
106 | cmdlineParser.usage();
107 | System.exit(0);
108 | }
109 |
110 | final DiffCriteria diffCriteria = config.publicOnly ? new PublicDiffCriteria() : new SimpleDiffCriteria();
111 | final Comparer comparer = new Comparer(diffCriteria, new File(config.baseJar), new File(config.newJar),
112 | config.includes, config.excludes);
113 | final Delta delta = comparer.diff();
114 |
115 | if (config.diff) {
116 | Dumper.dump(delta);
117 | }
118 |
119 | if (config.check) {
120 | System.out.println(delta.computeCompatibilityType());
121 | }
122 |
123 | if (config.infer) {
124 | System.out.println(delta.infer(Version.parse(config.baseVersion)));
125 | }
126 |
127 | if (config.validate) {
128 | System.out.println(delta.validate(Version.parse(config.baseVersion), Version.parse(config.newVersion)));
129 | }
130 |
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/api/src/main/java/org/semver/Version.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2012-2014 Julien Eluard and contributors
3 | * This project includes software developed by Julien Eluard: https://github.com/jeluard/
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.semver;
18 |
19 | import java.util.regex.Matcher;
20 | import java.util.regex.Pattern;
21 |
22 | import javax.annotation.Nonnegative;
23 | import javax.annotation.Nonnull;
24 | import javax.annotation.Nullable;
25 | import javax.annotation.concurrent.Immutable;
26 |
27 | import org.apache.commons.lang.StringUtils;
28 |
29 | /**
30 | *
31 | * Version following semantic defined by Semantic Versioning document.
32 | *
33 | */
34 | @Immutable
35 | public final class Version implements Comparable {
36 |
37 | /**
38 | * {@link Version} element. From most meaningful to less meaningful.
39 | */
40 | public enum Element {
41 | MAJOR, MINOR, PATCH, SPECIAL;
42 | }
43 |
44 | private static final String FORMAT = "(\\d+)\\.(\\d+)(?:\\.)?(\\d*)(\\.|-|\\+)?([0-9A-Za-z-.]*)?";
45 | private static final Pattern PATTERN = Pattern.compile(Version.FORMAT);
46 |
47 | private static final Pattern DIGITS_ONLY = Pattern.compile("\\d+");
48 |
49 | private static final String SNAPSHOT_VERSION_SUFFIX = "SNAPSHOT";
50 |
51 | private final int major;
52 | private final int minor;
53 | private final int patch;
54 | private final String separator;
55 | private final Special special;
56 |
57 | public Version(@Nonnegative final int major, @Nonnegative final int minor, @Nonnegative final int patch) {
58 | this(major, minor, patch, null, null);
59 | }
60 |
61 | public Version(@Nonnegative final int major, @Nonnegative final int minor, @Nonnegative final int patch, @Nullable final String separator, @Nullable final String special) {
62 | if (major < 0) {
63 | throw new IllegalArgumentException(Element.MAJOR+" must be positive");
64 | }
65 | if (minor < 0) {
66 | throw new IllegalArgumentException(Element.MINOR+" must be positive");
67 | }
68 | if (patch < 0) {
69 | throw new IllegalArgumentException(Element.PATCH+" must be positive");
70 | }
71 |
72 | this.major = major;
73 | this.minor = minor;
74 | this.patch = patch;
75 | this.separator = separator;
76 | this.special = parseSpecial(special);
77 | }
78 |
79 | private Special parseSpecial(String specialString) {
80 | if (specialString == null) {
81 | return null;
82 | }
83 | Special special = new Special(specialString);
84 | if (special.ids.length == 0) {
85 | return null;
86 | }
87 | return special;
88 | }
89 |
90 | /**
91 | *
92 | * Creates a Version from a string representation. Must match Version#FORMAT.
93 | *
94 | * @param version
95 | * @return
96 | */
97 | public static Version parse(@Nonnull final String version) {
98 | final Matcher matcher = Version.PATTERN.matcher(version);
99 | if (!matcher.matches()) {
100 | throw new IllegalArgumentException("<"+version+"> does not match format "+Version.FORMAT);
101 | }
102 |
103 | final int major = Integer.valueOf(matcher.group(1));
104 | final int minor = Integer.valueOf(matcher.group(2));
105 | final int patch;
106 | final String patchMatch = matcher.group(3);
107 | if (StringUtils.isNotEmpty(patchMatch)) {
108 | patch = Integer.valueOf(patchMatch);
109 | } else {
110 | patch = 0;
111 | }
112 | final String separator = matcher.group(4);
113 | final String special = matcher.group(5);
114 | return new Version(major, minor, patch, separator, "".equals(special) ? null : special);
115 | }
116 |
117 | /**
118 | * @param type
119 | * @return next {@link Version} regarding specified {@link Version.Element}
120 | */
121 | public Version next(@Nonnull final Version.Element element) {
122 | if (element == null) {
123 | throw new IllegalArgumentException("null element");
124 | }
125 |
126 | switch (element) {
127 | case MAJOR:
128 | if (special == null || this.minor != 0 || this.patch != 0) {
129 | return new Version(this.major + 1, 0, 0);
130 | } else {
131 | return new Version(this.major, 0, 0);
132 | }
133 | case MINOR:
134 | if (special == null || this.patch != 0) {
135 | return new Version(this.major, this.minor + 1, 0);
136 | } else {
137 | return new Version(this.major, this.minor, 0);
138 | }
139 | case PATCH:
140 | if (special == null) {
141 | return new Version(this.major, this.minor, this.patch + 1);
142 | } else {
143 | return new Version(this.major, this.minor, this.patch);
144 | }
145 | default:
146 | throw new IllegalArgumentException("Unknown element <"+element+">");
147 | }
148 | }
149 |
150 | /**
151 | * if this is a pre-release version, returns the corresponding release
152 | * return the same version if already a release
153 | * @return a release version
154 | */
155 | public Version toReleaseVersion() {
156 | return new Version(major, minor, patch);
157 | }
158 |
159 | public boolean isInDevelopment() {
160 | return this.major == 0;
161 | }
162 |
163 | public boolean isStable() {
164 | return !isInDevelopment();
165 | }
166 |
167 | public boolean isSnapshot() {
168 | return this.special != null && this.special.isSnapshot();
169 | }
170 |
171 | /**
172 | * @param version version to check with
173 | * @return {@code true}, if supplied version is compatible with this version, {@code false} - otherwise
174 | */
175 | public boolean isCompatible(Version version) {
176 | return version != null && this.major == version.major;
177 | }
178 |
179 | @Override
180 | public int hashCode() {
181 | int hash = 5;
182 | hash = 43 * hash + this.major;
183 | hash = 43 * hash + this.minor;
184 | hash = 43 * hash + this.patch;
185 | hash = 43 * hash + (this.special != null ? this.special.hashCode() : 0);
186 | return hash;
187 | }
188 |
189 | @Override
190 | public boolean equals(@Nullable final Object object) {
191 | if (!(object instanceof Version)) {
192 | return false;
193 | }
194 |
195 | final Version other = (Version) object;
196 | if (other.major != this.major || other.minor != this.minor || other.patch != this.patch) {
197 | return false;
198 | }
199 | return (this.special == null) ? other.special == null : this.special.equals(other.special);
200 | }
201 |
202 |
203 | private static SpecialId parseSpecialId(String id) {
204 | Matcher matcher = DIGITS_ONLY.matcher(id);
205 | if (matcher.matches()) {
206 | return new IntId(Integer.parseInt(id));
207 | } else {
208 | return new StringId(id);
209 | }
210 | }
211 |
212 | abstract private static class SpecialId implements Comparable {
213 |
214 | abstract public boolean isSnapshot();
215 |
216 | abstract public int compareTo(IntId other);
217 | abstract public int compareTo(StringId other);
218 | }
219 |
220 | private static class StringId extends SpecialId {
221 | private final String id;
222 | private StringId(String id) {
223 | this.id = id;
224 | }
225 | @Override
226 | public boolean isSnapshot() {
227 | return id.endsWith(SNAPSHOT_VERSION_SUFFIX);
228 | }
229 |
230 | @Override
231 | public int compareTo(SpecialId other) {
232 | return - other.compareTo(this);
233 | }
234 |
235 | @Override
236 | public String toString() {
237 | return id;
238 | }
239 | @Override
240 | public int compareTo(IntId other) {
241 | // Numeric identifiers always have lower precedence than non-numeric identifiers.
242 | return 1;
243 | }
244 | @Override
245 | public int compareTo(StringId other) {
246 | return id.compareTo(other.id);
247 | }
248 | }
249 |
250 | private static class IntId extends SpecialId {
251 | private final int id;
252 | public IntId(int id) {
253 | this.id = id;
254 | }
255 | @Override
256 | public boolean isSnapshot() {
257 | return false;
258 | }
259 |
260 | @Override
261 | public String toString() {
262 | return String.valueOf(id);
263 | }
264 | @Override
265 | public int compareTo(SpecialId other) {
266 | return - other.compareTo(this);
267 | }
268 |
269 | @Override
270 | public int compareTo(IntId other) {
271 | return id - other.id;
272 | }
273 | @Override
274 | public int compareTo(StringId other) {
275 | //Numeric identifiers always have lower precedence than non-numeric identifiers.
276 | return -1;
277 | }
278 | }
279 |
280 | private static class Special implements Comparable {
281 | private final SpecialId[] ids;
282 | Special(String s) {
283 | String[] split = s.split("\\.");
284 | ids = new SpecialId[split.length];
285 | for (int i = 0; i < split.length; i++) {
286 | ids[i] = parseSpecialId(split[i]);
287 | }
288 | }
289 |
290 | public SpecialId last() {
291 | return ids[ids.length - 1];
292 | }
293 |
294 | public boolean isSnapshot() {
295 | return last().isSnapshot();
296 | }
297 |
298 | @Override
299 | public int compareTo(Special other) {
300 | int min = Math.min(other.ids.length, ids.length);
301 | for (int i = 0; i < min; i++) {
302 | int c = ids[i].compareTo(other.ids[i]);
303 | if (c != 0) {
304 | return c;
305 | }
306 | }
307 | int max = Math.max(other.ids.length, ids.length);
308 | if (max != min) {
309 | if (ids.length > other.ids.length) {
310 | return 1;
311 | } else {
312 | return -1;
313 | }
314 | }
315 | return 0;
316 | }
317 |
318 | @Override
319 | public String toString() {
320 | final StringBuilder builder = new StringBuilder();
321 | for (int i = 0; i < ids.length; i++) {
322 | SpecialId s = ids[i];
323 | if (i != 0) {
324 | builder.append(".");
325 | }
326 | builder.append(s);
327 | }
328 | return builder.toString();
329 | }
330 | }
331 |
332 | @Override
333 | public int compareTo(final Version other) {
334 | if (equals(other)) {
335 | return 0;
336 | }
337 |
338 | if (this.major < other.major) {
339 | return -1;
340 | } else if (this.major == other.major) {
341 | if (this.minor < other.minor) {
342 | return -1;
343 | } else if (this.minor == other.minor) {
344 | if (this.patch < other.patch) {
345 | return -1;
346 | } else if (this.patch == other.patch) {
347 | if (this.special != null && other.special != null) {
348 | return this.special.compareTo(other.special);
349 | } else if (other.special != null) {
350 | return 1;
351 | } else if (this.special != null) {
352 | return -1;
353 | } // else handled by previous equals check
354 | }
355 | }
356 | }
357 | return 1; //if this (major, minor or patch) is > than other
358 | }
359 |
360 | @Override
361 | public String toString() {
362 | final StringBuilder builder = new StringBuilder();
363 | builder.append(this.major).append(".").append(this.minor).append(".").append(this.patch);
364 | if (this.separator != null) {
365 | builder.append(this.separator);
366 | }
367 | if (this.special != null) {
368 | builder.append(this.special);
369 | }
370 | return builder.toString();
371 | }
372 |
373 | }
374 |
--------------------------------------------------------------------------------
/api/src/main/java/org/semver/jardiff/DifferenceAccumulatingHandler.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2012-2014 Julien Eluard and contributors
3 | * This project includes software developed by Julien Eluard: https://github.com/jeluard/
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.semver.jardiff;
18 |
19 | import java.util.Collections;
20 | import java.util.HashSet;
21 | import java.util.Set;
22 | import java.util.regex.Matcher;
23 | import java.util.regex.Pattern;
24 |
25 | import javax.annotation.Nonnull;
26 |
27 | import org.osjava.jardiff.AbstractDiffHandler;
28 | import org.osjava.jardiff.ClassInfo;
29 | import org.osjava.jardiff.DiffException;
30 | import org.osjava.jardiff.FieldInfo;
31 | import org.osjava.jardiff.MethodInfo;
32 | import org.semver.Delta;
33 | import org.semver.Delta.Add;
34 | import org.semver.Delta.Change;
35 | import org.semver.Delta.Deprecate;
36 | import org.semver.Delta.Difference;
37 | import org.semver.Delta.Remove;
38 |
39 | /**
40 | *
41 | * {@link org.osjava.jardiff.DiffHandler} implementation accumulating {@link Difference}.
42 | *
43 | */
44 | public final class DifferenceAccumulatingHandler extends AbstractDiffHandler {
45 | private String currentClassName;
46 | private final Set includes;
47 | private final boolean includesAreRegExp;
48 | private final Set excludes;
49 | private final boolean excludesAreRegExp;
50 | private final Set differences = new HashSet();
51 |
52 | public DifferenceAccumulatingHandler() {
53 | this(Collections.emptySet(), Collections.emptySet());
54 | }
55 |
56 | public DifferenceAccumulatingHandler(@Nonnull final Set includes, @Nonnull final Set excludes) {
57 | this(includes, false, excludes, false);
58 | }
59 | public DifferenceAccumulatingHandler(@Nonnull final Set includes, final boolean includesAreRegExp,
60 | @Nonnull final Set excludes, final boolean excludesAreRegExp) {
61 | this.includes = includes;
62 | this.includesAreRegExp = includesAreRegExp;
63 | this.excludes = excludes;
64 | this.excludesAreRegExp = excludesAreRegExp;
65 | }
66 |
67 | public String getCurrentClassName() {
68 | return this.currentClassName;
69 | }
70 |
71 | @Override
72 | public void startDiff(final String previous, final String current) throws DiffException {
73 | }
74 |
75 | @Override
76 | public void endDiff() throws DiffException {
77 | }
78 |
79 | @Override
80 | public void startOldContents() throws DiffException {
81 | }
82 |
83 | @Override
84 | public void endOldContents() throws DiffException {
85 | }
86 |
87 | @Override
88 | public void startNewContents() throws DiffException {
89 | }
90 |
91 | @Override
92 | public void endNewContents() throws DiffException {
93 | }
94 |
95 | @Override
96 | public void contains(final ClassInfo classInfo) throws DiffException {
97 | }
98 |
99 | @Override
100 | public void startAdded() throws DiffException {
101 | }
102 |
103 | @Override
104 | public void classAdded(final ClassInfo classInfo) throws DiffException {
105 | if (!isClassConsidered(classInfo.getName())) {
106 | return;
107 | }
108 |
109 | this.differences.add(new Add(getClassName(classInfo.getName()), classInfo));
110 | }
111 |
112 | @Override
113 | public void fieldAdded(final FieldInfo fieldInfo) throws DiffException {
114 | if (!isClassConsidered(getCurrentClassName())) {
115 | return;
116 | }
117 |
118 | this.differences.add(new Add(getCurrentClassName(), fieldInfo));
119 | }
120 |
121 | @Override
122 | public void methodAdded(final MethodInfo methodInfo) throws DiffException {
123 | if (!isClassConsidered(getCurrentClassName())) {
124 | return;
125 | }
126 |
127 | this.differences.add(new Add(getCurrentClassName(), methodInfo));
128 | }
129 |
130 | @Override
131 | public void endAdded() throws DiffException {
132 | }
133 |
134 | @Override
135 | public void startChanged() throws DiffException {
136 | }
137 |
138 | @Override
139 | public void startClassChanged(final String className) throws DiffException {
140 | this.currentClassName = getClassName(className);
141 | }
142 |
143 | @Override
144 | public void classChanged(final ClassInfo oldClassInfo, final ClassInfo newClassInfo) throws DiffException {
145 | if (!isClassConsidered(oldClassInfo.getName())) {
146 | return;
147 | }
148 |
149 | this.differences.add(new Change(getClassName(oldClassInfo.getName()), oldClassInfo, newClassInfo));
150 | }
151 |
152 | @Override
153 | public void classDeprecated(final ClassInfo oldClassInfo, final ClassInfo newClassInfo) throws DiffException {
154 | if (!isClassConsidered(oldClassInfo.getName())) {
155 | return;
156 | }
157 |
158 | this.differences.add(new Deprecate(getClassName(oldClassInfo.getName()), oldClassInfo, newClassInfo));
159 | }
160 |
161 | @Override
162 | public void fieldChanged(final FieldInfo oldFieldInfo, final FieldInfo newFieldInfo) throws DiffException {
163 | if (!isClassConsidered(getCurrentClassName())) {
164 | return;
165 | }
166 |
167 | this.differences.add(new Change(getCurrentClassName(), oldFieldInfo, newFieldInfo));
168 | }
169 |
170 | @Override
171 | public void fieldDeprecated(final FieldInfo oldFieldInfo, final FieldInfo newFieldInfo) throws DiffException {
172 | if (!isClassConsidered(getCurrentClassName())) {
173 | return;
174 | }
175 |
176 | this.differences.add(new Deprecate(getCurrentClassName(), oldFieldInfo, newFieldInfo));
177 | }
178 |
179 | @Override
180 | public void methodChanged(final MethodInfo oldMethodInfo, final MethodInfo newMethodInfo) throws DiffException {
181 | if (!isClassConsidered(getCurrentClassName())) {
182 | return;
183 | }
184 |
185 | this.differences.add(new Change(getCurrentClassName(), oldMethodInfo, newMethodInfo));
186 | }
187 |
188 | @Override
189 | public void methodDeprecated(final MethodInfo oldMethodInfo, final MethodInfo newMethodInfo) throws DiffException {
190 | if (!isClassConsidered(getCurrentClassName())) {
191 | return;
192 | }
193 |
194 | this.differences.add(new Deprecate(getCurrentClassName(), oldMethodInfo, newMethodInfo));
195 | }
196 |
197 | @Override
198 | public void endClassChanged() throws DiffException {
199 | }
200 |
201 | @Override
202 | public void endChanged() throws DiffException {
203 | }
204 |
205 | @Override
206 | public void startRemoved() throws DiffException {
207 | }
208 |
209 | @Override
210 | public void classRemoved(final ClassInfo classInfo) throws DiffException {
211 | if (!isClassConsidered(classInfo.getName())) {
212 | return;
213 | }
214 |
215 | this.differences.add(new Remove(getClassName(classInfo.getName()), classInfo));
216 | }
217 |
218 | @Override
219 | public void fieldRemoved(final FieldInfo fieldInfo) throws DiffException {
220 | if (!isClassConsidered(getCurrentClassName())) {
221 | return;
222 | }
223 |
224 | this.differences.add(new Remove(getCurrentClassName(), fieldInfo));
225 | }
226 |
227 | @Override
228 | public void methodRemoved(final MethodInfo methodInfo) throws DiffException {
229 | if (!isClassConsidered(getCurrentClassName())) {
230 | return;
231 | }
232 |
233 | this.differences.add(new Remove(getCurrentClassName(), methodInfo));
234 | }
235 |
236 | @Override
237 | public void endRemoved() throws DiffException {
238 | }
239 |
240 | /**
241 | *
242 | * Is considered a class whose package:
243 | * * is included
244 | * * is not excluded
245 | *
246 | * If includes are provided then package must be defined here.
247 | *
248 | * @return
249 | */
250 | protected boolean isClassConsidered( final String className ) {
251 | // Fix case where class names are reported with '.'
252 | final String fixedClassName = className.replace('.', '/');
253 | for ( String exclude : this.excludes ) {
254 | final Pattern excludePattern;
255 | if( !excludesAreRegExp ) {
256 | if ( exclude.contains( "/**/" ) ) {
257 | exclude = exclude.replaceAll( "/\\*\\*/", "{0,1}**/" );
258 | }
259 | if ( exclude.contains( "/*/" ) ) {
260 | exclude = exclude.replaceAll( "/\\*/", "{0,1}*/{0,1}" );
261 | }
262 | excludePattern = simplifyRegularExpression( exclude, false );
263 | } else {
264 | excludePattern = Pattern.compile( exclude );
265 | }
266 | final Matcher excludeMatcher = excludePattern.matcher( fixedClassName );
267 |
268 | while ( excludeMatcher.find() ) {
269 | return false;
270 | }
271 | }
272 | if ( !this.includes.isEmpty() ) {
273 | for ( String include : this.includes ) {
274 | final Pattern includePattern;
275 | if( !includesAreRegExp ) {
276 | if ( include.contains( "/**/" ) ) {
277 | include = include.replaceAll( "/\\*\\*/", "{0,1}**/" );
278 | }
279 | if ( include.contains( "/*/" ) ) {
280 | include = include.replaceAll( "/\\*/", "{0,1}*/{0,1}" );
281 | }
282 | includePattern = simplifyRegularExpression( include, false );
283 | } else {
284 | includePattern = Pattern.compile( include );
285 | }
286 | final Matcher includeMatcher = includePattern.matcher( fixedClassName );
287 |
288 | while ( includeMatcher.find() ) {
289 | return true;
290 | }
291 | }
292 | return false;
293 | }
294 | return true;
295 | }
296 |
297 | /**
298 | *
299 | * Simplifies the given regular expression by the following pattern:
300 | * All substrings not containing "{0,1}", "*" and "?" get surrounded by "\\Q" and "\\E". Then all occurrences of
301 | * "**" are replaced by ".*", "*" with "[^/]*" and all occurrences of "?" are replaced by "." In the end a "$" will
302 | * be appended.
303 | *
304 | * @param regEx the regular expression which is in a simple form.
305 | * @return the simple regular expression converted to a normal regular expression.
306 | */
307 | private static Pattern simplifyRegularExpression( final String regEx, final boolean caseSensitive ) {
308 | final StringBuilder strBuild = new StringBuilder();
309 | final Pattern p = Pattern.compile( "\\{0,1\\}|\\*|\\?|[[^*^?^{^}]|^]+", Pattern.CASE_INSENSITIVE );
310 | final Matcher m = p.matcher( regEx );
311 |
312 | while ( m.find() ) {
313 | final String token = m.group();
314 | if ( token.equals( "*" ) || token.equals( "?" ) ) { //$NON-NLS-1$ //$NON-NLS-2$
315 | strBuild.append( token );
316 | } else if ( token.equals( "{0,1}" ) ) {
317 | strBuild.append( "/" );
318 | strBuild.append( token );
319 | } else {
320 | // Surround all tokens that are not "*" or "?" with "\\Q" and \\E"
321 | strBuild.append( "\\Q" ).append( token ).append( "\\E" ); //$NON-NLS-1$ //$NON-NLS-2$
322 | }
323 | }
324 | // Replace all "*" and "?" with .* and .+
325 | strBuild.append( "$" );
326 | String result = strBuild.toString();
327 | result = result.replaceAll( "(? EMPTY_DIFFERENCES = Collections.emptySet();
42 |
43 | @Test
44 | public void inferVersion() {
45 | final int major = 1;
46 | final int minor = 2;
47 | final int patch = 3;
48 | final Version version = new Version(major, minor, patch);
49 |
50 | assertEquals(version.next(MAJOR), inferNextVersion(version, NON_BACKWARD_COMPATIBLE));
51 | assertEquals(version.next(MINOR), inferNextVersion(version, BACKWARD_COMPATIBLE_USER));
52 | assertEquals(version.next(PATCH), inferNextVersion(version, BACKWARD_COMPATIBLE_IMPLEMENTER));
53 | }
54 |
55 | @Test(expected=IllegalArgumentException.class)
56 | public void shouldInferWithNullVersionFail() {
57 | inferNextVersion(null, BACKWARD_COMPATIBLE_IMPLEMENTER);
58 | }
59 |
60 | @Test(expected=IllegalArgumentException.class)
61 | public void shouldInferWithNullCompatibilityTypeFail() {
62 | inferNextVersion(new Version(1, 0, 0), null);
63 | }
64 |
65 | @Test(expected=IllegalArgumentException.class)
66 | public void shouldNullVersionNotBeInferable() {
67 | new Delta(EMPTY_DIFFERENCES).infer(null);
68 | }
69 |
70 | @Test(expected=IllegalArgumentException.class)
71 | public void shouldDevelopmentVersionNotBeInferable() {
72 | new Delta(EMPTY_DIFFERENCES).infer(new Version(0, 0, 0));
73 | }
74 |
75 | @Test
76 | public void shouldEmptyDeltaBeImplementerBackwardCompatible() {
77 | final int major = 1;
78 | final int minor = 2;
79 | final int patch = 3;
80 | final Version version = new Version(major, minor, patch);
81 |
82 | final Version inferedVersion = new Delta(EMPTY_DIFFERENCES).infer(version);
83 |
84 | assertEquals(new Version(major, minor, patch+1), inferedVersion);
85 | }
86 |
87 | @Test
88 | public void shouldDeltaWithAddsBeUserBackwardCompatible() {
89 | final int major = 1;
90 | final int minor = 2;
91 | final int patch = 3;
92 | final Version version = new Version(major, minor, patch);
93 |
94 | final Version inferedVersion = new Delta(Collections.singleton(new Delta.Add("class", new FieldInfo(0, "", "", "", null)))).infer(version);
95 |
96 | assertEquals(new Version(major, minor+1, 0), inferedVersion);
97 | }
98 |
99 | @Test
100 | public void shouldDeltaWithChangesBeNonBackwardCompatible() {
101 | final int major = 1;
102 | final int minor = 2;
103 | final int patch = 3;
104 | final Version version = new Version(major, minor, patch);
105 |
106 | final Version inferedVersion = new Delta(Collections.singleton(new Delta.Change("class", new FieldInfo(0, "", "", "", null), new FieldInfo(0, "", "", "", null)))).infer(version);
107 |
108 | assertEquals(new Version(major+1, 0, 0), inferedVersion);
109 | }
110 |
111 | @Test
112 | public void shouldDeltaWithRemovesBeNonBackwardCompatible() {
113 | final int major = 1;
114 | final int minor = 2;
115 | final int patch = 3;
116 | final Version version = new Version(major, minor, patch);
117 |
118 | final Version inferedVersion = new Delta(Collections.singleton(new Delta.Remove("class", new FieldInfo(0, "", "", "", null)))).infer(version);
119 |
120 | assertEquals(new Version(major+1, 0, 0), inferedVersion);
121 | }
122 |
123 | @Test(expected=IllegalArgumentException.class)
124 | public void shouldValidateWithNullPreviousVersionFail() {
125 | new Delta(EMPTY_DIFFERENCES).validate(null, new Version(1, 0, 0));
126 | }
127 |
128 | @Test(expected=IllegalArgumentException.class)
129 | public void shouldValidateWithNullCurrentVersionFail() {
130 | new Delta(EMPTY_DIFFERENCES).validate(new Version(1, 0, 0), null);
131 | }
132 |
133 | @Test
134 | public void shouldValidateWithCurrentVersionInDevelopmentSucceed() {
135 | validate(EMPTY_DIFFERENCES, new Version(0, 0, 0), new Version(0, 0, 1), true);
136 | }
137 |
138 | @Test(expected=IllegalArgumentException.class)
139 | public void shouldValidateWithPreviousVersionNextCurrentVersionFail() {
140 | new Delta(EMPTY_DIFFERENCES).validate(new Version(1, 1, 0), new Version(1, 0, 0));
141 | }
142 |
143 | @Test(expected=IllegalArgumentException.class)
144 | public void shouldValidateWithPreviousVersionEqualsCurrentVersionFail() {
145 | new Delta(EMPTY_DIFFERENCES).validate(new Version(1, 0, 0), new Version(1, 0, 0));
146 | }
147 |
148 | @Test
149 | public void shouldValidateWithCorrectVersionsSucceed() {
150 | validate(EMPTY_DIFFERENCES, new Version(1, 1, 0), new Version(1, 1, 1), true);
151 | }
152 |
153 | @Test
154 | public void shouldValidateWithCorrectPreVersionsSucceed() {
155 | validate(EMPTY_DIFFERENCES, new Version(1, 1, 0, "-", "rc1"), new Version(1, 1, 0, "-", "rc2"), true);
156 | }
157 |
158 | @Test
159 | public void shouldValidateWithIncorrectVersionFail() {
160 | validate(Collections.singleton(new Delta.Remove("class", new FieldInfo(0, "", "", "", null))), new Version(1, 1, 0), new Version(1, 1, 1), false);
161 | }
162 |
163 | @Test
164 | public void upgradeMinorVersionOnClassDeprecated() {
165 | validate(singleton(new Delta.Deprecate("class", new ClassInfo(1, 0, "", "", "", null, null, null), new ClassInfo(1, 0, "", "", "", null, null, null))), new Version(1, 1, 0), new Version(1, 2, 0), true);
166 | }
167 |
168 | @Test
169 | public void upgradeMinorVersionOnFieldDeprecated() {
170 | validate(singleton(new Delta.Deprecate("class", new FieldInfo(0, "", "", "", null), new FieldInfo(0, "", "", "", null))), new Version(1, 1, 0), new Version(1, 2, 0), true);
171 | }
172 |
173 | @Test
174 | public void upgradeMinorVersionOnMethodDeprecated() {
175 | validate(singleton(new Delta.Deprecate("class", new MethodInfo(0, "", "", "", null), new MethodInfo(0, "", "", "", null))), new Version(1, 1, 0), new Version(1, 2, 0), true);
176 | }
177 |
178 | private void validate(Set extends Delta.Difference> differences, Version previous, Version current, boolean valid) {
179 | assertEquals(
180 | "accept differences " + differences + " when changing version from " + previous + " to " + current,
181 | valid,
182 | new Delta(differences).validate(previous, current));
183 | }
184 | }
--------------------------------------------------------------------------------
/api/src/test/java/org/semver/VersionTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2012-2014 Julien Eluard and contributors
3 | * This project includes software developed by Julien Eluard: https://github.com/jeluard/
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.semver;
18 |
19 | import java.util.ArrayList;
20 | import java.util.List;
21 |
22 | import org.junit.Assert;
23 | import org.junit.Test;
24 |
25 | public class VersionTest {
26 |
27 | @Test
28 | public void shouldNegativVersionBeRejected() {
29 | try {
30 | new Version(-1, 0, 0);
31 | Assert.fail();
32 | } catch (IllegalArgumentException e) {
33 | }
34 | try {
35 | new Version(0, -1, 0);
36 | Assert.fail();
37 | } catch (IllegalArgumentException e) {
38 | }
39 | try {
40 | new Version(0, 0, -1);
41 | Assert.fail();
42 | } catch (IllegalArgumentException e) {
43 | }
44 | }
45 |
46 | @Test
47 | public void shouldValidVersionBeParsed() {
48 | Version.parse("1.2");
49 | Version.parse("1.2.3");
50 | Version.parse("10.20.30");
51 | Version.parse("1.2.3beta");
52 | Version.parse("1.2.3.DEV");
53 | Version.parse("1.2.3.DEV-SNAPSHOT");
54 | Version.parse("1.2-SNAPSHOT");
55 | Version.parse("1.2.3-SNAPSHOT");
56 | Version.parse("1.2.3-RC-SNAPSHOT");
57 | Version.parse("1.2-RC-SNAPSHOT");
58 | }
59 |
60 |
61 |
62 | @Test(expected=IllegalArgumentException.class)
63 | public void shouldInvalidVersion1NotBeParsed() {
64 | Version.parse("invalid");
65 | }
66 |
67 | @Test(expected=IllegalArgumentException.class)
68 | public void shouldInvalidVersion2NotBeParsed() {
69 | Version.parse("a.2.3");
70 | }
71 |
72 | @Test
73 | public void shouldMajorBeCorrectlyIncremented() {
74 | Assert.assertEquals(Version.parse("10.0.0"), Version.parse("9.0.0").next(Version.Element.MAJOR));
75 | }
76 |
77 | @Test
78 | public void shouldMinorBeCorrectlyIncremented() {
79 | Assert.assertEquals(Version.parse("0.10.0"), Version.parse("0.9.0").next(Version.Element.MINOR));
80 | }
81 |
82 | @Test
83 | public void shouldPatchBeCorrectlyIncremented() {
84 | Assert.assertEquals(Version.parse("0.0.10"), Version.parse("0.0.9").next(Version.Element.PATCH));
85 | }
86 |
87 | @Test
88 | public void shouldDevelopmentBeInDevelopment() {
89 | Assert.assertTrue(Version.parse("0.1.1").isInDevelopment());
90 | Assert.assertFalse(Version.parse("1.1.1").isInDevelopment());
91 | }
92 |
93 | @Test
94 | public void shouldStableVersionBeStable() {
95 | Assert.assertTrue(Version.parse("1.1.1").isStable());
96 | Assert.assertFalse(Version.parse("0.1.1").isStable());
97 | }
98 |
99 | @Test
100 | public void shouldBeSnapshotVersion() {
101 | Assert.assertTrue(Version.parse("1.5.30-SNAPSHOT").isSnapshot());
102 | }
103 |
104 | @Test
105 | public void shouldBeCompatible() {
106 | Assert.assertTrue(Version.parse("1.0.0").isCompatible(Version.parse("1.2.3-SNAPSHOT")));
107 | Assert.assertTrue(Version.parse("1.0.0").isCompatible(Version.parse("1.0.1")));
108 | Assert.assertTrue(Version.parse("1.0.0").isCompatible(Version.parse("1.1.0")));
109 | }
110 |
111 | @Test
112 | public void shouldBeIncompatible() {
113 | Assert.assertFalse(Version.parse("0.0.1-SNAPSHOT").isCompatible(null));
114 | Assert.assertFalse(Version.parse("1.0.1").isCompatible(Version.parse("2.0.0")));
115 | Assert.assertFalse(Version.parse("1.1.0-rc3").isCompatible(Version.parse("3.1.0-SNAPSHOT")));
116 | }
117 |
118 | @Test
119 | public void isNewer() {
120 | Assert.assertTrue(Version.parse("3.2.3").compareTo(Version.parse("3.2-M1-SNAPSHOT")) > 0);
121 | Assert.assertTrue(Version.parse("1.0.0").compareTo(Version.parse("0.0.0")) > 0);
122 | Assert.assertTrue(Version.parse("0.0.0").compareTo(Version.parse("1.0.0")) < 0);
123 | Assert.assertTrue(Version.parse("1.1.0").compareTo(Version.parse("1.0.0")) > 0);
124 | Assert.assertTrue(Version.parse("1.0.0").compareTo(Version.parse("1.1.0")) < 0);
125 | Assert.assertTrue(Version.parse("1.0.1").compareTo(Version.parse("1.0.0")) > 0);
126 | Assert.assertTrue(Version.parse("1.0.0").compareTo(Version.parse("1.0.1")) < 0);
127 | Assert.assertTrue(Version.parse("1.0.0Beta").compareTo(Version.parse("1.0.0Alpha")) > 0);
128 | Assert.assertFalse(Version.parse("0.0.0").compareTo(Version.parse("0.0.0")) > 0);
129 | Assert.assertFalse(Version.parse("0.0.0").compareTo(Version.parse("0.0.1")) > 0);
130 | // based on http://semver.org/
131 | // Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0.
132 | String[] versions = { "1.0.0-alpha", "1.0.0-alpha.1", "1.0.0-alpha.beta", "1.0.0-beta", "1.0.0-beta.2", "1.0.0-beta.11", "1.0.0-rc.1", "1.0.0" };
133 | assertTotalOrder(versions);
134 | }
135 |
136 | private void assertTotalOrder(String[] versions) {
137 | List problems = new ArrayList();
138 | for (int i = 0; i < versions.length - 1; i++) {
139 | Version v1 = Version.parse(versions[i]);
140 | for (int j = (i + 1); j < versions.length; j++) {
141 | Version v2 = Version.parse(versions[j]);
142 | int compare = v1.compareTo(v2);
143 | if (compare >= 0 ) {
144 | problems.add(v1 + ( compare == 0 ? " = " : " > ") + v2);
145 | }
146 | }
147 | }
148 | if (problems.size() > 0) {
149 | Assert.fail("incorrect comparisons: " + problems);
150 | }
151 | }
152 |
153 | @Test
154 | public void next() {
155 | final int major = 1;
156 | final int minor = 2;
157 | final int patch = 3;
158 | final Version version = new Version(major, minor, patch);
159 |
160 | Assert.assertEquals(version.next(Version.Element.MAJOR), new Version(major+1, 0, 0));
161 | Assert.assertEquals(version.next(Version.Element.MINOR), new Version(major, minor+1, 0));
162 | Assert.assertEquals(version.next(Version.Element.PATCH), new Version(major, minor, patch+1));
163 | }
164 |
165 | @Test
166 | public void nextFromPre() {
167 | final Version version1 = new Version(1, 0, 0, "-", "rc1");
168 | Assert.assertEquals(new Version(1, 0, 0), version1.next(Version.Element.MAJOR));
169 | Assert.assertEquals(new Version(1, 0, 0), version1.next(Version.Element.MINOR));
170 | Assert.assertEquals(new Version(1, 0, 0), version1.next(Version.Element.PATCH));
171 |
172 | final Version version2 = new Version(1, 1, 0, "-", "rc1");
173 | Assert.assertEquals(new Version(2, 0, 0), version2.next(Version.Element.MAJOR));
174 | Assert.assertEquals(new Version(1, 1, 0), version2.next(Version.Element.MINOR));
175 | Assert.assertEquals(new Version(1, 1, 0), version2.next(Version.Element.PATCH));
176 |
177 | final Version version3 = new Version(1, 1, 1, "-", "rc1");
178 | Assert.assertEquals(new Version(2, 0, 0), version3.next(Version.Element.MAJOR));
179 | Assert.assertEquals(new Version(1, 2, 0), version3.next(Version.Element.MINOR));
180 | Assert.assertEquals(new Version(1, 1, 1), version3.next(Version.Element.PATCH));
181 | }
182 |
183 |
184 | @Test(expected=IllegalArgumentException.class)
185 | public void shouldNextWithNullComparisonTypeFail() {
186 | final int major = 1;
187 | final int minor = 2;
188 | final int patch = 3;
189 | final Version version = new Version(major, minor, patch);
190 |
191 | version.next(null);
192 | }
193 |
194 | }
195 |
--------------------------------------------------------------------------------
/api/src/test/java/org/semver/jardiff/ClassInheritanceTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2012-2014 Julien Eluard and contributors
3 | * This project includes software developed by Julien Eluard: https://github.com/jeluard/
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.semver.jardiff;
18 | import java.util.HashMap;
19 | import java.util.Map;
20 |
21 | import junit.framework.Assert;
22 | import org.junit.Test;
23 | import org.objectweb.asm.ClassReader;
24 | import org.osjava.jardiff.ClassInfo;
25 | import org.osjava.jardiff.JarDiff;
26 | import org.osjava.jardiff.SimpleDiffCriteria;
27 | import org.semver.Delta;
28 | import org.semver.Delta.Change;
29 |
30 | public class ClassInheritanceTest {
31 |
32 | public static abstract class InheritanceRoot {
33 | public abstract void aMethod();
34 | }
35 |
36 | public static class DirectDescendant extends InheritanceRoot {
37 | @Override
38 | public void aMethod() {}
39 | }
40 |
41 | public static class ClassA extends InheritanceRoot {
42 | @Override
43 | public void aMethod() {}
44 | }
45 |
46 | public static class ClassB extends DirectDescendant {
47 | }
48 |
49 | @Test
50 | public void shouldInheritedMethodMatchImplementedMethod() throws Exception {
51 | /**
52 | * The situation we are testing is as follows:
53 | * Abstract class InheritanceRoot is initially implemented directly by ClassA.
54 | * ClassA is later modified to extend another implementation of InheritanceRoot
55 | * and the methods required by InheritanceRoot are now removed from ClassA directly,
56 | * and instead inherited from the new parent, DirectDescendant. For the purposes of
57 | * this test, this new ClassA is represented by ClassB (as we can't have the same
58 | * class declared twice in a test -- in real life, this would both be ClassA's,
59 | * in different jars).
60 | */
61 | Map oldClassInfoMap = new HashMap();
62 | Map newClassInfoMap = new HashMap();
63 | JarDiff jd = new JarDiff();
64 | addClassInfo(oldClassInfoMap, ClassA.class, jd);
65 | addClassInfo(oldClassInfoMap, DirectDescendant.class, jd);
66 | addClassInfo(oldClassInfoMap, InheritanceRoot.class, jd);
67 | addClassInfo(newClassInfoMap, ClassB.class, jd);
68 | addClassInfo(newClassInfoMap, DirectDescendant.class, jd);
69 | addClassInfo(newClassInfoMap, InheritanceRoot.class, jd);
70 |
71 | // Make B look like A
72 | ClassInfo a = oldClassInfoMap.get("org/semver/jardiff/ClassInheritanceTest$ClassA");
73 | ClassInfo b = newClassInfoMap.get("org/semver/jardiff/ClassInheritanceTest$ClassB");
74 | newClassInfoMap.put(a.getName(), new ClassInfo(b.getVersion(), b.getAccess(), a.getName(),
75 | b.getSignature(), b.getSupername(), b.getInterfaces(),
76 | b.getMethodMap(), b.getFieldMap()));
77 | newClassInfoMap.remove(b.getName());
78 | DifferenceAccumulatingHandler handler = new DifferenceAccumulatingHandler();
79 | jd.diff(handler, new SimpleDiffCriteria(),
80 | "0.1.0", "0.2.0", oldClassInfoMap, newClassInfoMap);
81 |
82 | for (Delta.Difference d: handler.getDelta().getDifferences()) {
83 | System.err.println(d.getClassName() + " : " + d.getClass().getName()
84 | + " : " + d.getInfo().getName() + " : " + d.getInfo().getAccessType());
85 | if (d instanceof Change) {
86 | System.err.println(" : " + ((Change) d).getModifiedInfo().getName());
87 | }
88 | }
89 | Assert.assertEquals("differences found", 1, handler.getDelta().getDifferences().size());
90 | }
91 |
92 | private void addClassInfo(Map classMap, Class klass, JarDiff jd) throws Exception {
93 | ClassInfo classInfo = jd.loadClassInfo(new ClassReader(klass.getName()));
94 | classMap.put(classInfo.getName(), classInfo);
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/api/src/test/java/org/semver/jardiff/DeprecateDetectionTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2012-2014 Julien Eluard and contributors
3 | * This project includes software developed by Julien Eluard: https://github.com/jeluard/
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.semver.jardiff;
18 | import java.util.HashMap;
19 | import java.util.Map;
20 | import java.util.Set;
21 |
22 | import junit.framework.Assert;
23 | import org.junit.Test;
24 | import org.objectweb.asm.ClassReader;
25 | import org.osjava.jardiff.ClassInfo;
26 | import org.osjava.jardiff.JarDiff;
27 | import org.osjava.jardiff.SimpleDiffCriteria;
28 | import org.semver.Delta.Deprecate;
29 | import org.semver.Delta.Difference;
30 | import org.semver.Dumper;
31 |
32 | public class DeprecateDetectionTest {
33 |
34 | public static abstract class InheritanceRoot {
35 | public abstract void aMethod();
36 | }
37 |
38 | public static class DirectDescendant extends InheritanceRoot {
39 | @Override
40 | public void aMethod() {}
41 | }
42 |
43 | public static class ClassA extends InheritanceRoot {
44 | @Override
45 | public void aMethod() {}
46 |
47 | public int aField = 0;
48 | }
49 |
50 | public static class ClassB extends DirectDescendant {
51 | @Override
52 | @Deprecated
53 | public void aMethod() {}
54 |
55 | @Deprecated
56 | public int aField = 0;
57 | }
58 |
59 | @Test
60 | public void shouldInheritedMethodMatchImplementedMethod() throws Exception {
61 | /**
62 | * The situation we are testing is as follows:
63 | * Abstract class InheritanceRoot is initially implemented directly by ClassA.
64 | * ClassA is later modified to extend another implementation of InheritanceRoot
65 | * and the methods required by InheritanceRoot are now removed from ClassA directly,
66 | * and instead inherited from the new parent, DirectDescendant. For the purposes of
67 | * this test, this new ClassA is represented by ClassB (as we can't have the same
68 | * class declared twice in a test -- in real life, this would both be ClassA's,
69 | * in different jars).
70 | */
71 | Map oldClassInfoMap = new HashMap();
72 | Map newClassInfoMap = new HashMap();
73 | JarDiff jd = new JarDiff();
74 | addClassInfo(oldClassInfoMap, ClassA.class, jd);
75 | addClassInfo(oldClassInfoMap, DirectDescendant.class, jd);
76 | addClassInfo(oldClassInfoMap, InheritanceRoot.class, jd);
77 | addClassInfo(newClassInfoMap, ClassB.class, jd);
78 | addClassInfo(newClassInfoMap, DirectDescendant.class, jd);
79 | addClassInfo(newClassInfoMap, InheritanceRoot.class, jd);
80 |
81 | // Make B look like A
82 | newClassInfoMap.put("org/semver/jardiff/DeprecateDetectionTest$ClassA",
83 | newClassInfoMap.get("org/semver/jardiff/DeprecateDetectionTest$ClassB"));
84 | newClassInfoMap.remove("org/semver/jardiff/DeprecateDetectionTest$ClassB");
85 | DifferenceAccumulatingHandler handler = new DifferenceAccumulatingHandler();
86 | jd.diff(handler, new SimpleDiffCriteria(),
87 | "0.1.0", "0.2.0", oldClassInfoMap, newClassInfoMap);
88 |
89 | Dumper.dump(handler.getDelta());
90 |
91 | Set differences = handler.getDelta().getDifferences();
92 | Assert.assertEquals("differences found", 3, differences.size());
93 | // Naive search for Deprecate.
94 | boolean hasDeprecate = false;
95 | for (Difference d : differences) {
96 | if (d instanceof Deprecate)
97 | hasDeprecate = true;
98 | }
99 | Assert.assertTrue("No Delta.Deprecate found", hasDeprecate);
100 | }
101 |
102 | private void addClassInfo(Map classMap, Class klass, JarDiff jd) throws Exception {
103 | ClassInfo classInfo = jd.loadClassInfo(new ClassReader(klass.getName()));
104 | classMap.put(classInfo.getName(), classInfo);
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/api/src/test/java/org/semver/jardiff/DifferenceAccumulatingHandlerTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2012-2014 Julien Eluard and contributors
3 | * This project includes software developed by Julien Eluard: https://github.com/jeluard/
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package org.semver.jardiff;
18 |
19 |
20 | import java.util.ArrayList;
21 | import java.util.HashSet;
22 | import java.util.List;
23 | import java.util.Set;
24 |
25 | import org.junit.Assert;
26 | import org.junit.Test;
27 |
28 |
29 | public class DifferenceAccumulatingHandlerTest {
30 |
31 | @Test
32 | public void shouldClassBeNotConsideredWithTwoPlaceholdersBeforeAndBehind() {
33 |
34 | List inclusions = new ArrayList();
35 | Set inclusionSet = new HashSet( inclusions );
36 | List exclusions = new ArrayList();
37 | exclusions.add( "**/java/**" );
38 | Set exclusionSet = new HashSet( exclusions );
39 |
40 | Assert.assertEquals( "Class should not be considered: ", false, new DifferenceAccumulatingHandler(
41 | inclusionSet, exclusionSet ).isClassConsidered( "de/test/java/regex/classImpl" ) );
42 | }
43 |
44 | @Test
45 | public void shouldClassBeConsideredWithOnePlaceholderBeforeAndBehind() {
46 |
47 | List inclusions = new ArrayList();
48 | Set inclusionSet = new HashSet( inclusions );
49 | List exclusions = new ArrayList();
50 | exclusions.add( "*/java/*" );
51 | Set exclusionSet = new HashSet( exclusions );
52 |
53 | Assert.assertEquals( "Class should be considered: ", true, new DifferenceAccumulatingHandler( inclusionSet,
54 | exclusionSet ).isClassConsidered( "de/test/java/regex/classImpl" ) );
55 | }
56 |
57 | @Test
58 | public void shouldClassBeNotConsideredWithTwoPlaceholderAfter() {
59 |
60 | List inclusions = new ArrayList();
61 | Set inclusionSet = new HashSet( inclusions );
62 | List exclusions = new ArrayList();
63 | exclusions.add( "java/**" );
64 | Set exclusionSet = new HashSet( exclusions );
65 |
66 | Assert.assertEquals( "Class should not be considered: ", false, new DifferenceAccumulatingHandler(
67 | inclusionSet, exclusionSet ).isClassConsidered( "de/test/java/regex/classImpl" ) );
68 | }
69 |
70 | @Test
71 | public void shouldClassBeConsideredWithTwoPlaceholderBefore() {
72 |
73 | List inclusions = new ArrayList();
74 | Set inclusionSet = new HashSet( inclusions );
75 | List exclusions = new ArrayList();
76 | exclusions.add( "**/java" );
77 | Set exclusionSet = new HashSet( exclusions );
78 |
79 | Assert.assertEquals( "Class should be considered: ", true, new DifferenceAccumulatingHandler( inclusionSet,
80 | exclusionSet ).isClassConsidered( "de/test/java/regex/classImpl" ) );
81 | }
82 |
83 | @Test
84 | public void shouldClassBeConsideredWithOnePlaceholderAfter() {
85 |
86 | List inclusions = new ArrayList();
87 | Set inclusionSet = new HashSet( inclusions );
88 | List exclusions = new ArrayList();
89 | exclusions.add( "java/*" );
90 | Set exclusionSet = new HashSet( exclusions );
91 |
92 | Assert.assertEquals( "Class should be considered: ", true, new DifferenceAccumulatingHandler( inclusionSet,
93 | exclusionSet ).isClassConsidered( "de/test/java/regex/classImpl" ) );
94 | }
95 |
96 | @Test
97 | public void shouldClassBeConsideredWithOnePlaceholderBefore() {
98 |
99 | List inclusions = new ArrayList();
100 | Set inclusionSet = new HashSet( inclusions );
101 | List exclusions = new ArrayList();
102 | exclusions.add( "*/java" );
103 | Set exclusionSet = new HashSet( exclusions );
104 |
105 | Assert.assertEquals( "Class should be considered: ", true, new DifferenceAccumulatingHandler( inclusionSet,
106 | exclusionSet ).isClassConsidered( "de/test/java/regex/classImpl" ) );
107 | }
108 |
109 | @Test
110 | public void shouldClassBeNotConsideredWithTwoPlaceholderInside() {
111 |
112 | List inclusions = new ArrayList();
113 | Set inclusionSet = new HashSet( inclusions );
114 | List exclusions = new ArrayList();
115 | exclusions.add( "de/**/java/**" );
116 | Set exclusionSet = new HashSet( exclusions );
117 |
118 | Assert.assertEquals( "Class should not be considered: ", false, new DifferenceAccumulatingHandler(
119 | inclusionSet, exclusionSet ).isClassConsidered( "de/test/java/regex/classImpl" ) );
120 | }
121 |
122 | @Test
123 | public void shouldClassBeNotConsideredWithOnePlaceholderInside() {
124 |
125 | List inclusions = new ArrayList();
126 | Set inclusionSet = new HashSet( inclusions );
127 | List exclusions = new ArrayList();
128 | exclusions.add( "de/*/java/**" );
129 | Set exclusionSet = new HashSet( exclusions );
130 |
131 | Assert.assertEquals( "Class should not be considered: ", false, new DifferenceAccumulatingHandler(
132 | inclusionSet, exclusionSet ).isClassConsidered( "de/test/java/regex/classImpl" ) );
133 | }
134 |
135 | @Test
136 | public void shouldClassBeConsideredWithOnePlaceholderInside() {
137 |
138 | List inclusions = new ArrayList();
139 | Set inclusionSet = new HashSet( inclusions );
140 | List exclusions = new ArrayList();
141 | exclusions.add( "de/*/classImpl" );
142 | Set exclusionSet = new HashSet( exclusions );
143 |
144 | Assert.assertEquals( "Class should be considered: ", true, new DifferenceAccumulatingHandler( inclusionSet,
145 | exclusionSet ).isClassConsidered( "de/test/java/regex/classImpl" ) );
146 | }
147 |
148 | @Test
149 | public void shouldClassBeConsideredWithTwoPlaceholderInsideAndSpecificEnd() {
150 |
151 | List inclusions = new ArrayList();
152 | Set inclusionSet = new HashSet( inclusions );
153 | List exclusions = new ArrayList();
154 | exclusions.add( "java/**/Impl" );
155 | Set exclusionSet = new HashSet( exclusions );
156 |
157 | Assert.assertEquals( "Class should be considered: ", true, new DifferenceAccumulatingHandler( inclusionSet,
158 | exclusionSet ).isClassConsidered( "de/test/java/regex/classImpl" ) );
159 | }
160 |
161 | @Test
162 | public void shouldClassNotBeConsideredWithOnePlaceholderInsideAndSpecificEnd() {
163 |
164 | List inclusions = new ArrayList();
165 | Set inclusionSet = new HashSet( inclusions );
166 | List exclusions = new ArrayList();
167 | exclusions.add( "java/*/*Impl" );
168 | Set exclusionSet = new HashSet( exclusions );
169 |
170 | Assert.assertEquals( "Class should not be considered: ", false, new DifferenceAccumulatingHandler(
171 | inclusionSet, exclusionSet ).isClassConsidered( "de/test/java/regex/classImpl" ) );
172 | }
173 |
174 | @Test
175 | public void shouldClassBeConsideredWithOnePlaceholderInsideAndSpecificEnd() {
176 |
177 | List inclusions = new ArrayList();
178 | Set inclusionSet = new HashSet( inclusions );
179 | List exclusions = new ArrayList();
180 | exclusions.add( "test/*/*Impl" );
181 | Set exclusionSet = new HashSet( exclusions );
182 |
183 | Assert.assertEquals( "Class should be considered: ", true, new DifferenceAccumulatingHandler( inclusionSet,
184 | exclusionSet ).isClassConsidered( "de/test/java/regex/classImpl" ) );
185 | }
186 |
187 | @Test
188 | public void shouldClassBeConsideredWithTwoPlaceholderInsidePlusHashAndUnspecificEnd3() {
189 |
190 | List inclusions = new ArrayList();
191 | Set inclusionSet = new HashSet( inclusions );
192 | List exclusions = new ArrayList();
193 | exclusions.add( "test/*/*Impl/*" );
194 | Set exclusionSet = new HashSet( exclusions );
195 |
196 | Assert.assertEquals( "Class should be considered: ", true, new DifferenceAccumulatingHandler( inclusionSet,
197 | exclusionSet ).isClassConsidered( "de/test/java/regex/Impl2/code" ) );
198 | }
199 |
200 | @Test
201 | public void shouldClassNotBeConsideredWithTwoPlaceholderInsidePlusHashAndUnspecificEnd2() {
202 |
203 | List inclusions = new ArrayList();
204 | Set inclusionSet = new HashSet( inclusions );
205 | List exclusions = new ArrayList();
206 | exclusions.add( "java/*/*Impl/*" );
207 | Set exclusionSet = new HashSet( exclusions );
208 |
209 | Assert.assertEquals( "Class should not be considered: ", false, new DifferenceAccumulatingHandler(
210 | inclusionSet, exclusionSet ).isClassConsidered( "de/test/java/regex/classImpl/code" ) );
211 | }
212 |
213 | @Test
214 | public void shouldClassBeConsideredWithTwoPlaceholderInsidePlusHashAndUnspecificEnd() {
215 |
216 | List inclusions = new ArrayList();
217 | Set inclusionSet = new HashSet( inclusions );
218 | List exclusions = new ArrayList();
219 | exclusions.add( "test/*/*Impl/*" );
220 | Set exclusionSet = new HashSet( exclusions );
221 |
222 | Assert.assertEquals( "Class should be considered: ", true, new DifferenceAccumulatingHandler( inclusionSet,
223 | exclusionSet ).isClassConsidered( "de/test/java/regex/classImpl/code/Implem" ) );
224 | }
225 |
226 | @Test
227 | public void shouldClassNotBeConsideredWithTwoPlaceholderInsidePlusHashAndUnspecificEnd() {
228 |
229 | List inclusions = new ArrayList();
230 | Set inclusionSet = new HashSet( inclusions );
231 | List exclusions = new ArrayList();
232 | exclusions.add( "java/*/*Impl/*" );
233 | Set exclusionSet = new HashSet( exclusions );
234 |
235 | Assert.assertEquals( "Class should not be considered: ", false, new DifferenceAccumulatingHandler(
236 | inclusionSet, exclusionSet ).isClassConsidered( "de/test/java/regex/classImpl/code" ) );
237 | }
238 |
239 | @Test
240 | public void shouldClassNotBeConsideredWithOnePlaceholderInsideAndUnspecificEnd() {
241 |
242 | List inclusions = new ArrayList();
243 | Set inclusionSet = new HashSet( inclusions );
244 | List exclusions = new ArrayList();
245 | exclusions.add( "java/*/*Impl*" );
246 | Set exclusionSet = new HashSet( exclusions );
247 |
248 | Assert.assertEquals( "Class should not be considered: ", false, new DifferenceAccumulatingHandler(
249 | inclusionSet, exclusionSet ).isClassConsidered( "de/test/java/regex/classImpl2" ) );
250 | }
251 |
252 | @Test
253 | public void shouldClassNotBeConsideredWithTwoPlaceholderInsideAndUnspecificEnd() {
254 |
255 | List inclusions = new ArrayList();
256 | Set inclusionSet = new HashSet( inclusions );
257 | List exclusions = new ArrayList();
258 | exclusions.add( "java/**/Impl*" );
259 | Set exclusionSet = new HashSet( exclusions );
260 |
261 | Assert.assertEquals( "Class should not be considered: ", false, new DifferenceAccumulatingHandler(
262 | inclusionSet, exclusionSet ).isClassConsidered( "de/test/java/regex/Impl" ) );
263 | }
264 |
265 | @Test
266 | public void shouldClassNotBeConsideredWithTwoPlaceholderInsideAndUnspecificEndWithNoUseOfPlaceholders() {
267 |
268 | List inclusions = new ArrayList();
269 | Set inclusionSet = new HashSet( inclusions );
270 | List exclusions = new ArrayList();
271 | exclusions.add( "regex/**/Impl*" );
272 | Set exclusionSet = new HashSet( exclusions );
273 |
274 | Assert.assertEquals( "Class should not be considered: ", false, new DifferenceAccumulatingHandler(
275 | inclusionSet, exclusionSet ).isClassConsidered( "de/test/java/regex/Impl" ) );
276 | }
277 |
278 | @Test
279 | public void shouldClassBeConsideredWithTwoPlaceholderInsideAndUnspecificEndWithNoUseOfPlaceholders() {
280 |
281 | List inclusions = new ArrayList();
282 | Set inclusionSet = new HashSet( inclusions );
283 | List exclusions = new ArrayList();
284 | exclusions.add( "regex/**/Impl*" );
285 | Set exclusionSet = new HashSet( exclusions );
286 |
287 | Assert.assertEquals( "Class should be considered: ", true, new DifferenceAccumulatingHandler( inclusionSet,
288 | exclusionSet ).isClassConsidered( "de/test/java/regex/test" ) );
289 | }
290 |
291 | @Test
292 | public void shouldClassNotBeConsideredWithTwoPlaceholderInsideAndUnspecificEndWith() {
293 |
294 | List inclusions = new ArrayList();
295 | Set inclusionSet = new HashSet( inclusions );
296 | List exclusions = new ArrayList();
297 | exclusions.add( "test/**/Impl*" );
298 | Set exclusionSet = new HashSet( exclusions );
299 |
300 | Assert.assertEquals( "Class should not be considered: ", false, new DifferenceAccumulatingHandler(
301 | inclusionSet, exclusionSet ).isClassConsidered( "de/test/java/regex/Impl" ) );
302 | }
303 |
304 | @Test
305 | public void shouldClassNotBeConsideredWhenNotMatchingAnyDefinedInclude() {
306 |
307 | List