-
Notifications
You must be signed in to change notification settings - Fork 75
Fix local class scanning #111
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,4 +10,5 @@ target | |
| *.pyc | ||
| build.log | ||
| **/*.versionsBackup | ||
| .DS_Store | ||
| .DS_Store | ||
| logdir* | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,18 +18,17 @@ | |
| package com.homeaway.devtools.jenkins.testing; | ||
|
|
||
| import java.lang.annotation.Annotation; | ||
| import java.util.HashMap; | ||
| import java.util.HashSet; | ||
| import java.util.HashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Map.Entry; | ||
| import java.util.Optional; | ||
| import java.util.Set; | ||
|
|
||
| import org.reflections.Reflections; | ||
| import org.reflections.ReflectionsException; | ||
| import org.reflections.scanners.SubTypesScanner; | ||
| import org.reflections.util.ClasspathHelper; | ||
| import org.reflections.util.ConfigurationBuilder; | ||
| import groovy.lang.Closure; | ||
| import io.github.classgraph.ClassGraph; | ||
| import io.github.classgraph.ScanResult; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
|
|
@@ -51,46 +50,33 @@ public class LocalProjectPipelineExtensionDetector extends APipelineExtensionDet | |
| @Override | ||
| public Set<Class<?>> getClassesOfTypeInPackage(Class<?> _supertype, Optional<String> _package) { | ||
|
|
||
| Set<Class<?>> classes = new HashSet<>(); | ||
|
|
||
| Map<String, Throwable> failures = new HashMap<>(); | ||
|
|
||
| Reflections reflector = new Reflections( | ||
| new ConfigurationBuilder() | ||
| .setScanners(new SubTypesScanner(false)) | ||
| .setUrls(ClasspathHelper.forPackage(_package.orElse("")))); | ||
|
|
||
| Set<String> all_types = new HashSet<>(); | ||
|
|
||
| try { | ||
| all_types = reflector.getAllTypes(); | ||
| } catch( ReflectionsException re ) { | ||
| if( re.getMessage().contains( "Couldn't find subtypes of Object." ) ) { | ||
| // this can happen if there are no classes local to the project. | ||
| // the full error message is | ||
| // Couldn't find subtypes of Object. | ||
| // Make sure SubTypesScanner initialized to include Object class - new SubTypesScanner(false) | ||
| // which we have done above, so that is NOT the actual cause. | ||
| // If there are no classes local to the project, that's OK! | ||
| // Just use an empty set instead of throwing an exception. | ||
| LOG.info( "Looks like there aren't any classes compiled by this project." ); | ||
| } else { | ||
| // We still do want to error with "real" exceptions, though. | ||
| throw re; | ||
| } | ||
| List<String> classnames; | ||
| try ( | ||
| ScanResult scanResult = new ClassGraph() | ||
| .acceptPackages(_package.orElse("")) | ||
| .disableJarScanning() | ||
| .scan() | ||
| ) { | ||
| classnames = scanResult | ||
| .getAllClasses() | ||
| .filter(ci -> !ci.extendsSuperclass(Closure.class.getName())) | ||
| .filter(ci -> !ci.extendsSuperclass("spock.lang.Specification")) | ||
| .filter(ci -> !(ci.extendsSuperclass(InvalidlyNamedScriptWrapper.class.getName()) || ci.getName().equals(InvalidlyNamedScriptWrapper.class.getName()))) | ||
| .filter(ci -> ci.extendsSuperclass(_supertype.getName())) | ||
| .getNames(); | ||
| } | ||
|
|
||
| for(String classname : all_types ) { | ||
|
|
||
| Class<?> clazz = null; | ||
| Set<Class<?>> classes = new HashSet<>(); | ||
| Map<String, Throwable> failures = new HashMap<>(); | ||
| for(String classname : classnames ) { | ||
| Class<?> clazz; | ||
|
|
||
| try { | ||
| clazz = Class.forName( classname ); | ||
| } catch( ClassNotFoundException | NoClassDefFoundError e ) { | ||
| failures.put( classname, e ); | ||
| continue; | ||
| } | ||
|
|
||
| classes.add( clazz ); | ||
| } | ||
|
|
||
|
|
@@ -114,12 +100,17 @@ public Set<Class<?>> getClassesOfTypeInPackage(Class<?> _supertype, Optional<Str | |
| @Override | ||
| public Set<Class<?>> getClassesWithAnnotationOfTypeInPackage( Class<? extends Annotation> _annotation, Class<?> _supertype, Optional<String> _package) { | ||
|
|
||
| Set<Class<?>> annotated_classes = new HashSet<>(); | ||
|
|
||
| for( Class<?> annotated_class : new Reflections( _package.orElse("") ).getTypesAnnotatedWith( _annotation ) ) { | ||
| if( _supertype.isAssignableFrom( annotated_class ) ) { | ||
| annotated_classes.add( annotated_class ); | ||
| } | ||
| Set<Class<?>> annotated_classes; | ||
| try ( | ||
| ScanResult scanResult = new ClassGraph() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this also |
||
| .acceptPackages(_package.orElse("")) | ||
| .enableAnnotationInfo() | ||
| .scan() | ||
| ) { | ||
| annotated_classes = new HashSet<>(scanResult | ||
| .getClassesWithAnnotation(_annotation.getName()) | ||
| .filter(ci -> ci.extendsSuperclass(_supertype.getName())) | ||
| .loadClasses(true)); | ||
| } | ||
|
|
||
| return annotated_classes; | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,18 @@ | ||||||
| package com.homeaway.devtools.jenkins.testing | ||||||
|
|
||||||
| import com.homeaway.devtools.jenkins.testing.functions.ScriptToTest | ||||||
|
|
||||||
| class LocalProjectPipelineExtensionDetectorSpec extends JenkinsPipelineSpecification { | ||||||
|
|
||||||
| def "LocalProjectPipelineExtensionDetectorSpec should detect classes that aren't direct subtypes of java-lang-Object" () { | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
It's really the LocalProjectPipelineExtensionDetector that you're testing, right? If you were testing the I think this test can actually just extend And if that's the case... since the class-under-test is in Java, can the test itself be written in ("matching") JUnit Java, too? (These are actual questions, not change requests phrased as questions, by the way.) |
||||||
| when: | ||||||
| Set<Class<?>> classes = new LocalProjectPipelineExtensionDetector() | ||||||
| .getClassesOfTypeInPackage( | ||||||
| Object.class, | ||||||
| Optional.<String>empty() | ||||||
| ) | ||||||
|
|
||||||
| then: | ||||||
| classes.contains(ScriptToTest) | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| package com.homeaway.devtools.jenkins.testing.functions | ||
|
|
||
| def helloWorld(String argument) { | ||
| echo argument | ||
| } |
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.
The motivation behind using Reflections AND GlassGraph, was that Reflections would only see files in the local project; nothing on the extended classpath from Maven dependencies would be included.
How does one configure the current ClassGraph to only see files that are physically present on disk in the current project?
Or, to ask another way:
With this code change, if there's a dependency of the project that includes a file like the
ScriptToTest.groovythat's added here, will these changes detect that script, or not?They should not.
Is that done with the
.disableJarScanning()here: https://github.com/ExpediaGroup/jenkins-spock/pull/111/files#diff-7cf77f094673fdcead882d1c0b654a186428288727ba01a59de640f2a9f80d7fR57 ?