Skip to content

Nondeterministic violation description for ModuleDependency: leads to failing test when used as frozen rule #1630

@mlkammer

Description

@mlkammer

We've noticed that when declaring a rule about allowed module dependencies, like so:

modules().definedByPackages("..root.(*)..").should().respectTheirAllowedDependencies(allow().fromModule("main").toModules("util"))

If there are multiple violations of this rule, the generated violation message has non-deterministic ordering.

Example violation message:

Module Dependency [main -> util]:
Method <util.UtilClass.foo()> calls method <main.SomeMainClass.bar()> in (UtilClass.java:12)
Method <othermodule.OtherClass.baz()> calls method <util.UtilClass.foo()> in (OtherClass.java:34)

Although it is functionally correct (the list includes all offending places), the fact that it may give a different ordering every time leads to problems when using the frozen rule mechanism, which checks against already known violations. Because the violation text isn't completely equal, it is seen as a new violation and therefore fails the test.

Problem:
The getDescription() method of the ModuleDependency class iterates over the classDependencies field, which is a Set. Although that set should preserve its insertion order, it seems that the order in which it gets populated can differ with each ArchUnit analysis run.

Solution alternatives:

  1. Make sure that the set of class dependencies that get inserted here always have deterministic ordering. Seems error prone / difficult to achieve: how will we ensure we have catched all places from which this could be populated?
  2. Make sure that, regardless of insertion order, the getDescription() method always produces deterministic output, for instance by putting a .sorted() operation into its stream. Seems like a small enough fix and even leads to better readable violation messages.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions