Allow package info classes to be resolved as a dependency#1565
Allow package info classes to be resolved as a dependency#1565wakingrufus wants to merge 1 commit into
Conversation
6da5209 to
43288eb
Compare
43288eb to
070d20f
Compare
|
I updated this PR to exclude the package-info classes when calculating package metrics. this fixes the failing tests |
070d20f to
81cfe2d
Compare
|
Happy New Year @codecholeric and @hankem ! |
81cfe2d to
09eb2b1
Compare
09eb2b1 to
ab61e25
Compare
|
Hi again @codecholeric and @hankem. I know I have a few PRs open at the moment, but this one is quite important. I would really appreciate of someone could take a look at this please. |
… class dependency when scanning since TNG/ArchUnit#1565 has not been merged in a timely manner, we need a custom implementation on our end to fix this issue until it is finally merged.
ab61e25 to
63c579b
Compare
… class dependency when scanning since TNG/ArchUnit#1565 has not been merged in a timely manner, we need a custom implementation on our end to fix this issue until it is finally merged.
… class dependency when scanning since TNG/ArchUnit#1565 has not been merged in a timely manner, we need a custom implementation on our end to fix this issue until it is finally merged.
… class dependency when scanning since TNG/ArchUnit#1565 has not been merged in a timely manner, we need a custom implementation on our end to fix this issue until it is finally merged.
63c579b to
3b8fda6
Compare
|
I'm very sorry for the late response! I'll try to find more time. 🤞 |
No problem, Thanks! |
There was a problem hiding this comment.
I dislike that the package-info is now included in JavaPackage.getClasses() (even as stub if it doesn't exist, which might be a breaking change for some use cases) and don't understand whether or how all classes in the package are retained when recreating the package in the JavaClass class.
IMO it also is the wrong location for the logic of resolving package info.
Can you explain in more detail which problem you want to solve?
To my understanding, the issue seems to be, that the package-info is not always resolved when you expect it to be loaded, but I'm not sure whether that's limited to loading single classes via new ClassFileImporter() or whether this also affects the "normal" execution with @AnalyzeClasses annotation
| } | ||
|
|
||
| @Test | ||
| public void function_getPackageInfo() { |
There was a problem hiding this comment.
looking at the previous tests in this class, I would expect that a test with this name would test
JavaClass.Functions.GET_PACKAGE_INFO, which doesn't exist.
This test doesn't fit the pattern of the other tests.
I'm not sure whether we would want to have that function, as I'm not sure what I should return apart from the package itself
There was a problem hiding this comment.
thanks. this is fixed now.
| @@ -0,0 +1,7 @@ | |||
| package com.tngtech.archunit.core.importer.testexamples.packageinforesolution; | |||
There was a problem hiding this comment.
I don't understand this new package name.
is it important that it is subpackage of the package in which OtherClass is defined or shall just be any other package?
There was a problem hiding this comment.
It is important that this class is in a different package than OtherClass. other than that, this package could be anything
There was a problem hiding this comment.
I already suspected that, but "package in for resolution" doesn't really tell me anything.
maybe sth like 'otherclassrefrencefromanotherpackage`?
damn is that long :D
and not really better than yours... maybe think a bit about a better name, but if you don't find one, it's also fine for me
| JavaClass otherClass = new ClassFileImporter() | ||
| .importClass(OtherClass.class); | ||
|
|
||
| JavaPackage checkedClassPackageInfo = otherClass.getPackage(); |
There was a problem hiding this comment.
The JavaPackage has the methods tryGetPackageInfo and getPackageInfo.
please include in the test they work as well.
as one calls the other, I would prefer to tests sth like assertThat(checkedClassPackageInfo.getPackageInfo()).isPresent(), even thought this is kind of redundant with the getAnnotations method which also relies on that field.
There was a problem hiding this comment.
Looks like these exist in JavaPackageTest:
@Test
public void test_getPackageInfo() {
JavaPackage annotatedPackage = importPackage("packageexamples.annotated");
JavaPackage nonAnnotatedPackage = importPackage("packageexamples");
assertThat(annotatedPackage.getPackageInfo()).isNotNull();
assertThatThrownBy(nonAnnotatedPackage::getPackageInfo)
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining(nonAnnotatedPackage.getDescription() + " does not contain a package-info.java");
}
@Test
public void test_tryGetPackageInfo() {
JavaPackage annotatedPackage = importPackage("packageexamples.annotated");
JavaPackage nonAnnotatedPackage = importPackage("packageexamples");
assertThat(annotatedPackage.tryGetPackageInfo()).isPresent();
assertThat(nonAnnotatedPackage.tryGetPackageInfo()).isEmpty();
}
There was a problem hiding this comment.
yes, but since you re-create the JavaPackage, it might be worth checking that the re-created package behaves as expected in these methods
There was a problem hiding this comment.
still open, please add an assertion like assertThat(checkedClassPackageInfo.getPackageInfo()).isPresent() to make explicit that the package-info is loaded.
Asserting it before the annotations will likely give better error messages if it ever breaks
| JavaPackage checkedClassPackageInfo = otherClass.getPackage(); | ||
| assertThat(checkedClassPackageInfo.getAnnotations()).hasSize(1); | ||
| assertThat(checkedClassPackageInfo.isAnnotatedWith(SomeAnnotation.class)).isTrue(); | ||
| } |
There was a problem hiding this comment.
When running checkedClassPackageInfo.getClasses(), this set now includes the package-info, which it didn't before.
I don't think that that class should be included in that Set as it is not a real class and shall instead be accessed via JavaPackage.getPackageInfo() which returns the JavaClass representing package-info as HasAnnotations. This change in type seems deliberate to me as this special class doesn't have all features of a JavaClass.
There was a problem hiding this comment.
This is already the normal behavior in scanning a package. Ill add this assertion to JavaPackageTest.test_getPackageInfo() to show this:
assertThat(annotatedPackage.getClasses().stream().map(JavaClass::getFullName).collect(Collectors.toList()))
.as("all classes and package-info are loaded as JavaClass")
.containsExactlyInAnyOrder(
"com.tngtech.archunit.core.domain.packageexamples.annotated.package-info",
"com.tngtech.archunit.core.domain.packageexamples.annotated.PackageLevelAnnotation",
"com.tngtech.archunit.core.domain.packageexamples.annotated.WithinAnnotatedPackage"
);
There was a problem hiding this comment.
how about packages without a package-info file?
IIRC you added somewhere a stub of it as JavaClass. If I don't have the package-info as explicit file in the package, I find it even weirder to have it returned as PackageInfo.
That is was like that before might not have been desired.
As you know, I'm new to the ArchUnit dev side (so far I only used it), but I find it surprising that this pseudo class is present at all in the result of getClasses() and even more surprising that it is now present even though I haven't even written that class.
I would prefer to never have package-info in this result
|
I can't really localize the issue (and it might be a me-problem), but using windows 11 and IntelliJ, incremental builds fail with this error as soon as I include your changes in |
Thanks for taking a look at this. I will add a more detailed description of the use case in the PR description. |
@StefanGraeber Actually, I updated the associated issue, instead: #1564 |
3b8fda6 to
7cd61cd
Compare
I was able to run |
7cd61cd to
7be3ecf
Compare
whether I used the IntelliJ buttons or the gradle command via terminal made no difference. |
fix for TNG#1564 exclude package-info classes from package metrics calculations Signed-off-by: John Burns <wakingrufus@gmail.com>
7be3ecf to
f0fca6f
Compare
|
rebased on latest main |
| components.add(MetricsComponent.of( | ||
| javaPackage.getName(), | ||
| javaPackage.getClassesInPackageTree().stream() | ||
| .filter(jc -> !jc.getSimpleName().equals("package-info")) |
There was a problem hiding this comment.
Sorry for opening a new frontier, but I needed a fresh review from the start after a month...
this is a behavior change in case of having a package-info file.
please look at MetricsComponentsTest.creates_components_from_Java_packages and make explicit that the package-info is not imported (you need to create one in the test scenario, probably best in case 1 for top-level and subpackage.
adding the package-info might also require changes to the the other test methods. it would be interesting to see whether creates_components_from_Java_classes stick with 3 components.
I hope this doesn't break too much for our users becasue the old behavior is unexpected for me and I haven't seen MetricsCompment before
| assertThat(annotatedPackage.getClasses().stream().map(JavaClass::getFullName).collect(Collectors.toList())) | ||
| .as("all classes and package-info are loaded as JavaClass") | ||
| .containsExactlyInAnyOrder( | ||
| "com.tngtech.archunit.core.domain.packageexamples.annotated.package-info", |
There was a problem hiding this comment.
should we really get the package-info in the classes?
I wouldn't expect that as user and don't really like having it as the package info is not really a class.
I just wanted to write that you can't get it via reflection as Class.forName("com.tngtech.archunit.core.domain.packageexamples.annotated.package-info") but you actually get it as an interface... there is always sth to learn about java...
What are your thoughts on including the package-info class in the classes of the package?
I have the feeling that it was like this before, but I don't like it.
What about a missing package-info file? I just checked that it is NOT included in that case. please extend the
test, I write a separate comment f0fca6f#r3332408963 for it
related/caused by to f0fca6f#r3136501904
There was a problem hiding this comment.
I also just checked how the libraries Reflections and ClassGraph handle this:
Reflections includes it if you look for all subclasses of Object and disable the filterr preventing obejct sublcasses like this:
Reflections reflections = new Reflections(
new ConfigurationBuilder()
.forPackages("com.tngtech.archunit.core.domain.packageexamples.annotated")
.setScanners(Scanners.SubTypes.filterResultsBy(c -> true)) // disable the Object filter
);
Set<Class<?>> subTypesOfObject = reflections.getSubTypesOf(Object.class);
which is quite hacky.
ClassGraph doesn't include it at all but provides special methods for this
| "com.tngtech.archunit.core.domain.packageexamples.annotated.WithinAnnotatedPackage" | ||
| ); | ||
|
|
||
| assertThatThrownBy(nonAnnotatedPackage::getPackageInfo) |
There was a problem hiding this comment.
created by f0fca6f#r3332389020
I'd prefer to exchange the tested package by one with some classes in it (e.g. packageexamples.first) and asserting that those 2 classes but not the package-info are returned from getClasses()
| JavaClass otherClass = new ClassFileImporter() | ||
| .importClass(OtherClass.class); | ||
|
|
||
| JavaPackage checkedClassPackageInfo = otherClass.getPackage(); |
There was a problem hiding this comment.
still open, please add an assertion like assertThat(checkedClassPackageInfo.getPackageInfo()).isPresent() to make explicit that the package-info is loaded.
Asserting it before the annotations will likely give better error messages if it ever breaks
| JavaPackage checkedClassPackageInfo = otherClass.getPackage(); | ||
| assertThat(checkedClassPackageInfo.getAnnotations()).hasSize(1); | ||
| assertThat(checkedClassPackageInfo.isAnnotatedWith(SomeAnnotation.class)).isTrue(); | ||
| } |
Allow package info classes to be resolved as a dependency
exclude package-info classes from package metrics calculations
Resolves #1564