Skip to content
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

ArchUnit performance is too slow to run per maven build #1160

Open
royteeuwen opened this issue Aug 28, 2023 · 5 comments
Open

ArchUnit performance is too slow to run per maven build #1160

royteeuwen opened this issue Aug 28, 2023 · 5 comments

Comments

@royteeuwen
Copy link

I am trying to integrate ArchUnit in our build, where it is ran on every compile as a unit test. This seems to be too slow though, if I look at the current performance. Is there a way to speed things up? For your reference, the project only contains 120 java classes so far, so nothing too big

[INFO] Running architecture.ArchitectureRulesTest
[INFO] Running architecture.rules.General
[INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 87.43 s -- in architecture.rules.General
[INFO] Running architecture.rules.OSGiConfigs
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.002 s -- in architecture.rules.OSGiConfigs
[INFO] Running architecture.rules.Services
[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.010 s -- in architecture.rules.Services
[INFO] Running architecture.rules.Servlets
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.005 s -- in architecture.rules.Servlets
[INFO] Running architecture.rules.SlingModels
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.011 s -- in architecture.rules.SlingModels
[INFO] Running architecture.rules.Utils
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.004 s -- in architecture.rules.Utils
[INFO] Tests run: 0, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 87.46 s -- in architecture.ArchitectureRulesTest

I am using the following setup:

@AnalyzeClasses(
        packages = {Constants.PROJECT_ROOT_PACKAGE},
        importOptions = {
                ImportOption.DoNotIncludeTests.class,
                ImportOption.DoNotIncludeJars.class,
                ImportOption.DoNotIncludeArchives.class
        }
)
public class ArchitectureRulesTest {

    @ArchTest
    static final ArchTests generalRules = ArchTests.in(General.class);

    @ArchTest
    static final ArchTests osgiConfigs = ArchTests.in(OSGiConfigs.class);

    @ArchTest
    static final ArchTests services = ArchTests.in(Services.class);

    @ArchTest
    static final ArchTests servlets = ArchTests.in(Servlets.class);

    @ArchTest
    static final ArchTests slingModes = ArchTests.in(SlingModels.class);

    @ArchTest
    static final ArchTests utils = ArchTests.in(Utils.class);

}
@hankem
Copy link
Member

hankem commented Aug 28, 2023

The architecture.rules.OSGiConfigs / Services / Servlets / SlingModels / Utils run in ≤ 11 ms each, which seems plausible (and IMO quite acceptable 🙂).

General is the only exception with almost 1.5 min. If you can share its code, we can have a look what's wrong in there.

@royteeuwen
Copy link
Author

Hey,

I also thought that, but it’s actually just the first test to run, caching the classes for subsequent tests. If I comment out every test from General, the next class is the slow one taking also around 90secs

if you still think its usefull, i can see to make an example project ? Cant give source code for this one sadly enough

@codecholeric
Copy link
Collaborator

codecholeric commented Aug 31, 2023

Sorry, I just saw that you already told your project has only 120 classes 🙈 So then 90 sec is definitely way too long and I think you just pull in a lot of classes somehow. If you can create an example project that reproduces this, sure, go for it!
Other than that, ArchUnit uses the SLF4J API for logging, so you could also hook in a logger and set the level of the ClassFileImporter to TRACE to see a list of all imported classes. There you could see how many you're importing. Or you just use a

@ArchTest
static void examine(JavaClasses classes) {
  System.out.println("Imported " + classes.size() + " classes");
}

dummy test. If the size of this collection should really just be 120 we can see if maybe a tweak to the automatic resolution behavior could speed things up.

@royteeuwen
Copy link
Author

I put all other tests in comment, only keeping the examine. As result I get, in 80 seconds

[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 80.10 s <<< FAILURE! -- in architecture.ArchitectureRulesTest
[ERROR] architecture.ArchitectureRulesTest.examine -- Time elapsed: 80.09 s <<< FAILURE!
org.opentest4j.AssertionFailedError: Imported 132 classes ==> expected: <120> but was: <132>

So it's a bit more but not a lot.

If I now use the resolveMissingDependenciesFromClassPath=false in archunit.properties. The time goes to:

[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 1.411 s <<< FAILURE! -- in architecture.ArchitectureRulesTest
[ERROR] architecture.ArchitectureRulesTest.examine -- Time elapsed: 1.407 s <<< FAILURE!
org.opentest4j.AssertionFailedError: Imported 132 classes ==> expected: <120> but was: <132>

Any downside on doing it this way? What could be causing this issue? My code compiles, so all dependencies are on the classpath I would think?

@codecholeric
Copy link
Collaborator

Downside could be that you have no/wrong information about classes outside of your project. I.e. the classes that are not directly imported will be stubs, they can miss annotations, superclasses or report a wrong isInterface() property... But I would be really interested how you can pull in so many dependencies transitively that the time goes up so crazy 🤔
You could also instead configure the number of resolution iterations and e.g. set the -1 values (i.e. follow up indefinitely) to something like 1 and see what happens 🤔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants