bmuschko / gradle-clover-plugin

Gradle plugin for generating a code coverage report using Clover
Apache License 2.0
74 stars 49 forks source link

ncLineCount column gives error #135

Closed freshcodemonger closed 5 years ago

freshcodemonger commented 5 years ago

report { //... columns { //.. ncLineCount format: 'raw' //...

https://confluence.atlassian.com/clover/clover-report-71600095.html#clover-report-Columns

* What went wrong:
Unable to store input properties for task ':cloverReport'. Property 'additionalColumns' with value
 '[com.bmuschko.gradle.clover.CloverReportColumn@1ccec4a6, 
com.bmuschko.gradle.clover.CloverReportColumn@4f828e6c, 
com.bmuschko.gradle.clover.CloverReportColumn@465ea763, 
com.bmuschko.gradle.clover.CloverReportColumn@716b1c17, 
com.bmuschko.gradle.clover.CloverReportColumn@135e0330]' cannot be serialized.
> java.lang.ClassNotFoundException: com.bmuschko.gradle.clover.CloverReportColumn
Alex-Vol-SV commented 5 years ago

I have not seen this one before. It will take some time to investigate.

freshcodemonger commented 5 years ago

Here is the stack if that helps

* Exception is:
org.gradle.api.UncheckedIOException: Unable to store input properties for task ':cloverReport'. Property 'additionalColumns' with value '[com.bmuschko.gradle.clover.CloverReportColumn@1d83a008, com.bmuschko.gradle.clover.CloverReportColumn@62d47126, com.bmuschko.gradle.clover.CloverReportColumn@2d13d16d, com.bmuschko.gradle.clover.CloverReportColumn@745fe8a1, com.bmuschko.gradle.clover.CloverReportColumn@450f255f]' cannot be serialized.
        at org.gradle.api.internal.changedetection.state.CacheBackedTaskHistoryRepository.snapshotTaskInputProperties(CacheBackedTaskHistoryRepository.java:312)
        at org.gradle.api.internal.changedetection.state.CacheBackedTaskHistoryRepository.createExecution(CacheBackedTaskHistoryRepository.java:146)
        at org.gradle.api.internal.changedetection.state.CacheBackedTaskHistoryRepository.access$100(CacheBackedTaskHistoryRepository.java:61)
        at org.gradle.api.internal.changedetection.state.CacheBackedTaskHistoryRepository$1.getCurrentExecution(CacheBackedTaskHistoryRepository.java:111)
        at org.gradle.api.internal.changedetection.changes.DefaultTaskArtifactStateRepository$TaskArtifactStateImpl.getStates(DefaultTaskArtifactStateRepository.java:203)
        at org.gradle.api.internal.changedetection.changes.DefaultTaskArtifactStateRepository$TaskArtifactStateImpl.isUpToDate(DefaultTaskArtifactStateRepository.java:89)
        at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:50)
        at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:54)
        at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:59)
        at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:101)
        at org.gradle.api.internal.tasks.execution.FinalizeInputFilePropertiesTaskExecuter.execute(FinalizeInputFilePropertiesTaskExecuter.java:44)
        at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:88)
        at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:62)
        at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
        at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
        at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
        at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.run(DefaultTaskGraphExecuter.java:248)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:199)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:110)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:241)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:230)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.processTask(DefaultTaskPlanExecutor.java:123)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.access$200(DefaultTaskPlanExecutor.java:79)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:104)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:98)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.execute(DefaultTaskExecutionPlan.java:623)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.executeWithTask(DefaultTaskExecutionPlan.java:578)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.run(DefaultTaskPlanExecutor.java:98)
        at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
        at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
Caused by: org.gradle.api.UncheckedIOException: java.lang.ClassNotFoundException: com.bmuschko.gradle.clover.CloverReportColumn
        at org.gradle.api.internal.changedetection.state.SerializedValueSnapshot.populateClass(SerializedValueSnapshot.java:106)
        at org.gradle.api.internal.changedetection.state.SerializedValueSnapshot.hasSameSerializedValue(SerializedValueSnapshot.java:70)
        at org.gradle.api.internal.changedetection.state.SerializedValueSnapshot.snapshot(SerializedValueSnapshot.java:51)
        at org.gradle.api.internal.changedetection.state.ValueSnapshotter.snapshot(ValueSnapshotter.java:209)
        at org.gradle.api.internal.changedetection.state.CacheBackedTaskHistoryRepository.snapshotTaskInputProperties(CacheBackedTaskHistoryRepository.java:309)
        ... 33 more
Caused by: java.lang.ClassNotFoundException: com.bmuschko.gradle.clover.CloverReportColumn
        at org.gradle.internal.io.ClassLoaderObjectInputStream.resolveClass(ClassLoaderObjectInputStream.java:40)
        at org.gradle.api.internal.changedetection.state.SerializedValueSnapshot.populateClass(SerializedValueSnapshot.java:104)
        ... 37 more

* Get more help at https://help.gradle.org

