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

Provide a way to store all current violations of a rule and successively only report new ones #181

Merged
merged 18 commits into from
Jul 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
a3c447d
Make CollectsLines deprecated. The API seems pretty useless, naturall…
codecholeric Apr 10, 2019
4cd639f
Initial draft of an "FreezingArchRule" capable to wrap a given ArchRu…
codecholeric Apr 10, 2019
350257e
Extended ArchConfiguration to make it possible to add further propert…
codecholeric Apr 11, 2019
278e8b7
Added DefaultViolationStoreFactory which returns a simple file based …
codecholeric May 3, 2019
7da683c
Made ViolationLineMatcher configurable via archunit.properties.
codecholeric Jun 8, 2019
2baef23
TextFileBasedViolationStore should create configured folder if it doe…
codecholeric Jun 8, 2019
fed0a45
Improved handling of known violations. So far if a condition event ad…
codecholeric Jun 8, 2019
103e1d3
Fixed violation handling for filtered events. Also added some logging.
codecholeric Jun 8, 2019
0479763
Added examples and integration test
codecholeric Jun 8, 2019
46b296b
Improve FreezingArchRule to only use LineMatcher once. Also fixed beh…
codecholeric Jun 15, 2019
bf8274a
Improve performance: Using a regex within the default line matcher ha…
codecholeric Jun 15, 2019
7747711
Added documentation
codecholeric Jun 15, 2019
e1e2c4b
some review comments on FreezingArchRule PR#181
hankem Jun 27, 2019
c8c98c6
some review comments on FreezingArchRule PR#181 (documentation)
hankem Jun 27, 2019
afc10e6
Further improve docs by documenting how to configure ViolationStore a…
codecholeric Jun 30, 2019
41b25d9
log number of violations
hankem Jul 3, 2019
5feb2fa
allow for rule-violations stored in files whose name is not a UUID
hankem Jul 3, 2019
8b0b6a9
Log of existing rule should not be level error, since this will happe…
codecholeric Jul 4, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Class <com.tngtech.archunit.example.service.ServiceViolatingDaoRules$MyEntityManager> implements interface <javax.persistence.EntityManager> in (ServiceViolatingDaoRules.java:0)
Field <com.tngtech.archunit.example.persistence.first.dao.jpa.SomeJpa.entityManager> has type <javax.persistence.EntityManager> in (SomeJpa.java:0)
Field <com.tngtech.archunit.example.persistence.second.dao.jpa.OtherJpa.entityManager> has type <javax.persistence.EntityManager> in (OtherJpa.java:0)
Field <com.tngtech.archunit.example.service.ServiceViolatingDaoRules.myEntityManager> has type <com.tngtech.archunit.example.service.ServiceViolatingDaoRules$MyEntityManager> in (ServiceViolatingDaoRules.java:0)
Method <com.tngtech.archunit.example.persistence.first.dao.jpa.SomeJpa.findById(long)> calls method <javax.persistence.EntityManager.find(java.lang.Class, java.lang.Object)> in (SomeJpa.java:20)
Method <com.tngtech.archunit.example.persistence.second.dao.OtherDao.getEntityManager()> has return type <javax.persistence.EntityManager> in (OtherDao.java:0)
Method <com.tngtech.archunit.example.persistence.second.dao.jpa.OtherJpa.findById(long)> calls method <javax.persistence.EntityManager.find(java.lang.Class, java.lang.Object)> in (OtherJpa.java:19)
Method <com.tngtech.archunit.example.persistence.second.dao.jpa.OtherJpa.getEntityManager()> has return type <javax.persistence.EntityManager> in (OtherJpa.java:0)
Method <com.tngtech.archunit.example.persistence.second.dao.jpa.OtherJpa.testConnection()> calls method <javax.persistence.EntityManager.unwrap(java.lang.Class)> in (OtherJpa.java:24)
Method <com.tngtech.archunit.example.service.ServiceViolatingDaoRules.illegallyUseEntityManager()> calls method <com.tngtech.archunit.example.service.ServiceViolatingDaoRules$MyEntityManager.persist(java.lang.Object)> in (ServiceViolatingDaoRules.java:27)
Method <com.tngtech.archunit.example.service.ServiceViolatingDaoRules.illegallyUseEntityManager()> calls method <javax.persistence.EntityManager.persist(java.lang.Object)> in (ServiceViolatingDaoRules.java:26)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Class <com.tngtech.archunit.example.persistence.layerviolation.DaoCallingService> implements interface <com.tngtech.archunit.example.service.ServiceInterface> in (DaoCallingService.java:0)
Class <com.tngtech.archunit.example.service.impl.ServiceImplementation> implements interface <com.tngtech.archunit.example.service.ServiceInterface> in (ServiceImplementation.java:0)
Constructor <com.tngtech.archunit.example.SomeMediator.<init>(com.tngtech.archunit.example.service.ServiceViolatingLayerRules)> has parameter of type <com.tngtech.archunit.example.service.ServiceViolatingLayerRules> in (SomeMediator.java:0)
Field <com.tngtech.archunit.example.SomeMediator.service> has type <com.tngtech.archunit.example.service.ServiceViolatingLayerRules> in (SomeMediator.java:0)
Field <com.tngtech.archunit.example.controller.SomeController.otherService> has type <com.tngtech.archunit.example.service.ServiceViolatingLayerRules> in (SomeController.java:0)
Field <com.tngtech.archunit.example.controller.SomeController.service> has type <com.tngtech.archunit.example.service.ServiceViolatingDaoRules> in (SomeController.java:0)
Field <com.tngtech.archunit.example.persistence.layerviolation.DaoCallingService.service> has type <com.tngtech.archunit.example.service.ServiceViolatingLayerRules> in (DaoCallingService.java:0)
Field <com.tngtech.archunit.example.service.ServiceViolatingDaoRules.myEntityManager> has type <com.tngtech.archunit.example.service.ServiceViolatingDaoRules$MyEntityManager> in (ServiceViolatingDaoRules.java:0)
Method <com.tngtech.archunit.example.SomeMediator.violateLayerRulesIndirectly()> calls method <com.tngtech.archunit.example.service.ServiceViolatingLayerRules.doSomething()> in (SomeMediator.java:15)
Method <com.tngtech.archunit.example.controller.SomeController.doSthController()> calls method <com.tngtech.archunit.example.service.ServiceViolatingDaoRules.doSthService()> in (SomeController.java:11)
Method <com.tngtech.archunit.example.controller.SomeController.doSthWithSecuredService()> calls method <com.tngtech.archunit.example.service.ServiceViolatingLayerRules.properlySecured()> in (SomeController.java:15)
Method <com.tngtech.archunit.example.controller.SomeGuiController.callServiceLayer()> calls constructor <com.tngtech.archunit.example.service.ServiceHelper.<init>()> in (SomeGuiController.java:7)
Method <com.tngtech.archunit.example.controller.SomeGuiController.callServiceLayer()> calls constructor <com.tngtech.archunit.example.service.ServiceHelper.<init>(java.lang.String)> in (SomeGuiController.java:8)
Method <com.tngtech.archunit.example.controller.SomeGuiController.callServiceLayer()> gets field <com.tngtech.archunit.example.service.ServiceHelper.insecure> in (SomeGuiController.java:10)
Method <com.tngtech.archunit.example.controller.SomeGuiController.callServiceLayer()> gets field <com.tngtech.archunit.example.service.ServiceHelper.properlySecured> in (SomeGuiController.java:11)
Method <com.tngtech.archunit.example.persistence.layerviolation.DaoCallingService.violateLayerRules()> calls method <com.tngtech.archunit.example.service.ServiceViolatingLayerRules.doSomething()> in (DaoCallingService.java:14)
Method <com.tngtech.archunit.example.service.ServiceViolatingDaoRules.illegallyUseEntityManager()> calls method <com.tngtech.archunit.example.service.ServiceViolatingDaoRules$MyEntityManager.persist(java.lang.Object)> in (ServiceViolatingDaoRules.java:27)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#
#Sun Jun 09 01:50:44 ICT 2019
no\ classes\ should\ depend\ on\ classes\ that\ reside\ in\ a\ package\ '..service..'=d8e3c650-e214-402f-8cfa-33a8607785d3
no\ classes\ should\ depend\ on\ classes\ that\ are\ assignable\ to\ javax.persistence.EntityManager=6f3a1e55-3673-4950-a3be-857751b3d515
4 changes: 4 additions & 0 deletions archunit-example/example-junit4/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ ext.moduleName = 'com.tngtech.archunit.example.junit4'
dependencies {
testCompile project(path: ':archunit-junit4')
testCompile project(path: ':archunit-example:example-plain')

testRuntime dependency.log4j_api
testRuntime dependency.log4j_core
testRuntime dependency.log4j_slf4j
}

