-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
First draft of the CompleteApiScraper and ApiComparator (Issue #3239) #3253
Conversation
Hooray Jenkins reported success with all tests good! |
@oniatus Tagging you so you can take a look at this and see if it fits what you wanted. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work! While checking this out I put the before + after + console log snippet in a Gist to make it more easy to review without running it:
https://gist.github.com/Cervator/f34259ba73f7af7e02f3f2627211b143
I added a method hi
to SectionDefinitionData
as that came up first in my first scan. Did not stay in the same position for the second scan though.
I left some review comments, mostly the usual - javadoc, comments, code style. Make sure you have Checkstyle working in your IDE, I left most of those un-commented since the plugin should be able to tell you about every line affected :-)
As for changes:
- Yep constructors definitely also need to be covered
- As do new classes (by definition they'd require a minor version bump)
- Could the empty parameters & exception lines be omitted? If not present just assume empty. Could also indent those lines one more
-
if present
@@ -84,6 +84,7 @@ public static void main(String[] args) throws Exception { | |||
} else { | |||
//System.out.println("Jar-based Class: " + apiClass + ", came from " + location); | |||
sortedApi.put(category, apiClass.getName() + (apiClass.isInterface() ? " (INTERFACE)" : " (CLASS)")); | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stray newline? If only change in file reverting would make for a cleaner diff/history :-)
import java.util.HashMap; | ||
|
||
/** | ||
* Created by iaron on 04/02/2018. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Proper javadoc on all the things FTW! :-)
import com.google.common.collect.Multimaps; | ||
import org.terasology.documentation.apiScraper.util.ApiMethod; | ||
|
||
import java.io.*; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid star imports - make sure you set up Checkstyle for IDE highlight of errors, this file lights up like a 🎄 :-)
import java.io.FileWriter; | ||
|
||
|
||
public final class ApiSaver { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moar javadoc please!
@@ -0,0 +1,135 @@ | |||
/* | |||
* Copyright 2015 MovingBlocks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mildly old copy pasta :D Might want to bump all the headers to 2018
} | ||
|
||
/** | ||
* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably could still have a brief method description rather than solely rely on return + throws
@iaronaraujo poke :-) Also: just realized something. A new method is usually not a problem. Unless it is in an interface or abstract class forcing any existing implementors to implement it. That would be an API violation too. How to detect that ... look for new methods in interfaces and abstract classes and if the line ends in a |
Alright! A new method on an interface or abstract class should be reported as a minor or major change? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Slight refactoring to conform to Terasology's code style.
} | ||
|
||
if (location == null) { | ||
System.out.println("Failed to get a class/package location, skipping " + apiClass); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a warning/error logged via an class level org.slf4j.Logger
constant created via LoggerFactory.getLogger(CompleteApiScraper.class)
. Same goes for all other System.out.print
calls (except in the main
methods used for testing).
break; | ||
|
||
default : | ||
System.out.println("Unknown protocol for: " + apiClass + ", came from " + location); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above.
|
||
case "file" : | ||
// If file based we know it is local so organize it like that | ||
category = "terasology engine"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"terasology engine"
should be replaced with a class-level constant. TERASOLOGY_API_CLASS_CATEGORY
is a potential name, but you may choose an appropriate name on your own.
System.out.println("Unknown protocol for: " + apiClass + ", came from " + location); | ||
} | ||
} | ||
api.putAll("external", ExternalApiWhitelist.CLASSES.stream() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider extracting "external"
to an appropriately named class-level constant.
} | ||
|
||
public ApiMethod(){ | ||
this(DEFAULT_VALUE,DEFAULT_VALUE,DEFAULT_VALUE,DEFAULT_VALUE,DEFAULT_VALUE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure if this constructor (or the default values) is required at all. The only uses that I can see are in the testing methods, and I am sure those can be worked around. Any specific reason why this constructor exists?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, there is, but with some refactors I may be able to remove it
result = 31 * result + getName().hashCode(); | ||
result = 31 * result + getReturnType().hashCode(); | ||
result = 31 * result + getExceptionType().hashCode(); | ||
result = 31 * result + getParametersType().hashCode(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IntelliJ code generation is at it again! I suggest replacing the method body with a single call to Objects.hash
.
…on the PR discussion. Added Javadoc and Comments
Hooray Jenkins reported success with all tests good! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really solid and impressive work, thanks for all the updates :-) Checkstyle is happy and the doc is awesome. Added some comments with mostly minor tweaks for here and there. Haven't tested yet this time around due to lack of time but thought I'd get initial comments in
|
||
|
||
/** | ||
* Generates a "New_API_file.txt" and compares it with the "API_file.txt" to detect major and minor version increases. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
General comment about Javadoc: the class-level comment should focus more on what the class is for, less so how it does that :-)
So in this case really the ApiComparator is meant to be used for better detecting API changes of various degrees. You could keep the file details (and especially filenames) closer to the methods that perform those functions
|
||
/** | ||
* Generates a "New_API_file.txt" and compares it with the "API_file.txt" to detect major and minor version increases. | ||
* Major increases: Deletion of class, new abstract method, method deletion, existing method's change of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Slight more detail: also "new non-default interface method" yet not "deletion of internal methods such as a private or protected method, since that wouldn't be visible to an outside API user. Maybe just throw "public" on there, would also apply to changing parameters etc.
* Generates a "New_API_file.txt" and compares it with the "API_file.txt" to detect major and minor version increases. | ||
* Major increases: Deletion of class, new abstract method, method deletion, existing method's change of | ||
* parameters types, exception types or return type. | ||
* Minor increases: Creation of a new class and new method in an interface or abstract class, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New method in an existing interface (that isn't a default method) should be a major change. New abstract method in an abstract class is also major, but new regular method in an abstract class is minor.
Interestingly, perhaps, the addition of a brand new interface (or abstract class) is indeed a minor change, not major (since nothing can be using it yet)
Also again "public" might be useful to throw in, since a new private method doesn't matter.
May also want to briefly note that any change not identified as major or minor would count as a patch-level change, just for completeness. Maybe link to https://semver.org/
} | ||
|
||
/** | ||
* Reads an api file and puts it's information in a map to be used in the api comparison |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trivial fix: its
not it's
Could alternatively shorten to something like "Loads API info from file into a map to ..."
* Reads an api file and puts it's information in a map to be used in the api comparison | ||
* @param br BufferedReader containing an api file content | ||
* @return A map with the api classes and interfaces as keys; | ||
* Their methods and as a list of ApiMethods in the values |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how javadoc would interpret a newline in a @return
description? I really haven't seen that before or considered it. Pretty much just single line descriptions, which this could probably be written as anyway :-)
|
||
/** | ||
* Checks creation and deletion of methods, as well as existing method changes | ||
* @param originalApi the original api generated from "API_file.txt" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might want to move the "API_file.txt" and related to constants and refer to those instead
System.out.println("================================================================="); | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another trivial note: you've got some odd extra newlines here and there :-)
private String parametersType; | ||
|
||
/** | ||
* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another spot where you could use a main javadoc description (or else any actual generated javadoc would end up with some empty boxes here and there)
Oh, also, one other request: please add the generated API files to the |
Hooray Jenkins reported success with all tests good! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I merged this locally for final testing before pushing it, did a few tiny javadoc tweaks, but during testing found a couple expectations off:
- a new public method in a non-abstract class is missed. This should be a minor change
- a new default method in an interface is missed. Also minor change
There could be more but it is tricky to test since if you break API, well, in some cases the whole thing doesn't compile anymore so you can't test without first "fixing" a bunch of stuff :-)
I'm thinking we should consider two big things:
- Write actual tests for the test - two sets of mock Java source that goes through every possible change type, with tests that go through and validate that yep, each is caught
- Maybe pull most of this into its own utility repository. Especially if we start adding tests for a set of utility classes that begins getting kinda crazy. And this could be hugely useful elsewhere, say to do the same kind of checking for DestSol (I wonder if such a lib might already exist?)
A split out mini-lib could be depended on by Terasology with a requirement to implement the ApiScraper pieces, hopefully with some boilerplate code provided by the lib. Then the user (Terasology) gets to define what is actually part of the API (stuff marked with @API
, on the whitelist, etc)
What do you think?
We could either try to go that route immediately or if you'd rather then finish up with the current expectation of SemVer, merge, then consider future improvements separately
For the additional Javadoc tweaks I pushed it for easier review in a commit than me leaving comments here, some are just adding .
at the end of the first sentence of the main javadoc description. See Cervator@53bfeaa - ignore the commit message, I forgot to write a new one ;-)
found = true; | ||
} | ||
} | ||
if (!found && isInterfaceOrAbstract(method2.getClassName())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This misses a new public method in a non-abstract class. That should count as a minor change :-)
I'm not sure if that was done previously and we're just going back and forth on what's included, or if it was legit missed
…thod is now being reported
Hooray Jenkins reported success with all tests good! |
I fixed the two points you mentioned. |
Contains
This is the first draft of #3239 , but instead of creating a JUnit test to check API's differences, I created a new class with a main method (ApiComparator) since I wasn't sure what would be a "failure" or a "pass".
How to test
1 - Run the main method in the ApiSaver class and it will generate a file called "API_file.txt".
2 - Create a new public method on a class or interface / change an existing public method's arguments, return type or exception type.
3 - Run the main method in the ApiComparator class. It will generate a file called "New_API_file.txt" and compare both, printing a report with minor and major changes.
Outstanding before merging