Deprecated Gradle features were used in this build, making it incompatible with Gradle 5.0.
See https://docs.gradle.org/4.5.1/userguide/command_line_interface.html#sec:command_line_warnings
Alex-Vol-SV commented 5 years ago

This stacktrace helps pinpoint the particular details I need to fix it. I think the newer versions of Gradle try to snapshot the state of input properties deeper than before and cause this issue.

Alex-Vol-SV commented 5 years ago

How do you add the Clover plugin in your projects? This seems to be a classpath issue more than a class serialization issue. The classes are properly serializable for the particular ones involved here.

freshcodemonger commented 5 years ago

I have them extracted to their own gradle include files but that requires calling out the full package names.

import com.bmuschko.gradle.clover.CloverPlugin import com.bmuschko.gradle.clover.GenerateCoverageReportTask

... dependencies { classpath 'com.bmuschko:gradle-clover-plugin:2.2.2' } ...

apply plugin: com.bmuschko.gradle.clover.CloverPlugin

dependencies {
    clover 'org.openclover:clover:4.3.1'
}
Alex-Vol-SV commented 5 years ago

That is probably why it fails. Are you adding the clover plugin in the top level build.gradle classpath or are you adding it in a separate included apply from: 'clover.gradle'?

I know that classpath additions in included gradle scripts are cause for classpath issues of this kind.

Alex-Vol-SV commented 5 years ago

I always make my classpath declarations only in the top build.gradle. I add configuration blocks in separate gradle scripts.

FYI, using buildscript classpath dependencies in included gradle modules will be deprecated and removed in a future version of Gradle as far as I know. It is known to be a common cause for classpath issues like the one you are describing here.

freshcodemonger commented 5 years ago

I didn't know that. I just hacked the gradle stuff together so open for improvements. I'll try to move the classpath dependency to build.gradle and see if that helps. Thank you !

freshcodemonger commented 5 years ago

I worked out the problem. Moving the classpath dependencies wasn't the solution.

Gradle creates a cache directory in your project called .gradle, gradle clean does not clean this.

If you change the columns in the report after running once and rebuild without removing the .gradle directory it will fail. I don't know exactly what output gradle has cached that causes this problem. If you generate a clover report and then change the columns do you not see the same problem? I don't think I have anything other than default gradle caching enabled.

Alex-Vol-SV commented 5 years ago

That is how Gradle works. The task inputs for the previous successfully executed tasks are cached in the .gradle directory in the root of your project. When Gradle needs to check your task for up to date conditions it will attempt to deserialize the cached inputs. At that point the classpath is important and will need to have the plugin classes to allow the deserialization to succeed. You should not need to remove this directory except when plugins change in incompatible ways. This particular class in the plugin has not changed since many iterations ago. So, I doubt what you are having issues with it any of my problem. All I can see from the stacktrace is that the class was not found which signals me to suspect your nested script classpath configuration.

freshcodemonger commented 5 years ago

I agree with you about how gradle works but independent of if you have the classpath dependencies in the main gradle file - as I tried this - re-running after changing a column will not work unless you wipe that .gradle directory. I can't speak to if this is a bug but it seems odd that I'd have to delete the .gradle directory after changing the column configuration in order for the report to work again. Did you give that a try on your end and see if you can run, change the columns, and re-run a build successfully?

Alex-Vol-SV commented 5 years ago

I have to try this myself and find out why it fails. The classnotfound error is a classpath error and there is not much I can do about that. I do not control the classpath Gradle uses for finding the plugin classes. As you see from the stacktrace you provided there is no code stack trace from this plugin in the entire stack so even if I wanted to try I would not have any way to change it.

So, I cannot promise you that I will be able to figure this out easily. It is not within this plugin execution path.

Alex-Vol-SV commented 5 years ago

I can certainly reproduce this easily with a simple project. I suspect this never worked well and had such an issue since I added the feature but as it is an obscure kind of feature not many people use it. If I am correct the problem might be that Gradle cannot handle task inputs with custom task types, it prefers simple primitive types and collections of the same or any Gradle provided types. This might mean that I would have to come up with a metadata representation of the columns when I attach them as task inputs and convert them in the task instead. The behavior implemented right now takes the DSL implementation classes which are correctly serializable and uses them as task inputs in a collection.

freshcodemonger commented 5 years ago

Ok thanks for looking at it and all the work on the plugin! I can work around the issue by just deleting the .gradle directory if I change the column format. It just took a lot of testing to figure out that was the issue. Probably just add some documentation for now and put it at the back of the pile - at least there is a work around.

Alex-Vol-SV commented 5 years ago

You will probably find that this fails if the code is changed and you rebuild the project incrementally. Any time the task cloverGenerateReport or cloverAggregateReports needs to execute the same kind of failure will happen if the task has executed successfully once before. It is standard incremental build issue with Gradle.

I think the solution is actually not hard to implement now that you brought this to my attention. Understanding why this was broken in the first place was the hard part. Now that I can make it happen easily it will not be long before it can be fixed the right way.

freshcodemonger commented 5 years ago

Sounds great ! Thank you !

Alex-Vol-SV commented 5 years ago

Fixed in release 2.2.3