test {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.tngtech.archunit.exampletest.junit4;

import javax.persistence.EntityManager;

import com.tngtech.archunit.ArchConfiguration;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.junit.ArchUnitRunner;
import com.tngtech.archunit.lang.ArchRule;
import com.tngtech.archunit.library.freeze.FreezingArchRule;
import com.tngtech.archunit.library.freeze.ViolationLineMatcher;
import com.tngtech.archunit.library.freeze.ViolationStore;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;

import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
import static com.tngtech.archunit.library.freeze.FreezingArchRule.freeze;

/**
* This test demonstrates the use of {@link FreezingArchRule} with 'default' configuration.
* While both rules shown have numerous violations, most of those violations have been 'frozen', i.e. at some point in time all violations
* were recorded as accepted for the moment. Only violations added afterwards will be reported.<br>
* You can see how the default text based {@link ViolationStore} stores the results under {@code src/test/resources/frozen} configured
* via {@value ArchConfiguration#ARCHUNIT_PROPERTIES_RESOURCE_NAME}. You can also
* observe that if you fix an old violation, this store will automatically be adjusted to not allow any regression.<br>
* Furthermore you can observe how the default {@link ViolationLineMatcher} will ignore changes in line numbers of recorded violations,
* i.e. if you only change the line numbers of frozen violations, the test will still pass.
*/
@Category(Example.class)
@RunWith(ArchUnitRunner.class)
@AnalyzeClasses(packages = "com.tngtech.archunit.example")
public class FrozenRulesTest {

@ArchTest
public static final ArchRule no_classes_should_depend_on_service =
freeze(noClasses().should().dependOnClassesThat().resideInAPackage("..service.."));

@ArchTest
public static final ArchRule no_classes_should_use_the_EntityManager =
freeze(noClasses().should().dependOnClassesThat().areAssignableTo(EntityManager.class));
}

Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolveMissingDependenciesFromClassPath=true
freeze.store.default.path=src/test/resources/frozen
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Class <com.tngtech.archunit.example.persistence.layerviolation.DaoCallingService> implements interface <com.tngtech.archunit.example.service.ServiceInterface> in (DaoCallingService.java:0)
Class <com.tngtech.archunit.example.service.impl.ServiceImplementation> implements interface <com.tngtech.archunit.example.service.ServiceInterface> in (ServiceImplementation.java:0)
Constructor <com.tngtech.archunit.example.SomeMediator.<init>(com.tngtech.archunit.example.service.ServiceViolatingLayerRules)> has parameter of type <com.tngtech.archunit.example.service.ServiceViolatingLayerRules> in (SomeMediator.java:0)
Field <com.tngtech.archunit.example.SomeMediator.service> has type <com.tngtech.archunit.example.service.ServiceViolatingLayerRules> in (SomeMediator.java:0)
Field <com.tngtech.archunit.example.controller.SomeController.otherService> has type <com.tngtech.archunit.example.service.ServiceViolatingLayerRules> in (SomeController.java:0)
Field <com.tngtech.archunit.example.controller.SomeController.service> has type <com.tngtech.archunit.example.service.ServiceViolatingDaoRules> in (SomeController.java:0)
Field <com.tngtech.archunit.example.persistence.layerviolation.DaoCallingService.service> has type <com.tngtech.archunit.example.service.ServiceViolatingLayerRules> in (DaoCallingService.java:0)
Field <com.tngtech.archunit.example.service.ServiceViolatingDaoRules.myEntityManager> has type <com.tngtech.archunit.example.service.ServiceViolatingDaoRules$MyEntityManager> in (ServiceViolatingDaoRules.java:0)
Method <com.tngtech.archunit.example.SomeMediator.violateLayerRulesIndirectly()> calls method <com.tngtech.archunit.example.service.ServiceViolatingLayerRules.doSomething()> in (SomeMediator.java:15)
Method <com.tngtech.archunit.example.controller.SomeGuiController.callServiceLayer()> calls constructor <com.tngtech.archunit.example.service.ServiceHelper.<init>()> in (SomeGuiController.java:7)
Method <com.tngtech.archunit.example.controller.SomeGuiController.callServiceLayer()> calls constructor <com.tngtech.archunit.example.service.ServiceHelper.<init>(java.lang.String)> in (SomeGuiController.java:8)
Method <com.tngtech.archunit.example.controller.SomeGuiController.callServiceLayer()> gets field <com.tngtech.archunit.example.service.ServiceHelper.insecure> in (SomeGuiController.java:10)
Method <com.tngtech.archunit.example.controller.SomeGuiController.callServiceLayer()> gets field <com.tngtech.archunit.example.service.ServiceHelper.properlySecured> in (SomeGuiController.java:11)
Method <com.tngtech.archunit.example.persistence.layerviolation.DaoCallingService.violateLayerRules()> calls method <com.tngtech.archunit.example.service.ServiceViolatingLayerRules.doSomething()> in (DaoCallingService.java:14)
Method <com.tngtech.archunit.example.service.ServiceViolatingDaoRules.illegallyUseEntityManager()> calls method <com.tngtech.archunit.example.service.ServiceViolatingDaoRules$MyEntityManager.persist(java.lang.Object)> in (ServiceViolatingDaoRules.java:27)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Class <com.tngtech.archunit.example.service.ServiceViolatingDaoRules$MyEntityManager> implements interface <javax.persistence.EntityManager> in (ServiceViolatingDaoRules.java:0)
Field <com.tngtech.archunit.example.persistence.first.dao.jpa.SomeJpa.entityManager> has type <javax.persistence.EntityManager> in (SomeJpa.java:0)
Field <com.tngtech.archunit.example.persistence.second.dao.jpa.OtherJpa.entityManager> has type <javax.persistence.EntityManager> in (OtherJpa.java:0)
Field <com.tngtech.archunit.example.service.ServiceViolatingDaoRules.myEntityManager> has type <com.tngtech.archunit.example.service.ServiceViolatingDaoRules$MyEntityManager> in (ServiceViolatingDaoRules.java:0)
Method <com.tngtech.archunit.example.persistence.first.dao.jpa.SomeJpa.findById(long)> calls method <javax.persistence.EntityManager.find(java.lang.Class, java.lang.Object)> in (SomeJpa.java:20)
Method <com.tngtech.archunit.example.persistence.second.dao.OtherDao.getEntityManager()> has return type <javax.persistence.EntityManager> in (OtherDao.java:0)
Method <com.tngtech.archunit.example.persistence.second.dao.jpa.OtherJpa.findById(long)> calls method <javax.persistence.EntityManager.find(java.lang.Class, java.lang.Object)> in (OtherJpa.java:19)
Method <com.tngtech.archunit.example.persistence.second.dao.jpa.OtherJpa.getEntityManager()> has return type <javax.persistence.EntityManager> in (OtherJpa.java:0)
Method <com.tngtech.archunit.example.persistence.second.dao.jpa.OtherJpa.testConnection()> calls method <javax.persistence.EntityManager.unwrap(java.lang.Class)> in (OtherJpa.java:24)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#
#Sun Jun 09 00:30:33 ICT 2019
no\ classes\ should\ depend\ on\ classes\ that\ reside\ in\ a\ package\ '..service..'=a81a2b54-5a18-4145-b544-7a580aba0425
no\ classes\ should\ depend\ on\ classes\ that\ are\ assignable\ to\ javax.persistence.EntityManager=e77ec262-4d5c-4a7b-b41f-362a71e5a1d8
4 changes: 4 additions & 0 deletions archunit-example/example-junit5/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ dependencies {
testCompile project(path: ':archunit-example:example-plain')

testRuntime project(path: ':archunit-junit5-engine')

testRuntime dependency.log4j_api
testRuntime dependency.log4j_core
testRuntime dependency.log4j_slf4j
}

test {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.tngtech.archunit.exampletest.junit5;

import javax.persistence.EntityManager;

import com.tngtech.archunit.ArchConfiguration;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTag;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule;
import com.tngtech.archunit.library.freeze.FreezingArchRule;
import com.tngtech.archunit.library.freeze.ViolationLineMatcher;
import com.tngtech.archunit.library.freeze.ViolationStore;

import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
import static com.tngtech.archunit.library.freeze.FreezingArchRule.freeze;

/**
* This test demonstrates the use of {@link FreezingArchRule} with 'default' configuration.
* While both rules shown have numerous violations, most of those violations have been 'frozen', i.e. at some point in time all violations
* were recorded as accepted for the moment. Only violations added afterwards will be reported.<br>
* You can see how the default text based {@link ViolationStore} stores the results under {@code src/test/resources/frozen} configured
* via {@value ArchConfiguration#ARCHUNIT_PROPERTIES_RESOURCE_NAME}. You can also
* observe that if you fix an old violation, this store will automatically be adjusted to not allow any regression.<br>
* Furthermore you can observe how the default {@link ViolationLineMatcher} will ignore changes in line numbers of recorded violations,
* i.e. if you only change the line numbers of frozen violations, the test will still pass.
*/
@ArchTag("example")
@AnalyzeClasses(packages = "com.tngtech.archunit.example")
public class FrozenRulesTest {

@ArchTest
static final ArchRule no_classes_should_depend_on_service =
freeze(noClasses().should().dependOnClassesThat().resideInAPackage("..service.."));

@ArchTest
static final ArchRule no_classes_should_use_the_EntityManager =
freeze(noClasses().should().dependOnClassesThat().areAssignableTo(EntityManager.class));
}

Original file line number Diff line number Diff line change
@@ -1 +1 @@
resolveMissingDependenciesFromClassPath=true
freeze.store.default.path=src/test/resources/frozen
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Class <com.tngtech.archunit.example.persistence.layerviolation.DaoCallingService> implements interface <com.tngtech.archunit.example.service.ServiceInterface> in (DaoCallingService.java:0)
Class <com.tngtech.archunit.example.service.impl.ServiceImplementation> implements interface <com.tngtech.archunit.example.service.ServiceInterface> in (ServiceImplementation.java:0)
Constructor <com.tngtech.archunit.example.SomeMediator.<init>(com.tngtech.archunit.example.service.ServiceViolatingLayerRules)> has parameter of type <com.tngtech.archunit.example.service.ServiceViolatingLayerRules> in (SomeMediator.java:0)
Field <com.tngtech.archunit.example.SomeMediator.service> has type <com.tngtech.archunit.example.service.ServiceViolatingLayerRules> in (SomeMediator.java:0)
Field <com.tngtech.archunit.example.controller.SomeController.otherService> has type <com.tngtech.archunit.example.service.ServiceViolatingLayerRules> in (SomeController.java:0)
Field <com.tngtech.archunit.example.controller.SomeController.service> has type <com.tngtech.archunit.example.service.ServiceViolatingDaoRules> in (SomeController.java:0)
Field <com.tngtech.archunit.example.persistence.layerviolation.DaoCallingService.service> has type <com.tngtech.archunit.example.service.ServiceViolatingLayerRules> in (DaoCallingService.java:0)
Field <com.tngtech.archunit.example.service.ServiceViolatingDaoRules.myEntityManager> has type <com.tngtech.archunit.example.service.ServiceViolatingDaoRules$MyEntityManager> in (ServiceViolatingDaoRules.java:0)
Method <com.tngtech.archunit.example.SomeMediator.violateLayerRulesIndirectly()> calls method <com.tngtech.archunit.example.service.ServiceViolatingLayerRules.doSomething()> in (SomeMediator.java:15)
Method <com.tngtech.archunit.example.controller.SomeGuiController.callServiceLayer()> calls constructor <com.tngtech.archunit.example.service.ServiceHelper.<init>()> in (SomeGuiController.java:7)
Method <com.tngtech.archunit.example.controller.SomeGuiController.callServiceLayer()> calls constructor <com.tngtech.archunit.example.service.ServiceHelper.<init>(java.lang.String)> in (SomeGuiController.java:8)
Method <com.tngtech.archunit.example.controller.SomeGuiController.callServiceLayer()> gets field <com.tngtech.archunit.example.service.ServiceHelper.insecure> in (SomeGuiController.java:10)
Method <com.tngtech.archunit.example.controller.SomeGuiController.callServiceLayer()> gets field <com.tngtech.archunit.example.service.ServiceHelper.properlySecured> in (SomeGuiController.java:11)
Method <com.tngtech.archunit.example.persistence.layerviolation.DaoCallingService.violateLayerRules()> calls method <com.tngtech.archunit.example.service.ServiceViolatingLayerRules.doSomething()> in (DaoCallingService.java:14)
Method <com.tngtech.archunit.example.service.ServiceViolatingDaoRules.illegallyUseEntityManager()> calls method <com.tngtech.archunit.example.service.ServiceViolatingDaoRules$MyEntityManager.persist(java.lang.Object)> in (ServiceViolatingDaoRules.java:27)