diff --git a/kover-gradle-plugin/api/kover-gradle-plugin.api b/kover-gradle-plugin/api/kover-gradle-plugin.api index efc6b61b..e98820a0 100644 --- a/kover-gradle-plugin/api/kover-gradle-plugin.api +++ b/kover-gradle-plugin/api/kover-gradle-plugin.api @@ -1,132 +1,14 @@ -public final class kotlinx/kover/api/CounterType : java/lang/Enum { - public static fun valueOf (Ljava/lang/String;)Lkotlinx/kover/api/CounterType; - public static fun values ()[Lkotlinx/kover/api/CounterType; -} - -public abstract class kotlinx/kover/api/CoverageEngineVariant { -} - -public final class kotlinx/kover/api/DefaultIntellijEngine { - public static final field INSTANCE Lkotlinx/kover/api/DefaultIntellijEngine; -} - -public final class kotlinx/kover/api/DefaultJacocoEngine { - public static final field INSTANCE Lkotlinx/kover/api/DefaultJacocoEngine; -} - -public final class kotlinx/kover/api/IntellijEngine { - public fun (Ljava/lang/String;)V -} - -public final class kotlinx/kover/api/JacocoEngine { - public fun (Ljava/lang/String;)V -} - -public class kotlinx/kover/api/KoverAnnotationFilter { - public fun ()V -} - -public class kotlinx/kover/api/KoverClassFilter { - public fun ()V -} - -public class kotlinx/kover/api/KoverMergedConfig { - public fun ()V - public final fun enable ()V - public final fun filters (Lkotlin/jvm/functions/Function0;)V - public final fun htmlReport (Lkotlin/jvm/functions/Function0;)V - public final fun verify (Lkotlin/jvm/functions/Function0;)V - public final fun xmlReport (Lkotlin/jvm/functions/Function0;)V -} - -public class kotlinx/kover/api/KoverMergedFilters { - public fun ()V -} - -public class kotlinx/kover/api/KoverMergedHtmlConfig { - public fun ()V -} - -public class kotlinx/kover/api/KoverMergedXmlConfig { - public fun ()V -} - -public final class kotlinx/kover/api/KoverMigrations { - public static final field INSTANCE Lkotlinx/kover/api/KoverMigrations; -} - -public final class kotlinx/kover/api/KoverNames { - public static final field INSTANCE Lkotlinx/kover/api/KoverNames; -} - -public final class kotlinx/kover/api/KoverPaths { - public static final field INSTANCE Lkotlinx/kover/api/KoverPaths; -} - -public class kotlinx/kover/api/KoverProjectConfig { - public fun ()V -} - -public class kotlinx/kover/api/KoverProjectFilters { - public fun ()V -} - -public class kotlinx/kover/api/KoverProjectHtmlConfig { - public fun ()V -} - -public class kotlinx/kover/api/KoverProjectInstrumentation { - public fun ()V -} - -public class kotlinx/kover/api/KoverProjectXmlConfig { - public fun ()V -} - -public class kotlinx/kover/api/KoverProjectsFilter { - public fun ()V -} - -public class kotlinx/kover/api/KoverTaskExtension { - public fun ()V - public final fun getExcludes ()Ljava/util/List; - public final fun getIncludes ()Ljava/util/List; - public final fun getReportFile ()Ljava/lang/Void; - public final fun isDisabled ()Z -} - -public class kotlinx/kover/api/KoverVerifyConfig { - public fun ()V -} - -public final class kotlinx/kover/api/KoverVersions { - public static final field INSTANCE Lkotlinx/kover/api/KoverVersions; -} - -public class kotlinx/kover/api/VerificationBound { - public fun ()V -} - -public class kotlinx/kover/api/VerificationRule { - public fun ()V -} - -public final class kotlinx/kover/api/VerificationTarget : java/lang/Enum { - public static fun valueOf (Ljava/lang/String;)Lkotlinx/kover/api/VerificationTarget; - public static fun values ()[Lkotlinx/kover/api/VerificationTarget; -} - -public final class kotlinx/kover/api/VerificationValueType : java/lang/Enum { - public static fun valueOf (Ljava/lang/String;)Lkotlinx/kover/api/VerificationValueType; - public static fun values ()[Lkotlinx/kover/api/VerificationValueType; -} - public final class kotlinx/kover/gradle/plugin/KoverGradlePlugin : org/gradle/api/Plugin { public fun ()V public synthetic fun apply (Ljava/lang/Object;)V public fun apply (Lorg/gradle/api/Project;)V } +public final class kotlinx/kover/gradle/plugin/commons/NamingKt { + public static final field KOVER_DEPENDENCY_NAME Ljava/lang/String; + public static final field KOVER_PROJECT_EXTENSION_NAME Ljava/lang/String; +} + public final class kotlinx/kover/gradle/plugin/dsl/AggregationType : java/lang/Enum { public static final field COVERED_COUNT Lkotlinx/kover/gradle/plugin/dsl/AggregationType; public static final field COVERED_PERCENTAGE Lkotlinx/kover/gradle/plugin/dsl/AggregationType; @@ -145,55 +27,38 @@ public final class kotlinx/kover/gradle/plugin/dsl/GroupingEntityType : java/lan public static fun values ()[Lkotlinx/kover/gradle/plugin/dsl/GroupingEntityType; } -public final class kotlinx/kover/gradle/plugin/dsl/GroupingEntityType$ALL { - public static final field INSTANCE Lkotlinx/kover/gradle/plugin/dsl/GroupingEntityType$ALL; -} - -public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverBinaryReportConfig { - public abstract fun filters (Lorg/gradle/api/Action;)V +public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverBinaryTaskConfig { + public fun filters (Lorg/gradle/api/Action;)V public abstract fun getFile ()Lorg/gradle/api/file/RegularFileProperty; public abstract fun getOnCheck ()Lorg/gradle/api/provider/Property; } -public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverDefaultReportsConfig : kotlinx/kover/gradle/plugin/dsl/KoverReportsConfig { - public abstract fun mergeWith (Ljava/lang/String;)V +public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverExtension { + public abstract fun getJacocoVersion ()Lorg/gradle/api/provider/Property; + public abstract fun getUseJacoco ()Lorg/gradle/api/provider/Property; + public abstract fun reports (Lorg/gradle/api/Action;)V + public abstract fun useJacoco ()V + public abstract fun useJacoco (Ljava/lang/String;)V + public abstract fun variants (Lorg/gradle/api/Action;)V } -public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverHtmlReportConfig { - public abstract fun filters (Lorg/gradle/api/Action;)V - public abstract fun getCharset ()Ljava/lang/String; - public abstract fun getOnCheck ()Z - public fun getReportDir ()Ljava/lang/Void; - public abstract fun getTitle ()Ljava/lang/String; - public fun overrideFilters (Lkotlin/jvm/functions/Function0;)V - public abstract fun setCharset (Ljava/lang/String;)V - public abstract fun setOnCheck (Z)V - public abstract fun setReportDir (Ljava/io/File;)V - public abstract fun setReportDir (Lorg/gradle/api/provider/Provider;)V - public abstract fun setTitle (Ljava/lang/String;)V -} - -public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverInstrumentationExclusions { - public abstract fun classes (Ljava/lang/Iterable;)V - public abstract fun classes ([Ljava/lang/String;)V - public abstract fun packages (Ljava/lang/Iterable;)V - public abstract fun packages ([Ljava/lang/String;)V +public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverHtmlTaskConfig { + public fun filters (Lorg/gradle/api/Action;)V + public abstract fun getCharset ()Lorg/gradle/api/provider/Property; + public abstract fun getHtmlDir ()Lorg/gradle/api/file/DirectoryProperty; + public abstract fun getOnCheck ()Lorg/gradle/api/provider/Property; + public abstract fun getTitle ()Lorg/gradle/api/provider/Property; + public fun setReportDir (Ljava/lang/Object;)V } -public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverLogReportConfig { - public abstract fun filters (Lorg/gradle/api/Action;)V - public abstract fun getAggregationForGroup ()Lkotlinx/kover/gradle/plugin/dsl/AggregationType; - public abstract fun getCoverageUnits ()Lkotlinx/kover/gradle/plugin/dsl/MetricType; - public abstract fun getFormat ()Ljava/lang/String; - public abstract fun getGroupBy ()Lkotlinx/kover/gradle/plugin/dsl/GroupingEntityType; - public abstract fun getHeader ()Ljava/lang/String; - public abstract fun getOnCheck ()Z - public abstract fun setAggregationForGroup (Lkotlinx/kover/gradle/plugin/dsl/AggregationType;)V - public abstract fun setCoverageUnits (Lkotlinx/kover/gradle/plugin/dsl/MetricType;)V - public abstract fun setFormat (Ljava/lang/String;)V - public abstract fun setGroupBy (Lkotlinx/kover/gradle/plugin/dsl/GroupingEntityType;)V - public abstract fun setHeader (Ljava/lang/String;)V - public abstract fun setOnCheck (Z)V +public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverLogTaskConfig { + public fun filters (Lorg/gradle/api/Action;)V + public abstract fun getAggregationForGroup ()Lorg/gradle/api/provider/Property; + public abstract fun getCoverageUnits ()Lorg/gradle/api/provider/Property; + public abstract fun getFormat ()Lorg/gradle/api/provider/Property; + public abstract fun getGroupBy ()Lorg/gradle/api/provider/Property; + public abstract fun getHeader ()Lorg/gradle/api/provider/Property; + public abstract fun getOnCheck ()Lorg/gradle/api/provider/Property; } public final class kotlinx/kover/gradle/plugin/dsl/KoverNames { @@ -204,13 +69,23 @@ public final class kotlinx/kover/gradle/plugin/dsl/KoverNames { public static final field DEFAULT_XML_REPORT_NAME Ljava/lang/String; public static final field DEPENDENCY_CONFIGURATION_NAME Ljava/lang/String; public static final field INSTANCE Lkotlinx/kover/gradle/plugin/dsl/KoverNames; + public static final field PLUGIN_ID Ljava/lang/String; public static final field PROJECT_EXTENSION_NAME Ljava/lang/String; public static final field REPORT_EXTENSION_NAME Ljava/lang/String; - public final fun androidBinaryReport (Ljava/lang/String;)Ljava/lang/String; - public final fun androidHtmlReport (Ljava/lang/String;)Ljava/lang/String; - public final fun androidLog (Ljava/lang/String;)Ljava/lang/String; - public final fun androidVerify (Ljava/lang/String;)Ljava/lang/String; - public final fun androidXmlReport (Ljava/lang/String;)Ljava/lang/String; + public final fun getConfigurationName ()Ljava/lang/String; + public final fun getExtensionName ()Ljava/lang/String; + public final fun getJvmVariantName ()Ljava/lang/String; + public final fun getKoverBinaryReportName ()Ljava/lang/String; + public final fun getKoverHtmlReportName ()Ljava/lang/String; + public final fun getKoverLogName ()Ljava/lang/String; + public final fun getKoverVerifyName ()Ljava/lang/String; + public final fun getKoverXmlReportName ()Ljava/lang/String; + public final fun getPluginId ()Ljava/lang/String; + public final fun koverBinaryReportName (Ljava/lang/String;)Ljava/lang/String; + public final fun koverHtmlReportName (Ljava/lang/String;)Ljava/lang/String; + public final fun koverLogName (Ljava/lang/String;)Ljava/lang/String; + public final fun koverVerifyName (Ljava/lang/String;)Ljava/lang/String; + public final fun koverXmlReportName (Ljava/lang/String;)Ljava/lang/String; } public final class kotlinx/kover/gradle/plugin/dsl/KoverNamesKt { @@ -228,60 +103,76 @@ public final class kotlinx/kover/gradle/plugin/dsl/KoverNamesKt { public static final fun koverAndroidXmlReportName (Lorg/gradle/api/tasks/TaskContainer;Ljava/lang/String;)Ljava/lang/String; } -public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverProjectExtension { - public abstract fun disable ()V - public abstract fun excludeInstrumentation (Lorg/gradle/api/Action;)V - public abstract fun excludeJavaCode ()V - public abstract fun excludeSourceSets (Lorg/gradle/api/Action;)V - public abstract fun excludeTests (Lorg/gradle/api/Action;)V - public fun filters (Lkotlin/jvm/functions/Function0;)V - public fun getEngine ()Ljava/lang/Void; - public fun htmlReport (Lkotlin/jvm/functions/Function0;)V - public fun instrumentation (Lkotlin/jvm/functions/Function1;)V - public fun isDisabled ()Z - public fun setEngine (Ljava/lang/Void;)V - public abstract fun useJacoco ()V - public abstract fun useJacoco (Ljava/lang/String;)V - public fun verify (Lkotlin/jvm/functions/Function0;)V - public fun xmlReport (Lkotlin/jvm/functions/Function0;)V -} - -public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverReportExtension { - public abstract fun androidReports (Ljava/lang/String;Lorg/gradle/api/Action;)V - public abstract fun defaults (Lorg/gradle/api/Action;)V +public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverReportConfig { + public fun androidReports (Ljava/lang/String;Lorg/gradle/api/Action;)V + public fun defaults (Lorg/gradle/api/Action;)V public abstract fun filters (Lorg/gradle/api/Action;)V + public abstract fun total (Lorg/gradle/api/Action;)V + public abstract fun variant (Ljava/lang/String;Lorg/gradle/api/Action;)V public abstract fun verify (Lorg/gradle/api/Action;)V } public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverReportFilter { + public fun androidGeneratedClasses ()V public abstract fun annotatedBy ([Ljava/lang/String;)V + public abstract fun annotatedBy ([Lorg/gradle/api/provider/Provider;)V public abstract fun classes (Ljava/lang/Iterable;)V + public abstract fun classes (Lorg/gradle/api/provider/Provider;)V public abstract fun classes ([Ljava/lang/String;)V + public abstract fun classes ([Lorg/gradle/api/provider/Provider;)V public abstract fun packages (Ljava/lang/Iterable;)V + public abstract fun packages (Lorg/gradle/api/provider/Provider;)V public abstract fun packages ([Ljava/lang/String;)V + public abstract fun packages ([Lorg/gradle/api/provider/Provider;)V } -public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverReportFilters { - public fun classes (Lkotlin/jvm/functions/Function0;)V +public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverReportFiltersConfig { public abstract fun excludes (Lorg/gradle/api/Action;)V - public fun getExcludes ()Ljava/util/List; - public fun getIncludes ()Ljava/util/List; public abstract fun includes (Lorg/gradle/api/Action;)V } -public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverReportsConfig { +public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverReportSetConfig { public abstract fun binary (Lorg/gradle/api/Action;)V public abstract fun filters (Lorg/gradle/api/Action;)V + public abstract fun filtersAppend (Lorg/gradle/api/Action;)V public abstract fun html (Lorg/gradle/api/Action;)V public abstract fun log (Lorg/gradle/api/Action;)V public abstract fun verify (Lorg/gradle/api/Action;)V + public abstract fun verifyAppend (Lorg/gradle/api/Action;)V public abstract fun xml (Lorg/gradle/api/Action;)V } -public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverTestsExclusions { - public fun getExcludeTasks ()Ljava/util/List; - public abstract fun tasks (Ljava/lang/Iterable;)V - public abstract fun tasks ([Ljava/lang/String;)V +public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverVariantConfig { + public abstract fun instrumentation (Lorg/gradle/api/Action;)V + public abstract fun sources (Lorg/gradle/api/Action;)V + public abstract fun testTasks (Lorg/gradle/api/Action;)V +} + +public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverVariantCreateConfig : kotlinx/kover/gradle/plugin/dsl/KoverVariantConfig { + public abstract fun add ([Ljava/lang/String;Z)V + public static synthetic fun add$default (Lkotlinx/kover/gradle/plugin/dsl/KoverVariantCreateConfig;[Ljava/lang/String;ZILjava/lang/Object;)V + public abstract fun addWithDependencies ([Ljava/lang/String;Z)V + public static synthetic fun addWithDependencies$default (Lkotlinx/kover/gradle/plugin/dsl/KoverVariantCreateConfig;[Ljava/lang/String;ZILjava/lang/Object;)V +} + +public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverVariantInstrumentation { + public abstract fun getExcludeAll ()Lorg/gradle/api/provider/Property; + public abstract fun getExcludedClasses ()Lorg/gradle/api/provider/SetProperty; +} + +public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverVariantSources { + public abstract fun getExcludeJava ()Lorg/gradle/api/provider/Property; + public abstract fun getExcludedSourceSets ()Lorg/gradle/api/provider/SetProperty; +} + +public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverVariantTestTasks { + public abstract fun getExcluded ()Lorg/gradle/api/provider/SetProperty; +} + +public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverVariantsRootConfig : kotlinx/kover/gradle/plugin/dsl/KoverVariantConfig { + public abstract fun create (Ljava/lang/String;Lorg/gradle/api/Action;)V + public abstract fun provided (Ljava/lang/String;Lorg/gradle/api/Action;)V + public abstract fun total (Lorg/gradle/api/Action;)V } public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverVerificationRulesConfig { @@ -290,45 +181,43 @@ public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverVerificatio } public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverVerifyBound { - public abstract fun getAggregation ()Lkotlinx/kover/gradle/plugin/dsl/AggregationType; - public fun getCounter ()Lkotlinx/kover/gradle/plugin/dsl/MetricType; - public abstract fun getMaxValue ()Ljava/lang/Integer; - public abstract fun getMetric ()Lkotlinx/kover/gradle/plugin/dsl/MetricType; - public abstract fun getMinValue ()Ljava/lang/Integer; - public fun getValueType ()Lkotlinx/kover/gradle/plugin/dsl/AggregationType; - public abstract fun setAggregation (Lkotlinx/kover/gradle/plugin/dsl/AggregationType;)V - public fun setCounter (Lkotlinx/kover/gradle/plugin/dsl/MetricType;)V - public abstract fun setMaxValue (Ljava/lang/Integer;)V - public abstract fun setMetric (Lkotlinx/kover/gradle/plugin/dsl/MetricType;)V - public abstract fun setMinValue (Ljava/lang/Integer;)V - public fun setValueType (Lkotlinx/kover/gradle/plugin/dsl/AggregationType;)V -} - -public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverVerifyReportConfig : kotlinx/kover/gradle/plugin/dsl/KoverVerificationRulesConfig { - public abstract fun getOnCheck ()Z - public abstract fun setOnCheck (Z)V + public fun getAggregation ()Lkotlinx/kover/gradle/plugin/dsl/AggregationType; + public abstract fun getAggregationForGroup ()Lorg/gradle/api/provider/Property; + public abstract fun getCoverageUnits ()Lorg/gradle/api/provider/Property; + public abstract fun getMax ()Lorg/gradle/api/provider/Property; + public fun getMaxValue ()Ljava/lang/Integer; + public fun getMetric ()Lkotlinx/kover/gradle/plugin/dsl/MetricType; + public abstract fun getMin ()Lorg/gradle/api/provider/Property; + public fun getMinValue ()Ljava/lang/Integer; + public fun setAggregation (Lkotlinx/kover/gradle/plugin/dsl/AggregationType;)V + public fun setMaxValue (Ljava/lang/Integer;)V + public fun setMetric (Lkotlinx/kover/gradle/plugin/dsl/MetricType;)V + public fun setMinValue (Ljava/lang/Integer;)V } public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverVerifyRule { public abstract fun bound (IILkotlinx/kover/gradle/plugin/dsl/MetricType;Lkotlinx/kover/gradle/plugin/dsl/AggregationType;)V public abstract fun bound (Lorg/gradle/api/Action;)V public static synthetic fun bound$default (Lkotlinx/kover/gradle/plugin/dsl/KoverVerifyRule;IILkotlinx/kover/gradle/plugin/dsl/MetricType;Lkotlinx/kover/gradle/plugin/dsl/AggregationType;ILjava/lang/Object;)V - public abstract fun filters (Lorg/gradle/api/Action;)V - public abstract fun getEntity ()Lkotlinx/kover/gradle/plugin/dsl/GroupingEntityType; - public abstract fun getName ()Ljava/lang/String; - public fun getTarget ()Lkotlinx/kover/gradle/plugin/dsl/GroupingEntityType; - public abstract fun isEnabled ()Z + public fun filters (Lorg/gradle/api/Action;)V + public abstract fun getDisabled ()Lorg/gradle/api/provider/Property; + public fun getEntity ()Lkotlinx/kover/gradle/plugin/dsl/GroupingEntityType; + public abstract fun getGroupBy ()Lorg/gradle/api/provider/Property; + public fun isEnabled ()Z public abstract fun maxBound (I)V public abstract fun maxBound (ILkotlinx/kover/gradle/plugin/dsl/MetricType;Lkotlinx/kover/gradle/plugin/dsl/AggregationType;)V + public abstract fun maxBound (Lorg/gradle/api/provider/Provider;)V public static synthetic fun maxBound$default (Lkotlinx/kover/gradle/plugin/dsl/KoverVerifyRule;ILkotlinx/kover/gradle/plugin/dsl/MetricType;Lkotlinx/kover/gradle/plugin/dsl/AggregationType;ILjava/lang/Object;)V public abstract fun minBound (I)V public abstract fun minBound (ILkotlinx/kover/gradle/plugin/dsl/MetricType;Lkotlinx/kover/gradle/plugin/dsl/AggregationType;)V + public abstract fun minBound (Lorg/gradle/api/provider/Provider;)V public static synthetic fun minBound$default (Lkotlinx/kover/gradle/plugin/dsl/KoverVerifyRule;ILkotlinx/kover/gradle/plugin/dsl/MetricType;Lkotlinx/kover/gradle/plugin/dsl/AggregationType;ILjava/lang/Object;)V - public fun overrideClassFilter (Lkotlin/jvm/functions/Function0;)V - public abstract fun setEnabled (Z)V - public abstract fun setEntity (Lkotlinx/kover/gradle/plugin/dsl/GroupingEntityType;)V - public abstract fun setName (Ljava/lang/String;)V - public fun setTarget (Lkotlinx/kover/gradle/plugin/dsl/GroupingEntityType;)V + public fun setEnabled (Z)V + public fun setEntity (Lkotlinx/kover/gradle/plugin/dsl/GroupingEntityType;)V +} + +public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverVerifyTaskConfig : kotlinx/kover/gradle/plugin/dsl/KoverVerificationRulesConfig { + public abstract fun getOnCheck ()Lorg/gradle/api/provider/Property; } public final class kotlinx/kover/gradle/plugin/dsl/KoverVersions { @@ -338,15 +227,12 @@ public final class kotlinx/kover/gradle/plugin/dsl/KoverVersions { public static final field MINIMUM_GRADLE_VERSION Ljava/lang/String; } -public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverXmlReportConfig { - public abstract fun filters (Lorg/gradle/api/Action;)V - public abstract fun getOnCheck ()Z - public fun getReportFile ()Ljava/lang/Void; +public abstract interface class kotlinx/kover/gradle/plugin/dsl/KoverXmlTaskConfig { + public fun filters (Lorg/gradle/api/Action;)V + public abstract fun getOnCheck ()Lorg/gradle/api/provider/Property; public abstract fun getTitle ()Lorg/gradle/api/provider/Property; - public fun overrideFilters (Lkotlin/jvm/functions/Function0;)V - public abstract fun setOnCheck (Z)V - public abstract fun setReportFile (Ljava/io/File;)V - public abstract fun setReportFile (Lorg/gradle/api/provider/Provider;)V + public abstract fun getXmlFile ()Lorg/gradle/api/file/RegularFileProperty; + public fun setReportFile (Ljava/lang/Object;)V } public final class kotlinx/kover/gradle/plugin/dsl/MetricType : java/lang/Enum { @@ -357,8 +243,21 @@ public final class kotlinx/kover/gradle/plugin/dsl/MetricType : java/lang/Enum { public static fun values ()[Lkotlinx/kover/gradle/plugin/dsl/MetricType; } -public abstract interface class kotlinx/kover/gradle/plugin/dsl/SourceSetsExclusions { - public abstract fun names (Ljava/lang/Iterable;)V - public abstract fun names ([Ljava/lang/String;)V +public abstract interface class kotlinx/kover/gradle/plugin/dsl/tasks/KoverBinaryReport : kotlinx/kover/gradle/plugin/dsl/tasks/KoverReport { +} + +public abstract interface class kotlinx/kover/gradle/plugin/dsl/tasks/KoverHtmlReport : kotlinx/kover/gradle/plugin/dsl/tasks/KoverReport { +} + +public abstract interface class kotlinx/kover/gradle/plugin/dsl/tasks/KoverLogReport : kotlinx/kover/gradle/plugin/dsl/tasks/KoverReport { +} + +public abstract interface class kotlinx/kover/gradle/plugin/dsl/tasks/KoverReport { +} + +public abstract interface class kotlinx/kover/gradle/plugin/dsl/tasks/KoverVerifyReport : kotlinx/kover/gradle/plugin/dsl/tasks/KoverReport { +} + +public abstract interface class kotlinx/kover/gradle/plugin/dsl/tasks/KoverXmlReport : kotlinx/kover/gradle/plugin/dsl/tasks/KoverReport { } diff --git a/kover-gradle-plugin/docs/configuring.md b/kover-gradle-plugin/docs/configuring.md index a7194e84..c417dc6e 100644 --- a/kover-gradle-plugin/docs/configuring.md +++ b/kover-gradle-plugin/docs/configuring.md @@ -95,10 +95,10 @@ koverReport { onCheck = false // XML report title (the location depends on the library) - title.set("Custom XML report title") + title = "Custom XML report title" // XML report file - setReportFile(layout.buildDirectory.file("my-project-report/result.xml")) + xmlFile = layout.buildDirectory.file("my-project-report/result.xml") // overriding filters only for the XML report filters { @@ -134,7 +134,7 @@ koverReport { onCheck = false // directory for HTML report - setReportDir(layout.buildDirectory.dir("my-project-report/html-result")) + htmlDir = layout.buildDirectory.dir("my-project-report/html-result") // overriding filters only for the HTML report filters { @@ -326,7 +326,7 @@ koverReport { onCheck = false // directory for HTML report - setReportDir(layout.buildDirectory.dir("my-project-report/html-result")) + htmlDir = layout.buildDirectory.dir("my-project-report/html-result") // overriding filters only for the HTML report filters { diff --git a/kover-gradle-plugin/docs/migrations/migration-to-0.7.0.md b/kover-gradle-plugin/docs/migrations/migration-to-0.7.0.md index a5f7d2a5..5c74e35f 100644 --- a/kover-gradle-plugin/docs/migrations/migration-to-0.7.0.md +++ b/kover-gradle-plugin/docs/migrations/migration-to-0.7.0.md @@ -159,7 +159,7 @@ koverReport { onCheck = false // directory for HTML report - setReportDir(layout.buildDirectory.dir("my-project-report/html-result")) + htmlDir = layout.buildDirectory.dir("my-project-report/html-result") // overriding filters only for the HTML report filters { diff --git a/kover-gradle-plugin/docs/migrations/migration-to-0.8.0.md b/kover-gradle-plugin/docs/migrations/migration-to-0.8.0.md new file mode 100644 index 00000000..694f4ce7 --- /dev/null +++ b/kover-gradle-plugin/docs/migrations/migration-to-0.8.0.md @@ -0,0 +1,228 @@ +# Kover migration guide from 0.7.x to 0.8.0 + +## Conceptual changes +### Single kover project extension +The `0.7.x` version used two project extensions named `kover` and `koverReports`. +This was confusing and made it difficult to search through documentation or examples. + +Since `0.8.0`, there is only one `kover` extension left. +All the settings that used to be in `koverReports` are now located in +```kotlin +kover { + reports { + // setting from old koverReports extension + } +} +``` +The settings from the old `kover` are now either left as is, or moved to `kover { variants { ... } }` block. + +Following configuration +```kotlin +kover { + // exclude classes compiled by Java compiler from all reports + excludeJavaCode() + + excludeTests { + // exclude Gradle test tasks + tasks(testTasks) + } + excludeInstrumentation { + // exclude classes from instrumentation + classes(unistrumentedClasses) + } + excludeSourceSets { + // exclude source classes of specified source sets from all reports + names(excludedSourceSet) + } +} +``` + +Since `0.8.0` looks like: +```kotlin +kover { + variants { + testTasks { + /* exclude Gradle test tasks */ + excluded.addAll(testTasks) + } + + instrumentation { + // exclude classes from instrumentation + excludedClasses.addAll(unistrumentedClasses) + } + + sources { + // exclude classes compiled by Java compiler from all reports + excludeJava = true + + // exclude source classes of specified source sets from all reports + excludedSourceSets.addAll(excludedSourceSet) + } + } +} +``` + +### The concept of default reports has been removed +In the `0.7.x` version, there were tasks named `koverHtmlReport`, `koverXmlReport`, `koverVerify`, `koverLog`, `koverBinaryReport`, +which included only classes and tests for JVM target, and it was also possible to add tests from classes of specified Android build variant using +```kotlin +koverReport { + defaults { + mergeWith("buildVariant") + } +} +``` + +Starting with the `0.8.0` version, these features have been removed. +However, to implement similar capabilities, the concepts of _Total reports_ and _Custom reports_ were introduced. + +### Total reports +Since `0.8.0` tasks `koverHtmlReport`, `koverXmlReport`, `koverVerify`, `koverLog`, `koverBinaryReport` are designed +to generate reports on all classes and tests of the project. +For JVM projects, nothing changes compared to the `0.7.x` version, however, for Android projects, +running one of these tasks will result running tests for all build variants present in the project. + +### Custom reports variants +In the `0.7.x` version, it was allowed to merge a report for several Android build variants only into default reports, +it was not possible to create several combinations with the arbitrary inclusion of different build variants. + +Since `0.8.0` in order to merge several Android build variants, you need to create a custom reports variant. + +The newly created variant is initially empty, in order for classes to appear in the report and tests to be executed +when generating it, need to add existing variants provided from Kotlin targets to it: it can be a `jvm` variant, or any of an Android build variant. +```kotlin +kover { + variants { + create("custom") { + add("jvm") + add("release") + } + } +} +``` + +Creating a variant with a name `custom` will add tasks named `koverHtmlReportCustom`, `koverXmlReportCustom`, `koverVerifyCustom`, `koverLogCustom`, `koverBinaryReportCustom`. + +### More about the reports variant +A reports variant is a set of information used to generate a report, namely: +project classes, a list of Gradle test tasks, classes that need to be excluded from instrumentation. + +There are several types of report variants: +- total variant (have no special name), always created +- variant for classes in JVM target (named `jvm`), created by Kover Gradle Plugin if JVM target is present +- variants for the Android build variant (the name matches the name of the build variant), created by Kover Gradle Plugin if there are Android build variants in the project +- custom variants (the name is specified when creating), declared in the build script configuration + +For each variant, a set of tasks is created in the project to generate reports on the information contained in it. + +The names are generated according to the following rule: +`koverHtmlReport`, `koverXmlReport`, `koverVerify`, `koverLog`, `koverBinaryReport` + + +The reports variant can be used in another project to create a merged reports. +To do this, in another project, which we will call the merging project, we will specify a dependency on the project from which we want to import classes and Gradle test tasks. +```kotlin +dependencies { + kover(project(":lib")) +} +``` +As a result, if you run the `:koverHtmlReport` task, it will run all the tests from merging project and `:lib` project +and generate a report for all classes from merging project and `:lib` project. + +However, if you call a task for a named variant, for example `:koverHtmlReportRelease`, then it will run tests for `release` variant from merging project and `:lib` project +and generate a report for classes of `release` variant from merging project and `:lib` project. + +At the same time, it is recommended that variant `release` is also present in the `:lib` project, however, +for technical reasons, such a check may not be implemented in `0.8.0` and subsequent versions. + +### The format-specific reports filters has been removed +Previously, it was acceptable to override filters for each report format (XML, HTML, etc.), like +```kotlin +koverReport { + filters { + // top-level filters + } + defaults { + filters { + // filters for all default reports + } + + html { + filters { + // filters for HTML default report + } + } + } +} +``` + +This is very confusing, because there are 3 levels in which you can write `filters { }` also it is difficult for users to understand exactly where to write filters - which leads to the fact of copy-paste the same filters are specified for all types of reports (inside xml, html, verify blocks). + +Since `0.8.0`, specifying filters for specific type of report (HTML or XML) is deprecated. It is now possible to create custom variants of reports, if it is necessary to generate a report with a different set of filters. +In this case, it is better to create a new custom variant and override the filters in it: +```kotlin +kover { + variants { + create("customJvm") { + add("jvm") + } + } + + reports { + variant("customJvm") { + filters { + // filters only for customJvm report set + } + } + } +} + +``` + + +### Added the ability of lazy configuration +In some cases, the values are not known at the time of configuration, for example, when using convention plugins and extensions in them. + +To do this, overloads have been added that allow to work with the value providers (Gradle `Provider` type). + +```kotlin +kover { + reports { + filters { + exludes { + classes(classProvider) + } + } + + verify { + rule { + bound { + min = minValueProvider + } + // or + minBound(minValueProvider) + } + } + } +} + +``` + + +### Added public interfaces for Kover tasks + +Now all Kover report tasks implement interface `kotlinx.kover.gradle.plugin.dsl.tasks.KoverReport`. + +Also, a separate interface has been created for each report type: + - `kotlinx.kover.gradle.plugin.dsl.tasks.KoverXmlReport` + - `kotlinx.kover.gradle.plugin.dsl.tasks.KoverHtmlReport` + - `kotlinx.kover.gradle.plugin.dsl.tasks.KoverLogReport` + - `kotlinx.kover.gradle.plugin.dsl.tasks.KoverVerifyReport` + - `kotlinx.kover.gradle.plugin.dsl.tasks.KoverBinaryReport` + +Adding public interfaces will allow to filter Kover tasks, for example, to specify them in dependencies +```kotlin +tasks.check { + dependsOn(tasks.matching { it is KoverHtmlReport }) +} +``` \ No newline at end of file diff --git a/kover-gradle-plugin/examples/android/dynamic/dyn/build.gradle.kts b/kover-gradle-plugin/examples/android/dynamic/dyn/build.gradle.kts index a90f1977..ebfe08f0 100644 --- a/kover-gradle-plugin/examples/android/dynamic/dyn/build.gradle.kts +++ b/kover-gradle-plugin/examples/android/dynamic/dyn/build.gradle.kts @@ -40,39 +40,29 @@ dependencies { kover(project(":app")) } - -koverReport { - // filters for all report types of all build variants - filters { - excludes { - classes( - "*Fragment", - "*Fragment\$*", - "*Activity", - "*Activity\$*", - "*.databinding.*", - "*.BuildConfig" - ) - } - } - - androidReports("release") { - // filters for all report types only of 'release' build type +kover { + reports { + // filters for all report types of all build variants filters { excludes { - classes( - "*Fragment", - "*Fragment\$*", - "*Activity", - "*Activity\$*", - "*.databinding.*", - "*.BuildConfig", + androidGeneratedClasses() + } + } - // excludes debug classes - "*.DebugUtil" - ) + variant("release") { + // filters for all report types only of 'release' build type + filters { + excludes { + androidGeneratedClasses() + + classes( + // excludes debug classes + "*.DebugUtil" + ) + } } } } + } diff --git a/kover-gradle-plugin/examples/android/flavors/app/build.gradle.kts b/kover-gradle-plugin/examples/android/flavors/app/build.gradle.kts index 58c7ebd8..8fd2e3f8 100644 --- a/kover-gradle-plugin/examples/android/flavors/app/build.gradle.kts +++ b/kover-gradle-plugin/examples/android/flavors/app/build.gradle.kts @@ -98,44 +98,36 @@ dependencies { } -koverReport { - // filters for all report types of all build variants - filters { - excludes { - classes( - "*Fragment", - "*Fragment\$*", - "*Activity", - "*Activity\$*", - "*.databinding.*", - "*.BuildConfig" - ) +kover { + variants { + create("custom") { + /** + * Tests, sources, classes, and compilation tasks of the 'app1AppDebug' build variant will be included in the report variant `custom`. + * Thus, information from the 'app1AppDebug' variant will be included in the 'custom' report for this project and any project that specifies this project as a dependency. + */ + addWithDependencies("app1AppDebug") } } - defaults { - /** - * Tests, sources, classes, and compilation tasks of the 'debug' build variant will be included in the default report. - * Thus, information from the 'app1AppDebug' variant will be included in the default report for this project and any project that specifies this project as a dependency. - */ - mergeWith("app1AppDebug") - } - - androidReports("app1AppRelease") { - // filters for all report types only of 'app1AppRelease' build variant + reports { + // filters for all report types of all build variants filters { excludes { - classes( - "*Fragment", - "*Fragment\$*", - "*Activity", - "*Activity\$*", - "*.databinding.*", - "*.BuildConfig", - - // excludes debug classes - "*.DebugUtil" - ) + androidGeneratedClasses() + } + } + + variant("app1AppRelease") { + // filters for all report types only of 'app1AppRelease' build variant + filters { + excludes { + androidGeneratedClasses() + + classes( + // excludes debug classes + "*.DebugUtil" + ) + } } } } diff --git a/kover-gradle-plugin/examples/android/flavors/lib/build.gradle.kts b/kover-gradle-plugin/examples/android/flavors/lib/build.gradle.kts index 634103d9..429625be 100644 --- a/kover-gradle-plugin/examples/android/flavors/lib/build.gradle.kts +++ b/kover-gradle-plugin/examples/android/flavors/lib/build.gradle.kts @@ -58,3 +58,9 @@ dependencies { implementation("androidx.appcompat:appcompat:1.5.0") testImplementation("junit:junit:4.13.2") } + +kover { + variants { + create("custom") {} + } +} diff --git a/kover-gradle-plugin/examples/android/minimal_groovy/app/build.gradle b/kover-gradle-plugin/examples/android/minimal_groovy/app/build.gradle index d3d963b3..2df7ced4 100644 --- a/kover-gradle-plugin/examples/android/minimal_groovy/app/build.gradle +++ b/kover-gradle-plugin/examples/android/minimal_groovy/app/build.gradle @@ -43,38 +43,29 @@ dependencies { testImplementation 'junit:junit:4.13.2' } -koverReport { - // filters for all report types of all build variants - filters { - excludes { - classes( - "*Fragment", - "*Fragment\$*", - "*Activity", - "*Activity\$*", - "*.databinding.*", - "*.BuildConfig" - ) - } - } - - androidReports("release") { - // filters for all report types only of 'release' build type +kover { + reports { + // filters for all report types of all build variants filters { excludes { - classes( - "*Fragment", - "*Fragment\$*", - "*Activity", - "*Activity\$*", - "*.databinding.*", - "*.BuildConfig", + androidGeneratedClasses() + } + } + + variant("release") { + // filters for all report types only of 'release' build type + filters { + excludes { + androidGeneratedClasses() - // excludes debug classes - "*.DebugUtil" - ) + classes( + // excludes debug classes + "*.DebugUtil" + ) + } } } } + } diff --git a/kover-gradle-plugin/examples/android/minimal_kts/app/build.gradle.kts b/kover-gradle-plugin/examples/android/minimal_kts/app/build.gradle.kts index 313ffbe5..307b8b6e 100644 --- a/kover-gradle-plugin/examples/android/minimal_kts/app/build.gradle.kts +++ b/kover-gradle-plugin/examples/android/minimal_kts/app/build.gradle.kts @@ -43,38 +43,27 @@ dependencies { testImplementation("junit:junit:4.13.2") } -koverReport { - // filters for all report types of all build variants - filters { - excludes { - classes( - "*Fragment", - "*Fragment\$*", - "*Activity", - "*Activity\$*", - "*.databinding.*", - "*.BuildConfig" - ) - } - } - - androidReports("release") { - // filters for all report types only of 'release' build type +kover { + reports { + // filters for all report types of all build variants filters { excludes { - classes( - "*Fragment", - "*Fragment\$*", - "*Activity", - "*Activity\$*", - "*.databinding.*", - "*.BuildConfig", + androidGeneratedClasses() + } + } + variant("release") { + // filters for all report types only of 'release' build type + filters { + excludes { + androidGeneratedClasses() + classes( // excludes debug classes "*.DebugUtil" - ) + ) + } } } - } + } } diff --git a/kover-gradle-plugin/examples/android/multiplatform/app/build.gradle.kts b/kover-gradle-plugin/examples/android/multiplatform/app/build.gradle.kts index df5f5c62..0a11849c 100644 --- a/kover-gradle-plugin/examples/android/multiplatform/app/build.gradle.kts +++ b/kover-gradle-plugin/examples/android/multiplatform/app/build.gradle.kts @@ -74,48 +74,40 @@ dependencies { kover(project(":lib")) } -koverReport { - // filters for all report types of all build variants - filters { - excludes { - classes( - "*Fragment", - "*Fragment\$*", - "*Activity", - "*Activity\$*", - "*.databinding.*", - "*.BuildConfig" - ) - } - } +kover { - defaults { - /** - * Tests, sources, classes, and compilation tasks of the 'debug' build variant will be included in the default reports. - * Thus, information from the 'debug' variant will be included in the default report for this project and any project that specifies this project as a dependency. - * - * Since the report already contains classes from the JVM target, they will be supplemented with classes from 'debug' build variant of Android target. - */ - mergeWith("debug") + variants { + create("custom") { + + add("jvm") + /** + * Tests, sources, classes, and compilation tasks of the 'debug' build variant will be included in the report variant `custom`. + * Thus, information from the 'debug' variant will be included in the `custom` report for this project and any project that specifies this project as a dependency. + */ + addWithDependencies("debug") + } } - androidReports("release") { - // filters for all report types only of 'release' build type + reports { + // filters for all report types of all build variants filters { excludes { - classes( - "*Fragment", - "*Fragment\$*", - "*Activity", - "*Activity\$*", - "*.databinding.*", - "*.BuildConfig", + androidGeneratedClasses() + } + } + variant("release") { + // filters for all report types only of 'release' build type + filters { + excludes { + androidGeneratedClasses() + classes( // excludes debug classes "*.DebugUtil" - ) + ) + } } } - } + } } diff --git a/kover-gradle-plugin/examples/android/multiplatform/build.gradle.kts b/kover-gradle-plugin/examples/android/multiplatform/build.gradle.kts index c8708c48..750f9dc7 100644 --- a/kover-gradle-plugin/examples/android/multiplatform/build.gradle.kts +++ b/kover-gradle-plugin/examples/android/multiplatform/build.gradle.kts @@ -8,3 +8,9 @@ plugins { dependencies { kover(project(":app")) } + +kover { + variants { + create("custom") { } + } +} \ No newline at end of file diff --git a/kover-gradle-plugin/examples/android/multiplatform/lib/build.gradle.kts b/kover-gradle-plugin/examples/android/multiplatform/lib/build.gradle.kts index 16ccdaf6..fd2179e3 100644 --- a/kover-gradle-plugin/examples/android/multiplatform/lib/build.gradle.kts +++ b/kover-gradle-plugin/examples/android/multiplatform/lib/build.gradle.kts @@ -37,4 +37,9 @@ dependencies { testImplementation("junit:junit:4.13.2") } +kover { + variants { + create("custom") { } + } +} diff --git a/kover-gradle-plugin/examples/android/multiproject/app/build.gradle.kts b/kover-gradle-plugin/examples/android/multiproject/app/build.gradle.kts index a7014307..14605d60 100644 --- a/kover-gradle-plugin/examples/android/multiproject/app/build.gradle.kts +++ b/kover-gradle-plugin/examples/android/multiproject/app/build.gradle.kts @@ -53,38 +53,27 @@ dependencies { } -koverReport { - // filters for all report types of all build variants - filters { - excludes { - classes( - "*Fragment", - "*Fragment\$*", - "*Activity", - "*Activity\$*", - "*.databinding.*", - "*.BuildConfig" - ) - } - } - - androidReports("release") { - // filters for all report types only of 'release' build type +kover { + reports { + // filters for all report types of all build variants filters { excludes { - classes( - "*Fragment", - "*Fragment\$*", - "*Activity", - "*Activity\$*", - "*.databinding.*", - "*.BuildConfig", + androidGeneratedClasses() + } + } + variant("release") { + // filters for all report types only of 'release' build type + filters { + excludes { + androidGeneratedClasses() + classes( // excludes debug classes "*.DebugUtil" - ) + ) + } } } - } + } } diff --git a/kover-gradle-plugin/examples/android/variantUsage/app/build.gradle.kts b/kover-gradle-plugin/examples/android/variantUsage/app/build.gradle.kts index f4c9dec3..2b2327ed 100644 --- a/kover-gradle-plugin/examples/android/variantUsage/app/build.gradle.kts +++ b/kover-gradle-plugin/examples/android/variantUsage/app/build.gradle.kts @@ -54,45 +54,38 @@ dependencies { kover(project(":lib")) } -koverReport { - // filters for all report types of all build variants - filters { - excludes { - classes( - "*Fragment", - "*Fragment\$*", - "*Activity", - "*Activity\$*", - "*.databinding.*", - "*.BuildConfig" - ) +kover { + variants { + create("custom") { + /** + * Tests, sources, classes, and compilation tasks of the 'debug' build variant will be included in the report variant `custom`. + * Thus, information from the 'debug' variant will be included in the `custom` report for this project and any project that specifies this project as a dependency. + */ + addWithDependencies("debug") } } - defaults { - /** - * Tests, sources, classes, and compilation tasks of the 'debug' build variant will be included in the default reports. - * Thus, information from the 'debug' variant will be included in the default report for this project and any project that specifies this project as a dependency. - */ - mergeWith("debug") - } - - androidReports("release") { - // filters for all report types only of 'release' build type + reports { + // filters for all report types of all build variants filters { excludes { - classes( - "*Fragment", - "*Fragment\$*", - "*Activity", - "*Activity\$*", - "*.databinding.*", - "*.BuildConfig", + androidGeneratedClasses() + } + } + + variant("release") { + // filters for all report types only of 'release' build type + filters { + excludes { + androidGeneratedClasses() + classes( // excludes debug classes "*.DebugUtil" - ) + ) + } } } + } } diff --git a/kover-gradle-plugin/examples/android/variantUsage/lib/build.gradle.kts b/kover-gradle-plugin/examples/android/variantUsage/lib/build.gradle.kts index 4f29288f..9eff8d98 100644 --- a/kover-gradle-plugin/examples/android/variantUsage/lib/build.gradle.kts +++ b/kover-gradle-plugin/examples/android/variantUsage/lib/build.gradle.kts @@ -34,3 +34,9 @@ dependencies { implementation("androidx.appcompat:appcompat:1.5.0") testImplementation("junit:junit:4.13.2") } + +kover { + variants { + create("custom") { } + } +} diff --git a/kover-gradle-plugin/examples/jvm/defaults/build.gradle.kts b/kover-gradle-plugin/examples/jvm/defaults/build.gradle.kts deleted file mode 100644 index ee63f9b2..00000000 --- a/kover-gradle-plugin/examples/jvm/defaults/build.gradle.kts +++ /dev/null @@ -1,119 +0,0 @@ -plugins { - kotlin("jvm") version "1.7.10" - id("org.jetbrains.kotlinx.kover") version "0.7.6" -} - -repositories { - mavenCentral() -} - -dependencies { - testImplementation(kotlin("test")) -} - -kover { - // disable() - - excludeJavaCode() - - excludeInstrumentation { - classes("com.example.subpackage.*") - } - - excludeTests { - tasks("myTest") - } -} - -koverReport { - filters { - includes { - classes("com.example.*") - } - } - - verify { - rule { - isEnabled = true - entity = kotlinx.kover.gradle.plugin.dsl.GroupingEntityType.APPLICATION - - filters { - excludes { - classes("com.example.verify.subpackage.*") - } - includes { - classes("com.example.verify.*") - } - } - - bound { - minValue = 1 - maxValue = 99 - metric = kotlinx.kover.gradle.plugin.dsl.MetricType.LINE - aggregation = kotlinx.kover.gradle.plugin.dsl.AggregationType.COVERED_PERCENTAGE - } - } - } - - defaults { - filters { - excludes { - classes("com.example.subpackage.*") - } - includes { - classes("com.example.*") - } - } - - xml { - onCheck = false - setReportFile(layout.buildDirectory.file("my-project-report/result.xml")) - - filters { - excludes { - classes("com.example2.subpackage.*") - } - includes { - classes("com.example2.*") - } - } - } - - html { - onCheck = false - setReportDir(layout.buildDirectory.dir("my-project-report/html-result")) - - filters { - excludes { - classes("com.example2.subpackage.*") - } - includes { - classes("com.example2.*") - } - } - } - - verify { - onCheck = true - rule { - entity = kotlinx.kover.gradle.plugin.dsl.GroupingEntityType.APPLICATION - - filters { - excludes { - classes("com.example.verify.subpackage.*") - } - includes { - classes("com.example.verify.*") - } - } - - bound { - minValue = 2 - maxValue = 98 - metric = kotlinx.kover.gradle.plugin.dsl.MetricType.LINE - aggregation = kotlinx.kover.gradle.plugin.dsl.AggregationType.COVERED_PERCENTAGE - } - } - } - } -} diff --git a/kover-gradle-plugin/examples/jvm/defaults/settings.gradle.kts b/kover-gradle-plugin/examples/jvm/defaults/settings.gradle.kts deleted file mode 100644 index 6cedb9ff..00000000 --- a/kover-gradle-plugin/examples/jvm/defaults/settings.gradle.kts +++ /dev/null @@ -1,8 +0,0 @@ -pluginManagement { - repositories { - gradlePluginPortal() - mavenCentral() - } -} -rootProject.name = "example-defaults" - diff --git a/kover-gradle-plugin/examples/jvm/defaults/src/main/kotlin/AppClasses.kt b/kover-gradle-plugin/examples/jvm/defaults/src/main/kotlin/AppClasses.kt deleted file mode 100644 index d4249460..00000000 --- a/kover-gradle-plugin/examples/jvm/defaults/src/main/kotlin/AppClasses.kt +++ /dev/null @@ -1,17 +0,0 @@ -package kotlinx.kover.examples.defaults - -class ExampleClass { - fun formatInt(i: Int): String { - if (i == 0) return "ZERO" - return if (i > 0) { - "POSITIVE=$i" - } else { - "NEGATIVE=${-i}" - } - } - - fun printClass() { - val name = this::class.qualifiedName - println(name) - } -} diff --git a/kover-gradle-plugin/examples/jvm/defaults/src/test/kotlin/TestClasses.kt b/kover-gradle-plugin/examples/jvm/defaults/src/test/kotlin/TestClasses.kt deleted file mode 100644 index b084c937..00000000 --- a/kover-gradle-plugin/examples/jvm/defaults/src/test/kotlin/TestClasses.kt +++ /dev/null @@ -1,10 +0,0 @@ -package kotlinx.kover.examples.defaults - -import kotlin.test.Test - -class TestClasses { - @Test - fun test() { - ExampleClass().formatInt(50) - } -} diff --git a/kover-gradle-plugin/examples/jvm/merged/build.gradle.kts b/kover-gradle-plugin/examples/jvm/merged/build.gradle.kts index 5cf1aef0..e5ae15a4 100644 --- a/kover-gradle-plugin/examples/jvm/merged/build.gradle.kts +++ b/kover-gradle-plugin/examples/jvm/merged/build.gradle.kts @@ -15,21 +15,23 @@ dependencies { kover(project(":subproject")) } -koverReport { - filters { - excludes { - classes("kotlinx.kover.examples.merged.utils.*", "kotlinx.kover.examples.merged.subproject.utils.*") - } - includes { - classes("kotlinx.kover.examples.merged.*") +kover { + reports { + filters { + excludes { + classes("kotlinx.kover.examples.merged.utils.*", "kotlinx.kover.examples.merged.subproject.utils.*") + } + includes { + classes("kotlinx.kover.examples.merged.*") + } } - } - verify { - rule { - bound { - minValue = 50 - maxValue = 75 + verify { + rule { + bound { + min.set(50) + max.set(75) + } } } } diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/AccessorsTests.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/AccessorsTests.kt index 162f4a75..80fa7442 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/AccessorsTests.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/AccessorsTests.kt @@ -62,46 +62,23 @@ internal class AccessorsTests { } - - tasks.koverXmlReportName mustBe "koverXmlReport" - tasks.koverXmlReportName mustBe KoverNames.DEFAULT_XML_REPORT_NAME - - tasks.koverHtmlReportName mustBe "koverHtmlReport" - tasks.koverHtmlReportName mustBe KoverNames.DEFAULT_HTML_REPORT_NAME - - tasks.koverBinaryReportName mustBe "koverBinaryReport" - tasks.koverBinaryReportName mustBe KoverNames.DEFAULT_BINARY_REPORT_NAME - - tasks.koverVerifyName mustBe "koverVerify" - tasks.koverVerifyName mustBe KoverNames.DEFAULT_VERIFY_REPORT_NAME - - tasks.koverLogName mustBe "koverLog" - tasks.koverLogName mustBe KoverNames.DEFAULT_LOG_REPORT_NAME - - - - tasks.koverAndroidXmlReportName("variant") mustBe "koverXmlReportVariant" - tasks.koverAndroidXmlReportName("variant") mustBe KoverNames.androidXmlReport("variant") - - tasks.koverAndroidHtmlReportName("variant") mustBe "koverHtmlReportVariant" - tasks.koverAndroidHtmlReportName("variant") mustBe KoverNames.androidHtmlReport("variant") - - tasks.koverAndroidBinaryReportName("variant") mustBe "koverBinaryReportVariant" - tasks.koverAndroidBinaryReportName("variant") mustBe KoverNames.androidBinaryReport("variant") - - tasks.koverAndroidVerifyName("variant") mustBe "koverVerifyVariant" - tasks.koverAndroidVerifyName("variant") mustBe KoverNames.androidVerify("variant") - - tasks.koverAndroidLogName("variant") mustBe "koverLogVariant" - tasks.koverAndroidLogName("variant") mustBe KoverNames.androidLog("variant") - - - - extensions.koverExtensionName mustBe "kover" - extensions.koverExtensionName mustBe KoverNames.PROJECT_EXTENSION_NAME - - extensions.koverReportExtensionName mustBe "koverReport" - extensions.koverReportExtensionName mustBe KoverNames.REPORT_EXTENSION_NAME + KoverNames.pluginId mustBe "org.jetbrains.kotlinx.kover" + KoverNames.jvmVariantName mustBe "jvm" + KoverNames.configurationName mustBe "kover" + KoverNames.extensionName mustBe "kover" + + + KoverNames.koverXmlReportName mustBe "koverXmlReport" + KoverNames.koverHtmlReportName mustBe "koverHtmlReport" + KoverNames.koverBinaryReportName mustBe "koverBinaryReport" + KoverNames.koverVerifyName mustBe "koverVerify" + KoverNames.koverLogName mustBe "koverLog" + + KoverNames.koverXmlReportName("variant") mustBe "koverXmlReportVariant" + KoverNames.koverHtmlReportName("variant") mustBe "koverHtmlReportVariant" + KoverNames.koverBinaryReportName("variant") mustBe "koverBinaryReportVariant" + KoverNames.koverVerifyName("variant") mustBe "koverVerifyVariant" + KoverNames.koverLogName("variant") mustBe "koverLogVariant" infix fun String.mustBe(a: String) { if (this != a) throw AssertionError("Expected " + a + ", actual " + this) diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/BinaryReportTests.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/BinaryReportTests.kt index f0666c55..6ad8df1c 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/BinaryReportTests.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/BinaryReportTests.kt @@ -1,7 +1,6 @@ package kotlinx.kover.gradle.plugin.test.functional.cases import kotlinx.kover.gradle.plugin.test.functional.framework.configurator.BuildConfigurator -import kotlinx.kover.gradle.plugin.test.functional.framework.configurator.fileInBuildDir import kotlinx.kover.gradle.plugin.test.functional.framework.starter.GeneratedTest import kotlin.test.assertTrue @@ -34,12 +33,16 @@ internal class BinaryReportTests { @GeneratedTest fun BuildConfigurator.testOnCheck() { addProjectWithKover { - koverReport { - defaults { - binary { - onCheck.set(true) + kover { + + reports { + total { + binary { + onCheck.set(true) + } } } + } } @@ -53,10 +56,12 @@ internal class BinaryReportTests { addProjectWithKover { sourcesFrom("simple") - koverReport { - defaults { - binary { - file.set(fileInBuildDir("custom/fileName")) + kover { + reports { + total { + binary { + file.set(it.layout.buildDirectory.file("custom/fileName")) + } } } } diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/ConfigurationCacheTests.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/ConfigurationCacheTests.kt index 1412946f..36062c66 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/ConfigurationCacheTests.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/ConfigurationCacheTests.kt @@ -3,10 +3,10 @@ */ package kotlinx.kover.gradle.plugin.test.functional.cases +import kotlinx.kover.gradle.plugin.test.functional.framework.common.kotlinVersionCurrent import kotlinx.kover.gradle.plugin.test.functional.framework.configurator.* import kotlinx.kover.gradle.plugin.test.functional.framework.starter.* -import java.io.* -import kotlin.test.* +import kotlinx.kover.gradle.plugin.util.SemVer internal class ConfigurationCacheTests { private val subprojectPath = ":common" @@ -15,7 +15,6 @@ internal class ConfigurationCacheTests { fun BuildConfigurator.testConfigCache() { addProjectWithKover(subprojectPath) { sourcesFrom("multiproject-common") - } addProjectWithKover { @@ -36,10 +35,15 @@ internal class ConfigurationCacheTests { fun BuildConfigurator.testProjectIsolation() { addProjectWithKover(subprojectPath) { sourcesFrom("multiproject-common") - } - addProjectWithKover { + // Only since 1.9.20 Kotlin is fully compatible with project isolation + val kotlinVersion = if (SemVer.ofThreePartOrNull(kotlinVersionCurrent)!! < SemVer.ofThreePartOrNull("1.9.20")!!) { + "1.9.20" + } else { + kotlinVersionCurrent + } + addProjectWithKover(kotlinVersion = kotlinVersion) { sourcesFrom("multiproject-user") dependencyKover(subprojectPath) } @@ -48,48 +52,7 @@ internal class ConfigurationCacheTests { ":koverXmlReport", ":koverHtmlReport", ":koverVerify", - "-Dorg.gradle.unsafe.isolated-projects=true", - errorExpected = null, - ) { - if (!hasError) { - return@run - } - - // With the current versions of the Kotlin plugin, an error occurs breaking the isolation of projects. The easiest way to check that the error is not caused by the Kover plugin, then it is enough to find occurrences of this word. - // However, the report in any case contains 'kover' words (for example, task names), so they need to be excluded - val errorLine: String = output.lineSequence().firstOrNull { it.startsWith("See the complete report at ") } - ?: throw AssertionError("Expected Project isolation errors for current Kotlin implementations") - val filePath = errorLine.substringAfter("See the complete report at ") - - val hasKoverErrors = File(filePath.removePrefix("file://")).bufferedReader() - .containsWord("kover", "kover-functional-test", "koverXmlReport", "koverHtmlReport", "koverVerify", - // {"name":"build/kover/default.artifact"}]},{"trace":[{"kind":"BuildLogicClass","type":"kotlinx.kover.gradle.plugin.commons.ArtifactsKt"}] - "kover/default.artifact", - "kover.gradle.plugin.commons.ArtifactsKt", - // Execution of task ':common:compileKotlin' caused invocation of 'Task.project' by task ':common:koverFindJar' at execution time which is unsupported. - "koverFindJar' at execution time which is unsupported.", - // Execution of task ':common:compileKotlin' caused invocation of 'Task.project' by task ':common:koverGenerateArtifact' at execution time which is unsupported. - "koverGenerateArtifact' at execution time which is unsupported.") - - assertFalse(hasKoverErrors, "Project isolation report contains unexpected words 'kover', perhaps the Kover plugin breaks the project isolation") - } - } - - // Text contains [searchingWord] except of words [exceptions] - private fun BufferedReader.containsWord(searchingWord: String, vararg exceptions: String): Boolean { - lineSequence().forEach { line -> - var index = 0 - while (true) { - index = line.indexOf(searchingWord, index) - if (index < 0) break - - val tail = line.substring(index) - if (exceptions.none { exception -> tail.startsWith(exception) }) { - return true - } - index++ - } - } - return false + "-Dorg.gradle.unsafe.isolated-projects=true" + ) } } diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/ConfigurationOrderTests.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/ConfigurationOrderTests.kt index 2bdfe42b..f90a821d 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/ConfigurationOrderTests.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/ConfigurationOrderTests.kt @@ -20,12 +20,12 @@ internal class ConfigurationOrderTests { * A test to verify that the order of application of the Kover plugin does not affect the correct operation. * Kover + Kotlin Android Plugin */ - @TemplateTest("android-inverse-order", [":app:koverXmlReport", ":app:koverXmlReportRelease"]) + @TemplateTest("android-inverse-order", [":app:koverXmlReportCustom", ":app:koverXmlReportRelease"]) fun CheckerContext.testAndroidInverseOrder() { subproject(":app") { - checkXmlReport() + checkXmlReport("custom") checkXmlReport("release") - checkOutcome(":app:koverXmlReport", "SUCCESS") + checkOutcome(":app:koverXmlReportCustom", "SUCCESS") checkOutcome(":app:koverXmlReportRelease", "SUCCESS") } } @@ -34,11 +34,11 @@ internal class ConfigurationOrderTests { * A test to verify that the order of application of the Kover plugin does not affect the correct operation. * Kover + Kotlin Multiplatform Plugin with Android target */ - @TemplateTest("android-mpp-inverse-order", [":koverXmlReport", ":koverXmlReportRelease"]) + @TemplateTest("android-mpp-inverse-order", [":koverXmlReportCustom", ":koverXmlReportRelease"]) fun CheckerContext.testAndroidMppInverseOrder() { - checkXmlReport() + checkXmlReport("custom") checkXmlReport("release") - checkOutcome(":koverXmlReport", "SUCCESS") + checkOutcome(":koverXmlReportCustom", "SUCCESS") checkOutcome(":koverXmlReportRelease", "SUCCESS") } @@ -50,7 +50,7 @@ internal class ConfigurationOrderTests { buildResult.checkNoAndroidSdk() assertFalse(buildResult.isSuccessful, "Build must fall") - assertContains(buildResult.output, "impossible to configure Android reports for it") + assertContains(buildResult.output, "variant because it does not exist") } @Test @@ -61,7 +61,7 @@ internal class ConfigurationOrderTests { buildResult.checkNoAndroidSdk() assertFalse(buildResult.isSuccessful, "Build must fall") - assertContains(buildResult.output, "impossible to merge default reports with its measurements") + assertContains(buildResult.output, "Could not find the provided variant") } } \ No newline at end of file diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/ExamplesBuildTests.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/ExamplesBuildTests.kt index 89636635..71b14807 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/ExamplesBuildTests.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/ExamplesBuildTests.kt @@ -8,10 +8,6 @@ import kotlinx.kover.gradle.plugin.test.functional.framework.checker.CheckerCont import kotlinx.kover.gradle.plugin.test.functional.framework.starter.ExamplesTest internal class ExamplesBuildTests { - @ExamplesTest("jvm/defaults") - fun CheckerContext.jvmDefaultValues() { - // build only - } @ExamplesTest("jvm/merged") fun CheckerContext.jvmMerged() { diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/InstrumentationFilteringTests.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/InstrumentationFilteringTests.kt index 786a2b6b..cd3a0f01 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/InstrumentationFilteringTests.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/InstrumentationFilteringTests.kt @@ -15,8 +15,10 @@ internal class InstrumentationFilteringTests { sourcesFrom("simple") kover { - excludeInstrumentation { - classes("org.jetbrains.*Exa?ple*") + variants { + instrumentation { + excludedClasses.add("org.jetbrains.*Exa?ple*") + } } } } diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/LoggingTaskTests.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/LoggingTaskTests.kt index 6399d918..0df54b82 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/LoggingTaskTests.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/LoggingTaskTests.kt @@ -68,11 +68,13 @@ internal class LoggingTaskTests { addProjectWithKover { sourcesFrom("simple") - koverReport { - defaults { - log { - header = "Custom header" - format = "My format for is " + kover { + reports { + total { + log { + header.set("Custom header") + format.set("My format for is ") + } } } } @@ -91,14 +93,16 @@ internal class LoggingTaskTests { addProjectWithKover { sourcesFrom("simple") - koverReport { - defaults { - log { - header = "Coverage for classes:" - format = "Class covered instructions=" - groupBy = GroupingEntityType.CLASS - aggregationForGroup = AggregationType.COVERED_COUNT - coverageUnits = MetricType.INSTRUCTION + kover { + reports { + total { + log { + header.set("Coverage for classes:") + format.set("Class covered instructions=") + groupBy.set(GroupingEntityType.CLASS) + aggregationForGroup.set(AggregationType.COVERED_COUNT) + coverageUnits.set(MetricType.INSTRUCTION) + } } } } @@ -123,14 +127,16 @@ internal class LoggingTaskTests { addProjectWithKover { sourcesFrom("simple") - koverReport { - defaults { - log { - header = "Coverage for classes:" - format = "Class covered instructions=" - groupBy = GroupingEntityType.CLASS - aggregationForGroup = AggregationType.COVERED_COUNT - coverageUnits = MetricType.INSTRUCTION + kover { + reports { + total { + log { + header.set("Coverage for classes:") + format.set("Class covered instructions=") + groupBy.set(GroupingEntityType.CLASS) + aggregationForGroup.set(AggregationType.COVERED_COUNT) + coverageUnits.set(MetricType.INSTRUCTION) + } } } } diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/MultiProjectTests.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/MultiProjectTests.kt index 6404efcb..a212ce61 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/MultiProjectTests.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/MultiProjectTests.kt @@ -62,7 +62,11 @@ internal class MultiProjectTests { addProjectWithKover(subprojectPath) { sourcesFrom("multiproject-common") kover { - disable() + variants { + instrumentation { + excludeAll.set(true) + } + } } } @@ -70,7 +74,11 @@ internal class MultiProjectTests { sourcesFrom("multiproject-user") dependencyKover(subprojectPath) kover { - disable() + variants { + instrumentation { + excludeAll.set(true) + } + } } } @@ -91,8 +99,10 @@ internal class MultiProjectTests { addProjectWithKover(subprojectPath) { sourcesFrom("multiproject-common") kover { - excludeTests{ - tasks(defaultTestTaskName(slice.type)) + variants { + testTasks { + excluded.add(defaultTestTaskName(slice.type)) + } } } } @@ -101,8 +111,10 @@ internal class MultiProjectTests { sourcesFrom("multiproject-user") dependencyKover(subprojectPath) kover { - excludeTests{ - tasks(defaultTestTaskName(slice.type)) + variants { + testTasks { + excluded.add(defaultTestTaskName(slice.type)) + } } } } diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/NoDependencyTests.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/NoDependencyTests.kt index 1a13630d..bd5f9e21 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/NoDependencyTests.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/NoDependencyTests.kt @@ -7,6 +7,7 @@ import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import kotlin.test.assertContains import kotlin.test.assertFalse +import kotlin.test.assertTrue /** * Tests on dependency check https://github.com/Kotlin/kotlinx-kover/issues/478. @@ -20,8 +21,10 @@ class NoDependencyTests { val buildSource = buildFromTemplate("no-dependency-jvm") val build = buildSource.generate() val buildResult = build.runWithParams("koverHtmlReport") - assertFalse(buildResult.isSuccessful) - assertContains(buildResult.output, "Kover plugin is not applied") + + // temporarily, this behavior is now allowed, see https://github.com/gradle/gradle/issues/27019 + // build listeners also can't be used because of project isolation https://github.com/Kotlin/kotlinx-kover/issues/513 + assertTrue(buildResult.isSuccessful) } @Test @@ -31,8 +34,9 @@ class NoDependencyTests { val buildResult = build.runWithParams(":app:koverHtmlReportDebug") buildResult.checkNoAndroidSdk() - assertFalse(buildResult.isSuccessful) - assertContains(buildResult.output, "Kover plugin is not applied") + // temporarily, this behavior is now allowed, see https://github.com/gradle/gradle/issues/27019 + // build listeners also can't be used because of project isolation https://github.com/Kotlin/kotlinx-kover/issues/513 + assertTrue(buildResult.isSuccessful) } @Test @@ -43,6 +47,6 @@ class NoDependencyTests { buildResult.checkNoAndroidSdk() assertFalse(buildResult.isSuccessful) - assertContains(buildResult.output, "Kover android variant 'extra' was not matched with any variant from dependency") + assertContains(buildResult.output, "Could not resolve all task dependencies for configuration ':app-extra:koverExternalArtifactsExtra'") } } \ No newline at end of file diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/ReportAnnotationFilterTests.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/ReportAnnotationFilterTests.kt index ce7c3d51..372bb987 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/ReportAnnotationFilterTests.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/ReportAnnotationFilterTests.kt @@ -16,20 +16,22 @@ internal class ReportAnnotationFilterTests { fun BuildConfigurator.testExclusions() { addProjectWithKover { sourcesFrom("annotations-main") - koverReport { - filters { - excludes { - annotatedBy("org.jetbrains.Exclude", "*ByMask") + kover { + reports { + filters { + excludes { + annotatedBy("org.jetbrains.Exclude", "*ByMask") + } } - } - verify { - rule { - bound { - metric = LINE - aggregation = AggregationType.COVERED_COUNT - minValue = 9 - maxValue = 9 + verify { + rule { + bound { + coverageUnits.set(LINE) + aggregationForGroup.set(AggregationType.COVERED_COUNT) + min.set(9) + max.set(9) + } } } } @@ -55,29 +57,28 @@ internal class ReportAnnotationFilterTests { fun BuildConfigurator.testOverride() { addProjectWithKover { sourcesFrom("annotations-main") - koverReport { - filters { - excludes { - annotatedBy("org.jetbrains.Exclude", "*ByMask") + kover { + reports { + filters { + excludes { + annotatedBy("org.jetbrains.Exclude", "*ByMask") + } } - } - verify { - rule { + verify { filters { // clear all filters } - - bound { - metric = LINE - aggregation = AggregationType.COVERED_COUNT - minValue = 16 - maxValue = 16 + rule { + bound { + coverageUnits.set(LINE) + aggregationForGroup.set(AggregationType.COVERED_COUNT) + min.set(15) + max.set(15) + } } } - } - defaults { - xml { + total { filters { excludes { annotatedBy("org.jetbrains.OverriddenExclude") @@ -88,7 +89,7 @@ internal class ReportAnnotationFilterTests { } } - run("koverXmlReport", "check") { + run("koverXmlReport", "koverHtmlReport", "check") { xmlReport { methodCounter("org.jetbrains.PartiallyExcludedClass", "function1").assertAbsent() methodCounter("org.jetbrains.PartiallyExcludedClass", "function2").assertFullyCovered() diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/ReportsFilteringTests.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/ReportsFilteringTests.kt index 38c966f7..9a4ebe59 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/ReportsFilteringTests.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/ReportsFilteringTests.kt @@ -14,16 +14,18 @@ internal class ReportsFilteringTests { addProjectWithKover { sourcesFrom("simple") - koverReport { - filters { - excludes { - classes("org.jetbrains.*Exa?ple*") + kover { + reports { + filters { + excludes { + classes("org.jetbrains.*Exa?ple*") + } } - } - verify { - rule { - // without ExampleClass covered lines count = 2, but 4 with it - maxBound(2, aggregation = AggregationType.COVERED_COUNT) + verify { + rule { + // without ExampleClass covered lines count = 2, but 4 with it + maxBound(2, aggregation = AggregationType.COVERED_COUNT) + } } } } @@ -41,23 +43,25 @@ internal class ReportsFilteringTests { addProjectWithKover { sourcesFrom("simple") - koverReport { - filters { - excludes { - classes("org.*") + kover { + reports { + filters { + excludes { + classes("org.*") + } } - } - verify { - rule { - // without ExampleClass covered lines count = 2, but 4 with it - maxBound(2, aggregation = AggregationType.COVERED_COUNT) + verify { + rule { + // without ExampleClass covered lines count = 2, but 4 with it + maxBound(2, aggregation = AggregationType.COVERED_COUNT) + } } - } - defaults { - filters { - excludes { - classes("org.jetbrains.*Exa?ple*") + total { + filters { + excludes { + classes("org.jetbrains.*Exa?ple*") + } } } } @@ -76,37 +80,31 @@ internal class ReportsFilteringTests { addProjectWithKover { sourcesFrom("simple") - koverReport { - filters { - excludes { - classes("foo.*") - } - } - - defaults { + kover { + reports { filters { excludes { - classes("org.*") + classes("foo.*") } } - xml { + total { filters { excludes { - classes("org.jetbrains.*Exa?ple*") + classes("org.*") } } - } - verify { - filters { - excludes { - classes("org.jetbrains.*Exa?ple*") + verify { + filters { + excludes { + classes("org.jetbrains.*Exa?ple*") + } + } + rule { + // without ExampleClass covered lines count = 2, but 4 with it + maxBound(2, aggregation = AggregationType.COVERED_COUNT) } - } - rule { - // without ExampleClass covered lines count = 2, but 4 with it - maxBound(2, aggregation = AggregationType.COVERED_COUNT) } } } @@ -125,17 +123,19 @@ internal class ReportsFilteringTests { addProjectWithKover { sourcesFrom("simple") - koverReport { - filters { - excludes { - classes("org.jetbrains.*Exa?ple*") - } + kover { + reports { + filters { + excludes { + classes("org.jetbrains.*Exa?ple*") + } - includes { - classes("org.jetbrains.*Cla?s") + includes { + classes("org.jetbrains.*Cla?s") + } } - } + } } } run("koverXmlReport") { @@ -159,13 +159,15 @@ internal class ReportsFilteringTests { addProjectWithKover { sourcesFrom("different-packages") - koverReport { - filters { - excludes { - packages("foo") + kover { + reports { + filters { + excludes { + packages("foo") + } } - } + } } } run("koverXmlReport") { diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/TaskFilteringTests.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/TaskFilteringTests.kt index 19b38c17..9e692922 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/TaskFilteringTests.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/TaskFilteringTests.kt @@ -18,8 +18,10 @@ internal class TaskFilteringTests { addProjectWithKover { sourcesFrom("simple") kover { - excludeTests { - tasks(defaultTestTaskName(slice.type)) + variants { + testTasks { + excluded.add(defaultTestTaskName(slice.type)) + } } } } diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/TasksOrderingTests.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/TasksOrderingTests.kt index 93909546..231b46cc 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/TasksOrderingTests.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/TasksOrderingTests.kt @@ -11,10 +11,12 @@ internal class TasksOrderingTests { fun BuildConfigurator.testProjectTasks() { addProjectWithKover { sourcesFrom("simple") - koverReport { - verify { - rule { - minBound(100) + kover { + reports { + verify { + rule { + minBound(100) + } } } } diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/VariantUsageTests.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/VariantUsageTests.kt index d2651ad0..9c785e11 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/VariantUsageTests.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/VariantUsageTests.kt @@ -7,18 +7,19 @@ import kotlinx.kover.gradle.plugin.test.functional.framework.checker.* import kotlinx.kover.gradle.plugin.test.functional.framework.starter.* internal class VariantUsageTests { - @ExamplesTest("android/variantUsage", [":app:koverXmlReport"]) + @ExamplesTest("android/variantUsage", [":app:koverXmlReportCustom"]) fun CheckerContext.testAndroidVariantUsage() { subproject(":app") { - xmlReport { + xmlReport("custom") { // check test tasks checkOutcome(":app:testDebugUnitTest", "SUCCESS") checkOutcome(":lib:testDebugUnitTest", "SUCCESS") // check artifact generation tasks checkOutcome(":app:koverGenerateArtifactDebug", "SUCCESS") + checkOutcome(":app:koverGenerateArtifactCustom", "SUCCESS") checkOutcome(":lib:koverGenerateArtifactDebug", "SUCCESS") - checkOutcome(":app:koverGenerateArtifact", "SUCCESS") + checkOutcome(":lib:koverGenerateArtifactCustom", "SUCCESS") classCounter("kotlinx.kover.test.android.DebugUtil").assertFullyCovered() classCounter("kotlinx.kover.test.android.lib.DebugUtil").assertFullyCovered() @@ -28,9 +29,9 @@ internal class VariantUsageTests { } } - @ExamplesTest("android/multiplatform", [":koverXmlReport"]) + @ExamplesTest("android/multiplatform", [":koverXmlReportCustom"]) fun CheckerContext.testMultiplatformVariantUsage() { - xmlReport { + xmlReport("custom") { // check test tasks checkOutcome(":app:testDebugUnitTest", "SUCCESS") checkOutcome(":lib:testDebugUnitTest", "SUCCESS") @@ -38,7 +39,7 @@ internal class VariantUsageTests { // check artifact generation tasks checkOutcome(":lib:koverGenerateArtifactDebug", "SUCCESS") checkOutcome(":app:koverGenerateArtifactDebug", "SUCCESS") - checkOutcome(":app:koverGenerateArtifact", "SUCCESS") + checkOutcome(":app:koverGenerateArtifactCustom", "SUCCESS") // check android classes from :lib classCounter("kotlinx.kover.test.android.lib.DebugUtil").assertFullyCovered() @@ -51,7 +52,7 @@ internal class VariantUsageTests { } } - @ExamplesTest("android/flavors", [":app:koverXmlReport"]) + @ExamplesTest("android/flavors", [":app:koverXmlReportCustom"]) fun CheckerContext.testFlavoursFallbacksAndMissingDimensions() { // check test tasks checkOutcome(":app:testApp1AppDebugUnitTest", "SUCCESS") @@ -60,7 +61,7 @@ internal class VariantUsageTests { // check artifact generation tasks checkOutcome(":app:koverGenerateArtifactApp1AppDebug", "SUCCESS") checkOutcome(":lib:koverGenerateArtifactLib1LibDebug", "SUCCESS") - checkOutcome(":app:koverGenerateArtifact", "SUCCESS") + checkOutcome(":app:koverGenerateArtifactCustom", "SUCCESS") } } diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/VerificationTests.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/VerificationTests.kt index 34258f33..f63c606d 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/VerificationTests.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/VerificationTests.kt @@ -21,17 +21,19 @@ internal class VerificationTests { addProjectWithKover { sourcesFrom("simple") - koverReport { - verify { - rule("test rule") { - bound { - minValue = 50 - maxValue = 60 - } - bound { - aggregation = AggregationType.COVERED_COUNT - minValue = 2 - maxValue = 10 + kover{ + reports { + verify { + rule("test rule") { + bound { + min.set(50) + max.set(60) + } + bound { + aggregationForGroup.set(AggregationType.COVERED_COUNT) + min.set(2) + max.set(10) + } } } } @@ -46,48 +48,50 @@ internal class VerificationTests { addProjectWithKover { sourcesFrom("verification") - koverReport { - verify { - rule("counts rule") { - bound { - minValue = 58 - maxValue = 60 - } - bound { - aggregation = AggregationType.COVERED_COUNT - minValue = 2 - maxValue = 3 + kover { + reports { + verify { + rule("counts rule") { + bound { + min.set(58) + max.set(60) + } + bound { + aggregationForGroup.set(AggregationType.COVERED_COUNT) + min.set(2) + max.set(3) + } } - } - rule("fully uncovered instructions by classes") { - entity = GroupingEntityType.CLASS - bound { - metric = MetricType.INSTRUCTION - aggregation = AggregationType.MISSED_PERCENTAGE - minValue = 100 + rule("fully uncovered instructions by classes") { + groupBy.set(GroupingEntityType.CLASS) + bound { + coverageUnits.set(MetricType.INSTRUCTION) + aggregationForGroup.set(AggregationType.MISSED_PERCENTAGE) + min.set(100) + } } - } - rule("fully covered instructions by packages") { - entity = GroupingEntityType.PACKAGE - bound { - metric = MetricType.INSTRUCTION - aggregation = AggregationType.COVERED_PERCENTAGE - minValue = 100 + rule("fully covered instructions by packages") { + groupBy.set(GroupingEntityType.PACKAGE) + bound { + coverageUnits.set(MetricType.INSTRUCTION) + aggregationForGroup.set(AggregationType.COVERED_PERCENTAGE) + min.set(100) + } } - } - rule("branches by classes") { - entity = GroupingEntityType.CLASS - bound { - metric = MetricType.BRANCH - aggregation = AggregationType.COVERED_COUNT - minValue = 1000 + rule("branches by classes") { + groupBy.set(GroupingEntityType.CLASS) + bound { + coverageUnits.set(MetricType.BRANCH) + aggregationForGroup.set(AggregationType.COVERED_COUNT) + min.set(1000) + } } - } - rule("missed packages") { - entity = GroupingEntityType.PACKAGE - bound { - aggregation = AggregationType.MISSED_COUNT - maxValue = 1 + rule("missed packages") { + groupBy.set(GroupingEntityType.PACKAGE) + bound { + aggregationForGroup.set(AggregationType.MISSED_COUNT) + max.set(1) + } } } } @@ -153,11 +157,13 @@ Rule violated: lines missed count for package 'org.jetbrains.kover.test.function addProjectWithKover { sourcesFrom("simple") - koverReport { - verify { - rule("root rule") { - bound { - minValue = 99 + kover { + reports { + verify { + rule("root rule") { + bound { + min.set(99) + } } } } @@ -177,19 +183,21 @@ Rule violated: lines missed count for package 'org.jetbrains.kover.test.function addProjectWithKover { sourcesFrom("simple") - koverReport { - verify { - rule("root rule") { - bound { - minValue = 99 + kover { + reports { + verify { + rule("root rule") { + bound { + min.set(99) + } } - } - defaults { - verify { - rule("root rule") { - bound { - minValue = 10 + total { + verify { + rule("root rule") { + bound { + min.set(10) + } } } } diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/XmlReportTests.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/XmlReportTests.kt index 1d253366..eb51ab17 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/XmlReportTests.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/cases/XmlReportTests.kt @@ -40,10 +40,12 @@ internal class XmlReportTests { addProjectWithKover(":nested") { sourcesFrom("simple") - koverReport { - defaults { - xml { - this.title.set("My Custom XML title") + kover { + reports { + total { + xml { + this.title.set("My Custom XML title") + } } } } diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/checker/Checker.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/checker/Checker.kt index 20f92525..1f368ace 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/checker/Checker.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/checker/Checker.kt @@ -147,6 +147,11 @@ private class CheckerContextImpl( assertNull(result.taskOutcome(taskPath), "Task '$taskNameOrPath' should not have been called") } + override fun taskIsCalled(taskNameOrPath: String) { + val taskPath = taskNameOrPath.asPath() + assertNotNull(result.taskOutcome(taskPath), "Task '$taskNameOrPath' should have been called") + } + override fun taskOutput(taskNameOrPath: String, checker: String.() -> Unit) { val taskPath = taskNameOrPath.asPath() val taskLog = result.taskLog(taskPath) ?: noTaskFound(taskNameOrPath, taskPath) @@ -194,10 +199,10 @@ private class ProjectAnalysisDataImpl(override val rootDir: File, override val p override val buildDir: File = projectDir.resolve("build") override val buildScript: String by lazy { buildFile.readText() } - override val language = if (buildFile.name.endsWith(".kts")) ScriptLanguage.KOTLIN else ScriptLanguage.GROOVY + override val language = if (buildFile.name.endsWith(".kts")) ScriptLanguage.KTS else ScriptLanguage.GROOVY override val kotlinPlugin by lazy { buildScript.kotlinPluginType(language) } override val definedKoverVersion: String? by lazy { buildScript.definedKoverVersion() } - override val toolVariant: CoverageToolVariant by lazy { buildScript.definedTool() ?: KoverToolDefaultVariant } + override val toolVariant: CoverageToolVariant by lazy { buildScript.definedTool() ?: KoverToolBuiltin } override fun allProjects(): List { return mutableListOf(this) + subprojects() @@ -304,7 +309,7 @@ private fun String.detectSubprojects(rootPath: String): Map { } private fun String.kotlinPluginType(language: ScriptLanguage): AppliedKotlinPlugin { - return if (language == ScriptLanguage.KOTLIN) { + return if (language == ScriptLanguage.KTS) { when { contains("""kotlin("jvm")""") -> AppliedKotlinPlugin(KotlinPluginType.JVM) contains("""kotlin("multiplatform")""") -> AppliedKotlinPlugin(KotlinPluginType.MULTIPLATFORM) diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/checker/CheckerTypes.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/checker/CheckerTypes.kt index 8bb25655..9b7a4bb0 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/checker/CheckerTypes.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/checker/CheckerTypes.kt @@ -39,6 +39,7 @@ internal interface CheckerContext { fun checkHtmlReport(variantName: String = "", mustExist: Boolean = true) fun checkOutcome(taskNameOrPath: String, vararg expectedOutcome: String) fun taskNotCalled(taskNameOrPath: String) + fun taskIsCalled(taskNameOrPath: String) fun checkDefaultReports(mustExist: Boolean = true) fun checkDefaultBinReport(mustExist: Boolean = true) } diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/common/Types.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/common/Types.kt index a331e61d..3f76458e 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/common/Types.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/common/Types.kt @@ -7,18 +7,18 @@ package kotlinx.kover.gradle.plugin.test.functional.framework.common import kotlinx.kover.gradle.plugin.commons.* import java.io.File -internal enum class ScriptLanguage { KOTLIN, GROOVY } +internal enum class ScriptLanguage { KTS, GROOVY } internal fun File.extractScriptLanguage(): ScriptLanguage { return when(name) { - "build.gradle.kts" -> ScriptLanguage.KOTLIN + "build.gradle.kts" -> ScriptLanguage.KTS "build.gradle" -> ScriptLanguage.GROOVY else -> throw Exception("File $name is not a Gradle build script file") } } -internal val BuildSlice.scriptExtension get() = if (language == ScriptLanguage.KOTLIN) "gradle.kts" else "gradle" +internal val BuildSlice.scriptExtension get() = if (language == ScriptLanguage.KTS) "gradle.kts" else "gradle" internal val BuildSlice.mainPath: String @@ -49,7 +49,7 @@ internal data class BuildSlice( ) { override fun toString(): String { val languageText = when (language) { - ScriptLanguage.KOTLIN -> "Kotlin" + ScriptLanguage.KTS -> "Kotlin" ScriptLanguage.GROOVY -> "Groovy" } val typeText = when (type) { diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/configurator/BuildConfigurator.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/configurator/BuildConfigurator.kt index fda3b923..b799536d 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/configurator/BuildConfigurator.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/configurator/BuildConfigurator.kt @@ -4,11 +4,13 @@ package kotlinx.kover.gradle.plugin.test.functional.framework.configurator +import kotlinx.kover.gradle.plugin.commons.CoverageToolVendor import kotlinx.kover.gradle.plugin.dsl.* import kotlinx.kover.gradle.plugin.test.functional.framework.checker.* import kotlinx.kover.gradle.plugin.test.functional.framework.common.* import kotlinx.kover.gradle.plugin.test.functional.framework.common.kotlinVersionCurrent -import kotlinx.kover.gradle.plugin.test.functional.framework.writer.* +import kotlinx.kover.gradle.plugin.test.functional.framework.mirroring.printGradleDsl +import org.gradle.api.Project internal fun createConfigurator(): BuildConfigurator { return TestBuildConfigurator() @@ -63,11 +65,11 @@ private open class TestBuildConfigurator : BuildConfigurator { projects[path] = TestProjectConfigurator().also(generator) } - override fun addProjectWithKover(path: String, name: String, generator: ProjectConfigurator.() -> Unit) { + override fun addProjectWithKover(path: String, name: String, kotlinVersion: String?, generator: ProjectConfigurator.() -> Unit) { addProject(path, name) { plugins { if (path == ":") { - kotlin(kotlinVersionCurrent) + kotlin(kotlinVersion ?: kotlinVersionCurrent) kover(koverVersionCurrent) } else { kotlin() @@ -114,7 +116,7 @@ internal class TestProjectConfigurator(private val name: String? = null) : Proje val sourceTemplates: MutableSet = mutableSetOf() val plugins: PluginsConfiguratorImpl = PluginsConfiguratorImpl() val projectDependencies: MutableList = mutableListOf() - val rawBlocks: MutableList = mutableListOf() + val rawBlocks: MutableList<(BuildSlice, String) -> String> = mutableListOf() private val repositoriesConfigurator: TestRepositoriesConfigurator = TestRepositoriesConfigurator() @@ -129,29 +131,10 @@ internal class TestProjectConfigurator(private val name: String? = null) : Proje repositoriesConfigurator.also(block) } - override fun kover(config: KoverProjectExtension.() -> Unit) { - val builder = StringBuilder() - val writer = FormattedWriter(builder::append) - - writer.call("kover") { - val koverWriter = KoverWriter(this) - config(koverWriter) + override fun kover(config: KoverExtension.(Project) -> Unit) { + rawBlocks += { slice, gradle -> + printGradleDsl(slice.language, gradle, "kover", config) } - - rawBlocks += builder.toString() - } - - override fun koverReport(config: KoverReportExtension.() -> Unit) { - val builder = StringBuilder() - val writer = FormattedWriter(builder::append) - - writer.call("koverReport") { - val koverWriter = KoverReportExtensionWriter(this) - config(koverWriter) - } - - - rawBlocks += builder.toString() } @@ -162,6 +145,20 @@ internal class TestProjectConfigurator(private val name: String? = null) : Proje override fun dependencyKover(path: String) { projectDependencies += path } + + fun blocks(): List<(BuildSlice, String) -> String> { + // block with JaCoCo + return rawBlocks + { slice, gradleVersion -> + val vendor = slice.toolVendor + if (vendor == CoverageToolVendor.JACOCO) { + printGradleDsl(slice.language, gradleVersion, "kover") { + useJacoco() + } + } else { + "" + } + } + } } diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/configurator/ConfiguratorTypes.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/configurator/ConfiguratorTypes.kt index 66b5a896..c1cebff0 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/configurator/ConfiguratorTypes.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/configurator/ConfiguratorTypes.kt @@ -7,7 +7,9 @@ package kotlinx.kover.gradle.plugin.test.functional.framework.configurator import kotlinx.kover.gradle.plugin.commons.KoverCriticalException import kotlinx.kover.gradle.plugin.dsl.* import kotlinx.kover.gradle.plugin.test.functional.framework.checker.* +import org.gradle.api.Project import org.gradle.api.Transformer +import org.gradle.api.file.ProjectLayout import org.gradle.api.file.RegularFile import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Provider @@ -16,7 +18,7 @@ import java.util.function.BiFunction internal interface BuildConfigurator { - fun addProjectWithKover(path: String = ":", name: String = path.substringAfterLast(":"), generator: ProjectConfigurator.() -> Unit) + fun addProjectWithKover(path: String = ":", name: String = path.substringAfterLast(":"), kotlinVersion: String? = null, generator: ProjectConfigurator.() -> Unit) fun addProject(path: String, name: String, generator: ProjectConfigurator.() -> Unit) @@ -38,9 +40,7 @@ internal interface ProjectConfigurator { fun repositories(block: RepositoriesConfigurator.() -> Unit) - fun kover(config: KoverProjectExtension.() -> Unit) - - fun koverReport(config: KoverReportExtension.() -> Unit) + fun kover(config: KoverExtension.(Project) -> Unit) fun sourcesFrom(template: String) @@ -57,11 +57,15 @@ internal interface RepositoriesConfigurator { fun repository(name: String) } +internal interface ProjectScope { + val layout: ProjectLayout +} + internal abstract class BuilderConfiguratorWrapper(private val origin: BuildConfigurator) : BuildConfigurator { - override fun addProjectWithKover(path: String, name: String, generator: ProjectConfigurator.() -> Unit) { - origin.addProjectWithKover(path, name, generator) + override fun addProjectWithKover(path: String, name: String, kotlinVersion: String?, generator: ProjectConfigurator.() -> Unit) { + origin.addProjectWithKover(path, name, kotlinVersion, generator) } override fun addProject(path: String, name: String, generator: ProjectConfigurator.() -> Unit) { diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/mirroring/Invoking.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/mirroring/Invoking.kt new file mode 100644 index 00000000..ecb1b212 --- /dev/null +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/mirroring/Invoking.kt @@ -0,0 +1,271 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.test.functional.framework.mirroring + +import org.gradle.api.Action +import org.gradle.api.provider.Provider +import java.lang.reflect.* + +@Suppress("UNCHECKED_CAST") +internal fun collectInvokesWithScope( + dslType: Class, + scopeType: Class, + block: Dsl.(Scope) -> Unit +): List { + val slicer = BlockSlicer() + slicer.stepInto() + + val dsl = Proxy.newProxyInstance( + dslType.getClassLoader(), + arrayOf>(dslType), + Handler(null, slicer) + ) as Dsl + + val scope = Proxy.newProxyInstance( + scopeType.getClassLoader(), + arrayOf>(scopeType), + Handler(null, slicer) + ) as Scope + + dsl.block(scope) + + return slicer.stepOut() +} + +@Suppress("UNCHECKED_CAST") +internal fun collectInvokes( + dslType: Class, + block: Dsl.() -> Unit +): List { + val slicer = BlockSlicer() + slicer.stepInto() + + val dsl = Proxy.newProxyInstance( + dslType.getClassLoader(), + arrayOf>(dslType), + Handler(null, slicer) + ) as Dsl + + dsl.block() + + return slicer.stepOut() +} + + +private class BlockSlicer { + private var frame: CodeFrame? = null + operator fun plusAssign(invoke: Invoke) { + val currentFrame = frame ?: throw IllegalStateException("No current code block") + currentFrame.invokes += invoke + } + + fun stepInto() { + val new = CodeFrame(frame, mutableListOf()) + frame = new + } + + fun stepOut(): List { + val currentFrame = frame ?: throw IllegalStateException("No current code block") + frame = currentFrame.parent + + return currentFrame.invokes + } + + private class CodeFrame(val parent: CodeFrame?, val invokes: MutableList) +} + + +internal sealed class Invoke(val prev: Invoke?) { + private var usageCount: Int = 0 + + init { + if (prev != null) { + prev.usageCount++ + } + } + + fun asArgument() { + usageCount++ + } + + fun isUnused(): Boolean { + return usageCount == 0 + } + + fun isUsedSeveralTimes(): Boolean { + return usageCount > 1 + } +} + +internal open class FunctionInvoke( + prev: Invoke?, + val name: String, + val returnType: Class<*>, + val thisType: Class<*>?, + val args: List = emptyList() +) : Invoke(prev) { + init { + args.forEach { arg -> arg.asArgument() } + } +} + + +internal class VarargInvoke(val elements: List): Invoke(null) + +internal class ValueInvoke(val value: Any?): Invoke(null) + +internal class EnumEntryInvoke(val enumType: Class, val entry: Enum<*>): Invoke(null) + +internal class LambdaInvoke(val invokes: List): Invoke(null) + +internal interface ExpressionResult { + fun `$getMyself`(): Invoke +} + + +private class Handler( + private val receiver: Invoke? = null, + private val slicer: BlockSlicer +) : InvocationHandler { + + override fun invoke(proxy: Any?, method: Method, args: Array?): Any? { + val methodName = method.name + val params = method.parameters + + if (methodName == "toString") { + return receiver?.let { "$it." } ?: "" + } + + if (methodName == "\$getMyself") { + return receiver + } + + val argInvokes = invokeArgs(params.toList(), args) + + val invoke = FunctionInvoke(receiver, methodName, method.returnType, proxy?.javaClass, argInvokes) + slicer += invoke + return wrapResul(invoke, method, proxy, args) + } + + private fun invokeArgs(params: List, args: Array?): List { + return params.mapIndexed { index, parameter -> + if (parameter.isVarArgs) { + varargInvoke(parameter, slicer, args!![index]) + } else { + valueInvoke(parameter, slicer, args!![index]) + } + } + } + + private fun wrapResul(resultAsReceiver: Invoke?, method: Method, proxy: Any?, args: Array?): Any? { + val type = method.returnType + + if (ProviderClass.isAssignableFrom(type)) { + return Proxy.newProxyInstance( + type.getClassLoader(), + arrayOf>(type, ExpressionResult::class.java), + Handler(resultAsReceiver, slicer) + ) + } + + if (type.isInterface) { + return Proxy.newProxyInstance( + type.getClassLoader(), + arrayOf(type, ExpressionResult::class.java), + Handler(resultAsReceiver, slicer) + ) + } + + if (type == Void.TYPE) { + return null + } + + return if (args == null) { + method.invoke(proxy) + } else { + method.invoke(proxy, args) + } + } +} + +private fun varargInvoke(parameter: Parameter, slicer: BlockSlicer, value: Any?): Invoke { + if (value == null) return ValueInvoke(null) + + @Suppress("UNCHECKED_CAST") val elements = (value as Array).map { element -> + valueInvoke(parameter, slicer, element) + } + + return VarargInvoke(elements) +} + +private fun valueInvoke(parameter: Parameter, slicer: BlockSlicer, value: Any?): Invoke { + if (value == null) return ValueInvoke(null) + + if (value is ExpressionResult) { + return value.`$getMyself`() + } + + if (parameter.isFunctionalType()) { + return lambdaInvoke(parameter, slicer, value) + } + + val actualType = value.javaClass + + if (actualType.isEnum) { + return EnumEntryInvoke(actualType, value as Enum<*>) + } + + return ValueInvoke(value) +} + +private fun Parameter.isFunctionalType(): Boolean { + return type == Action::class.java || Function1::class.java.isAssignableFrom(type) +} + +private fun lambdaInvoke(parameter: Parameter, slicer: BlockSlicer, value: Any): Invoke { + val parameterType = + (parameter.parameterizedType as ParameterizedType).actualTypeArguments[0] + + val actionClass = when (parameterType) { + is Class<*> -> { + if (!parameterType.isInterface) { + throw IllegalStateException("Supports only interface parameter types but has '$parameterType'") + } + parameterType + } + + is WildcardType -> { + parameterType.lowerBounds.filterIsInstance>().firstOrNull { + it.isInterface + } ?: throw IllegalStateException("Now interface lower bound for type '$parameterType'") + } + + else -> { + throw IllegalStateException("Unknown parameter type '$parameterType'") + } + } + + // TODO support non-final classes (should use another lib?) + val actionReceiver = Proxy.newProxyInstance( + actionClass.getClassLoader(), + arrayOf(actionClass), + Handler(null, slicer) + ) + + slicer.stepInto() + if (parameter.type == Action::class.java) { + @Suppress("UNCHECKED_CAST") + (value as Action).execute(actionReceiver) + } else if (Function1::class.java.isAssignableFrom(parameter.type)) { + @Suppress("UNCHECKED_CAST") + (value as Function1).invoke(actionReceiver) + } else { + throw IllegalStateException("Unknown action type ${parameter.type}") + } + val blockInvokes = slicer.stepOut() + return LambdaInvoke(blockInvokes) +} + +internal val ProviderClass = Provider::class.java diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/mirroring/Mirroring.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/mirroring/Mirroring.kt new file mode 100644 index 00000000..8b45b5c7 --- /dev/null +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/mirroring/Mirroring.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.test.functional.framework.mirroring + +import kotlinx.kover.gradle.plugin.test.functional.framework.common.ScriptLanguage + + +internal inline fun printGradleDsl( + language: ScriptLanguage, + gradleVersion: String, + rootBlock: String? = null, + noinline block: Dsl.(Scope) -> Unit +): String { + val invokes = collectInvokesWithScope(Dsl::class.java, Scope::class.java, block) + val parsedBlock = parse(invokes) + return printCode(rootBlock, language, gradleVersion, parsedBlock) +} + +internal inline fun printGradleDsl( + language: ScriptLanguage, + gradleVersion: String, + rootBlock: String? = null, + noinline block: Dsl.() -> Unit +): String { + val invokes = collectInvokes(Dsl::class.java, block) + val parsedBlock = parse(invokes) + return printCode(rootBlock, language, gradleVersion, parsedBlock) +} \ No newline at end of file diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/mirroring/MirroringTest.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/mirroring/MirroringTest.kt new file mode 100644 index 00000000..18b9498a --- /dev/null +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/mirroring/MirroringTest.kt @@ -0,0 +1,130 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.test.functional.framework.mirroring + +import kotlinx.kover.gradle.plugin.dsl.KoverExtension +import kotlinx.kover.gradle.plugin.test.functional.framework.common.ScriptLanguage +import org.gradle.api.Project +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + + +private val groovyExample = """ + reports { + total { + xml { + xmlFile = layout.buildDirectory.file("foo") + def var0 = layout.projectDirectory.file("bar") + xmlFile = var0 + xmlFile = var0 + } + filters { + excludes { + classes("AAA", "bbb") + } + } + verify { + rule("my Rule") { + bound(10, 20, kotlinx.kover.gradle.plugin.dsl.MetricType.LINE, kotlinx.kover.gradle.plugin.dsl.AggregationType.COVERED_PERCENTAGE) + } + } + } + } + + """.trimIndent() + +private val ktsExample = """ + reports { + total { + xml { + xmlFile.set(layout.buildDirectory.file("foo")) + val var0 = layout.projectDirectory.file("bar") + xmlFile.set(var0) + xmlFile.set(var0) + } + filters { + excludes { + classes("AAA", "bbb") + } + } + verify { + rule("my Rule") { + bound(10, 20, kotlinx.kover.gradle.plugin.dsl.MetricType.LINE, kotlinx.kover.gradle.plugin.dsl.AggregationType.COVERED_PERCENTAGE) + } + } + } + } + + """.trimIndent() + +private val ktsAfter8Example = """ + reports { + total { + xml { + xmlFile = layout.buildDirectory.file("foo") + val var0 = layout.projectDirectory.file("bar") + xmlFile = var0 + xmlFile = var0 + } + filters { + excludes { + classes("AAA", "bbb") + } + } + verify { + rule("my Rule") { + bound(10, 20, kotlinx.kover.gradle.plugin.dsl.MetricType.LINE, kotlinx.kover.gradle.plugin.dsl.AggregationType.COVERED_PERCENTAGE) + } + } + } + } + +""".trimIndent() + +class MirroringTest { + + @Test + fun test() { + val groovyScript = printGradleDsl(ScriptLanguage.GROOVY, "7.1.0", block = config) + assertEquals(groovyExample, groovyScript) + + val ktsScript = printGradleDsl(ScriptLanguage.KTS, "7.1.0", block = config) + assertEquals(ktsExample, ktsScript) + + val ktsAfter8Script = printGradleDsl(ScriptLanguage.KTS, "8.1.0", block = config) + assertEquals(ktsAfter8Example, ktsAfter8Script) + } + + + private val config: KoverExtension.(Project) -> Unit = { + + reports { + total { + val my = it.layout + xml { + xmlFile.set(it.layout.buildDirectory.file("foo")) + val file = my.projectDirectory.file("bar") + xmlFile.set(file) + xmlFile.set(file) + } + + filters { + excludes { + classes("AAA", "bbb") + } + } + + verify { + rule("my Rule") { + // default values are always printed + bound(10, 20) + } + } + } + } + + } + +} diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/mirroring/Parsing.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/mirroring/Parsing.kt new file mode 100644 index 00000000..be38bc00 --- /dev/null +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/mirroring/Parsing.kt @@ -0,0 +1,133 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.test.functional.framework.mirroring + +import java.util.Locale + +internal fun parse(invokes: List): CodeBlock { + val context = ParseContext() + return parseBlock(invokes, context) +} + +private fun parseBlock(invokes: List, context: ParseContext) : CodeBlock { + val statements = invokes.mapNotNull { invoke -> + if (invoke.isUnused()) { + parseExpression(invoke, context) + } else if (invoke.isUsedSeveralTimes()) { + val name = context.addVariable(invoke) + val expr = parseExpression(invoke, context, false) + VariableInitialization(name, expr) + } else { + null + } + } + + return CodeBlock(statements) +} + + +private class ParseContext { + private val variables: MutableMap = mutableMapOf() + + fun addVariable(invoke: Invoke): String { + val name = "var${variables.size}" + variables[invoke] = name + return name + } + + fun getVariable(invoke: Invoke): String { + return variables[invoke] ?: throw IllegalStateException("Not found variable for invoke: $invoke") + } +} + + +private fun parseExpression(invoke: Invoke, context: ParseContext, mayBeVariable: Boolean = true): Expression { + if (invoke.isUsedSeveralTimes() && mayBeVariable) { + return VariableUsage(context.getVariable(invoke)) + } + + return when (invoke) { + is ValueInvoke -> parseValue(invoke.value) + is VarargInvoke -> parseVararg(invoke, context) + is EnumEntryInvoke -> EnumEntryLiteral(invoke.enumType, invoke.entry) + is FunctionInvoke -> parseFunction(invoke, context) + is LambdaInvoke -> LambdaExpression(parseBlock(invoke.invokes, context)) + } + +} + +private fun parseFunction(invoke: FunctionInvoke, context: ParseContext): Expression { + val receiver = invoke.prev?.let { parseExpression(it, context) } + + // special override for provider setter + if (invoke.name == "set" + && invoke.args.size == 1 + && receiver != null + && invoke.thisType != null + && ProviderClass.isAssignableFrom(invoke.thisType) + ) { + val rightSide = parseExpression(invoke.args[0], context) + return SetOperatorCall(receiver, rightSide) + } + + if (invoke.name.startsWith("get") && invoke.name != "get" && invoke.args.isEmpty()) { + val propertyName = extractPropertyName(invoke.name) + return PropertyGetter(propertyName, receiver) + } + + if (invoke.name.startsWith("set") && invoke.name != "set" && invoke.args.size == 1) { + val propertyName = extractPropertyName(invoke.name) + val value = parseExpression(invoke.args[0], context) + return PropertySetter(propertyName, value, receiver) + } + + val args = invoke.args.map { arg -> + parseExpression(arg, context) + } + + return FunctionCall(invoke.name, receiver, args) +} + +private fun parseVararg(invoke: VarargInvoke, context: ParseContext): VarargExpression { + val elements = invoke.elements.map { element -> + parseExpression(element, context) + } + return VarargExpression(elements) +} + +private fun parseValue(value: Any?): LiteralExpression { + if (value == null) return NullLiteral + return ValueLiteral(value) +} + +private fun extractPropertyName(methodName: String): String { + return methodName.substring(3).replaceFirstChar { it.lowercase(Locale.getDefault()) } +} + +internal class CodeBlock(val statements: List) + +internal sealed interface Statement + +internal sealed class Expression(val receiver: Expression?): Statement + +internal class VariableInitialization(val name: String, val rightSide: Expression): Statement + +internal class LambdaExpression(val block: CodeBlock): Expression(null) + +internal class VariableUsage(val name: String): Expression(null) + +internal class SetOperatorCall(val leftSide: Expression, val rightSide: Expression): Expression(null) +internal class PropertyGetter(val name: String, receiver: Expression?): Expression(receiver) +internal class PropertySetter(val name: String, val value: Expression, receiver: Expression?): Expression(receiver) + +internal class FunctionCall(val name: String, receiver: Expression?, val args: List): Expression(receiver) + +internal class VarargExpression(val elements: List): Expression(null) + +internal sealed class LiteralExpression: Expression(null) + +internal object NullLiteral: LiteralExpression() +internal class EnumEntryLiteral(val enumType: Class, val entry: Enum<*>): LiteralExpression() +internal class ValueLiteral(val value: Any): LiteralExpression() diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/mirroring/Printing.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/mirroring/Printing.kt new file mode 100644 index 00000000..fc820948 --- /dev/null +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/mirroring/Printing.kt @@ -0,0 +1,191 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.test.functional.framework.mirroring + +import kotlinx.kover.gradle.plugin.test.functional.framework.common.ScriptLanguage +import kotlinx.kover.gradle.plugin.util.SemVer + + +internal fun printCode(name: String?, language: ScriptLanguage, gradleVersion: String, block: CodeBlock): String { + val builder = StringBuilder() + val context = PrintingContext(builder::append, language, SemVer.ofThreePartOrNull(gradleVersion)!!) + + if (name != null) { + context.print(name) + context.print(" {") + context.newLine() + context.stepIn() + } + + doPrint(context, block) + + if (name != null) { + context.stepOut() + context.print("}") + } + + return builder.toString() +} + +private fun doPrint(context: PrintingContext, block: CodeBlock) { + block.statements.forEach { statement -> + when (statement) { + is VariableInitialization -> printVariableInit(context, statement) + is Expression -> printStatement(context, statement) + } + } +} + +private fun printVariableInit(context: PrintingContext, init: VariableInitialization) { + if (context.language == ScriptLanguage.GROOVY) { + context.print("def ") + } else { + context.print("val ") + } + context.print(init.name) + context.print(" = ") + + printExpression(context, init.rightSide) + context.newLine() +} + +private fun printStatement(context: PrintingContext, expression: Expression) { + printExpression(context, expression) + context.newLine() +} + +private fun printExpression(context: PrintingContext, expression: Expression) { + if (expression.receiver != null) { + printExpression(context, expression.receiver) + context.print(".") + } + + when (expression) { + is PropertyGetter -> context.print(expression.name) + is FunctionCall -> handleFunctionCall(context, expression) + is LambdaExpression -> handleLambdaExpression(context, expression) + is EnumEntryLiteral -> handleEnumEntryLiteral(context, expression) + NullLiteral -> context.print("null") + is ValueLiteral -> handleValueLiteral(context, expression) + is PropertySetter -> handlePropertySetter(context, expression) + is SetOperatorCall -> handleSetOperatorCall(context, expression) + is VarargExpression -> handleVarargExpression(context, expression) + is VariableUsage -> context.print(expression.name) + } +} + +private fun handleFunctionCall(context: PrintingContext, expression: FunctionCall) { + context.print(expression.name) + + val lastLambda = + if (expression.args.lastOrNull() is LambdaExpression) expression.args.last() as LambdaExpression else null + + val argsWithoutLastLambda = if (lastLambda == null) { + expression.args + } else { + expression.args.dropLast(1) + } + + val needBraces = (lastLambda == null || argsWithoutLastLambda.isNotEmpty()) + + if (needBraces) { + context.print("(") + } + argsWithoutLastLambda.forEachIndexed { index, arg -> + printExpression(context, arg) + if (index < argsWithoutLastLambda.lastIndex) { + context.print(", ") + } + } + if (needBraces) { + context.print(")") + } + + if (lastLambda != null) { + context.print(" ") + handleLambdaExpression(context, lastLambda) + } +} + +private fun handleLambdaExpression(context: PrintingContext, expression: LambdaExpression) { + context.print("{") + context.newLine() + context.stepIn() + doPrint(context, expression.block) + context.stepOut() + context.print("}") +} + +private fun handleEnumEntryLiteral(context: PrintingContext, expression: EnumEntryLiteral) { + context.print(expression.enumType.canonicalName) + context.print(".") + context.print(expression.entry.name) +} + +private fun handleValueLiteral(context: PrintingContext, expression: ValueLiteral) { + when (expression.value) { + is String -> context.print("\"${expression.value}\"") + is Int -> context.print(expression.value.toString()) + is Boolean -> context.print(expression.value.toString()) + else -> throw IllegalStateException("Value '${expression.value}' with type ${expression.value.javaClass} unsupported as argument") + } +} + +private fun handlePropertySetter(context: PrintingContext, expression: PropertySetter) { + context.print(expression.name) + context.print(" = ") + printExpression(context, expression.value) +} + +private fun handleSetOperatorCall(context: PrintingContext, expression: SetOperatorCall) { + printExpression(context, expression.leftSide) + if (context.language == ScriptLanguage.GROOVY + || (context.language == ScriptLanguage.KTS && context.gradleVersion >= SemVer.ofThreePartOrNull("8.0.0")!!) + ) { + context.print(" = ") + printExpression(context, expression.rightSide) + } else { + context.print(".set(") + printExpression(context, expression.rightSide) + context.print(")") + } +} + +private fun handleVarargExpression(context: PrintingContext, expression: VarargExpression) { + expression.elements.forEachIndexed { index, element -> + printExpression(context, element) + if (index < expression.elements.lastIndex) { + context.print(", ") + } + } +} + +private class PrintingContext(private val appender: (String) -> Unit, val language: ScriptLanguage, val gradleVersion: SemVer) { + var indents: Int = 0 + var emptyLine: Boolean = true + + fun newLine() { + appender.invoke("\n") + emptyLine = true + } + + fun stepIn() { + indents++ + } + fun stepOut() { + indents-- + } + + fun print(message: String) { + if (emptyLine) { + for (i in 0 until indents) { + appender.invoke(" ") + } + } + appender.invoke(message) + emptyLine = false + } + +} diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/runner/BuildsRunner.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/runner/BuildsRunner.kt index 2b26508e..91cd1f54 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/runner/BuildsRunner.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/runner/BuildsRunner.kt @@ -129,6 +129,8 @@ private class GradleBuildImpl( gradleArgs += "--no-daemon" } + gradleArgs += "--stacktrace" + logInfo("Run Gradle commands $gradleArgs for project '${targetDir.canonicalPath}' with wrapper '${env.wrapperDir.canonicalPath}'") val envVars: MutableMap = mutableMapOf() diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/starter/Commons.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/starter/Commons.kt index 32207a4d..1104cbba 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/starter/Commons.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/starter/Commons.kt @@ -119,7 +119,7 @@ internal fun File.patchSettingsFile( localRepositoryPath: String, overrideKotlinVersion: String? ) { - val language = if (name.endsWith(".kts")) ScriptLanguage.KOTLIN else ScriptLanguage.GROOVY + val language = if (name.endsWith(".kts")) ScriptLanguage.KTS else ScriptLanguage.GROOVY val originLines = readLines() diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/starter/Single.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/starter/Single.kt index 1d0ec3d2..db259bd8 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/starter/Single.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/starter/Single.kt @@ -12,7 +12,6 @@ import kotlinx.kover.gradle.plugin.test.functional.framework.writer.* import org.junit.jupiter.api.* import org.junit.jupiter.api.extension.* import java.lang.reflect.* -import java.nio.file.* @Target(AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.RUNTIME) @@ -22,7 +21,7 @@ import java.nio.file.* ExtendWith(SingleTestInterceptor::class) ) internal annotation class GeneratedTest( - val language: ScriptLanguage = ScriptLanguage.KOTLIN, + val language: ScriptLanguage = ScriptLanguage.KTS, val type: KotlinPluginType = KotlinPluginType.JVM, val tool: CoverageToolVendor = CoverageToolVendor.KOVER, // since nullable types are not allowed in annotations, we store the attribute of an unspecified tool separately diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/starter/Sliced.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/starter/Sliced.kt index 678ecfd5..e57f197a 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/starter/Sliced.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/starter/Sliced.kt @@ -13,7 +13,6 @@ import org.junit.jupiter.api.extension.* import org.junit.jupiter.params.* import org.junit.jupiter.params.provider.* import java.lang.reflect.* -import java.nio.file.* import java.util.stream.* @Target(AnnotationTarget.FUNCTION) @@ -32,7 +31,7 @@ internal interface SlicedBuildConfigurator : BuildConfigurator { val slice: BuildSlice } -private val ALL_LANGUAGES = listOf(ScriptLanguage.KOTLIN, ScriptLanguage.GROOVY) +private val ALL_LANGUAGES = listOf(ScriptLanguage.KTS, ScriptLanguage.GROOVY) private val ALL_TOOLS = listOf(CoverageToolVendor.KOVER, CoverageToolVendor.JACOCO) private val ALL_TYPES = listOf(KotlinPluginType.JVM, KotlinPluginType.MULTIPLATFORM) @@ -104,7 +103,7 @@ private class SlicedTestArgumentsProvider : ArgumentsProvider { } // filling default values - if (languages.isEmpty()) languages += ScriptLanguage.KOTLIN + if (languages.isEmpty()) languages += ScriptLanguage.KTS if (types.isEmpty()) types += KotlinPluginType.JVM if (tools.isEmpty()) tools += null diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/writer/BuildScriptWriter.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/writer/BuildScriptWriter.kt index b90f7978..d478b58b 100644 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/writer/BuildScriptWriter.kt +++ b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/writer/BuildScriptWriter.kt @@ -14,8 +14,7 @@ internal fun File.writeBuildScript(projectConfig: TestProjectConfigurator, slice writePlugins(projectConfig.plugins, slice) writeRepositories(projectConfig.repositories) writeDependencies(projectConfig.projectDependencies, slice) - writeBlocks(projectConfig.rawBlocks) - writeOverriddenTool(slice) + writeBlocks(projectConfig.blocks(), slice) } } @@ -29,8 +28,8 @@ private fun FormattedWriter.writePlugins(plugins: PluginsConfiguratorImpl, slice val name = when { language == ScriptLanguage.GROOVY && type == KotlinPluginType.JVM -> """id "org.jetbrains.kotlin.jvm"""" language == ScriptLanguage.GROOVY && type == KotlinPluginType.MULTIPLATFORM -> """id "org.jetbrains.kotlin.multiplatform"""" - language == ScriptLanguage.KOTLIN && type == KotlinPluginType.JVM -> """kotlin("jvm")""" - language == ScriptLanguage.KOTLIN && type == KotlinPluginType.MULTIPLATFORM -> """kotlin("multiplatform")""" + language == ScriptLanguage.KTS && type == KotlinPluginType.JVM -> """kotlin("jvm")""" + language == ScriptLanguage.KTS && type == KotlinPluginType.MULTIPLATFORM -> """kotlin("multiplatform")""" else -> throw Exception("Unsupported test combination: language $language and project type $type") } val version = plugins.kotlinVersion?.let { """version "$it"""" } ?: "" @@ -38,7 +37,7 @@ private fun FormattedWriter.writePlugins(plugins: PluginsConfiguratorImpl, slice } if (plugins.useKover) { - val name = if (language == ScriptLanguage.KOTLIN) { + val name = if (language == ScriptLanguage.KTS) { """id("org.jetbrains.kotlinx.kover")""" } else { """id "org.jetbrains.kotlinx.kover"""" @@ -67,8 +66,8 @@ private fun FormattedWriter.writeDependencies(koverDependencies: List, s val template = when { language == ScriptLanguage.GROOVY && type == KotlinPluginType.JVM -> GROOVY_JVM_DEPS language == ScriptLanguage.GROOVY && type == KotlinPluginType.MULTIPLATFORM -> GROOVY_MPP_DEPS - language == ScriptLanguage.KOTLIN && type == KotlinPluginType.JVM -> KOTLIN_JVM_DEPS - language == ScriptLanguage.KOTLIN && type == KotlinPluginType.MULTIPLATFORM -> KOTLIN_MPP_DEPS + language == ScriptLanguage.KTS && type == KotlinPluginType.JVM -> KOTLIN_JVM_DEPS + language == ScriptLanguage.KTS && type == KotlinPluginType.MULTIPLATFORM -> KOTLIN_MPP_DEPS else -> throw Exception("Unsupported test combination: language $language and project type $type") } template.replace(DEPS_PLACEHOLDER, subprojectsPart).lines().forEach { @@ -84,24 +83,13 @@ private fun FormattedWriter.writeDependencies(koverDependencies: List, s } } -private fun FormattedWriter.writeBlocks(rawBlocks: List) { - rawBlocks.forEach { - text(it) +private fun FormattedWriter.writeBlocks(rawBlocks: List<(BuildSlice, String) -> String>, slice: BuildSlice) { + val gradleVersion = overriddenGradleVersion ?: defaultGradleVersion + rawBlocks.forEach { block -> + text(block(slice, gradleVersion)) } } -private fun FormattedWriter.writeOverriddenTool(slice: BuildSlice) { - val vendor = slice.toolVendor ?: return - - call("kover") { - val koverWriter = KoverWriter(this) - if (vendor == CoverageToolVendor.JACOCO) { - koverWriter.useJacoco() - } - } -} - - private const val DEPS_PLACEHOLDER = "/*DEPS*/" private const val KOTLIN_JVM_DEPS = """ diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/writer/KoverReportWriter.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/writer/KoverReportWriter.kt deleted file mode 100644 index 88b45301..00000000 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/writer/KoverReportWriter.kt +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.kover.gradle.plugin.test.functional.framework.writer - -import kotlinx.kover.gradle.plugin.dsl.* -import org.gradle.api.* -import org.gradle.api.file.* -import org.gradle.api.provider.* -import java.io.* - -internal class KoverReportExtensionWriter(private val writer: FormattedWriter) : KoverReportExtension { - override fun filters(config: Action) { - writer.call("filters", config) { KoverReportFiltersWriter(it) } - } - - override fun verify(config: Action) { - writer.call("verify", config) { KoverVerificationRulesConfigWriter(it) } - } - - override fun defaults(config: Action) { - writer.call("defaults", config) { KoverDefaultReportsWriter(it) } - } - - override fun androidReports(variant: String, config: Action) { - writer.call("androidReports", config) { KoverReportsWriter(it) } - } - -} - -internal class KoverDefaultReportsWriter(private val writer: FormattedWriter): KoverReportsWriter(writer), KoverDefaultReportsConfig { - override fun mergeWith(otherVariant: String) { - writer.call("mergeWith", otherVariant) - } - -} - - -internal open class KoverReportsWriter(private val writer: FormattedWriter): KoverReportsConfig { - override fun filters(config: Action) { - writer.call("filters", config) { KoverReportFiltersWriter(it) } - } - - override fun html(config: Action) { - writer.call("html", config) { KoverHtmlReportConfigWriter(it) } - } - - override fun xml(config: Action) { - writer.call("xml", config) { KoverXmlReportConfigWriter(it) } - } - - override fun binary(config: Action) { - writer.call("binary", config) { KoverBinaryReportConfigWriter(it) } - } - - override fun verify(config: Action) { - writer.call("verify", config) { KoverVerifyReportConfigWriter(it) } - } - - override fun log(config: Action) { - writer.call("log", config) { KoverLogReportConfigWriter(it) } - } -} - -internal class KoverReportFiltersWriter(private val writer: FormattedWriter) : KoverReportFilters { - override fun excludes(config: Action) { - writer.call("excludes", config) { KoverReportFilterWriter(it) } - } - - override fun includes(config: Action) { - writer.call("includes", config) { KoverReportFilterWriter(it) } - } -} - -internal class KoverReportFilterWriter(private val writer: FormattedWriter) : KoverReportFilter { - - override fun classes(vararg names: String) { - this.classes(names.asIterable()) - } - - override fun classes(names: Iterable) { - writer.callStr("classes", names) - } - - override fun packages(vararg names: String) { - packages(names.asIterable()) - } - - override fun packages(names: Iterable) { - writer.callStr("packages", names) - } - - override fun annotatedBy(vararg annotationName: String) { - writer.callStr("annotatedBy", annotationName.asIterable()) - } - -} - -internal class KoverHtmlReportConfigWriter(private val writer: FormattedWriter) : KoverHtmlReportConfig { - override var onCheck: Boolean = false - set(value) { - writer.assign("onCheck", value.toString()) - field = value - } - override var title: String? = null - set(value) { - writer.assign("title", "\"$value\"") - field = value - } - override var charset: String? = null - set(value) { - writer.assign("charset", "\"$value\"") - field = value - } - - override fun setReportDir(dir: File) { - writer.assign("reportDir", dir.forScript()) - } - - override fun setReportDir(dir: Provider) { - error("Not supported!") - } - - override fun filters(config: Action) { - writer.call("filters", config) { KoverReportFiltersWriter(it) } - } - -} - -internal class KoverXmlReportConfigWriter(private val writer: FormattedWriter) : KoverXmlReportConfig { - override var onCheck: Boolean = false - set(value) { - writer.assign("onCheck", value.toString()) - field = value - } - - override val title: Property = PropertyWriter("title", writer) - - override fun setReportFile(xmlFile: File) { - writer.assign("reportFile", xmlFile.forScript()) - } - - override fun setReportFile(xmlFile: Provider) { - error("Not supported!") - } - - override fun filters(config: Action) { - writer.call("filters", config) { KoverReportFiltersWriter(it) } - } - -} - -internal class KoverBinaryReportConfigWriter(private val writer: FormattedWriter) : KoverBinaryReportConfig { - override val onCheck: Property = PropertyWriter("onCheck", writer) - - override val file: RegularFileProperty = FilePropertyWriter("file", writer) - - override fun filters(config: Action) { - writer.call("filters", config) { KoverReportFiltersWriter(it) } - } -} - -internal class KoverVerifyReportConfigWriter(private val writer: FormattedWriter) : - KoverVerificationRulesConfigWriter(writer), KoverVerifyReportConfig { - - override var onCheck: Boolean = true - set(value) { - writer.assign("onCheck", value.toString()) - field = value - } -} - -internal open class KoverVerificationRulesConfigWriter(private val writer: FormattedWriter) : - KoverVerificationRulesConfig { - - override fun rule(config: Action) { - writer.call("rule", config) { KoverVerifyRuleWriter(it) } - } - - override fun rule(name: String, config: Action) { - writer.callStr("rule", listOf(name), config) { KoverVerifyRuleWriter(it) } - } -} - -internal class KoverLogReportConfigWriter(private val writer: FormattedWriter) : KoverLogReportConfig { - override var onCheck: Boolean = true - set(value) { - writer.assign("onCheck", value.toString()) - field = value - } - - override fun filters(config: Action) { - writer.call("filters", config) { KoverReportFiltersWriter(it) } - } - - override var header: String? = null - set(value) { - if (value == null) { - writer.assign("header", "null") - } else { - writer.assign("header", "\"$value\"") - } - field = value - } - - override var format: String? = null - set(value) { - if (value == null) { - writer.assign("format", "null") - } else { - writer.assign("format", "\"$value\"") - } - field = value - } - - override var groupBy: GroupingEntityType? = null - set(value) { - if (value == null) { - writer.assign("groupBy", "null") - } else { - writer.assign("groupBy", GroupingEntityType::class.qualifiedName + "." + value) - } - field = value - } - - override var coverageUnits: MetricType? = null - set(value) { - if (value == null) { - writer.assign("coverageUnits", "null") - } else { - writer.assign("coverageUnits", MetricType::class.qualifiedName + "." + value) - } - field = value - } - override var aggregationForGroup: AggregationType? = null - set(value) { - if (value == null) { - writer.assign("aggregationForGroup", "null") - } else { - writer.assign("aggregationForGroup", AggregationType::class.qualifiedName + "." + value) - } - field = value - } -} - -internal class KoverVerifyRuleWriter(private val writer: FormattedWriter): KoverVerifyRule { - override var isEnabled: Boolean = true - set(value) { - writer.assign("isEnabled", value.toString()) - field = value - } - - override var entity: GroupingEntityType = GroupingEntityType.APPLICATION - set(value) { - writer.assign("entity", value.forScript()) - field = value - } - - override fun filters(config: Action) { - writer.call("filters", config) { KoverReportFiltersWriter(it) } - } - - override fun bound(config: Action) { - writer.call("bound", config) { KoverVerifyBoundWriter(it) } - } - - override fun bound(minValue: Int, maxValue: Int, metric: MetricType, aggregation: AggregationType) { - writer.call("bound", minValue.toString(), maxValue.toString(), metric.forScript(), aggregation.forScript()) - } - - override fun minBound(minValue: Int) { - writer.call("minBound", minValue.toString()) - } - - override fun minBound(minValue: Int, metric: MetricType, aggregation: AggregationType) { - writer.call("minBound", minValue.toString(), metric.forScript(), aggregation.forScript()) - } - - override fun maxBound(maxValue: Int) { - writer.call("maxBound", maxValue.toString()) - } - - override fun maxBound(maxValue: Int, metric: MetricType, aggregation: AggregationType) { - writer.call("maxBound", maxValue.toString(), metric.forScript(), aggregation.forScript()) - } - - @Deprecated(message = "Removed") - override var name: String? = null -} - -internal class KoverVerifyBoundWriter(private val writer: FormattedWriter): KoverVerifyBound { - override var minValue: Int? = null - set(value) { - writer.assign("minValue", value.toString()) - field = value - } - override var maxValue: Int? = null - set(value) { - writer.assign("maxValue", value.toString()) - field = value - } - override var metric: MetricType = MetricType.LINE - set(value) { - writer.assign("metric", value.forScript()) - field = value - } - override var aggregation: AggregationType = AggregationType.COVERED_PERCENTAGE - set(value) { - writer.assign("aggregation", value.forScript()) - field = value - } -} - diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/writer/KoverWriter.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/writer/KoverWriter.kt deleted file mode 100644 index bcba114c..00000000 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/writer/KoverWriter.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.kover.gradle.plugin.test.functional.framework.writer - -import kotlinx.kover.gradle.plugin.dsl.* -import org.gradle.api.* - -internal class KoverWriter(private val writer: FormattedWriter) : KoverProjectExtension { - - override fun disable() { - writer.call("disable") - } - - override fun useJacoco() { - writer.call("useJacoco") - } - - - override fun useJacoco(version: String) { - writer.call("useJacoco", version) - } - - override fun excludeJavaCode() { - writer.call("excludeJavaCode") - } - - override fun excludeTests(config: Action) { - writer.call("excludeTests", config) { KoverTestsExclusionsWriter(it) } - } - - override fun excludeSourceSets(config: Action) { - writer.call("excludeCompilations", config) { SourceSetsExclusionsWriter(it) } - } - - override fun excludeInstrumentation(config: Action) { - writer.call("excludeInstrumentation", config) { KoverInstrumentationExclusionsWriter(it) } - } -} - -private class KoverTestsExclusionsWriter(private val writer: FormattedWriter) : KoverTestsExclusions { - override fun tasks(vararg name: String) { - tasks(name.asIterable()) - } - - override fun tasks(names: Iterable) { - writer.callStr("tasks", names) - } -} - -private class SourceSetsExclusionsWriter(private val writer: FormattedWriter) : SourceSetsExclusions { - - override fun names(vararg name: String) { - names(name.toList()) - } - - override fun names(names: Iterable) { - writer.callStr("names", names) - } -} - -private class KoverInstrumentationExclusionsWriter(private val writer: FormattedWriter) : - KoverInstrumentationExclusions { - override fun classes(vararg names: String) { - this.classes(names.asIterable()) - } - - override fun classes(names: Iterable) { - writer.callStr("classes", names) - } - - override fun packages(vararg names: String) { - packages(names.asIterable()) - } - - override fun packages(names: Iterable) { - writer.callStr("packages", names) - } - -} diff --git a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/writer/Providers.kt b/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/writer/Providers.kt deleted file mode 100644 index 8bf00421..00000000 --- a/kover-gradle-plugin/src/functionalTest/kotlin/kotlinx/kover/gradle/plugin/test/functional/framework/writer/Providers.kt +++ /dev/null @@ -1,228 +0,0 @@ -package kotlinx.kover.gradle.plugin.test.functional.framework.writer - -import kotlinx.kover.gradle.plugin.commons.KoverCriticalException -import org.gradle.api.Transformer -import org.gradle.api.file.RegularFile -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Property -import org.gradle.api.provider.Provider -import java.io.File -import java.util.function.BiFunction - -internal class PropertyWriter( - private val propertyName: String, - private val writer: FormattedWriter -) : Property { - - override fun get(): T { - throw KoverCriticalException("Operation not supported in functional test") - } - - override fun getOrNull(): T? { - throw KoverCriticalException("Operation not supported in functional test") - } - - override fun isPresent(): Boolean { - throw KoverCriticalException("Operation not supported in functional test") - } - - override fun forUseAtConfigurationTime(): Provider { - writer.call("$propertyName.forUseAtConfigurationTime") - return this - } - - override fun finalizeValue() { - writer.call("$propertyName.finalizeValue") - } - - override fun finalizeValueOnRead() { - writer.call("$propertyName.finalizeValueOnRead") - } - - override fun disallowChanges() { - writer.call("$propertyName.disallowChanges") - } - - override fun disallowUnsafeRead() { - writer.call("$propertyName.disallowUnsafeRead") - } - - override fun convention(provider: Provider): Property { - writer.call("$propertyName.convention", provider.toString()) - return this - } - - override fun convention(value: T?): Property { - writer.call("$propertyName.convention", formatForProperty(value)) - return this - } - - override fun value(provider: Provider): Property { - writer.call("$propertyName.provider", provider.toString()) - return this - } - - override fun value(value: T?): Property { - writer.call("$propertyName.value", formatForProperty(value)) - return this - } - - override fun set(provider: Provider) { - writer.call("$propertyName.set", provider.toString()) - } - - override fun set(value: T?) { - writer.call("$propertyName.set", formatForProperty(value)) - } - - override fun zip(right: Provider, combiner: BiFunction): Provider { - throw KoverCriticalException("Operation not supported in functional test") - } - - override fun orElse(provider: Provider): Provider { - writer.call("$propertyName.orElse", provider.toString()) - return this - } - - override fun orElse(value: T): Provider { - writer.call("$propertyName.orElse", formatForProperty(value)) - return this - } - - override fun flatMap(transformer: Transformer?, in T>): Provider { - throw KoverCriticalException("Operation not supported in functional test") - } - - override fun map(transformer: Transformer): Provider { - throw KoverCriticalException("Operation not supported in functional test") - } - - override fun getOrElse(defaultValue: T): T { - throw KoverCriticalException("Operation not supported in functional test") - } -} - -internal class FilePropertyWriter( - private val propertyName: String, - private val writer: FormattedWriter -) : RegularFileProperty { - override fun get(): RegularFile { - throw KoverCriticalException("Operation not supported in functional test") - } - - override fun getOrNull(): RegularFile? { - throw KoverCriticalException("Operation not supported in functional test") - } - - override fun getOrElse(defaultValue: RegularFile): RegularFile { - throw KoverCriticalException("Operation not supported in functional test") - } - - override fun map(transformer: Transformer): Provider { - throw KoverCriticalException("Operation not supported in functional test") - } - - override fun flatMap(transformer: Transformer?, in RegularFile>): Provider { - throw KoverCriticalException("Operation not supported in functional test") - } - - override fun isPresent(): Boolean { - throw KoverCriticalException("Operation not supported in functional test") - } - - override fun orElse(value: RegularFile): Provider { - throw KoverCriticalException("Operation not supported in functional test") - } - - override fun orElse(provider: Provider): Provider { - throw KoverCriticalException("Operation not supported in functional test") - } - - override fun forUseAtConfigurationTime(): Provider { - TODO("Not yet implemented") - } - - override fun zip( - right: Provider, - combiner: BiFunction - ): Provider { - throw KoverCriticalException("Operation not supported in functional test") - } - - override fun finalizeValue() { - TODO("Not yet implemented") - } - - override fun finalizeValueOnRead() { - TODO("Not yet implemented") - } - - override fun disallowChanges() { - TODO("Not yet implemented") - } - - override fun disallowUnsafeRead() { - TODO("Not yet implemented") - } - - override fun set(file: File?) { - writer.call("$propertyName.set", formatForProperty(file)) - } - - override fun set(value: RegularFile?) { - writer.call("$propertyName.set", value.toString()) - } - - override fun set(provider: Provider) { - writer.call("$propertyName.set", provider.toString()) - } - - override fun value(value: RegularFile?): RegularFileProperty { - writer.call("$propertyName.value", value.toString()) - return this - } - - override fun value(provider: Provider): RegularFileProperty { - writer.call("$propertyName.value", provider.toString()) - return this - } - - override fun convention(value: RegularFile?): RegularFileProperty { - writer.call("$propertyName.convention", value.toString()) - return this - } - - override fun convention(provider: Provider): RegularFileProperty { - writer.call("$propertyName.convention", provider.toString()) - return this - } - - override fun getAsFile(): Provider { - throw KoverCriticalException("Operation not supported in functional test") - } - - override fun fileValue(file: File?): RegularFileProperty { - writer.call("$propertyName.fileValue", formatForProperty(file)) - return this - } - - override fun fileProvider(provider: Provider): RegularFileProperty { - writer.call("$propertyName.fileProvider", provider.toString()) - return this - } - - override fun getLocationOnly(): Provider { - writer.call("$propertyName.getLocationOnly") - return this - } - -} - -private fun formatForProperty(value: T): String { - return when (value) { - null -> "null" - is String -> "\"$value\"" - is File -> "file(\"$value\")" - else -> value.toString() - } -} \ No newline at end of file diff --git a/kover-gradle-plugin/src/functionalTest/templates/builds/android-inverse-order/app/build.gradle.kts b/kover-gradle-plugin/src/functionalTest/templates/builds/android-inverse-order/app/build.gradle.kts index 7c5b1b9c..d73c0159 100644 --- a/kover-gradle-plugin/src/functionalTest/templates/builds/android-inverse-order/app/build.gradle.kts +++ b/kover-gradle-plugin/src/functionalTest/templates/builds/android-inverse-order/app/build.gradle.kts @@ -48,28 +48,25 @@ dependencies { * Kover configs */ -koverReport { - defaults { - mergeWith("release") +kover { + variants { + create("custom") { + add("release") + } } - - androidReports("release") { - // filters for all report types only of 'release' build type - filters { - excludes { - classes( - "*Fragment", - "*Fragment\$*", - "*Activity", - "*Activity\$*", - "*.databinding.*", - "*.BuildConfig", - + reports { + variant("release") { + // filters for all report types only of 'release' build type + filters { + excludes { + androidGeneratedClasses() + classes( // excludes debug classes "*.DebugUtil" - ) + ) + } } } - } + } } diff --git a/kover-gradle-plugin/src/functionalTest/templates/builds/android-mpp-inverse-order/build.gradle.kts b/kover-gradle-plugin/src/functionalTest/templates/builds/android-mpp-inverse-order/build.gradle.kts index 24682298..ddd8f383 100644 --- a/kover-gradle-plugin/src/functionalTest/templates/builds/android-mpp-inverse-order/build.gradle.kts +++ b/kover-gradle-plugin/src/functionalTest/templates/builds/android-mpp-inverse-order/build.gradle.kts @@ -68,26 +68,29 @@ kotlin { */ -koverReport { - defaults { - mergeWith("release") +kover { + variants { + create("custom") { + /** + * Tests, sources, classes, and compilation tasks of the 'release' build variant will be included in the `custom` reports. + * Thus, information from the 'release' variant will be included in the `custom` report for this project and any project that specifies this project as a dependency. + */ + add("release") + } } - androidReports("release") { - // filters for all report types only of 'release' build type - filters { - excludes { - classes( - "*Fragment", - "*Fragment\$*", - "*Activity", - "*Activity\$*", - "*.databinding.*", - "*.BuildConfig", - - // excludes debug classes - "*.DebugUtil" - ) + reports { + variant("release") { + // filters for all report types only of 'release' build type + filters { + excludes { + androidGeneratedClasses() + + classes( + // excludes debug classes + "*.DebugUtil" + ) + } } } } diff --git a/kover-gradle-plugin/src/functionalTest/templates/builds/android-no-variant-for-config/app/build.gradle.kts b/kover-gradle-plugin/src/functionalTest/templates/builds/android-no-variant-for-config/app/build.gradle.kts index 61e04683..83da926a 100644 --- a/kover-gradle-plugin/src/functionalTest/templates/builds/android-no-variant-for-config/app/build.gradle.kts +++ b/kover-gradle-plugin/src/functionalTest/templates/builds/android-no-variant-for-config/app/build.gradle.kts @@ -48,8 +48,10 @@ dependencies { * Kover configs */ -koverReport { - androidReports("unexisted") { - // any config +kover { + reports { + variant("unexisted") { + // any config + } } } diff --git a/kover-gradle-plugin/src/functionalTest/templates/builds/android-no-variant-for-merge/app/build.gradle.kts b/kover-gradle-plugin/src/functionalTest/templates/builds/android-no-variant-for-merge/app/build.gradle.kts index 1a3f0d51..25918b60 100644 --- a/kover-gradle-plugin/src/functionalTest/templates/builds/android-no-variant-for-merge/app/build.gradle.kts +++ b/kover-gradle-plugin/src/functionalTest/templates/builds/android-no-variant-for-merge/app/build.gradle.kts @@ -48,8 +48,10 @@ dependencies { * Kover configs */ -koverReport { - defaults { - mergeWith("unexisted") +kover { + variants { + create("custom") { + add("unexisted") + } } } diff --git a/kover-gradle-plugin/src/functionalTest/templates/builds/disabledUnitTests/app/build.gradle.kts b/kover-gradle-plugin/src/functionalTest/templates/builds/disabledUnitTests/app/build.gradle.kts index 9a94d4eb..6bea8236 100644 --- a/kover-gradle-plugin/src/functionalTest/templates/builds/disabledUnitTests/app/build.gradle.kts +++ b/kover-gradle-plugin/src/functionalTest/templates/builds/disabledUnitTests/app/build.gradle.kts @@ -49,38 +49,27 @@ dependencies { testImplementation("junit:junit:4.13.2") } -koverReport { - // filters for all report types of all build variants - filters { - excludes { - classes( - "*Fragment", - "*Fragment\$*", - "*Activity", - "*Activity\$*", - "*.databinding.*", - "*.BuildConfig" - ) - } - } - - androidReports("release") { - // filters for all report types only of 'release' build type +kover { + reports { + // filters for all report types of all build variants filters { excludes { - classes( - "*Fragment", - "*Fragment\$*", - "*Activity", - "*Activity\$*", - "*.databinding.*", - "*.BuildConfig", + androidGeneratedClasses() + } + } + + variant("release") { + // filters for all report types only of 'release' build type + filters { + excludes { + androidGeneratedClasses() + classes( // excludes debug classes "*.DebugUtil" - ) + ) + } } } } - } diff --git a/kover-gradle-plugin/src/functionalTest/templates/builds/no-tests-jvm/build.gradle.kts b/kover-gradle-plugin/src/functionalTest/templates/builds/no-tests-jvm/build.gradle.kts index a26e525a..27f8569e 100644 --- a/kover-gradle-plugin/src/functionalTest/templates/builds/no-tests-jvm/build.gradle.kts +++ b/kover-gradle-plugin/src/functionalTest/templates/builds/no-tests-jvm/build.gradle.kts @@ -7,10 +7,12 @@ repositories { mavenCentral() } -koverReport { - verify { - rule { - minBound(50) +kover { + reports { + verify { + rule { + minBound(50) + } } } } diff --git a/kover-gradle-plugin/src/functionalTest/templates/builds/no-tests-mpp/build.gradle.kts b/kover-gradle-plugin/src/functionalTest/templates/builds/no-tests-mpp/build.gradle.kts index fa5f1108..89b5272c 100644 --- a/kover-gradle-plugin/src/functionalTest/templates/builds/no-tests-mpp/build.gradle.kts +++ b/kover-gradle-plugin/src/functionalTest/templates/builds/no-tests-mpp/build.gradle.kts @@ -16,10 +16,12 @@ repositories { * Kover configs */ -koverReport { - verify { - rule { - minBound(50) +kover { + reports { + verify { + rule { + minBound(50) + } } } } diff --git a/kover-gradle-plugin/src/functionalTest/templates/builds/sourcesets-mpp/build.gradle.kts b/kover-gradle-plugin/src/functionalTest/templates/builds/sourcesets-mpp/build.gradle.kts index 667ccbd2..20bcc468 100644 --- a/kover-gradle-plugin/src/functionalTest/templates/builds/sourcesets-mpp/build.gradle.kts +++ b/kover-gradle-plugin/src/functionalTest/templates/builds/sourcesets-mpp/build.gradle.kts @@ -8,8 +8,10 @@ repositories { } kover { - excludeSourceSets { - names("extra") + variants { + sources { + excludedSourceSets.add("extra") + } } } diff --git a/kover-gradle-plugin/src/functionalTest/templates/builds/sourcesets/build.gradle.kts b/kover-gradle-plugin/src/functionalTest/templates/builds/sourcesets/build.gradle.kts index 152acb9c..083c527d 100644 --- a/kover-gradle-plugin/src/functionalTest/templates/builds/sourcesets/build.gradle.kts +++ b/kover-gradle-plugin/src/functionalTest/templates/builds/sourcesets/build.gradle.kts @@ -10,8 +10,10 @@ repositories { sourceSets.create("extra") kover { - excludeSourceSets { - names("extra") + variants { + sources { + excludedSourceSets.add("extra") + } } } diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/api/CoverageTools.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/api/CoverageTools.kt deleted file mode 100644 index 5694a5fa..00000000 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/api/CoverageTools.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.kover.api - -import kotlinx.kover.gradle.plugin.commons.KoverMigrations.MIGRATION_0_6_TO_0_7 - - -// DEPRECATIONS -// TODO delete deprecations in 0.8.x -@Deprecated( - message = "Class was renamed removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public sealed class CoverageEngineVariant - -@Deprecated( - message = "Class has been removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public class IntellijEngine(@Suppress("UNUSED_PARAMETER") version: String) - -@Deprecated( - message = "Class has been removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public object DefaultIntellijEngine - -@Deprecated( - message = "Class has been replaced by using a function [useJacoco(String)]. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public class JacocoEngine(@Suppress("UNUSED_PARAMETER") version: String) - -@Deprecated( - message = "Class has been replaced by using a function [useJacoco()]. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public object DefaultJacocoEngine diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/api/KoverConfig.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/api/KoverConfig.kt deleted file mode 100644 index c3565298..00000000 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/api/KoverConfig.kt +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:Suppress("UNUSED_PARAMETER") - -package kotlinx.kover.api - -import kotlinx.kover.gradle.plugin.commons.KoverMigrations.MIGRATION_0_6_TO_0_7 - -@Deprecated( - message = "Class was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public open class KoverProjectConfig - -@Deprecated( - message = "Class was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public open class KoverProjectFilters - -@Deprecated( - message = "Class was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public open class KoverProjectInstrumentation - -@Deprecated( - message = "Class was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public open class KoverProjectXmlConfig - -@Deprecated( - message = "Class was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public open class KoverProjectHtmlConfig - -@Deprecated( - message = "Kover merged report config was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.WARNING -) -public open class KoverMergedConfig { - - @Deprecated( - message = "Kover merged report config was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR - ) - public fun enable() { - } - - @Deprecated( - message = "Kover merged report config was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR - ) - public fun filters(config: () -> Unit) { - } - - @Deprecated( - message = "Kover merged report config was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR - ) - public fun xmlReport(config: () -> Unit) { - } - - @Deprecated( - message = "Kover merged report config was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR - ) - public fun htmlReport(config: () -> Unit) { - } - - @Deprecated( - message = "Kover merged report config was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR - ) - public fun verify(config: () -> Unit) { - } -} - -@Deprecated( - message = "Class was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public open class KoverMergedFilters - -@Deprecated( - message = "Class was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public open class KoverMergedXmlConfig - -@Deprecated( - message = "Class was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public open class KoverMergedHtmlConfig - -@Deprecated( - message = "Class was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public open class KoverProjectsFilter - -@Deprecated( - message = "Class was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public open class KoverVerifyConfig - -@Deprecated( - message = "Class was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public open class KoverClassFilter - -@Deprecated( - message = "Class was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public open class KoverAnnotationFilter - -@Deprecated( - message = "Class was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public open class VerificationRule - -@Deprecated( - message = "Class was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public open class VerificationBound - -@Deprecated( - message = "Class was removed, use class 'kotlinx.kover.gradle.plugin.dsl.GroupingEntityType' instead. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public enum class VerificationTarget - -@Deprecated( - message = "Class was removed, use class 'kotlinx.kover.gradle.plugin.dsl.MetricType' instead. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public enum class CounterType - -@Deprecated( - message = "Class was removed, use class 'kotlinx.kover.gradle.plugin.dsl.AggregationType' instead. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public enum class VerificationValueType diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/api/KoverConstants.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/api/KoverConstants.kt deleted file mode 100644 index 4e35ad6b..00000000 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/api/KoverConstants.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.kover.api - -import kotlinx.kover.gradle.plugin.commons.KoverMigrations.MIGRATION_0_6_TO_0_7 - -@Deprecated( - message = "Class was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public object KoverNames - -@Deprecated( - message = "Class was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public object KoverPaths - -@Deprecated( - message = "Class was removed. Please refer to migration guide in order to migrate: $MIGRATION_0_6_TO_0_7", - level = DeprecationLevel.ERROR -) -public object KoverMigrations diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/api/KoverTaskExtension.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/api/KoverTaskExtension.kt deleted file mode 100644 index 47794a3c..00000000 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/api/KoverTaskExtension.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.kover.api - -import kotlinx.kover.gradle.plugin.commons.KoverMigrations - -@Deprecated( - message = "Class was removed. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - level = DeprecationLevel.WARNING -) -public open class KoverTaskExtension { - @Deprecated( - message = "Kover test task config was removed. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - level = DeprecationLevel.ERROR - ) - public val isDisabled: Boolean = false - - @Deprecated( - message = "Kover test task config was removed. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - level = DeprecationLevel.ERROR - ) - public val reportFile: Nothing? = null - - @Deprecated( - message = "Kover test task config was removed. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - level = DeprecationLevel.ERROR - ) - public val includes: MutableList = mutableListOf() - - @Deprecated( - message = "Kover test task config was removed. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - level = DeprecationLevel.ERROR - ) - public val excludes: MutableList = mutableListOf() -} - diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/api/KoverVersions.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/api/KoverVersions.kt deleted file mode 100644 index 94717359..00000000 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/api/KoverVersions.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ -package kotlinx.kover.api - -import kotlinx.kover.gradle.plugin.commons.KoverMigrations - -@Deprecated( - message = "Class has been renamed to 'kotlinx.kover.gradle.plugin.dsl.KoverVersions'. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - level = DeprecationLevel.ERROR -) -public object KoverVersions diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/KoverGradlePlugin.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/KoverGradlePlugin.kt index ab7056df..e1c305bf 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/KoverGradlePlugin.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/KoverGradlePlugin.kt @@ -4,17 +4,13 @@ package kotlinx.kover.gradle.plugin -import kotlinx.kover.api.* -import kotlinx.kover.gradle.plugin.appliers.ProjectApplier +import kotlinx.kover.gradle.plugin.appliers.finalizing +import kotlinx.kover.gradle.plugin.appliers.prepare import kotlinx.kover.gradle.plugin.dsl.KoverVersions.MINIMUM_GRADLE_VERSION +import kotlinx.kover.gradle.plugin.locators.ProvidedVariantsLocator import kotlinx.kover.gradle.plugin.util.SemVer import org.gradle.api.* import org.gradle.api.invocation.* -import org.gradle.api.tasks.testing.Test -import org.gradle.kotlin.dsl.* -import org.jetbrains.kotlin.gradle.plugin.extraProperties - -private const val LISTENER_ADDED_PROPERTY_NAME = "kover-dependency-listener-added" /** * Gradle Plugin for JVM Coverage Tools. @@ -29,10 +25,11 @@ class KoverGradlePlugin : Plugin { override fun apply(target: Project) { target.gradle.checkVersion() - val applier = ProjectApplier(target) - applier.onApply() - - target.addDeprecations() + val context = prepare(target) + ProvidedVariantsLocator(target) { provided -> + // this code will be executed in after evaluate stage, after all used plugins are finalized + context.finalizing(provided) + } } /** @@ -47,11 +44,4 @@ class KoverGradlePlugin : Plugin { ) } - @Suppress("DEPRECATION") - private fun Project.addDeprecations() { - extensions.create("koverMerged") - tasks.withType().configureEach { - this.extensions.create("kover") - } - } } diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/AppliersCommons.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/AppliersCommons.kt deleted file mode 100644 index 4e630812..00000000 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/AppliersCommons.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.kover.gradle.plugin.appliers - -import kotlinx.kover.gradle.plugin.commons.DEFAULT_KOVER_VARIANT_NAME -import kotlinx.kover.gradle.plugin.commons.htmlReportPath -import kotlinx.kover.gradle.plugin.commons.xmlReportPath -import kotlinx.kover.gradle.plugin.dsl.internal.KoverDefaultReportsConfigImpl -import org.gradle.api.file.ProjectLayout -import org.gradle.api.model.ObjectFactory -import org.gradle.kotlin.dsl.newInstance - -internal fun ObjectFactory.defaultReports(layout: ProjectLayout): KoverDefaultReportsConfigImpl { - val buildDir = layout.buildDirectory - - val reports = newInstance(this) - - reports.xml { - setReportFile(buildDir.file(xmlReportPath(DEFAULT_KOVER_VARIANT_NAME))) - onCheck = false - } - - reports.html { - setReportDir(buildDir.dir(htmlReportPath(DEFAULT_KOVER_VARIANT_NAME))) - onCheck = false - } - - return reports -} - diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/FinalizeKover.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/FinalizeKover.kt new file mode 100644 index 00000000..45b82f67 --- /dev/null +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/FinalizeKover.kt @@ -0,0 +1,159 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.appliers + +import kotlinx.kover.gradle.plugin.appliers.tasks.VariantTasks +import kotlinx.kover.gradle.plugin.appliers.artifacts.* +import kotlinx.kover.gradle.plugin.appliers.instrumentation.instrument +import kotlinx.kover.gradle.plugin.appliers.origin.AndroidVariantOrigin +import kotlinx.kover.gradle.plugin.appliers.origin.JvmVariantOrigin +import kotlinx.kover.gradle.plugin.appliers.origin.AllVariantOrigins +import kotlinx.kover.gradle.plugin.commons.JVM_VARIANT_NAME +import kotlinx.kover.gradle.plugin.commons.KoverIllegalConfigException +import kotlinx.kover.gradle.plugin.commons.ReportVariantType +import kotlinx.kover.gradle.plugin.commons.TOTAL_VARIANT_NAME +import kotlinx.kover.gradle.plugin.dsl.internal.KoverReportSetConfigImpl +import kotlinx.kover.gradle.plugin.dsl.internal.KoverVariantCreateConfigImpl +import org.gradle.kotlin.dsl.newInstance + + +/** + * The second stage of applying the Kover plugin. + * + * Objects are created that depend on the full configuration of the project: the availability of Kotlin plugins, + * the availability and settings of the Android plugin, the user settings of the Kover plugin itself. + */ +internal fun KoverContext.finalizing(origins: AllVariantOrigins) { + val jvmVariant = + origins.jvm?.createVariant(this, variantConfig(JVM_VARIANT_NAME)) + + if (jvmVariant != null) { + VariantTasks( + project, + JVM_VARIANT_NAME, + ReportVariantType.JVM, + toolProvider, + reportsConfig(JVM_VARIANT_NAME, project.path), + reporterClasspath + ).assign(jvmVariant) + } + + val androidVariants = origins.android.map { providedDetails -> + providedDetails.createVariant(this, variantConfig(providedDetails.buildVariant.buildVariant)) + } + + val variantArtifacts = mutableMapOf() + jvmVariant?.let { variantArtifacts[JVM_VARIANT_NAME] = it } + androidVariants.forEach { variantArtifacts[it.variantName] = it } + + val availableVariants = variantArtifacts.keys + projectExtension.variants.customVariants.keys + projectExtension.reports.byName.forEach { (requestedVariant, _) -> + if (requestedVariant !in availableVariants) { + throw KoverIllegalConfigException("It is not possible to configure the '$requestedVariant' variant because it does not exist") + } + } + + val totalVariant = + TotalVariantArtifacts(project, toolProvider, koverBucketConfiguration, variantConfig(TOTAL_VARIANT_NAME)) + variantArtifacts.values.forEach { totalVariant.mergeWith(it) } + totalVariantTasks.assign(totalVariant) + + projectExtension.variants.providedVariants.forEach { (name, _) -> + if (name !in variantArtifacts) { + throw KoverIllegalConfigException("It is unacceptable to configure provided variant '$name', since there is no such variant in the project.\nAcceptable variants: ${variantArtifacts.keys}") + } + } + + projectExtension.variants.customVariants.forEach { (name, config) -> + if (name == JVM_VARIANT_NAME) { + throw KoverIllegalConfigException("It is unacceptable to create a custom reports variant '$JVM_VARIANT_NAME', because this name is reserved for JVM code") + } + if (name in variantArtifacts) { + throw KoverIllegalConfigException("It is unacceptable to create a custom reports variant '$name', because this name is reserved for provided Android reports variant.") + } + + val customVariant = + CustomVariantArtifacts(project, name, toolProvider, koverBucketConfiguration, config) + + config.variantsByName.forEach { (mergedName, optionality) -> + val mergedVariant = variantArtifacts[mergedName] + if (mergedVariant != null) { + if (optionality.withDependencies) { + customVariant.mergeWithDependencies(mergedVariant) + } else { + customVariant.mergeWith(mergedVariant) + } + } else { + if (!optionality.optional) { + throw KoverIllegalConfigException("Could not find the provided variant '$mergedName' to create a custom variant '$name'.\nSpecify an existing 'jvm' variant or Android build variant name, or delete the merge.") + } + } + } + + VariantTasks( + project, + name, + ReportVariantType.CUSTOM, + toolProvider, + reportsConfig(name, project.path), + reporterClasspath + ).assign(customVariant) + + } + + androidVariants.forEach { androidVariant -> + VariantTasks( + project, + androidVariant.variantName, + ReportVariantType.ANDROID, + toolProvider, + reportsConfig(androidVariant.variantName, project.path), + reporterClasspath + ).assign(androidVariant) + } +} + +private fun KoverContext.variantConfig(variantName: String): KoverVariantCreateConfigImpl { + return projectExtension.variants.customVariants.getOrElse(variantName) { + val variantConfig = projectExtension.variants.objects.newInstance(variantName) + variantConfig.deriveFrom(projectExtension.variants) + variantConfig + } +} + +private fun KoverContext.reportsConfig(variantName: String, projectPath: String): KoverReportSetConfigImpl { + return projectExtension.reports.byName.getOrElse(variantName) { + projectExtension.reports.createVariant(variantName, projectPath) + } +} + +private fun JvmVariantOrigin.createVariant( + koverContext: KoverContext, + config: KoverVariantCreateConfigImpl, +): JvmVariantArtifacts { + tests.instrument(koverContext, config.instrumentation.excludedClasses) + return JvmVariantArtifacts( + koverContext.project, + koverContext.toolProvider, + koverContext.koverBucketConfiguration, + this, + config + ) +} + +private fun AndroidVariantOrigin.createVariant( + koverContext: KoverContext, + config: KoverVariantCreateConfigImpl, +): AndroidVariantArtifacts { + tests.instrument(koverContext, config.instrumentation.excludedClasses) + return AndroidVariantArtifacts( + koverContext.project, + buildVariant.buildVariant, + koverContext.toolProvider, + koverContext.koverBucketConfiguration, + this, + config + ) +} diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/KoverContext.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/KoverContext.kt new file mode 100644 index 00000000..6dfe7d4b --- /dev/null +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/KoverContext.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.appliers + +import kotlinx.kover.gradle.plugin.appliers.tasks.VariantTasks +import kotlinx.kover.gradle.plugin.dsl.internal.KoverExtensionImpl +import kotlinx.kover.gradle.plugin.tasks.services.KoverAgentJarTask +import kotlinx.kover.gradle.plugin.tools.CoverageTool +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.provider.Provider + +/** + * Kover objects, created at the very beginning of configuration, immediately when applying the plugin. + * + * The presence of these objects does not depend on the user's settings, its will be used during further configuration. + */ +internal class KoverContext( + val project: Project, + val projectExtension: KoverExtensionImpl, + val toolProvider: Provider, + val findAgentJarTask: Provider, + val koverBucketConfiguration: Configuration, + val agentClasspath: Configuration, + val reporterClasspath: Configuration, + val totalVariantTasks: VariantTasks +) \ No newline at end of file diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/PrepareKover.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/PrepareKover.kt new file mode 100644 index 00000000..4fa01d3e --- /dev/null +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/PrepareKover.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.appliers + +import kotlinx.kover.gradle.plugin.appliers.tasks.VariantTasks +import kotlinx.kover.gradle.plugin.commons.* +import kotlinx.kover.gradle.plugin.dsl.internal.KoverExtensionImpl +import kotlinx.kover.gradle.plugin.tasks.services.KoverAgentJarTask +import kotlinx.kover.gradle.plugin.tools.CoverageToolFactory +import org.gradle.api.Project +import org.gradle.kotlin.dsl.create +import org.gradle.kotlin.dsl.named +import org.gradle.kotlin.dsl.register + +/** + * The first stage of applying the Kover plugin. + * Objects are created that will be available in user build scripts during the evaluation step. + */ +internal fun prepare(project: Project): KoverContext { + val koverBucketConfiguration = project.configurations.create(KOVER_DEPENDENCY_NAME) { + asBucket() + } + + val projectExtension = project.extensions.create( + KOVER_PROJECT_EXTENSION_NAME, + project.objects, + project.layout, + project.path + ) + + val toolProvider = CoverageToolFactory.get(projectExtension) + + // DEPS + val agentClasspath = project.configurations.create(JVM_AGENT_CONFIGURATION_NAME) { + asTransitiveDependencies() + } + project.dependencies.add(JVM_AGENT_CONFIGURATION_NAME, toolProvider.map { tool -> tool.jvmAgentDependency }) + + project.configurations.register("koverEmptyArtifact") { + // disable generation of Kover artifacts on `assemble`, fix of https://github.com/Kotlin/kotlinx-kover/issues/353 + isVisible = false + asProducer() + attributes { + // common Kover artifact attributes + attribute(VariantNameAttr.ATTRIBUTE, project.objects.named("!kover##__empty__##")) + attribute(ProjectPathAttr.ATTRIBUTE, project.objects.named(project.path)) + } + } + + /* + * Uses lazy jar search for the agent, because an eager search will cause a resolution at the configuration stage, + * which may affect performance. + * See https://github.com/Kotlin/kotlinx-kover/issues/235 + */ + val findAgentJarTask = project.tasks.register(FIND_JAR_TASK) + findAgentJarTask.configure { + // depends on agent classpath to resolve it in execute-time + dependsOn(agentClasspath) + + this.tool.convention(toolProvider) + this.agentJar.set(project.layout.buildDirectory.map { dir -> dir.file(agentFilePath(toolProvider.get().variant)) }) + this.agentClasspath.from(agentClasspath) + } + + val reporterClasspath = project.configurations.create(JVM_REPORTER_CONFIGURATION_NAME) { + asTransitiveDependencies() + } + project.dependencies.add( + JVM_REPORTER_CONFIGURATION_NAME, + toolProvider.map { tool -> tool.jvmReporterDependency }) + project.dependencies.add( + JVM_REPORTER_CONFIGURATION_NAME, + toolProvider.map { tool -> tool.jvmReporterExtraDependency }) + + val totalReports = VariantTasks( + project, + TOTAL_VARIANT_NAME, + ReportVariantType.TOTAL, + toolProvider, + projectExtension.reports.total, + reporterClasspath + ) + + return KoverContext( + project, + projectExtension, + toolProvider, + findAgentJarTask, + koverBucketConfiguration, + agentClasspath, + reporterClasspath, + totalReports + ) +} + + diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/ProjectApplier.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/ProjectApplier.kt deleted file mode 100644 index a7a73cbe..00000000 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/ProjectApplier.kt +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.kover.gradle.plugin.appliers - -import kotlinx.kover.gradle.plugin.appliers.reports.AndroidVariantApplier -import kotlinx.kover.gradle.plugin.appliers.reports.DefaultVariantApplier -import kotlinx.kover.gradle.plugin.appliers.reports.androidReports -import kotlinx.kover.gradle.plugin.commons.* -import kotlinx.kover.gradle.plugin.dsl.KoverNames.DEPENDENCY_CONFIGURATION_NAME -import kotlinx.kover.gradle.plugin.dsl.KoverNames.PROJECT_EXTENSION_NAME -import kotlinx.kover.gradle.plugin.dsl.KoverNames.REPORT_EXTENSION_NAME -import kotlinx.kover.gradle.plugin.dsl.internal.KoverProjectExtensionImpl -import kotlinx.kover.gradle.plugin.dsl.internal.KoverReportExtensionImpl -import kotlinx.kover.gradle.plugin.locators.CompilationsListener -import kotlinx.kover.gradle.plugin.locators.CompilationsListenerManager -import kotlinx.kover.gradle.plugin.tasks.services.KoverAgentJarTask -import kotlinx.kover.gradle.plugin.tools.CoverageTool -import kotlinx.kover.gradle.plugin.tools.CoverageToolFactory -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.TaskProvider -import org.gradle.kotlin.dsl.create -import org.gradle.kotlin.dsl.register - -/** - * Main Gradle Plugin applier of the project. - */ -internal class ProjectApplier(private val project: Project) { - private lateinit var projectExtension: KoverProjectExtensionImpl - private lateinit var reportExtension: KoverReportExtensionImpl - - private val androidAppliers: MutableMap = mutableMapOf() - - /** - * The code executed right at the moment of applying of the plugin. - */ - fun onApply() { - val koverDependencies = project.configurations.create(DEPENDENCY_CONFIGURATION_NAME) { - asBucket() - } - - projectExtension = project.extensions.create(PROJECT_EXTENSION_NAME) - reportExtension = project.extensions.create(REPORT_EXTENSION_NAME) - - val toolProvider = CoverageToolFactory.get(project, projectExtension) - - // DEPS - val agentClasspath = project.configurations.create(JVM_AGENT_CONFIGURATION_NAME) { - asTransitiveDependencies() - } - project.dependencies.add(JVM_AGENT_CONFIGURATION_NAME, toolProvider.map { tool -> tool.jvmAgentDependency }) - - val reporterClasspath = project.configurations.create(JVM_REPORTER_CONFIGURATION_NAME) { - asTransitiveDependencies() - } - project.dependencies.add( - JVM_REPORTER_CONFIGURATION_NAME, - toolProvider.map { tool -> tool.jvmReporterDependency }) - project.dependencies.add( - JVM_REPORTER_CONFIGURATION_NAME, - toolProvider.map { tool -> tool.jvmReporterExtraDependency }) - - val defaultApplier = DefaultVariantApplier( - project, - koverDependencies, - reporterClasspath, - toolProvider - ) - - val instrData = collectInstrumentationData(toolProvider, agentClasspath) - - val listener = object : CompilationsListener { - override fun onJvmCompilation(kit: JvmCompilationKit) { - kit.tests.instrument(instrData) - defaultApplier.applyCompilationKit(kit) - } - - override fun onAndroidCompilations(kits: List) { - kits.forEach { kit -> - kit.tests.instrument(instrData) - val applier = - AndroidVariantApplier(project, kit.buildVariant, koverDependencies, reporterClasspath, toolProvider) - - val configs = - reportExtension.namedReports[kit.buildVariant] ?: project.androidReports(kit.buildVariant) - - applier.applyConfig(configs, reportExtension.filters, reportExtension.verify) - applier.applyCompilationKit(kit) - - androidAppliers[kit.buildVariant] = applier - } - } - - override fun onFinalize() { - reportExtension.namedReports.keys.forEach { variantName -> - if (variantName !in androidAppliers) { - throw KoverIllegalConfigException("Build variant '$variantName' not found in project '${project.path}' - impossible to configure Android reports for it.\nAvailable variations: ${androidAppliers.keys}") - } - } - - reportExtension.default.merged.forEach { variantName -> - val applier = androidAppliers[variantName] ?: throw KoverIllegalConfigException("Build variant '$variantName' not found in project '${project.path}' - impossible to merge default reports with its measurements.\n" + - "Available variations: ${androidAppliers.keys}") - defaultApplier.mergeWith(applier) - } - - defaultApplier.applyConfig(reportExtension.default, reportExtension.filters, reportExtension.verify) - } - } - - CompilationsListenerManager.locate(project, projectExtension, listener) - } - - /** - * Collect all configured data, required for online instrumentation. - */ - private fun collectInstrumentationData( - toolProvider: Provider, - agentClasspath: Configuration - ): InstrumentationData { - /* - * Uses lazy jar search for the agent, because an eager search will cause a resolution at the configuration stage, - * which may affect performance. - * See https://github.com/Kotlin/kotlinx-kover/issues/235 - */ - val findAgentJarTask = project.tasks.register(FIND_JAR_TASK) - findAgentJarTask.configure { - // depends on agent classpath to resolve it in execute-time - dependsOn(agentClasspath) - - this.tool.convention(toolProvider) - this.agentJar.set(project.layout.buildDirectory.map { dir -> dir.file(agentFilePath(toolProvider.get().variant)) }) - this.agentClasspath.from(agentClasspath) - } - - return InstrumentationData( - findAgentJarTask, - toolProvider, - projectExtension.instrumentation.classes - ) - } -} - -/** - * All configured data used in online instrumentation. - */ -internal class InstrumentationData( - val findAgentJarTask: TaskProvider, - val toolProvider: Provider, - val excludedClasses: Set -) diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/artifacts/AbstractVariantArtifacts.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/artifacts/AbstractVariantArtifacts.kt new file mode 100644 index 00000000..0069493b --- /dev/null +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/artifacts/AbstractVariantArtifacts.kt @@ -0,0 +1,105 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.appliers.artifacts + +import kotlinx.kover.gradle.plugin.commons.* +import kotlinx.kover.gradle.plugin.dsl.internal.KoverVariantConfigImpl +import kotlinx.kover.gradle.plugin.appliers.origin.VariantOrigin +import kotlinx.kover.gradle.plugin.tasks.services.KoverArtifactGenerationTask +import kotlinx.kover.gradle.plugin.tools.CoverageTool +import org.gradle.api.NamedDomainObjectProvider +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.TaskProvider +import org.gradle.kotlin.dsl.named +import org.gradle.kotlin.dsl.register + +internal sealed class AbstractVariantArtifacts( + protected val project: Project, + val variantName: String, + private val toolProvider: Provider, + private val koverBucketConfiguration: Configuration?, + private val variantConfig: KoverVariantConfigImpl +) { + internal val artifactGenTask: TaskProvider + protected val producerConfiguration: NamedDomainObjectProvider + + internal val consumerConfiguration: NamedDomainObjectProvider + + init { + artifactGenTask = project.tasks.register(artifactGenerationTaskName(variantName)) + + val buildDirectory = project.layout.buildDirectory + + artifactGenTask.configure { + artifactFile.set(buildDirectory.file(artifactFilePath(variantName))) + } + + val artifactProperty = artifactGenTask.flatMap { task -> task.artifactFile } + producerConfiguration = project.configurations.register(artifactConfigurationName(variantName)) { + // disable generation of Kover artifacts on `assemble`, fix of https://github.com/Kotlin/kotlinx-kover/issues/353 + isVisible = false + asProducer() + attributes { + // common Kover artifact attributes + attribute(VariantNameAttr.ATTRIBUTE, project.objects.named(variantName)) + attribute(ProjectPathAttr.ATTRIBUTE, project.objects.named(project.path)) + } + + outgoing.artifact(artifactProperty) { + // Before resolving this configuration, it is necessary to execute the task of generating an artifact + builtBy(artifactGenTask) + } + } + + consumerConfiguration = project.configurations.register(externalArtifactConfigurationName(variantName)) { + asConsumer() + if (koverBucketConfiguration != null) { + extendsFrom(koverBucketConfiguration) + } + } + } + + protected fun fromOrigin(origin: VariantOrigin, compilationFilter: (String) -> Boolean = { true }) { + val tests = origin.tests.matching { + // skip all tests from instrumentation if Kover Plugin is disabled for the project + !variantConfig.instrumentation.excludeAll.get() + // skip this test if it disabled by name + && it.name !in variantConfig.testTasks.excluded.getOrElse(emptySet()) + } + + // filter some compilation, e.g. JVM source sets + val compilations = origin.compilations.map { it.filterKeys(compilationFilter).values } + + // local files and compile tasks + val kotlinCompileTasks = compilations.map { compilation -> compilation.mapNotNull { it.kotlin.compileTask } } + val kotlinOutputs = compilations.map { compilation -> compilation.flatMap { it.kotlin.outputs } } + + val javaCompileTasks = + compilations.map { compilation -> if (variantConfig.classes.excludeJava.get()) emptyList() else compilation.mapNotNull { it.java.compileTask } } + val javaOutputs = + compilations.map { compilation -> if (variantConfig.classes.excludeJava.get()) emptyList() else compilation.flatMap { it.java.outputs } } + + + val sources = compilations.map { unit -> unit.flatMap { it.sources } } + val binReportFiles = project.layout.buildDirectory.dir(binReportsRootPath()) + .map { dir -> tests.map { dir.file(binReportName(it.name, toolProvider.get().variant.vendor)) } } + + artifactGenTask.configure { + // to generate an artifact, need to compile the entire project and perform all test tasks + dependsOn(tests) + dependsOn(kotlinCompileTasks) + dependsOn(javaCompileTasks) + + this.sources.from(sources) + this.outputDirs.from(kotlinOutputs) + this.outputDirs.from(javaOutputs) + this.reports.from(binReportFiles) + } + + } + +} \ No newline at end of file diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/artifacts/AndroidVariantArtifacts.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/artifacts/AndroidVariantArtifacts.kt new file mode 100644 index 00000000..16206f81 --- /dev/null +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/artifacts/AndroidVariantArtifacts.kt @@ -0,0 +1,192 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.appliers.artifacts + +import kotlinx.kover.gradle.plugin.commons.BuildTypeAttr +import kotlinx.kover.gradle.plugin.commons.ProductFlavorAttr +import kotlinx.kover.gradle.plugin.commons.ReportVariantType +import kotlinx.kover.gradle.plugin.commons.VariantOriginAttr +import kotlinx.kover.gradle.plugin.dsl.internal.KoverVariantConfigImpl +import kotlinx.kover.gradle.plugin.appliers.origin.AndroidVariantOrigin +import kotlinx.kover.gradle.plugin.tools.CoverageTool +import org.gradle.api.Named +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.attributes.* +import org.gradle.api.provider.Provider +import org.gradle.kotlin.dsl.named +import javax.inject.Inject + +internal class AndroidVariantArtifacts( + project: Project, + variantName: String, + toolProvider: Provider, + koverBucketConfiguration: Configuration, + variantSource: AndroidVariantOrigin, + variantConfig: KoverVariantConfigImpl +): AbstractVariantArtifacts(project, variantName, toolProvider, koverBucketConfiguration, variantConfig) { + init { + val androidDetails = variantSource.buildVariant + + producerConfiguration.configure { + attributes { + attribute(VariantOriginAttr.ATTRIBUTE, project.objects.named(ReportVariantType.ANDROID.name)) + + + // set android attributes for Kover Android artifact + + attribute(BuildTypeAttr.ATTRIBUTE, project.objects.named(androidDetails.buildType)) + + androidDetails.flavors.forEach { flavor -> + attribute(ProductFlavorAttr.of(flavor.dimension), project.objects.named(flavor.name)) + } + } + } + + consumerConfiguration.configure { + attributes { + // depends only on android variants + attribute(VariantOriginAttr.ATTRIBUTE, project.objects.named(ReportVariantType.ANDROID.name)) + + // set attribute requirements + attribute(BuildTypeAttr.ATTRIBUTE, project.objects.named(androidDetails.buildType)) + + androidDetails.missingDimensions.forEach { (dimension, flavorName) -> + attribute(ProductFlavorAttr.of(dimension), project.objects.named(flavorName)) + } + + androidDetails.flavors.forEach { flavor -> + attribute(ProductFlavorAttr.of(flavor.dimension), project.objects.named(flavor.name)) + } + } + } + + val schema = project.dependencies.attributesSchema + setBuildTypeStrategy(schema, androidDetails.fallbacks.buildTypes) + setupFlavorStrategy(schema, androidDetails.fallbacks.flavors) + + fromOrigin(variantSource) + } + + /* + Sources taken from Android Gradle Plugin with minor changes. + + see working with build variants https://developer.android.com/studio/build/build-variants + */ + + private fun setBuildTypeStrategy( + schema: AttributesSchema, + alternateMap: Map> + ) { + if (alternateMap.isNotEmpty()) { + val buildTypeStrategy = schema.attribute(BuildTypeAttr.ATTRIBUTE) + + buildTypeStrategy + .compatibilityRules + .add(AlternateCompatibilityRule.BuildTypeRule::class.java) { + setParams(alternateMap) + } + buildTypeStrategy + .disambiguationRules + .add(AlternateDisambiguationRule.BuildTypeRule::class.java) { + setParams(alternateMap) + } + } + } + + private fun setupFlavorStrategy( + schema: AttributesSchema, + flavorFallbacks: Map>> + ) { + // now that we know we have all the fallbacks for each dimensions, we can create the + // rule instances. + for ((dimension, alternateMap) in flavorFallbacks) { + val attr = ProductFlavorAttr.of(dimension) + val flavorStrategy = schema.attribute(attr) + flavorStrategy + .compatibilityRules + .add(AlternateCompatibilityRule.ProductFlavorRule::class.java) { + setParams(alternateMap) + } + flavorStrategy + .disambiguationRules + .add(AlternateDisambiguationRule.ProductFlavorRule::class.java) { + setParams(alternateMap) + } + } + } + + + /** alternate-based Compat rule to handle the different values of attributes. */ + internal open class AlternateCompatibilityRule + protected constructor(private val alternates: Map>) : AttributeCompatibilityRule { + override fun execute(details: CompatibilityCheckDetails) { + val producerValue: T = details.producerValue!! + val consumerValue: T = details.consumerValue!! + if (producerValue == consumerValue) { + details.compatible() + } else { + alternates[consumerValue.name]?.let { alternatesForValue -> + if (alternatesForValue.contains(producerValue.name)) { + details.compatible() + } + } + } + } + + class BuildTypeRule @Inject constructor(alternates: Map>) : + AlternateCompatibilityRule(alternates) + + class ProductFlavorRule @Inject constructor(alternates: Map>) : + AlternateCompatibilityRule(alternates) + } + + /** alternate-based Disambiguation rule to handle the different values of attributes. */ + internal open class AlternateDisambiguationRule + protected constructor( + /** Sorted alternates from high to low priority, associated to a requested value. */ + private val alternates: Map> + ) : AttributeDisambiguationRule { + override fun execute(details: MultipleCandidatesDetails) { + val consumerValue: T = details.consumerValue ?: return + val alternatesForValue = alternates[consumerValue.name] ?: return + val candidates: Set = details.candidateValues + if (candidates.contains(consumerValue)) { + details.closestMatch(consumerValue) + } else if (alternatesForValue.size == 1) { + val fallback = alternatesForValue[0] + // quick optim for single alternate + for (candidate in candidates) { + if (candidate.name == fallback) { + details.closestMatch(candidate) + return + } + } + } else { + // build a map to go from name->T + val map: MutableMap = HashMap(candidates.size) + for (candidate in candidates) { + map[candidate.name] = candidate + } + + // then go through the alternates and pick the first one + for (fallback in alternatesForValue) { + val candidate = map[fallback] + if (candidate != null) { + details.closestMatch(candidate) + return + } + } + } + } + + class BuildTypeRule @Inject constructor(alternates: Map>) : + AlternateDisambiguationRule(alternates) + + class ProductFlavorRule @Inject constructor(alternates: Map>) : + AlternateDisambiguationRule(alternates) + } + +} \ No newline at end of file diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/artifacts/CustomVariantArtifacts.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/artifacts/CustomVariantArtifacts.kt new file mode 100644 index 00000000..6213acca --- /dev/null +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/artifacts/CustomVariantArtifacts.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.appliers.artifacts + +import kotlinx.kover.gradle.plugin.commons.ReportVariantType +import kotlinx.kover.gradle.plugin.commons.VariantNameAttr +import kotlinx.kover.gradle.plugin.commons.VariantOriginAttr +import kotlinx.kover.gradle.plugin.dsl.internal.KoverVariantCreateConfigImpl +import kotlinx.kover.gradle.plugin.tools.CoverageTool +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.provider.Provider +import org.gradle.kotlin.dsl.named + +internal open class CustomVariantArtifacts( + project: Project, + variantName: String, + toolProvider: Provider, + koverBucketConfiguration: Configuration?, + variantConfig: KoverVariantCreateConfigImpl +): AbstractVariantArtifacts(project, variantName, toolProvider, koverBucketConfiguration, variantConfig) { + init { + producerConfiguration.configure { + attributes { + attribute(VariantOriginAttr.ATTRIBUTE, project.objects.named(ReportVariantType.CUSTOM.name)) + } + } + + consumerConfiguration.configure { + attributes { + // depends on custom variants + attribute(VariantOriginAttr.ATTRIBUTE, project.objects.named(ReportVariantType.CUSTOM.name)) + attribute(VariantNameAttr.ATTRIBUTE, project.objects.named(variantName)) + } + } + } + + fun mergeWith(otherVariant: AbstractVariantArtifacts) { + artifactGenTask.configure { + additionalArtifacts.from(otherVariant.artifactGenTask.map { task -> task.artifactFile }) + dependsOn(otherVariant.artifactGenTask) + } + } + + fun mergeWithDependencies(otherVariant: AbstractVariantArtifacts) { + artifactGenTask.configure { + additionalArtifacts.from( + otherVariant.artifactGenTask.map { task -> task.artifactFile }, + otherVariant.consumerConfiguration + ) + dependsOn(otherVariant.artifactGenTask, otherVariant.consumerConfiguration) + } + } +} diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/artifacts/JvmVariantArtifacts.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/artifacts/JvmVariantArtifacts.kt new file mode 100644 index 00000000..6da724ee --- /dev/null +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/artifacts/JvmVariantArtifacts.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.appliers.artifacts + +import kotlinx.kover.gradle.plugin.commons.ReportVariantType +import kotlinx.kover.gradle.plugin.commons.VariantNameAttr +import kotlinx.kover.gradle.plugin.commons.VariantOriginAttr +import kotlinx.kover.gradle.plugin.dsl.internal.KoverVariantConfigImpl +import kotlinx.kover.gradle.plugin.appliers.origin.JvmVariantOrigin +import kotlinx.kover.gradle.plugin.commons.JVM_VARIANT_NAME +import kotlinx.kover.gradle.plugin.tools.CoverageTool +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.provider.Provider +import org.gradle.kotlin.dsl.named + +internal class JvmVariantArtifacts( + project: Project, + toolProvider: Provider, + koverBucketConfiguration: Configuration, + variantOrigin: JvmVariantOrigin, + variantConfig: KoverVariantConfigImpl +) : AbstractVariantArtifacts(project, JVM_VARIANT_NAME, toolProvider, koverBucketConfiguration, variantConfig) { + init { + producerConfiguration.configure { + attributes { + attribute(VariantOriginAttr.ATTRIBUTE, project.objects.named(ReportVariantType.JVM.name)) + } + } + + consumerConfiguration.configure { + attributes { + // depends on JVM-only variants + attribute(VariantOriginAttr.ATTRIBUTE, project.objects.named(ReportVariantType.JVM.name)) + attribute(VariantNameAttr.ATTRIBUTE, project.objects.named(variantName)) + } + } + + fromOrigin(variantOrigin) { compilationName -> + compilationName !in variantConfig.classes.excludedSourceSets.get() + && compilationName != "test" + } + } + +} \ No newline at end of file diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/artifacts/TotalVariantArtifacts.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/artifacts/TotalVariantArtifacts.kt new file mode 100644 index 00000000..60977339 --- /dev/null +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/artifacts/TotalVariantArtifacts.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.appliers.artifacts + +import kotlinx.kover.gradle.plugin.commons.ReportVariantType +import kotlinx.kover.gradle.plugin.commons.TOTAL_VARIANT_NAME +import kotlinx.kover.gradle.plugin.commons.VariantNameAttr +import kotlinx.kover.gradle.plugin.commons.VariantOriginAttr +import kotlinx.kover.gradle.plugin.dsl.internal.KoverVariantConfigImpl +import kotlinx.kover.gradle.plugin.tools.CoverageTool +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.provider.Provider +import org.gradle.kotlin.dsl.named + +internal class TotalVariantArtifacts( + project: Project, + toolProvider: Provider, + koverBucketConfiguration: Configuration, + variantConfig: KoverVariantConfigImpl +): AbstractVariantArtifacts(project, TOTAL_VARIANT_NAME, toolProvider, koverBucketConfiguration, variantConfig) { + init { + producerConfiguration.configure { + attributes { + attribute(VariantOriginAttr.ATTRIBUTE, project.objects.named(ReportVariantType.TOTAL.name)) + } + } + + consumerConfiguration.configure { + attributes { + // depends on total-only variants + attribute(VariantOriginAttr.ATTRIBUTE, project.objects.named(ReportVariantType.TOTAL.name)) + attribute(VariantNameAttr.ATTRIBUTE, project.objects.named(TOTAL_VARIANT_NAME)) + } + } + } + + fun mergeWith(otherVariant: AbstractVariantArtifacts) { + artifactGenTask.configure { + /* + We take only `artifactGenTask` but not the `consumerConfiguration` because total task + takes only dependencies with origin total. + + This will protect from the problem if there is no overlapping set of variants in the dependencies: + e.g. current project have `custom` report variant, but it missed in dependency. + */ + additionalArtifacts.from(otherVariant.artifactGenTask.map { task -> task.artifactFile }) + dependsOn(otherVariant.artifactGenTask) + } + } +} \ No newline at end of file diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/JvmTestTaskConfigurator.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/instrumentation/JvmTestTaskConfigurator.kt similarity index 64% rename from kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/JvmTestTaskConfigurator.kt rename to kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/instrumentation/JvmTestTaskConfigurator.kt index 3131eeed..0073751d 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/JvmTestTaskConfigurator.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/instrumentation/JvmTestTaskConfigurator.kt @@ -2,13 +2,12 @@ * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ -package kotlinx.kover.gradle.plugin.appliers +package kotlinx.kover.gradle.plugin.appliers.instrumentation +import kotlinx.kover.gradle.plugin.appliers.KoverContext import kotlinx.kover.gradle.plugin.commons.binReportPath -import kotlinx.kover.gradle.plugin.dsl.* import kotlinx.kover.gradle.plugin.tools.* import org.gradle.api.* -import org.gradle.api.artifacts.* import org.gradle.api.file.RegularFile import org.gradle.api.provider.* import org.gradle.api.tasks.* @@ -19,46 +18,35 @@ import java.io.File /** * Add online instrumentation to all JVM test tasks. */ -internal fun TaskCollection.instrument(data: InstrumentationData) { +internal fun TaskCollection.instrument(koverContext: KoverContext, excludedClasses: Provider>) { configureEach { - JvmTestTaskConfigurator(this, data).apply() - } -} - -/** - * Gradle Plugin applier of the specific JVM test task. - */ -internal class JvmTestTaskConfigurator( - private val testTask: Test, - private val data: InstrumentationData -) { - fun apply() { val binReportProvider = - testTask.project.layout.buildDirectory.map { dir -> - dir.file(binReportPath(testTask.name, data.toolProvider.get().variant.vendor)) + project.layout.buildDirectory.map { dir -> + dir.file(binReportPath(name, koverContext.toolProvider.get().variant.vendor)) } - testTask.dependsOn(data.findAgentJarTask) + dependsOn(koverContext.findAgentJarTask) - testTask.doFirst { + doFirst { // delete report so that when the data is re-measured, it is not appended to an already existing file // see https://github.com/Kotlin/kotlinx-kover/issues/489 binReportProvider.get().asFile.delete() } - - val excluded = data.excludedClasses + - listOf( + // Always excludes android classes, see https://github.com/Kotlin/kotlinx-kover/issues/89 + val excludedClassesWithAndroid = excludedClasses.map { it + + setOf( // Always excludes android classes, see https://github.com/Kotlin/kotlinx-kover/issues/89 "android.*", "com.android.*", // excludes JVM internal classes, in some cases, errors occur when trying to instrument these classes, for example, when using JaCoCo + Robolectric. There is also no point in instrumenting them in Kover. "jdk.internal.*" ) + } - testTask.jvmArgumentProviders += JvmTestTaskArgumentProvider( - testTask.temporaryDir, - data.toolProvider, - data.findAgentJarTask.map { it.agentJar.get().asFile }, - excluded, + jvmArgumentProviders += JvmTestTaskArgumentProvider( + temporaryDir, + koverContext.toolProvider, + koverContext.findAgentJarTask.map { it.agentJar.get().asFile }, + excludedClassesWithAndroid, binReportProvider ) } @@ -77,7 +65,7 @@ private class JvmTestTaskArgumentProvider( val agentJar: Provider, @get:Input - val excludedClasses: Set, + val excludedClasses: Provider>, @get:OutputFile val reportProvider: Provider @@ -93,7 +81,8 @@ private class JvmTestTaskArgumentProvider( } override fun asArguments(): MutableIterable { - return toolProvider.get().jvmAgentArgs(agentJar.get(), tempDir, reportProvider.get().asFile, excludedClasses) + return toolProvider.get() + .jvmAgentArgs(agentJar.get(), tempDir, reportProvider.get().asFile, excludedClasses.get()) .toMutableList() } } diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/origin/VariantOrigin.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/origin/VariantOrigin.kt new file mode 100644 index 00000000..3db550c0 --- /dev/null +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/origin/VariantOrigin.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.appliers.origin + +import kotlinx.kover.gradle.plugin.commons.AndroidBuildVariant +import org.gradle.api.Task +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.TaskCollection +import org.gradle.api.tasks.testing.Test +import java.io.File + +/** + * Common interface for all variant origins. + */ +internal interface VariantOrigin { + val tests: TaskCollection + + /** + * The compilation units are associated with their names. + * E.g. each source set in Kotlin JVM has its own compilation. + */ + val compilations: Provider> +} + +/** + * Represents details about some independent compilation unit. + */ +internal class CompilationDetails( + /** + * Directories with all source files. + * + * They are not separated by languages because both `.java` and `.kt` files can be located in the same directory. + */ + val sources: Set, + + /** + * Compilation details specific to Kotlin code. + */ + val kotlin: LanguageCompilation, + + /** + * Compilation details specific to Java code. + */ + val java: LanguageCompilation +) + +/** + * Information about compilation unit for specific language (Kotlin or Java) + */ +internal class LanguageCompilation( + /** + * Directories with compiled classes, outputs of [compileTasks]. + */ + val outputs: Set, + + /** + * In case when no one compile tasks will be triggered, + * output dirs will be empty and reporter can't determine project classes. + * + * So compile tasks must be triggered anyway. + */ + val compileTask: Task? +) + +internal class JvmVariantOrigin( + override val tests: TaskCollection, + override val compilations: Provider>, +) : VariantOrigin + +internal class AndroidVariantOrigin( + override val tests: TaskCollection, + override val compilations: Provider>, + val buildVariant: AndroidBuildVariant +) : VariantOrigin + +internal class AllVariantOrigins(val jvm: JvmVariantOrigin?, val android: List) diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/reports/AndroidVariantApplier.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/reports/AndroidVariantApplier.kt deleted file mode 100644 index 5a4426f5..00000000 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/reports/AndroidVariantApplier.kt +++ /dev/null @@ -1,215 +0,0 @@ -package kotlinx.kover.gradle.plugin.appliers.reports - -import kotlinx.kover.gradle.plugin.commons.* -import kotlinx.kover.gradle.plugin.commons.AndroidVariantCompilationKit -import kotlinx.kover.gradle.plugin.commons.BuildTypeAttr -import kotlinx.kover.gradle.plugin.commons.ProductFlavorAttr -import kotlinx.kover.gradle.plugin.commons.ReportsVariantType -import kotlinx.kover.gradle.plugin.dsl.internal.KoverReportsConfigImpl -import kotlinx.kover.gradle.plugin.tools.CoverageTool -import org.gradle.api.Named -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.attributes.* -import org.gradle.api.file.ProjectLayout -import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.Provider -import org.gradle.kotlin.dsl.named -import org.gradle.kotlin.dsl.newInstance -import javax.inject.Inject - -/** - * Apply plugin for Android build variant: instrument unit-tests, create generation tasks and set of report tasks. - */ -internal class AndroidVariantApplier( - private val project: Project, - buildVariantName: String, - koverDependencies: Configuration, - reportClasspath: Configuration, - toolProvider: Provider -) : ReportsVariantApplier( - project, - buildVariantName, - ReportsVariantType.ANDROID, - koverDependencies, - reportClasspath, - toolProvider -) { - fun applyCompilationKit(kit: AndroidVariantCompilationKit) { - applyCommonCompilationKit(kit) - - config.configure { - attributes { - // set android attributes for Kover Android artifact - - attribute(BuildTypeAttr.ATTRIBUTE, project.objects.named(kit.buildType)) - - kit.flavors.forEach { flavor -> - attribute(ProductFlavorAttr.of(flavor.dimension), project.objects.named(flavor.name)) - } - } - } - - dependencies.configure { - attributes { - // set attribute requirements - attribute(KoverMarkerAttr.ATTRIBUTE, project.objects.named("Kover")) - - attribute(BuildTypeAttr.ATTRIBUTE, project.objects.named(kit.buildType)) - - kit.missingDimensions.forEach { (dimension, flavorName) -> - attribute(ProductFlavorAttr.of(dimension), project.objects.named(flavorName)) - } - - kit.flavors.forEach { flavor -> - attribute(ProductFlavorAttr.of(flavor.dimension), project.objects.named(flavor.name)) - } - } - } - - val schema = project.dependencies.attributesSchema - setBuildTypeStrategy(schema, kit.fallbacks.buildTypes) - setupFlavorStrategy(schema, kit.fallbacks.flavors) - } -} - -internal fun ObjectFactory.androidReports(variant: String, layout: ProjectLayout): KoverReportsConfigImpl { - val buildDir = layout.buildDirectory - val reports = newInstance(this) - - reports.xml { - setReportFile(buildDir.file(xmlReportPath(variant))) - onCheck = false - } - - reports.html { - setReportDir(buildDir.dir(htmlReportPath(variant))) - onCheck = false - } - - return reports -} - -internal fun Project.androidReports(variant: String): KoverReportsConfigImpl { - return objects.androidReports(variant, layout) -} - - -/* -Sources taken from Android Gradle Plugin with minor changes. - -see working with build variants https://developer.android.com/studio/build/build-variants - */ - -private fun setBuildTypeStrategy( - schema: AttributesSchema, - alternateMap: Map> -) { - if (alternateMap.isNotEmpty()) { - val buildTypeStrategy = schema.attribute(BuildTypeAttr.ATTRIBUTE) - - buildTypeStrategy - .compatibilityRules - .add(AlternateCompatibilityRule.BuildTypeRule::class.java) { - setParams(alternateMap) - } - buildTypeStrategy - .disambiguationRules - .add(AlternateDisambiguationRule.BuildTypeRule::class.java) { - setParams(alternateMap) - } - } -} - -private fun setupFlavorStrategy( - schema: AttributesSchema, - flavorFallbacks: Map>> -) { - // now that we know we have all the fallbacks for each dimensions, we can create the - // rule instances. - for ((dimension, alternateMap) in flavorFallbacks) { - val attr = ProductFlavorAttr.of(dimension) - val flavorStrategy = schema.attribute(attr) - flavorStrategy - .compatibilityRules - .add(AlternateCompatibilityRule.ProductFlavorRule::class.java) { - setParams(alternateMap) - } - flavorStrategy - .disambiguationRules - .add(AlternateDisambiguationRule.ProductFlavorRule::class.java) { - setParams(alternateMap) - } - } -} - - -/** alternate-based Compat rule to handle the different values of attributes. */ -internal open class AlternateCompatibilityRule -protected constructor(private val alternates: Map>) : AttributeCompatibilityRule { - override fun execute(details: CompatibilityCheckDetails) { - val producerValue: T = details.producerValue!! - val consumerValue: T = details.consumerValue!! - if (producerValue == consumerValue) { - details.compatible() - } else { - alternates[consumerValue.name]?.let { alternatesForValue -> - if (alternatesForValue.contains(producerValue.name)) { - details.compatible() - } - } - } - } - - class BuildTypeRule @Inject constructor(alternates: Map>) : - AlternateCompatibilityRule(alternates) - - class ProductFlavorRule @Inject constructor(alternates: Map>) : - AlternateCompatibilityRule(alternates) -} - -/** alternate-based Disambiguation rule to handle the different values of attributes. */ -internal open class AlternateDisambiguationRule -protected constructor( - /** Sorted alternates from high to low priority, associated to a requested value. */ - private val alternates: Map> -) : AttributeDisambiguationRule { - override fun execute(details: MultipleCandidatesDetails) { - val consumerValue: T = details.consumerValue ?: return - val alternatesForValue = alternates[consumerValue.name] ?: return - val candidates: Set = details.candidateValues - if (candidates.contains(consumerValue)) { - details.closestMatch(consumerValue) - } else if (alternatesForValue.size == 1) { - val fallback = alternatesForValue[0] - // quick optim for single alternate - for (candidate in candidates) { - if (candidate.name == fallback) { - details.closestMatch(candidate) - return - } - } - } else { - // build a map to go from name->T - val map: MutableMap = HashMap(candidates.size) - for (candidate in candidates) { - map[candidate.name] = candidate - } - - // then go through the alternates and pick the first one - for (fallback in alternatesForValue) { - val candidate = map[fallback] - if (candidate != null) { - details.closestMatch(candidate) - return - } - } - } - } - - class BuildTypeRule @Inject constructor(alternates: Map>) : - AlternateDisambiguationRule(alternates) - - class ProductFlavorRule @Inject constructor(alternates: Map>) : - AlternateDisambiguationRule(alternates) -} diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/reports/DefaultVariantApplier.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/reports/DefaultVariantApplier.kt deleted file mode 100644 index 2fe4d7a0..00000000 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/reports/DefaultVariantApplier.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.kover.gradle.plugin.appliers.reports - -import kotlinx.kover.gradle.plugin.commons.* -import kotlinx.kover.gradle.plugin.tools.CoverageTool -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.provider.Provider -import org.gradle.kotlin.dsl.named - -internal class DefaultVariantApplier( - private val project: Project, - koverDependencies: Configuration, - reportClasspath: Configuration, - toolProvider: Provider -) : ReportsVariantApplier( - project, - DEFAULT_KOVER_VARIANT_NAME, - ReportsVariantType.DEFAULT, - koverDependencies, - reportClasspath, - toolProvider -) { - init { - // always configure dependencies because variant name is constant for default reports - dependencies.configure { - attributes { - attribute(KoverMarkerAttr.ATTRIBUTE, project.objects.named("Kover")) - attribute(VariantNameAttr.ATTRIBUTE, project.objects.named(DEFAULT_KOVER_VARIANT_NAME)) - } - } - } - - fun applyCompilationKit(kit: JvmCompilationKit) { - applyCommonCompilationKit(kit) - } -} diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/reports/ReportsVariantApplier.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/reports/ReportsVariantApplier.kt index 45591e05..e69de29b 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/reports/ReportsVariantApplier.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/reports/ReportsVariantApplier.kt @@ -1,307 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.kover.gradle.plugin.appliers.reports - -import kotlinx.kover.gradle.plugin.commons.* -import kotlinx.kover.gradle.plugin.commons.VariantNameAttr -import kotlinx.kover.gradle.plugin.commons.ProjectPathAttr -import kotlinx.kover.gradle.plugin.commons.ReportsVariantType -import kotlinx.kover.gradle.plugin.commons.artifactFilePath -import kotlinx.kover.gradle.plugin.commons.artifactGenerationTaskName -import kotlinx.kover.gradle.plugin.commons.asConsumer -import kotlinx.kover.gradle.plugin.commons.externalArtifactConfigurationName -import kotlinx.kover.gradle.plugin.commons.htmlReportTaskName -import kotlinx.kover.gradle.plugin.commons.artifactConfigurationName -import kotlinx.kover.gradle.plugin.dsl.AggregationType -import kotlinx.kover.gradle.plugin.dsl.GroupingEntityType -import kotlinx.kover.gradle.plugin.dsl.MetricType -import kotlinx.kover.gradle.plugin.dsl.internal.* -import kotlinx.kover.gradle.plugin.dsl.internal.KoverReportFiltersImpl -import kotlinx.kover.gradle.plugin.dsl.internal.KoverReportsConfigImpl -import kotlinx.kover.gradle.plugin.dsl.internal.KoverVerifyBoundImpl -import kotlinx.kover.gradle.plugin.dsl.internal.KoverVerifyRuleImpl -import kotlinx.kover.gradle.plugin.tasks.reports.* -import kotlinx.kover.gradle.plugin.tasks.reports.AbstractKoverReportTask -import kotlinx.kover.gradle.plugin.tasks.reports.KoverHtmlTask -import kotlinx.kover.gradle.plugin.tasks.reports.KoverVerifyTask -import kotlinx.kover.gradle.plugin.tasks.reports.KoverXmlTask -import kotlinx.kover.gradle.plugin.tasks.services.KoverArtifactGenerationTask -import kotlinx.kover.gradle.plugin.tasks.services.KoverPrintLogTask -import kotlinx.kover.gradle.plugin.tools.CoverageTool -import org.gradle.api.NamedDomainObjectProvider -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.TaskContainer -import org.gradle.api.tasks.TaskProvider -import org.gradle.kotlin.dsl.named -import org.gradle.kotlin.dsl.register -import org.gradle.language.base.plugins.LifecycleBasePlugin - -internal abstract class ReportsVariantApplier( - private val project: Project, - private val variantName: String, - private val type: ReportsVariantType, - private val koverDependencies: Configuration, - private val reportClasspath: Configuration, - private val toolProvider: Provider -) { - private val htmlTask: TaskProvider - private val xmlTask: TaskProvider - private val binTask: TaskProvider - private val verifyTask: TaskProvider - private val logTask: TaskProvider - - private val artifactGenTask: TaskProvider - protected val config: NamedDomainObjectProvider - protected val dependencies: NamedDomainObjectProvider - - init { - artifactGenTask = project.tasks.register(artifactGenerationTaskName(variantName)) - - val artifactProperty = project.layout.buildDirectory.file(artifactFilePath(variantName)) - artifactGenTask.configure { - artifactFile.set(artifactProperty) - } - - config = project.configurations.register(artifactConfigurationName(variantName)) { - // disable generation of Kover artifacts on `assemble`, fix of https://github.com/Kotlin/kotlinx-kover/issues/353 - isVisible = false - outgoing.artifact(artifactProperty) { - asProducer() - attributes { - // common Kover artifact attributes - attribute(KoverMarkerAttr.ATTRIBUTE, project.objects.named("Kover")) - attribute(VariantNameAttr.ATTRIBUTE, project.objects.named(variantName)) - attribute(ProjectPathAttr.ATTRIBUTE, project.objects.named(project.path)) - } - // Before resolving this configuration, it is necessary to execute the task of generating an artifact - builtBy(artifactGenTask) - } - } - - dependencies = project.configurations.register(externalArtifactConfigurationName(variantName)) { - asConsumer() - isTransitive = false - extendsFrom(koverDependencies) - } - - - htmlTask = project.tasks.createReportTask( - htmlReportTaskName(variantName), - htmlTaskDescription() - ) - xmlTask = project.tasks.createReportTask( - xmlReportTaskName(variantName), - xmlTaskDescription() - ) - binTask = project.tasks.createReportTask( - binaryReportTaskName(variantName), - icTaskDescription() - ) - verifyTask = project.tasks.createReportTask( - verifyTaskName(variantName), - verifyTaskDescription() - ) - logTask = project.tasks.createReportTask( - logTaskName(variantName), - logTaskDescription() - ) - val printCoverageTask = project.tasks.register(printLogTaskName(variantName)) - printCoverageTask.configure { - fileWithMessage.convention(logTask.flatMap { it.outputFile }) - onlyIf { - fileWithMessage.asFile.get().exists() - } - } - logTask.configure { - finalizedBy(printCoverageTask) - } - } - - - fun applyConfig( - reportConfig: KoverReportsConfigImpl, - commonFilters: KoverReportFiltersImpl? = null, - commonVerify: KoverVerificationRulesConfigImpl? = null - ) { - reportConfig.binary.onCheck.convention(false) - reportConfig.binary.file.convention(project.layout.buildDirectory.file(binaryReportPath(variantName))) - reportConfig.xml.title.convention("Kover Gradle Plugin XML report for ${project.path}") - - val runOnCheck = mutableListOf() - - htmlTask.configure { - onlyIf { printPath() } - - reportDir.convention(project.layout.dir(reportConfig.html.reportDirProperty)) - title.convention(reportConfig.html.title ?: project.name) - charset.convention(reportConfig.html.charset) - filters.set((reportConfig.html.filters ?: reportConfig.filters ?: commonFilters).convert()) - } - if (reportConfig.html.onCheck) { - runOnCheck += htmlTask - } - - xmlTask.configure { - reportFile.convention(project.layout.file(reportConfig.xml.reportFileProperty)) - title.convention(reportConfig.xml.title) - filters.set((reportConfig.xml.filters ?: reportConfig.filters ?: commonFilters).convert()) - } - if (reportConfig.xml.onCheck) { - runOnCheck += xmlTask - } - - binTask.configure { - file.convention(reportConfig.binary.file) - filters.set((reportConfig.binary.filters ?: reportConfig.filters ?: commonFilters).convert()) - } - runOnCheck += reportConfig.binary.onCheck.map { run -> - if (run) listOf(binTask) else emptyList() - } - - verifyTask.configure { - val resultRules = reportConfig.verify?.rules ?: commonVerify?.rules ?: emptyList() - val converted = resultRules.map { it.convert() } - - // path can't be changed - resultFile.convention(project.layout.buildDirectory.file(verificationErrorsPath(variantName))) - filters.set((reportConfig.verify?.filters ?: reportConfig.filters ?: commonFilters).convert()) - rules.addAll(converted) - - shouldRunAfter(htmlTask) - shouldRunAfter(xmlTask) - shouldRunAfter(binTask) - } - if (reportConfig.verify?.onCheck == true || (reportConfig.verify == null && variantName == DEFAULT_KOVER_VARIANT_NAME)) { - runOnCheck += verifyTask - } - - logTask.configure { - header.convention(reportConfig.log.header) - lineFormat.convention(reportConfig.log.format ?: " line coverage: %") - groupBy.convention(reportConfig.log.groupBy ?: GroupingEntityType.APPLICATION) - coverageUnits.convention(reportConfig.log.coverageUnits ?: MetricType.LINE) - aggregationForGroup.convention(reportConfig.log.aggregationForGroup ?: AggregationType.COVERED_PERCENTAGE) - outputFile.convention(project.layout.buildDirectory.file(coverageLogPath(variantName))) - - filters.set((reportConfig.log.filters ?: reportConfig.filters ?: commonFilters).convert()) - } - if (reportConfig.log.onCheck) { - runOnCheck += logTask - } - - project.tasks - .matching { it.name == LifecycleBasePlugin.CHECK_TASK_NAME } - .configureEach { dependsOn(runOnCheck) } - } - - fun mergeWith(otherVariant: ReportsVariantApplier) { - artifactGenTask.configure { - additionalArtifacts.from( - otherVariant.artifactGenTask.map { task -> task.artifactFile }, - otherVariant.dependencies - ) - dependsOn(otherVariant.artifactGenTask, otherVariant.dependencies) - } - } - - protected fun applyCommonCompilationKit(kit: CompilationKit) { - val tests = kit.tests - val compilations = kit.compilations.map { it.values } - - // local files and compile tasks - val compileTasks = compilations.map { unit -> unit.flatMap { it.compileTasks } } - val outputs = compilations.map { unit -> unit.flatMap { it.outputs } } - val sources = compilations.map { unit -> unit.flatMap { it.sources } } - val binReportFiles = project.layout.buildDirectory.dir(binReportsRootPath()) - .map { dir -> tests.map { dir.file(binReportName(it.name, toolProvider.get().variant.vendor)) } } - - artifactGenTask.configure { - // to generate an artifact, need to compile the entire project and perform all test tasks - dependsOn(tests) - dependsOn(compileTasks) - - this.sources.from(sources) - this.outputDirs.from(outputs) - this.reports.from(binReportFiles) - } - } - - private inline fun TaskContainer.createReportTask( - name: String, - taskDescription: String - ): TaskProvider { - val task = register(name) - task.configure { - group = LifecycleBasePlugin.VERIFICATION_GROUP - description = taskDescription - tool.convention(toolProvider) - reportClasspath.from(this@ReportsVariantApplier.reportClasspath) - - dependsOn(artifactGenTask) - dependsOn(dependencies) - - localArtifact.set(artifactGenTask.flatMap { task -> task.artifactFile }) - externalArtifacts.from(dependencies) - } - return task - } - - private fun xmlTaskDescription(): String { - return when (type) { - ReportsVariantType.DEFAULT -> "Task to generate XML coverage report for JVM project or Kotlin/MPP JVM targets. Android measurements for specific build variant can be merged" - ReportsVariantType.ANDROID -> "Task to generate XML coverage report for '$variantName' Android build variant" - } - } - - private fun icTaskDescription(): String { - return when (type) { - ReportsVariantType.DEFAULT -> "Task to generate binary coverage report in IntelliJ format for JVM project or Kotlin/MPP JVM targets. Android measurements for specific build variant can be merged" - ReportsVariantType.ANDROID -> "Task to generate binary coverage report in IntelliJ format for '$variantName' Android build variant" - } - } - - private fun htmlTaskDescription(): String { - return when (type) { - ReportsVariantType.DEFAULT -> "Task to generate HTML coverage report for JVM project or Kotlin/MPP JVM targets. Android measurements for specific build variant can be merged" - ReportsVariantType.ANDROID -> "Task to generate HTML coverage report for '$variantName' Android build variant" - } - } - - private fun verifyTaskDescription(): String { - return when (type) { - ReportsVariantType.DEFAULT -> "Task to validate coverage bounding rules for JVM project or Kotlin/MPP JVM targets. Android measurements for specific build variant can be merged" - ReportsVariantType.ANDROID -> "Task to validate coverage bounding rules for '$variantName' Android build variant" - } - } - - private fun logTaskDescription(): String { - return when (type) { - ReportsVariantType.DEFAULT -> "Task to print coverage to log for JVM project or Kotlin/MPP JVM targets. Android measurements for specific build variant can be merged" - ReportsVariantType.ANDROID -> "Task to print coverage to log for '$variantName' Android build variant" - } - } -} - - -private fun KoverVerifyRuleImpl.convert(): VerificationRule { - return VerificationRule(isEnabled, filters?.convert(), internalName, entity, bounds.map { it.convert() }) -} - -private fun KoverVerifyBoundImpl.convert(): VerificationBound { - return VerificationBound(minValue?.toBigDecimal(), maxValue?.toBigDecimal(), metric, aggregation) -} - -private fun KoverReportFiltersImpl?.convert(): ReportFilters { - this ?: return emptyFilters - - return ReportFilters( - includesIntern.classes, includesIntern.annotations, - excludesIntern.classes, excludesIntern.annotations - ) -} - -private val emptyFilters = ReportFilters() diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/tasks/VariantTasks.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/tasks/VariantTasks.kt new file mode 100644 index 00000000..568f6f4a --- /dev/null +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/appliers/tasks/VariantTasks.kt @@ -0,0 +1,202 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.appliers.tasks + +import kotlinx.kover.gradle.plugin.appliers.artifacts.AbstractVariantArtifacts +import kotlinx.kover.gradle.plugin.commons.* +import kotlinx.kover.gradle.plugin.dsl.KoverVerifyBound +import kotlinx.kover.gradle.plugin.dsl.internal.KoverReportFiltersConfigImpl +import kotlinx.kover.gradle.plugin.dsl.internal.KoverReportSetConfigImpl +import kotlinx.kover.gradle.plugin.dsl.internal.KoverVerifyRuleImpl +import kotlinx.kover.gradle.plugin.tasks.reports.* +import kotlinx.kover.gradle.plugin.tasks.services.KoverPrintLogTask +import kotlinx.kover.gradle.plugin.tools.CoverageTool +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.artifacts.Configuration +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider +import org.gradle.kotlin.dsl.register +import org.gradle.language.base.plugins.LifecycleBasePlugin + + +internal class VariantTasks( + private val project: Project, + private val variantName: String, + private val type: ReportVariantType, + private val toolProvider: Provider, + private val config: KoverReportSetConfigImpl, + private val reporterConfiguration: Configuration +) { + private val htmlTask: TaskProvider + private val xmlTask: TaskProvider + private val binTask: TaskProvider + private val verifyTask: TaskProvider + private val logTask: TaskProvider + + init { + htmlTask = project.tasks.createReportTask( + htmlReportTaskName(variantName), + "Task to generate HTML coverage report for ${variantSuffix()}" + ) + xmlTask = project.tasks.createReportTask( + xmlReportTaskName(variantName), + "Task to generate XML coverage report for ${variantSuffix()}" + ) + binTask = project.tasks.createReportTask( + binaryReportTaskName(variantName), + "Task to generate binary coverage report in IntelliJ format for ${variantSuffix()}" + ) + + verifyTask = project.tasks.createReportTask( + verifyTaskName(variantName), + "Task to validate coverage bounding rules for ${variantSuffix()}" + ) + logTask = project.tasks.createReportTask( + logTaskName(variantName), + "Task to print coverage to log for ${variantSuffix()}" + ) + val printCoverageTask = project.tasks.register(printLogTaskName(variantName)) + + val runOnCheck = mutableListOf>>>() + + htmlTask.configure { + onlyIf { printPath(); true } + + reportDir.convention(config.html.htmlDir) + title.convention(config.html.title.orElse(project.name)) + charset.convention(config.html.charset) + filters.set((config.filters).convert()) + } + runOnCheck += config.html.onCheck.map { run -> + if (run) listOf(htmlTask) else emptyList() + } + + xmlTask.configure { + reportFile.convention(config.xml.xmlFile) + title.convention(config.xml.title) + filters.set((config.filters).convert()) + } + runOnCheck += config.xml.onCheck.map { run -> + if (run) listOf(xmlTask) else emptyList() + } + + binTask.configure { + file.convention(config.binary.file) + filters.set((config.filters).convert()) + } + runOnCheck += config.binary.onCheck.map { run -> + if (run) listOf(binTask) else emptyList() + } + + verifyTask.configure { + val resultRules = config.verify.rules + val converted = resultRules.map { rules -> rules.map { it.convert() } } + + // path can't be changed + resultFile.convention(project.layout.buildDirectory.file(verificationErrorsPath(variantName))) + + filters.set((config.filters).convert()) + rules.addAll(converted) + + shouldRunAfter(htmlTask) + shouldRunAfter(xmlTask) + shouldRunAfter(binTask) + shouldRunAfter(logTask) + } + runOnCheck += config.verify.onCheck.map { run -> + if (run) listOf(verifyTask) else emptyList() + } + + printCoverageTask.configure { + fileWithMessage.convention(logTask.flatMap { it.outputFile }) + onlyIf { + fileWithMessage.asFile.get().exists() + } + } + + logTask.configure { + header.convention(config.log.header) + lineFormat.convention(config.log.format) + groupBy.convention(config.log.groupBy) + coverageUnits.convention(config.log.coverageUnits) + aggregationForGroup.convention(config.log.aggregationForGroup) + + outputFile.convention(project.layout.buildDirectory.file(coverageLogPath(variantName))) + + filters.set((config.filters).convert()) + + finalizedBy(printCoverageTask) + } + runOnCheck += config.log.onCheck.map { run -> + if (run) listOf(logTask) else emptyList() + } + + + project.tasks + .matching { it.name == LifecycleBasePlugin.CHECK_TASK_NAME } + .configureEach { dependsOn(runOnCheck) } + } + + internal fun assign(variant: AbstractVariantArtifacts) { + htmlTask.assign(variant) + xmlTask.assign(variant) + binTask.assign(variant) + verifyTask.assign(variant) + logTask.assign(variant) + } + + private inline fun TaskContainer.createReportTask( + name: String, + taskDescription: String + ): TaskProvider { + val task = register(name) + task.configure { + group = LifecycleBasePlugin.VERIFICATION_GROUP + description = taskDescription + tool.convention(toolProvider) + reportClasspath.from(reporterConfiguration) + } + return task + } + + private fun TaskProvider.assign(variant: AbstractVariantArtifacts) { + configure { + dependsOn(variant.artifactGenTask) + dependsOn(variant.consumerConfiguration) + + localArtifact.set(variant.artifactGenTask.flatMap { task -> task.artifactFile }) + externalArtifacts.from(variant.consumerConfiguration) + } + } + + private fun variantSuffix(): String { + return when (type) { + ReportVariantType.TOTAL -> "all code." + ReportVariantType.ANDROID -> "'$variantName' Android build variant" + ReportVariantType.JVM -> "Kotlin JVM" + ReportVariantType.CUSTOM -> "custom report variant '$variantName'" + } + } + + private fun KoverReportFiltersConfigImpl.convert(): Provider { + return project.provider { + ReportFilters( + includesImpl.classes.get(), includesImpl.annotations.get(), + excludesImpl.classes.get(), excludesImpl.annotations.get() + ) + } + } +} + + +private fun KoverVerifyRuleImpl.convert(): VerificationRule { + return VerificationRule(!disabled.get(), name, groupBy.get(), bounds.map { it.convert() }) +} + +private fun KoverVerifyBound.convert(): VerificationBound { + return VerificationBound(min.orNull?.toBigDecimal(), max.orNull?.toBigDecimal(), coverageUnits.get(), aggregationForGroup.get()) +} diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/AndroidTypes.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/AndroidTypes.kt new file mode 100644 index 00000000..08617d4c --- /dev/null +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/AndroidTypes.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.commons + +internal class AndroidBuildVariant( + val buildVariant: String, + val buildType: String, + val flavors: List, + + val fallbacks: AndroidFallbacks, + + /** + * The flavors used in case the dependency contains a dimension that is missing in the current project. + * Specific only for this build variant. + * + * map of (dimension > flavor) + */ + val missingDimensions: Map +) + +internal class AndroidFallbacks( + /** + * Specifies a sorted list of fallback build types that the + * Kover can try to use when a dependency does not include a + * key build type. Kover selects the first build type that's + * available in the dependency + * + * map of (buildtype > fallbacks) + * */ + val buildTypes: Map>, + + /** + * first loop through all the flavors and collect for each dimension, and each value, its + * fallbacks. + * + * map of (dimension > (requested > fallbacks)) + */ + val flavors: Map>>, +) + +/** + * Flavor in Android Project. + */ +internal class AndroidFlavor( + val dimension: String, + val name: String, +) \ No newline at end of file diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Configurations.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Configurations.kt index 2511f97e..79bd1d15 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Configurations.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Configurations.kt @@ -32,6 +32,18 @@ internal interface VariantNameAttr : Named { } } +/** + * Build type of the Android project. + */ +internal interface VariantOriginAttr : Named { + companion object { + val ATTRIBUTE = Attribute.of( + "kotlinx.kover.variant.origin", + VariantOriginAttr::class.java + ) + } +} + /** * Path of the project for which the artifact is published. */ @@ -50,7 +62,7 @@ internal interface ProjectPathAttr : Named { internal interface BuildTypeAttr : Named { companion object { val ATTRIBUTE = Attribute.of( - "kotlinx.kover.android.build-type", + "kotlinx.kover.variant.android.build-type", BuildTypeAttr::class.java ) } @@ -63,7 +75,7 @@ internal interface ProductFlavorAttr : Named { companion object { fun of(flavorDimension: String): Attribute { return Attribute.of( - "kotlinx.kover.android.flavor-dimension:$flavorDimension", + "kotlinx.kover.variant.android.flavor-dimension:$flavorDimension", ProductFlavorAttr::class.java ) } diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Constants.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Constants.kt index 4aff3a0a..120bd798 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Constants.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Constants.kt @@ -12,4 +12,6 @@ internal object KoverMigrations { const val MIGRATION_0_5_TO_0_6 = "https://github.com/Kotlin/kotlinx-kover/blob/v0.6.0/docs/migration-to-0.6.0.md" /** URL of migration guideline from versions 0.6.x to 0.7.0 */ const val MIGRATION_0_6_TO_0_7 = "https://github.com/Kotlin/kotlinx-kover/blob/v0.7.0/docs/gradle-plugin/migrations/migration-to-0.7.0.md" + /** URL of migration guideline from versions 0.7.x to 0.8.0 */ + const val MIGRATION_0_7_TO_0_8 = "https://github.com/Kotlin/kotlinx-kover/blob/v0.8.0/docs/gradle-plugin/migrations/migration-to-0.8.0.md" } diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Exceptions.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Exceptions.kt index 1f80acae..6a3d8f4a 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Exceptions.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Exceptions.kt @@ -6,8 +6,10 @@ package kotlinx.kover.gradle.plugin.commons import org.gradle.api.GradleException -internal class KoverCriticalException(message: String, cause: Throwable? = null): GradleException("$message\nPlease create a bug in Kover Gradle Plugin bugtracker https://github.com/Kotlin/kotlinx-kover/issues", cause) +internal class KoverCriticalException(message: String, cause: Throwable? = null): GradleException("Kover error: $message\nPlease create an issue in Kover Gradle Plugin repository: https://github.com/Kotlin/kotlinx-kover/issues", cause) -internal class KoverIllegalConfigException(message: String): GradleException(message) +internal open class KoverIllegalConfigException(message: String): GradleException(message) + +internal class KoverDeprecationException(message: String): KoverIllegalConfigException(message) internal class KoverVerificationException(violations: String): GradleException(violations) diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Naming.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Naming.kt index 00e5cc10..30a1606a 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Naming.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Naming.kt @@ -4,23 +4,66 @@ package kotlinx.kover.gradle.plugin.commons -import kotlinx.kover.gradle.plugin.dsl.KoverNames.DEFAULT_HTML_REPORT_NAME -import kotlinx.kover.gradle.plugin.dsl.KoverNames.DEFAULT_BINARY_REPORT_NAME -import kotlinx.kover.gradle.plugin.dsl.KoverNames.DEFAULT_LOG_REPORT_NAME -import kotlinx.kover.gradle.plugin.dsl.KoverNames.DEFAULT_VERIFY_REPORT_NAME -import kotlinx.kover.gradle.plugin.dsl.KoverNames.DEFAULT_XML_REPORT_NAME import java.util.* /** - * Name of the default Kover variant. + * ID of Kover Gradle Plugin. */ -internal const val DEFAULT_KOVER_VARIANT_NAME = "" +internal const val KOVER_PLUGIN_ID = "org.jetbrains.kotlinx.kover" + +/** + * Name of the project extension to configure Kover plugin. + */ +public const val KOVER_PROJECT_EXTENSION_NAME = "kover" + +/** + * Name of the configuration to add dependency on Kover reports from another project. + */ +public const val KOVER_DEPENDENCY_NAME = "kover" + +/** + * Name of reports variant for total report. + * + * It is an empty string in order to accurately avoid clashes with custom names. + */ +internal const val TOTAL_VARIANT_NAME = "" /** * Name of task to find online instrumentation agent jar file. */ internal const val FIND_JAR_TASK = "koverFindJar" +/** + * Name of the XML report generation task for Kotlin JVM and Kotlin multiplatform projects. + */ +internal const val XML_REPORT_NAME = "koverXmlReport" + +/** + * Name of the binary report generation task for Kotlin JVM and Kotlin multiplatform projects. + */ +internal const val BINARY_REPORT_NAME = "koverBinaryReport" + +/** + * Name of the HTML report generation task for Kotlin JVM and Kotlin multiplatform projects. + */ +internal const val HTML_REPORT_NAME = "koverHtmlReport" + +/** + * Name of the verification task for Kotlin JVM and Kotlin multiplatform projects. + */ +internal const val VERIFY_REPORT_NAME = "koverVerify" + +/** + * Name of the coverage logging task for Kotlin JVM and Kotlin multiplatform projects. + */ +internal const val LOG_REPORT_NAME = "koverLog" + +/** + * Name of reports variant for JVM targets. + * It includes all code from a project using the Kotlin/JVM plugin, or the code of the JVM target from a project using Kotlin/Multiplatform. + */ +internal const val JVM_VARIANT_NAME = "jvm" + /** * Name for task for generating Kover artifact. */ @@ -29,27 +72,27 @@ internal fun artifactGenerationTaskName(variant: String) = "koverGenerateArtifac /** * Name for HTML reporting task for specified report namespace. */ -internal fun htmlReportTaskName(variant: String) = "$DEFAULT_HTML_REPORT_NAME${variant.capitalized()}" +internal fun htmlReportTaskName(variant: String) = "$HTML_REPORT_NAME${variant.capitalized()}" /** * Name for XML reporting task for specified report namespace. */ -internal fun xmlReportTaskName(variant: String) = "$DEFAULT_XML_REPORT_NAME${variant.capitalized()}" +internal fun xmlReportTaskName(variant: String) = "$XML_REPORT_NAME${variant.capitalized()}" /** * Name for binary reporting task for specified report namespace. */ -internal fun binaryReportTaskName(variant: String) = "$DEFAULT_BINARY_REPORT_NAME${variant.capitalized()}" +internal fun binaryReportTaskName(variant: String) = "$BINARY_REPORT_NAME${variant.capitalized()}" /** * Name for verifying task for specified report namespace. */ -internal fun verifyTaskName(variant: String) = "$DEFAULT_VERIFY_REPORT_NAME${variant.capitalized()}" +internal fun verifyTaskName(variant: String) = "$VERIFY_REPORT_NAME${variant.capitalized()}" /** * Name for coverage logging task. */ -internal fun logTaskName(variant: String) = "$DEFAULT_LOG_REPORT_NAME${variant.capitalized()}" +internal fun logTaskName(variant: String) = "$LOG_REPORT_NAME${variant.capitalized()}" /** * Name for task to print coverage to the log. diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Paths.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Paths.kt index 21c45a48..01518de5 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Paths.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Paths.kt @@ -42,12 +42,6 @@ internal fun coverageLogPath(variant: String): String { return "kover${separator}coverage${variant.capitalized()}.txt" } -internal fun artifactFilePath(variant: String): String { - return if (variant == DEFAULT_KOVER_VARIANT_NAME) { - "kover${separator}default.artifact" - } else { - "kover${separator}$variant.artifact" - } -} +internal fun artifactFilePath(variant: String): String = "kover${separator}$variant.artifact" private val separator = File.separatorChar diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Plugins.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Plugins.kt new file mode 100644 index 00000000..a6ca7f5c --- /dev/null +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Plugins.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.commons + +/** + * The ID of the Kotlin JVM Gradle plugin. + */ +internal const val KOTLIN_JVM_PLUGIN_ID = "kotlin" + +/** + * The ID of the Kotlin Multiplatform Gradle plugin. + */ +internal const val KOTLIN_MULTIPLATFORM_PLUGIN_ID = "kotlin-multiplatform" + +/** + * The ID of the Kotlin Android Gradle plugin. + */ +internal const val KOTLIN_ANDROID_PLUGIN_ID = "kotlin-android" + +/** + * The plugin ID for the Android application Gradle plugin. + */ +internal const val ANDROID_APP_PLUGIN_ID = "com.android.application" + +/** + * The plugin ID for the Android library Gradle plugin. + */ +internal const val ANDROID_LIB_PLUGIN_ID = "com.android.library" + +/** + * The plugin ID for the Android dynamic feature Gradle plugin. + */ +internal const val ANDROID_DYNAMIC_PLUGIN_ID = "com.android.dynamic-feature" \ No newline at end of file diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Types.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Types.kt index 3df942b0..029200d0 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Types.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/commons/Types.kt @@ -8,9 +8,7 @@ import kotlinx.kover.gradle.plugin.dsl.* import org.gradle.api.* import org.gradle.api.file.* import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.Provider import org.gradle.api.tasks.* -import org.gradle.api.tasks.testing.* import org.gradle.workers.WorkerExecutor import java.io.* import java.math.BigDecimal @@ -37,11 +35,15 @@ internal enum class KotlinPluginType { } /** - * Source of reports variant. + * Type of report variant. + * + * A variant can be created based on a specific origin, total for all origins, or it can be declared by a user. */ -internal enum class ReportsVariantType { - DEFAULT, - ANDROID +internal enum class ReportVariantType { + TOTAL, + JVM, + ANDROID, + CUSTOM } /** @@ -51,99 +53,6 @@ internal enum class ReportsVariantType { */ internal class AppliedKotlinPlugin(val type: KotlinPluginType?) -/** - * Set of Kotlin compilations and test on them. - */ -internal interface CompilationKit { - val tests: TaskCollection - val compilations: Provider> -} - -/** - * Grouped JVM compilations and tests running on them. - */ -internal class JvmCompilationKit( - override val tests: TaskCollection, - // source set -> compilation - override val compilations: Provider>, -): CompilationKit - -/** - * Grouped named Android compilations and tests running on them. - * - * Contains additional information about the build variant taken from the Android Gradle Plugin - */ -internal class AndroidVariantCompilationKit( - val buildVariant: String, - val buildType: String, - val flavors: List, - - val fallbacks: AndroidFallbacks, - - /** - * The flavors used in case the dependency contains a dimension that is missing in the current project. - * Specific only for this build variant. - * - * map of (dimension > flavor) - */ - val missingDimensions: Map, - - override val tests: TaskCollection, - override val compilations: Provider> -): CompilationKit - -internal class AndroidFallbacks( - /** - * Specifies a sorted list of fallback build types that the - * Kover can try to use when a dependency does not include a - * key build type. Kover selects the first build type that's - * available in the dependency - * - * map of (buildtype > fallbacks) - * */ - val buildTypes: Map>, - - /** - * first loop through all the flavors and collect for each dimension, and each value, its - * fallbacks. - * - * map of (dimension > (requested > fallbacks)) - */ - val flavors: Map>>, -) - -/** - * Flavor in Android Project. - */ -internal class AndroidFlavor( - val dimension: String, - val name: String, -) - -/** - * Atomic portion of information about the building of part of the project. - */ -internal class CompilationUnit( - /** - * Directories of sources, used in [compileTasks]. - */ - val sources: Set = emptySet(), - - /** - * Directories with compiled classes, outputs of [compileTasks]. - */ - val outputs: Set = emptySet(), - - /** - * In case when no one compile tasks will be triggered, - * output dirs will be empty and reporter can't determine project classes. - * - * So compile tasks must be triggered anyway. - */ - val compileTasks: List = emptyList(), -) - - internal class ReportContext( val files: ArtifactContent, val filters: ReportFilters, @@ -174,15 +83,8 @@ internal open class VerificationRule @Inject constructor( @get:Input val isEnabled: Boolean, - @get:Nested - @get:Nullable - @get:Optional - val filters: ReportFilters?, - @get:Input - @get:Nullable - @get:Optional - val name: String?, + val name: String, @get:Input val entityType: GroupingEntityType, diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/KoverExtension.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/KoverExtension.kt new file mode 100644 index 00000000..3cfc31a1 --- /dev/null +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/KoverExtension.kt @@ -0,0 +1,112 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.dsl + +import kotlinx.kover.gradle.plugin.dsl.KoverVersions.JACOCO_TOOL_DEFAULT_VERSION +import org.gradle.api.Action +import org.gradle.api.provider.Property + +/** + * Project extension for Kover Gradle Plugin. + */ +public interface KoverExtension { + + /** + * Use [JaCoCo](https://www.jacoco.org/jacoco/) as coverage tool with version [JACOCO_TOOL_DEFAULT_VERSION] for measure coverage and generate reports. + */ + public fun useJacoco() + + /** + * Use [JaCoCo](https://www.jacoco.org/jacoco/) as coverage tool with version [version] for measure coverage and generate reports. + */ + public fun useJacoco(version: String) + + /** + * Specifies usage of [JaCoCo](https://www.jacoco.org/jacoco/) as coverage tool for measure coverage and generate reports. + * + * The version specified in the [jacocoVersion] will be used. + */ + public val useJacoco: Property + + /** + * Specifies version of [JaCoCo](https://www.jacoco.org/jacoco/) coverage tool. + * + * This property has an effect only if JaCoCo usage is enabled. + * + * [JACOCO_TOOL_DEFAULT_VERSION] by default. + */ + public val jacocoVersion: Property + + /** + * Customize report variants shared by the current project. + * + * A report variant is a set of information used to generate a reports, namely: + * project classes, a list of Gradle test tasks, classes that need to be excluded from instrumentation. + * + * ``` + * variants { + * // create report variant with custom name, + * // in which it is acceptable to add information from other variants of the current project, as well as `kover` dependencies + * create("custom") { + * // ... + * } + * + * // Configure the variant that is automatically created in the current project + * // For example, "jvm" for JVM target or "debug" for Android build variant + * provided("jvm") { + * // ... + * } + * + * // Configure the variant for all the code that is available in the current project. + * // This variant always exists for any type of project. + * total { + * // ... + * } + * } + * ``` + */ + public fun variants(block: Action) + + + /** + * Configuration of Kover reports. + * + * An individual set of reports is created for each Kover report variant. + * All these sets can be configured independently of each other. + * + * The main difference between the reports sets and the report variants is that the reports are individual for each project, the settings of reports in different projects do not affect each other in any way. + * At the same time, changing a report variant affects all reports that are based on it, for example, if several projects import a variant through a dependency `kover(project(":subproject"))`. + * + * Example of usage: + * ``` + * kover { + * reports { + * filters { + * // common filters for all reports of all variants + * } + * verify { + * // common verification rules for all variants + * } + * + * /* + * Total reports set - special reports for all code of current project and it's kover dependencies. + * These are the reports for total variant of current project and it's kover dependencies. + * */ + * total { + * // config + * } + * + * /* + * Configure custom reports set with name "custom". + * These are the reports for variant "custom" of current project and it's kover dependencies. + * */ + * variant("custom") { + * } + * } + * } + * ``` + */ + public fun reports(block: Action) +} diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/KoverNames.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/KoverNames.kt index 34923295..3ed42ea5 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/KoverNames.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/KoverNames.kt @@ -1,7 +1,13 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package kotlinx.kover.gradle.plugin.dsl -import kotlinx.kover.gradle.plugin.commons.htmlReportTaskName +import kotlinx.kover.gradle.plugin.commons.* +import kotlinx.kover.gradle.plugin.commons.KoverMigrations import kotlinx.kover.gradle.plugin.commons.binaryReportTaskName +import kotlinx.kover.gradle.plugin.commons.htmlReportTaskName import kotlinx.kover.gradle.plugin.commons.logTaskName import kotlinx.kover.gradle.plugin.commons.verifyTaskName import kotlinx.kover.gradle.plugin.commons.xmlReportTaskName @@ -13,175 +19,285 @@ import org.gradle.api.tasks.TaskContainer */ public object KoverNames { /** - * Name of the configuration to add dependency on Kover reports from another project. + * ID of Kover Gradle Plugin. */ - public const val DEPENDENCY_CONFIGURATION_NAME = "kover" + public val pluginId: String + get() = KOVER_PLUGIN_ID /** - * Name of the project extension to configure Kover measurements. + * Name of reports variant for JVM targets. + * It includes all code from a project using the Kotlin/JVM plugin, or the code of the JVM target from a project using Kotlin/Multiplatform. */ - public const val PROJECT_EXTENSION_NAME = "kover" + public val jvmVariantName: String + get() = JVM_VARIANT_NAME /** - * Name of the project extension to configure Kover reports. + * Name of the configuration to add dependency on Kover reports from another project. */ - public const val REPORT_EXTENSION_NAME = "koverReport" + public val configurationName: String + get() = KOVER_DEPENDENCY_NAME /** - * Name of the XML report generation task for Kotlin JVM and Kotlin multiplatform projects. + * Name of the project extension to configure Kover. */ - public const val DEFAULT_XML_REPORT_NAME = "koverXmlReport" + public val extensionName: String + get() = KOVER_PROJECT_EXTENSION_NAME /** - * Name of the binary report generation task for Kotlin JVM and Kotlin multiplatform projects. + * Name of the XML report generation task for Kotlin JVM and Kotlin multiplatform projects. */ - public const val DEFAULT_BINARY_REPORT_NAME = "koverBinaryReport" + public val koverXmlReportName + get() = XML_REPORT_NAME /** * Name of the HTML report generation task for Kotlin JVM and Kotlin multiplatform projects. */ - public const val DEFAULT_HTML_REPORT_NAME = "koverHtmlReport" + public val koverHtmlReportName + get() = HTML_REPORT_NAME + + /** + * Name of the binary report generation task for Kotlin JVM and Kotlin multiplatform projects. + */ + public val koverBinaryReportName + get() = BINARY_REPORT_NAME /** * Name of the verification task for Kotlin JVM and Kotlin multiplatform projects. */ - public const val DEFAULT_VERIFY_REPORT_NAME = "koverVerify" + public val koverVerifyName + get() = VERIFY_REPORT_NAME /** * Name of the coverage logging task for Kotlin JVM and Kotlin multiplatform projects. */ - public const val DEFAULT_LOG_REPORT_NAME = "koverLog" + public val koverLogName + get() = LOG_REPORT_NAME + /** - * Name of the XML report generation task for [buildVariant] Android build variant for Android projects. + * Name of the XML report generation task for [variant] Kover report variant. */ - public fun androidXmlReport(buildVariant: String): String { - return xmlReportTaskName(buildVariant) + public fun koverXmlReportName(variant: String): String { + return xmlReportTaskName(variant) } /** - * Name of the HTML report generation task for [buildVariant] Android build variant for Android projects. + * Name of the HTML report generation task for [variant] Kover report variant. */ - public fun androidHtmlReport(buildVariant: String): String { - return htmlReportTaskName(buildVariant) + public fun koverHtmlReportName(variant: String): String { + return htmlReportTaskName(variant) } /** - * Name of the binary report generation task for [buildVariant] Android build variant for Android projects. + * Name of the binary report generation task for [variant] Kover report variant. */ - public fun androidBinaryReport(buildVariant: String): String { - return binaryReportTaskName(buildVariant) + public fun koverBinaryReportName(variant: String): String { + return binaryReportTaskName(variant) } /** - * Name of the verification task for [buildVariant] Android build variant for Android projects. + * Name of the verification task for [variant] Kover report variant. */ - public fun androidVerify(buildVariant: String): String { - return verifyTaskName(buildVariant) + public fun koverVerifyName(variant: String): String { + return verifyTaskName(variant) } /** - * Name of the coverage logging task for [buildVariant] Android build variant for Android projects. + * Name of the coverage logging task for [variant] Kover report variant. */ - public fun androidLog(buildVariant: String): String { - return logTaskName(buildVariant) + public fun koverLogName(variant: String): String { + return logTaskName(variant) } + + // === + // Deprecations + // Remove in 0.9.0 + + @Deprecated( + message = "Constant was removed, use KoverNames.pluginId property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("pluginId"), + level = DeprecationLevel.ERROR + ) + public const val PLUGIN_ID = KOVER_PLUGIN_ID + + @Deprecated( + message = "Constant was removed, use KoverNames.configurationName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("configurationName"), + level = DeprecationLevel.ERROR + ) + public const val DEPENDENCY_CONFIGURATION_NAME = KOVER_DEPENDENCY_NAME + + @Deprecated( + message = "Constant was removed, use KoverNames.extensionName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("extensionName"), + level = DeprecationLevel.ERROR + ) + public const val PROJECT_EXTENSION_NAME = KOVER_PROJECT_EXTENSION_NAME + + @Deprecated( + message = "Extension was removed, use kover { ... } extension. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("kover"), + level = DeprecationLevel.ERROR + ) + public const val REPORT_EXTENSION_NAME = "koverReport" + + @Deprecated( + message = "Constant was removed, use KoverNames.koverXmlReportName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("koverXmlReportName"), + level = DeprecationLevel.ERROR + ) + public const val DEFAULT_XML_REPORT_NAME = "koverXmlReport" + + @Deprecated( + message = "Constant was removed, use KoverNames.koverBinaryReportName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("koverBinaryReportName"), + level = DeprecationLevel.ERROR + ) + public const val DEFAULT_BINARY_REPORT_NAME = "koverBinaryReport" + + @Deprecated( + message = "Constant was removed, use KoverNames.koverHtmlReportName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("koverHtmlReportName"), + level = DeprecationLevel.ERROR + ) + public const val DEFAULT_HTML_REPORT_NAME = "koverHtmlReport" + + @Deprecated( + message = "Constant was removed, use KoverNames.koverVerifyName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("koverVerifyName"), + level = DeprecationLevel.ERROR + ) + public const val DEFAULT_VERIFY_REPORT_NAME = "koverVerify" + + @Deprecated( + message = "Constant was removed, use KoverNames.koverLogName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("koverLogName"), + level = DeprecationLevel.ERROR + ) + public const val DEFAULT_LOG_REPORT_NAME = "koverLog" } -/** - * Name of the XML report generation task for Kotlin JVM and Kotlin multiplatform projects. - * - * Has the same value as [KoverNames.DEFAULT_XML_REPORT_NAME]. - */ -public val TaskContainer.koverXmlReportName - get() = KoverNames.DEFAULT_XML_REPORT_NAME -/** - * Name of the HTML report generation task for Kotlin JVM and Kotlin multiplatform projects. - * - * Has the same value as [KoverNames.DEFAULT_HTML_REPORT_NAME]. - */ -public val TaskContainer.koverHtmlReportName - get() = KoverNames.DEFAULT_HTML_REPORT_NAME +// === +// Deprecations +// Remove in 0.9.0 -/** - * Name of the binary report generation task for Kotlin JVM and Kotlin multiplatform projects. - * - * Has the same value as [KoverNames.DEFAULT_BINARY_REPORT_NAME]. - */ -public val TaskContainer.koverBinaryReportName - get() = KoverNames.DEFAULT_BINARY_REPORT_NAME +@Deprecated( + message = "Property was removed, use KoverNames.koverXmlReportName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("KoverNames.koverXmlReportName"), + level = DeprecationLevel.ERROR +) +public val TaskContainer.koverXmlReportName: String + get() { + throw KoverDeprecationException("Property koverXmlReportName was removed, use KoverNames.koverXmlReportName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } -/** - * Name of the verification task for Kotlin JVM and Kotlin multiplatform projects. - * - * Has the same value as [KoverNames.DEFAULT_VERIFY_REPORT_NAME]. - */ -public val TaskContainer.koverVerifyName - get() = KoverNames.DEFAULT_VERIFY_REPORT_NAME +@Deprecated( + message = "Property was removed, use KoverNames.koverHtmlReportName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("KoverNames.koverHtmlReportName"), + level = DeprecationLevel.ERROR +) +public val TaskContainer.koverHtmlReportName: String + get() { + throw KoverDeprecationException("Property koverHtmlReportName was removed, use KoverNames.koverHtmlReportName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } -/** - * Name of the coverage logging task for Kotlin JVM and Kotlin multiplatform projects. - * - * Has the same value as [KoverNames.DEFAULT_LOG_REPORT_NAME]. - */ -public val TaskContainer.koverLogName - get() = KoverNames.DEFAULT_LOG_REPORT_NAME +@Deprecated( + message = "Property was removed, use KoverNames.koverBinaryReportName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("KoverNames.koverBinaryReportName"), + level = DeprecationLevel.ERROR +) +public val TaskContainer.koverBinaryReportName: String + get() { + throw KoverDeprecationException("Property koverBinaryReportName was removed, use KoverNames.koverBinaryReportName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } +@Deprecated( + message = "Property was removed, use KoverNames.koverVerifyName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("KoverNames.koverVerifyName"), + level = DeprecationLevel.ERROR +) +public val TaskContainer.koverVerifyName: String + get() { + throw KoverDeprecationException("Property koverVerifyName was removed, use KoverNames.koverVerifyName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } -/** - * Name of the XML report generation task for [buildVariantName] Android build variant for Android projects. - * - * Returns the same value as [KoverNames.androidXmlReport]. - */ +@Deprecated( + message = "Property was removed, use KoverNames.koverLogName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("KoverNames.koverLogName"), + level = DeprecationLevel.ERROR +) +public val TaskContainer.koverLogName: String + get() { + throw KoverDeprecationException("Property koverLogName was removed, use KoverNames.koverLogName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + +@Deprecated( + message = "Function was removed, use KoverNames.koverXmlReportName(buildVariantName) function. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("KoverNames.koverXmlReportName(buildVariantName)"), + level = DeprecationLevel.ERROR +) +@Suppress("UNUSED_PARAMETER") public fun TaskContainer.koverAndroidXmlReportName(buildVariantName: String): String { - return KoverNames.androidXmlReport(buildVariantName) + throw KoverDeprecationException("Function koverAndroidXmlReportName(buildVariantName) was removed, use KoverNames.koverXmlReportName(buildVariantName) function. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") } -/** - * Name of the HTML report generation task for [buildVariantName] Android build variant for Android projects. - * - * Returns the same value as [KoverNames.androidHtmlReport]. - */ +@Deprecated( + message = "Function was removed, use KoverNames.koverHtmlReportName(buildVariantName) function. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("KoverNames.koverHtmlReportName(buildVariantName)"), + level = DeprecationLevel.ERROR +) +@Suppress("UNUSED_PARAMETER") public fun TaskContainer.koverAndroidHtmlReportName(buildVariantName: String): String { - return KoverNames.androidHtmlReport(buildVariantName) + throw KoverDeprecationException("Function koverAndroidHtmlReportName(buildVariantName) was removed, use KoverNames.koverHtmlReportName(buildVariantName) function. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") } -/** - * Name of the binary report generation task for [buildVariantName] Android build variant for Android projects. - * - * Returns the same value as [KoverNames.androidBinaryReport]. - */ +@Deprecated( + message = "Function was removed, use KoverNames.koverBinaryReportName(buildVariantName) function. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("KoverNames.koverBinaryReportName(buildVariantName)"), + level = DeprecationLevel.ERROR +) +@Suppress("UNUSED_PARAMETER") public fun TaskContainer.koverAndroidBinaryReportName(buildVariantName: String): String { - return KoverNames.androidBinaryReport(buildVariantName) + throw KoverDeprecationException("Function koverAndroidBinaryReportName(buildVariantName) was removed, use koverBinaryReportName(buildVariantName) function. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") } -/** - * Name of the XML report generation task for [buildVariantName] Android build variant for Android projects. - * - * Returns the same value as [KoverNames.androidVerify]. - */ +@Deprecated( + message = "Function was removed, use KoverNames.koverVerifyName(buildVariantName) function. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("KoverNames.koverVerifyName(buildVariantName)"), + level = DeprecationLevel.ERROR +) +@Suppress("UNUSED_PARAMETER") public fun TaskContainer.koverAndroidVerifyName(buildVariantName: String): String { - return KoverNames.androidVerify(buildVariantName) + throw KoverDeprecationException("Function koverAndroidVerifyName(buildVariantName) was removed, use KoverNames.koverVerifyName(buildVariantName) function. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") } -/** - * Name of the coverage logging task for [buildVariantName] Android build variant for Android projects. - * - * Returns the same value as [KoverNames.androidLog]. - */ +@Deprecated( + message = "Function was removed, use KoverNames.koverLogName(buildVariantName) function. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("KoverNames.koverLogName(buildVariantName)"), + level = DeprecationLevel.ERROR +) +@Suppress("UNUSED_PARAMETER") public fun TaskContainer.koverAndroidLogName(buildVariantName: String): String { - return KoverNames.androidLog(buildVariantName) + throw KoverDeprecationException("Function koverAndroidLogName(buildVariantName) was removed, use KoverNames.koverLogName(buildVariantName) function. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") } -/** - * Name of the project extension to configure Kover measurements. - */ +@Deprecated( + message = "Property was removed, use KoverNames.extensionName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("KoverNames.extensionName"), + level = DeprecationLevel.ERROR +) public val ExtensionContainer.koverExtensionName: String - get() = KoverNames.PROJECT_EXTENSION_NAME + get() { + throw KoverDeprecationException("Property koverExtensionName was removed, use KoverNames.extensionName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } -/** - * Name of the project extension to configure Kover reports. - */ +@Deprecated( + message = "Property was removed, use KoverNames.extensionName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("KoverNames.extensionName"), + level = DeprecationLevel.ERROR +) public val ExtensionContainer.koverReportExtensionName: String - get() = KoverNames.REPORT_EXTENSION_NAME + get() { + throw KoverDeprecationException("Property koverReportExtensionName was removed, use KoverNames.extensionName property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/KoverProjectExtension.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/KoverProjectExtension.kt deleted file mode 100644 index 07e344d1..00000000 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/KoverProjectExtension.kt +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.kover.gradle.plugin.dsl - -import kotlinx.kover.gradle.plugin.commons.KoverMigrations -import kotlinx.kover.gradle.plugin.dsl.KoverNames.REPORT_EXTENSION_NAME -import kotlinx.kover.gradle.plugin.dsl.KoverVersions.JACOCO_TOOL_DEFAULT_VERSION -import org.gradle.api.* - -/** - * Setting up how the coverage for the current project will be collected. - * - * Example of usage: - * ``` - * kover { - * // disable measurement of the coverage for current project - * disable() - * - * // skip classes defined in java source files - * excludeJavaCode() - * - * excludeTests { - * // execution of the specified tests will not be taken into account in the coverage - * } - * - * excludeInstrumentation { - * // bytecode of specified classes will not be modified when running tests - this solves some rare problems with instrumentation - * } - * } - * ``` - */ -public interface KoverProjectExtension { - /** - * Disables instrumentation of all tests in the corresponding project, also excludes all sources of this project are excluded from the reports. - * - * When generating reports, if all projects are disabled, then no report will be generated. - */ - public fun disable() - - /** - * JaCoCo Coverage Tool with default version [JACOCO_TOOL_DEFAULT_VERSION]. - */ - public fun useJacoco() - - /** - * Coverage Tool by [JaCoCo](https://www.jacoco.org/jacoco/). - */ - public fun useJacoco(version: String) - - /** - * Excludes from report all classes, defined in Java source files. - * - * As a side effect, reports cease to depend on Java compilation task. - */ - public fun excludeJavaCode() - - /** - * Disables instrumentation of specified tests. - * - * This means that even if the excluded test is executed, the function calls that occurred in it will not be counted in the reports. - * - * As a side effect, Kover reports cease to depend on the specified test tasks. - */ - public fun excludeTests(config: Action) - - /** - * Excludes specified class from instrumentation. - * - * This means that even if these classes were actually called, their coverage will always be 0 in reports. - * - * This is necessary when there are errors in the instrumentation of classes from external dependencies, for example https://github.com/Kotlin/kotlinx-kover/issues/89 - */ - public fun excludeInstrumentation(config: Action) - - /** - * Excludes classes of the specified source sets from Kover reports. - * - * As a side effect, the generation of Kover reports ceases to depend on the compilation tasks of these source sets. - * - * Example: - * ``` - * kover { - * excludeSourceSets { - * names("test1", "extra") - * } - * } - * ``` - */ - public fun excludeSourceSets(config: Action) - - /* - * Deprecations - * TODO remove in 0.8.0 - */ - - /** - * Property is deprecated, please use `use...Tool()` functions. - */ - @Deprecated( - message = "Property was removed. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - level = DeprecationLevel.ERROR - ) - public var engine: Nothing? - get() = null - set(@Suppress("UNUSED_PARAMETER") value) {} - - @Deprecated( - message = "Property was replaced with 'disable()' function. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - replaceWith = ReplaceWith("disable()"), - level = DeprecationLevel.ERROR - ) - public val isDisabled: Boolean - get() = false - - @Deprecated( - message = "Common filters were moved to '$REPORT_EXTENSION_NAME { filters { } }'. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - level = DeprecationLevel.ERROR - ) - public fun filters(block: () -> Unit) { - } - - @Deprecated( - message = "Tasks filters was renamed to 'excludeTests'. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - replaceWith = ReplaceWith("excludeTests"), - level = DeprecationLevel.ERROR - ) - public fun instrumentation(block: KoverTestsExclusions.() -> Unit) { - } - - @Deprecated( - message = "XML report setting was moved to '$REPORT_EXTENSION_NAME { }' project extension. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - level = DeprecationLevel.ERROR - ) - public fun xmlReport(block: () -> Unit) { - } - - @Deprecated( - message = "HTML report setting was moved to '$REPORT_EXTENSION_NAME { }' project extension. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - level = DeprecationLevel.ERROR - ) - public fun htmlReport(block: () -> Unit) { - } - - @Deprecated( - message = "Verification report setting was moved to '$REPORT_EXTENSION_NAME { }' project extension. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - level = DeprecationLevel.ERROR - ) - public fun verify(block: () -> Unit) { - } -} - -/** - * Disables instrumentation of test tasks. - * - * This means that even if the excluded tests are executed, the function calls that happened in it will not be counted in the coverage reports. - * - * As a side effect, reports stop depending on the specified test tasks. - * - * Example: - * ``` - * kover { - * excludeTests { - * tasks("test1", "test2") - * } - * } - * ``` - */ -public interface KoverTestsExclusions { - - /** - * Disables instrumentation of specified test tasks. - * - * This means that even if the tests from excluded tasks are executed, the function calls that happened in it will not be counted in the coverage reports. - * - * As a side effect, reports cease to depend on the specified test tasks. - */ - public fun tasks(vararg name: String) - - /** - * Disables instrumentation of specified test tasks. - * - * This means that even if the tests from excluded tasks are executed, the function calls that happened in it will not be counted in the coverage reports. - * - * As a side effect, reports cease to depend on the specified test tasks. - */ - public fun tasks(names: Iterable) - - @Deprecated( - message = "Use function `tasks(...)` instead. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - replaceWith = ReplaceWith("tasks"), - level = DeprecationLevel.ERROR - ) - public val excludeTasks: MutableList - get() = mutableListOf() -} - -/** - * Excludes classes of the specified source sets from Kover reports. - * - * As a side effect, the generation of Kover reports ceases to depend on the compilation tasks of these source sets. - * - * Example: - * ``` - * kover { - * excludeSourceSets { - * names("test1", "extra") - * } - * } - * ``` - */ -public interface SourceSetsExclusions { - /** - * Excludes classes of the specified source sets from Kover reports. - * - * As a side effect, the generation of Kover reports ceases to depend on the compilation tasks of these source sets. - * - * Example: - * ``` - * kover { - * excludeSourceSets { - * names("test1", "extra") - * } - * } - * ``` - */ - public fun names(vararg name: String) - - /** - * Excludes classes of the specified source sets from Kover reports. - * - * As a side effect, the generation of Kover reports ceases to depend on the compilation tasks of these source sets. - * - * Example: - * ``` - * kover { - * excludeSourceSets { - * names(listOf("test1", "extra")) - * } - * } - * ``` - */ - public fun names(names: Iterable) -} - -/** - * Excludes classes from instrumentation. - * - * This means that even if these classes were actually invoked, their coverage will always be 0 in reports. - * - * This is necessary when there are errors in the instrumentation of classes from external dependencies, for example https://github.com/Kotlin/kotlinx-kover/issues/89 - * - * Example: - * ``` - * kover { - * excludeInstrumentation { - * // excludes from instrumentations classes by fully-qualified JVM class name, wildcards '*' and '?' are available - * classes("*Foo*", "*Bar") - * - * // excludes from instrumentations all classes located in specified package and it subpackages, wildcards '*' and '?' are available - * packages("com.project") - * } - * } - * ``` - */ -public interface KoverInstrumentationExclusions { - /** - * Excludes specified classes from instrumentation. - * - * This means that even if these classes were actually called, their coverage will always be 0 in reports. - * - * This is necessary when there are errors in the instrumentation of classes from external dependencies, for example https://github.com/Kotlin/kotlinx-kover/issues/89 - */ - public fun classes(vararg names: String) - - /** - * Excludes specified classes from instrumentation. - * - * This means that even if these classes were actually called, their coverage will always be 0 in reports. - * - * This is necessary when there are errors in the instrumentation of classes from external dependencies, for example https://github.com/Kotlin/kotlinx-kover/issues/89 - */ - public fun classes(names: Iterable) - - /** - * Excludes classes from specified packages and its subpackages from instrumentation. - * - * This means that even if these classes were actually called, their coverage will always be 0 in reports. - * - * This is necessary when there are errors in the instrumentation of classes from external dependencies, for example https://github.com/Kotlin/kotlinx-kover/issues/89 - */ - public fun packages(vararg names: String) - - /** - * Excludes classes from specified packages and its subpackages from instrumentation. - * - * This means that even if these classes were actually called, their coverage will always be 0 in reports. - * - * This is necessary when there are errors in the instrumentation of classes from external dependencies, for example https://github.com/Kotlin/kotlinx-kover/issues/89 - */ - public fun packages(names: Iterable) -} - diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/KoverReportConfig.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/KoverReportConfig.kt new file mode 100644 index 00000000..f7468f8f --- /dev/null +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/KoverReportConfig.kt @@ -0,0 +1,1242 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +@file:Suppress("UNUSED_PARAMETER") + +package kotlinx.kover.gradle.plugin.dsl + +import kotlinx.kover.gradle.plugin.commons.KoverDeprecationException +import kotlinx.kover.gradle.plugin.commons.KoverMigrations +import org.gradle.api.* +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider + +/** + * Configuration of Kover reports. + * + * Example of usage: + * ``` + * kover { + * reports { + * filters { + * // common filters for all reports of all variants + * } + * verify { + * // common verification rules for all variants + * } + * + * /* + * Total reports set - special reports for all code of current project and it's kover dependencies. + * These are the reports for total variant of current project and it's kover dependencies. + * */ + * total { + * filters { + * // override report filters for total reports + * } + * + * html { + * // configure total HTML report + * } + * + * xml { + * // configure total XML report + * } + * + * verify { + * // configure total coverage verification + * } + * } + * + * variant("custom") { + * filters { + * // override report filters for reports of 'custom' variant + * } + * + * html { + * // configure HTML report for reports of 'custom' variant + * } + * + * xml { + * // configure XML report for reports of 'custom' variant + * } + * + * verify { + * // configure coverage verification for reports of 'custom' variant + * } + * } + * } + * } + * ``` + */ +public interface KoverReportConfig { + /** + * Specify common filters for all report variants, these filters will be inherited in HTML/XML/verification reports. + * They can be redefined in the settings of a specific report variant. + * ``` + * filters { + * excludes { + * // ... + * } + * + * includes { + * // ... + * } + * } + * ``` + */ + public fun filters(config: Action) + + + /** + * Specify common verification rules for all report variants: JVM and Android build variants. + * They can be overridden in the settings for a specific report set for particular variant. + * ``` + * verify { + * rule { + * // verification rule + * } + * + * rule("custom rule name") { + * // named verification rule + * } + * } + * ``` + */ + public fun verify(config: Action) + + @Deprecated( + message = "Default reports was removed, the concepts of total and custom reports are now used. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + level = DeprecationLevel.ERROR + ) + public fun defaults(config: Action<*>) { + throw KoverDeprecationException("Default reports was removed, the concepts of total and custom reports are now used. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + + @Deprecated( + message = "Block was renamed to variant. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("variant"), + level = DeprecationLevel.ERROR + ) + public fun androidReports(variant: String, config: Action) { + throw KoverDeprecationException("Block androidReports was renamed to variant. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + + /** + * Configure reports for all code of current project and `kover` dependencies. + * + * example: + * ``` + * kover { + * reports { + * total { + * filters { + * // override report filters for total reports + * } + * html { + * // configure HTML report for all code of current project and `kover` dependencies. + * } + * xml { + * // configure XML report for all code of current project and `kover` dependencies. + * } + * verify { + * // configure coverage verification all code of current project and `kover` dependencies. + * } + * } + * } + * } + * ``` + */ + public fun total(config: Action) + + /** + * Configure reports for classes of specified named Kover report variant. + * + * example: + * ``` + * kover { + * reports { + * variant("debug") { + * filters { + * // override report filters for reports of 'debug' variant + * } + * + * html { + * // configure HTML report for 'debug' variant + * } + * + * xml { + * // configure XML report for 'debug' variant + * } + * + * verify { + * // configure coverage verification for 'debug' variant + * } + * } + * } + * } + * ``` + */ + public fun variant(variant: String, config: Action) + +} + +/** + * Type to configure report set for a specific variant + * + * example: + * ``` + * filters { + * // override report filters + * } + * html { + * // configure HTML report + * } + * xml { + * // configure XML report + * } + * verify { + * // configure coverage verification + * } + * ``` + */ +public interface KoverReportSetConfig { + /** + * Specify common report filters, these filters will be inherited in HTML/XML/verification and other reports. + * + * Using this block clears all the filters specified earlier. + * In order not to clear the existing filters, but to add new ones, use [filtersAppend]. + * + * ``` + * filters { + * excludes { + * // ... + * } + * + * includes { + * // ... + * } + * } + * ``` + */ + public fun filters(config: Action) + + /** + * Specify common report filters, these filters will be inherited in HTML/XML/verification and other reports. + * + * Using this block will add additional filters to those that were inherited and specified earlier. + * In order to clear the existing filters, use [filters]. + * + * ``` + * filtersAppend { + * excludes { + * // ... + * } + * + * includes { + * // ... + * } + * } + * ``` + */ + public fun filtersAppend(config: Action) + + + + /** + * Configure HTML report for current report variant. + * ``` + * html { + * title = "Custom title" + * + * // Generate an HTML report when running the `check` task + * onCheck = false + * + * // Specify HTML report directory + * htmlDir = layout.buildDirectory.dir("my-html-report") + * } + * ``` + */ + public fun html(config: Action) + + /** + * Configure XML report for current report variant. + * ``` + * xml { + * // Generate an XML report when running the `check` task + * onCheck = true + * + * // XML report title (the location depends on the library) + * title = "Custom XML report title" + * + * // Specify file to generate XML report + * xmlFile = layout.buildDirectory.file("my-xml-report.xml") + * } + * ``` + */ + public fun xml(config: Action) + + /** + * Configure Kover binary report for current report variant. + * ``` + * binary { + * // Generate binary report when running the `check` task + * onCheck = true + * + * // Specify file to generate binary report + * file = layout.buildDirectory.file("my-project-report/report.bin") + * } + * ``` + * + * Kover binary report is compatible with IntelliJ Coverage report (ic) + */ + public fun binary(config: Action) + + /** + * Configure coverage verification for current report variant. + * + * Using this block clears all the bounds specified earlier. + * In order not to clear the existing bounds, but to add new ones, use [verifyAppend]. + * + * ``` + * verify { + * onCheck = true + * + * rule { + * // ... + * } + * + * rule("Custom Name") { + * // ... + * } + * } + * ``` + */ + public fun verify(config: Action) + + /** + * Configure coverage verification for current report variant. + * + * Using this block will add additional bounds to those that were inherited and specified earlier. + * In order to clear the existing bounds, use [verify]. + * + * ``` + * verifyAppend { + * onCheck = true + * + * rule { + * // ... + * } + * + * rule("Custom Name") { + * // ... + * } + * } + * ``` + */ + public fun verifyAppend(config: Action) + + /** + * Configure coverage printing to the log for current report variant. + * ``` + * log { + * onCheck = true + * + * filters { + * // ... + * } + * header = null + * format = " line coverage: %" + * groupBy = GroupingEntityType.APPLICATION + * coverageUnits = MetricType.LINE + * aggregationForGroup = AggregationType.COVERED_PERCENTAGE + * } + * ``` + */ + public fun log(config: Action) +} + +/** + * Configuration of coverage printing to the log for current report variant. + * ``` + * log { + * onCheck = true + * + * filters { + * // ... + * } + * header = null + * format = " line coverage: %" + * groupBy = GroupingEntityType.APPLICATION + * coverageUnits = MetricType.LINE + * aggregationForGroup = AggregationType.COVERED_PERCENTAGE + * } + * ``` + */ +public interface KoverLogTaskConfig { + @Deprecated( + message = "It is forbidden to override filters for a specific report, use custom report variants to create reports with a different set of filters. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + level = DeprecationLevel.ERROR + ) + public fun filters(config: Action) { + throw KoverDeprecationException("It is forbidden to override filters for the log report, use custom report variants to create reports with a different set of filters. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + + /** + * Print coverage when running the `check` task. + * + * `false` by default. + */ + public val onCheck: Property + + /** + * Add a header line to the output before the lines with coverage. + * + * Absent by default. + */ + public val header: Property + + /** + * Format of the strings to print coverage for the specified in [groupBy] group. + * + * The following placeholders can be used: + * - `` - coverage value + * - `` - name of the entity by which the grouping took place. `application` if [groupBy] is [GroupingEntityType.APPLICATION]. + * + * `" line coverage: %"` by default. + */ + public val format: Property + + /** + * Specifies by which entity the code for separate coverage evaluation will be grouped. + * + * + * [GroupingEntityType.APPLICATION] by default. + */ + public val groupBy: Property + + /** + * The type of application code division (unit type) whose unit coverage will be considered independently. + * + * [MetricType.LINE] by default. + */ + public val coverageUnits: Property + + /** + * Specifies aggregation function that will be calculated over all the units of the same group. + * + * This function used to calculate the aggregated coverage value, it uses the values of the covered and uncovered units of type [coverageUnits] as arguments. + * + * Result value will be printed. + * + * [AggregationType.COVERED_PERCENTAGE] by default. + */ + public val aggregationForGroup: Property +} + +/** + * Filters to exclude classes from reports + */ +public interface KoverReportFiltersConfig { + /** + * Configures class filter in order to exclude classes and functions. + * + * Example: + * ``` + * excludes { + * classes("com.example.FooBar?", "com.example.*Bar") + * packages("com.example.subpackage") + * annotatedBy("*Generated*") + * } + * ``` + * Excludes have priority over includes. + */ + public fun excludes(config: Action) + + /** + * Configures class filter in order to include classes. + * + * Example: + * ``` + * includes { + * classes("com.example.FooBar?", "com.example.*Bar") + * packages("com.example.subpackage") + * } + * ``` + * Excludes have priority over includes. + */ + public fun includes(config: Action) +} + +/** + * Exclusion or inclusion class filter for Kover reports. + * + * Exclusions example for Kotlin: + * ``` + * excludes { + * classes("*.foo.Bar", "*.M?Class") + * classes(listOf("*.foo.Bar", "*.M?Class")) + * packages("foo.b?r", "com.*.example") + * val somePackages = + * packages(listOf("foo.b?r", "com.*.example")) + * annotatedBy("*Generated*", "com.example.KoverExclude") + * } + * ``` + */ +public interface KoverReportFilter { + /** + * Add specified classes to current filters. + * + * It is acceptable to use `*` and `?` wildcards, + * `*` means any number of arbitrary characters (including no chars), `?` means one arbitrary character. + * + * Example: + * ``` + * classes("*.foo.Bar", "*.M?Class") + * ``` + */ + public fun classes(vararg names: String) + + /** + * Add specified classes to current filters. + * + * It is acceptable to use `*` and `?` wildcards, + * `*` means any number of arbitrary characters (including no chars), `?` means one arbitrary character. + * + * Example for Groovy: + * ``` + * def someClasses = ["*.foo.Bar", "*.M?Class"] + * ... + * classes(someClasses) + * ``` + * + * Example for Kotlin: + * ``` + * val someClasses = listOf("*.foo.Bar", "*.M?Class") + * ... + * classes(someClasses) + * ``` + */ + public fun classes(names: Iterable) + + /** + * Add specified classes to current filters. + * + * Used for lazy setup. + * + * It is acceptable to use `*` and `?` wildcards, + * `*` means any number of arbitrary characters (including no chars), `?` means one arbitrary character. + * + * Example: + * ``` + * val excludedClass: Provider = ... + * ... + * classes(excludedClass) + * ``` + */ + public fun classes(vararg names: Provider) + + /** + * Add specified classes to current filters. + * + * Used for lazy setup. + * + * It is acceptable to use `*` and `?` wildcards, + * `*` means any number of arbitrary characters (including no chars), `?` means one arbitrary character. + * + * Example: + * ``` + * val someClasses: Provider> = ... + * ... + * classes(someClasses) + * ``` + */ + public fun classes(names: Provider>) + + /** + * Add all classes in specified package and its subpackages to current filters. + * + * It is acceptable to use `*` and `?` wildcards, + * `*` means any number of arbitrary characters (including no chars), `?` means one arbitrary character. + * + * Example: + * ``` + * packages("foo.b?r", "com.*.example") + * ``` + */ + public fun packages(vararg names: String) + + /** + * Add all classes in specified package and its subpackages to current filters. + * + * It is acceptable to use `*` and `?` wildcards, + * `*` means any number of arbitrary characters (including no chars), `?` means one arbitrary character. + * + * Example for Groovy: + * ``` + * def somePackages = ["foo.b?r", "com.*.example"] + * + * packages(somePackages) + * ``` + * + * Example for Kotlin: + * ``` + * val somePackages = listOf("foo.b?r", "com.*.example") + * ... + * packages(somePackages) + * ``` + */ + public fun packages(names: Iterable) + + /** + * Add all classes in specified package and its subpackages to current filters. + * + * Used for lazy setup. + * + * It is acceptable to use `*` and `?` wildcards, + * `*` means any number of arbitrary characters (including no chars), `?` means one arbitrary character. + * + * Example: + * ``` + * val classA: Provider = ... + * val classB: Provider = ... + * packages(classA, classB) + * ``` + */ + public fun packages(vararg names: Provider) + + /** + * Add all classes in specified package and its subpackages to current filters. + * + * Used for lazy setup. + * + * It is acceptable to use `*` and `?` wildcards, + * `*` means any number of arbitrary characters (including no chars), `?` means one arbitrary character. + * + * Example: + * ``` + * val somePackages: Provider> = ... + * ... + * packages(somePackages) + * ``` + */ + public fun packages(names: Provider>) + + /** + * Add to filters all classes and functions marked by specified annotations. + * + * It is acceptable to use `*` and `?` wildcards, + * `*` means any number of arbitrary characters (including no chars), `?` means one arbitrary character. + * + * Example: + * ``` + * annotatedBy("*Generated*", "com.example.KoverExclude") + * ``` + */ + public fun annotatedBy(vararg annotationName: String) + + /** + * Add to filters all classes and functions marked by specified annotations. + * + * Used for lazy setup. + * + * It is acceptable to use `*` and `?` wildcards, + * `*` means any number of arbitrary characters (including no chars), `?` means one arbitrary character. + * + * Example: + * ``` + * val annotation: Provider = ... + * annotatedBy(annotation) + * ``` + */ + public fun annotatedBy(vararg annotationName: Provider) + + /** + * Add all classes generated by Android plugin to filters. + * + * It is shortcut for: + * ``` + * classes( + * "*Fragment", + * "*Fragment\$*", + * "*Activity", + * "*Activity\$*", + * "*.databinding.*", + * "*.BuildConfig" + * ) + * ``` + */ + public fun androidGeneratedClasses() { + classes( + "*Fragment", + "*Fragment\$*", + "*Activity", + "*Activity\$*", + "*.databinding.*", + "*.BuildConfig" + ) + } +} + +/** + * Configure Kover HTML Report. + * + * Example: + * ``` + * ... + * html { + * title = "Custom title" + * + * // Generate an HTML report when running the `check` task + * onCheck = false + * + * // Specify HTML report directory + * htmlDir = layout.buildDirectory.dir("my-html-report") + * } + * ... + * ``` + */ +public interface KoverHtmlTaskConfig { + @Deprecated( + message = "It is forbidden to override filters for a specific report, use custom report variants to create reports with a different set of filters. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + level = DeprecationLevel.ERROR + ) + public fun filters(config: Action) { + throw KoverDeprecationException("It is forbidden to override filters for the HTML report, use custom report variants to create reports with a different set of filters. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + + /** + * Specify header in HTML reports. + * + * If not specified, project path is used instead. + */ + public val title: Property + + /** + * Specify charset in HTML reports. + * + * If not specified, used return value of `Charset.defaultCharset()` for Kover report generator and UTF-8 is used for JaCoCo. + */ + public val charset: Property + + /** + * Generate an HTML report when running the `check` task. + * + * `false` by default. + */ + public val onCheck: Property + + @Deprecated( + message = "Function was removed, use htmlDir property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("htmlDir"), + level = DeprecationLevel.ERROR + ) + public fun setReportDir(dir: Any) { + throw KoverDeprecationException("Function setReportDir(dir) was removed, use htmlDir property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + + /** + * HTML report directory. + * + * `"${buildDirectory}/reports/kover/html${variantName}"` by default. + * + * This value should not be hardcoded, it is always necessary to read the actual value from the property. + */ + public val htmlDir: DirectoryProperty +} + +/** + * Configure Kover XML Report. + * + * Example: + * ``` + * ... + * xml { + * // Generate an XML report when running the `check` task + * onCheck = true + * + * // XML report title (the location depends on the library) + * title = "Custom XML report title" + * + * // Specify file to generate XML report + * xmlFile = layout.buildDirectory.file("my-xml-report.xml") + * } + * ... + * ``` + */ +public interface KoverXmlTaskConfig { + @Deprecated( + message = "It is forbidden to override filters for a specific report, use custom report variants to create reports with a different set of filters. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + level = DeprecationLevel.ERROR + ) + public fun filters(config: Action) { + throw KoverDeprecationException("It is forbidden to override filters for the XML report, use custom report variants to create reports with a different set of filters. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + + /** + * Generate an XML report when running the `check` task. + * + * `false` by default. + */ + public val onCheck: Property + + /** + * Specify file to generate XML report. + */ + @Deprecated( + message = "Function was removed, use xmlFile property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("xmlFile"), + level = DeprecationLevel.ERROR + ) + public fun setReportFile(xmlFile: Any) { + throw KoverDeprecationException("Function xmlFile was removed, use xmlFile property. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + + /** + * File for saving generated XML report. + * + * `"${buildDirectory}/reports/kover/report${variantName}.xml"` by default. + * + * This value should not be hardcoded, it is always necessary to read the actual value from the property. + */ + public val xmlFile: RegularFileProperty + + /** + * Specify title in XML report. + * + * `"Kover Gradle Plugin XML report for $projectPath"` by default. + */ + public val title: Property +} + +/** + * Configure Kover binary Report. + * + * Example: + * ``` + * ... + * binary { + * // Generate binary report when running the `check` task + * onCheck = true + * + * // Specify file to generate binary report + * file = layout.buildDirectory.file("my-project-report/report.bin") + * } + * ... + * ``` + */ +public interface KoverBinaryTaskConfig { + @Deprecated( + message = "It is forbidden to override filters for a specific report, use custom report variants to create reports with a different set of filters. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + level = DeprecationLevel.ERROR + ) + public fun filters(config: Action) { + throw KoverDeprecationException("It is forbidden to override filters for the binary report, use custom report variants to create reports with a different set of filters. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + + /** + * Generate binary report when running the `check` task. + * + * `false` by default. + */ + public val onCheck: Property + + /** + * Specify file to generate binary report. + * + * `"${buildDirectory}/reports/kover/report${variantName}.bin"` by default. + * + * This value should not be hardcoded, it is always necessary to read the actual value from the property. + */ + public val file: RegularFileProperty +} + +/** + * Configuration of the coverage's result verification with the specified rules. + * + * Example: + * ``` + * verify { + * onCheck = true + * + * rule { + * // ... + * } + * + * rule("Custom Name") { + * // ... + * } + * } + * ``` + */ +public interface KoverVerifyTaskConfig: KoverVerificationRulesConfig { + /** + * Verify coverage when running the `check` task. + * + * `true` for total verification of all code in the project, `false` otherwise. + */ + public val onCheck: Property +} + +/** + * Configuration to specify verification rules. + * + * Example: + * ``` + * verify { + * rule { + * // verification rule + * } + * + * rule("custom rule name") { + * // named verification rule + * } + * } + * ``` + */ +public interface KoverVerificationRulesConfig { + /** + * Add new coverage verification rule to check after test task execution. + */ + public fun rule(config: Action) + + /** + * Add new named coverage verification rule to check after test task execution. + * + * The name will be displayed in case of a verification error if Kover Tool was used. + */ + public fun rule(name: String, config: Action) +} + +/** + * Describes a single Kover verification task rule (that is part of Gradle's verify), + * with the following configurable parameters: + * + * - Which classes and packages are included or excluded into the current rule + * - What coverage bounds are enforced by current rules + * - What kind of bounds (branches, lines, bytecode instructions) are checked by bound rules. + */ +public interface KoverVerifyRule { + /** + * Specifies by which entity the code for separate coverage evaluation will be grouped. + * [GroupingEntityType.APPLICATION] by default. + */ + public val groupBy: Property + + /** + * Specifies that the rule is checked during verification. + * + * `false` by default. + */ + public val disabled: Property + + /** + * Specifies that the rule is checked during verification. + */ + @Deprecated( + message = "Property was renamed to disabled and inverted. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + level = DeprecationLevel.ERROR + ) + public var isEnabled: Boolean + get() { + throw KoverDeprecationException("Property isEnabled was renamed to disabled and inverted. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + set(value) { + throw KoverDeprecationException("Property isEnabled was renamed to disabled and inverted. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + + @Deprecated( + message = "Property was renamed to groupBy. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("groupBy"), + level = DeprecationLevel.ERROR + ) + public var entity: GroupingEntityType + get() { + throw KoverDeprecationException("Property entity was renamed to groupBy. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + set(value) { + throw KoverDeprecationException("Property entity was renamed to groupBy. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + + @Deprecated( + message = "It is forbidden to override filters for a specific report, use custom report variants to create reports with a different set of filters. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + level = DeprecationLevel.ERROR + ) + public fun filters(config: Action) { + throw KoverDeprecationException("It is forbidden to override filters for the verification report, use custom report variants to create reports with a different set of filters. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + + /** + * Specifies the set of verification rules that control the + * coverage conditions required for the verification task to pass. + * + * An example of bound configuration: + * ``` + * // At least 75% of lines should be covered in order for build to pass + * bound { + * aggregationForGroup = AggregationType.COVERED_PERCENTAGE // Default aggregation + * metric = MetricType.LINE + * min = 75 + * } + * ``` + * + * @see KoverVerifyBound + */ + public fun bound(config: Action) + + /** + * A shortcut for + * ``` + * bound { + * min = min + * } + * ``` + * + * @see bound + */ + public fun minBound(min: Int) + + /** + * A shortcut for + * ``` + * bound { + * min = min + * } + * ``` + * + * @see bound + */ + public fun minBound(min: Provider) + + /** + * A shortcut for + * ``` + * bound { + * max = max + * } + * ``` + * + * @see bound + */ + public fun maxBound(max: Int) + + /** + * A shortcut for + * ``` + * bound { + * max = max + * } + * ``` + * + * @see bound + */ + public fun maxBound(max: Provider) + + // Default parameters values supported only in Kotlin. + + /** + * A shortcut for + * ``` + * bound { + * min = minValue + * coverageUnits = coverageUnits + * aggregationForGroup = aggregationForGroup + * } + * ``` + * + * @see bound + */ + public fun minBound( + minValue: Int, + coverageUnits: MetricType = MetricType.LINE, + aggregationForGroup: AggregationType = AggregationType.COVERED_PERCENTAGE + ) + + /** + * A shortcut for + * ``` + * bound { + * max = maxValue + * coverageUnits = coverageUnits + * aggregation = aggregation + * } + * ``` + * + * @see bound + */ + public fun maxBound( + maxValue: Int, + coverageUnits: MetricType = MetricType.LINE, + aggregation: AggregationType = AggregationType.COVERED_PERCENTAGE + ) + + /** + * A shortcut for + * ``` + * bound { + * max = max + * min = min + * coverageUnits = coverageUnits + * aggregation = aggregation + * } + * ``` + * + * @see bound + */ + public fun bound( + min: Int, + max: Int, + coverageUnits: MetricType = MetricType.LINE, + aggregation: AggregationType = AggregationType.COVERED_PERCENTAGE + ) + +} + +/** + * Describes a single bound for the verification rule to enforce; + * Bound specifies what type of coverage is enforced (branches, lines, instructions), + * how coverage is aggregated (raw number or percents) and what numerical values of coverage + * are acceptable. + */ +public interface KoverVerifyBound { + /** + * Specifies minimal value to compare with aggregated coverage value. + * The comparison occurs only if the value is present. + * + * Absent by default. + */ + public val min: Property + + /** + * Specifies maximal value to compare with counter value. + * The comparison occurs only if the value is present. + * + * Absent by default. + */ + public val max: Property + + /** + * The type of application code division (unit type) whose unit coverage will be considered independently. + * It affects which blocks the value of the covered and missed units will be calculated for. + * + * [MetricType.LINE] by default. + */ + public val coverageUnits: Property + + /** + * Specifies aggregation function that will be calculated over all the units of the same group. + * + * This function used to calculate the aggregated coverage value, it uses the values of the covered and uncovered units of type [coverageUnits] as arguments. + * + * Result value will be compared with the bounds. + * + * [AggregationType.COVERED_PERCENTAGE] by default. + */ + public val aggregationForGroup: Property + + + @Deprecated( + message = "Property was renamed to min. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("min"), + level = DeprecationLevel.ERROR + ) + public var minValue: Int? + get() { + throw KoverDeprecationException("Property minValue was renamed to min. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + set(value) { + throw KoverDeprecationException("Property minValue was renamed to min. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + + + @Deprecated( + message = "Property was renamed to max. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("max"), + level = DeprecationLevel.ERROR + ) + public var maxValue: Int? + get() { + throw KoverDeprecationException("Property maxValue was renamed to max. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + set(value) { + throw KoverDeprecationException("Property maxValue was renamed to max. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + + @Deprecated( + message = "Property was renamed to coverageUnits. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("coverageUnits"), + level = DeprecationLevel.ERROR + ) + public var metric: MetricType + get() { + throw KoverDeprecationException("Property metric was renamed to coverageUnits. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + set(value) { + throw KoverDeprecationException("Property metric was renamed to coverageUnits. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + + @Deprecated( + message = "Property was renamed to aggregationForGroup. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}", + replaceWith = ReplaceWith("aggregationForGroup"), + level = DeprecationLevel.ERROR + ) + public var aggregation: AggregationType + get() { + throw KoverDeprecationException("Property aggregation was renamed to aggregationForGroup. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } + set(value) { + throw KoverDeprecationException("Property aggregation was renamed to aggregationForGroup. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_7_TO_0_8}") + } +} + +/** + * Type of the metric to evaluate code coverage. + */ +public enum class MetricType { + /** + * Number of lines. + */ + LINE, + + /** + * Number of JVM bytecode instructions. + */ + INSTRUCTION, + + /** + * Number of branches covered. + */ + BRANCH +} + +/** + * Type of counter value to compare with minimal and maximal values if them defined. + */ +public enum class AggregationType(val isPercentage: Boolean) { + COVERED_COUNT(false), + MISSED_COUNT(false), + COVERED_PERCENTAGE(true), + MISSED_PERCENTAGE(true) +} + +/** + * Entity type for grouping code to coverage evaluation. + */ +public enum class GroupingEntityType { + /** + * Counts the coverage values for all code. + */ + APPLICATION, + + /** + * Counts the coverage values for each class separately. + */ + CLASS, + + /** + * Counts the coverage values for each package that has classes separately. + */ + PACKAGE +} diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/KoverReportExtension.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/KoverReportExtension.kt deleted file mode 100644 index a8675035..00000000 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/KoverReportExtension.kt +++ /dev/null @@ -1,1032 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.kover.gradle.plugin.dsl - -import kotlinx.kover.gradle.plugin.commons.KoverMigrations -import org.gradle.api.* -import org.gradle.api.file.Directory -import org.gradle.api.file.RegularFile -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Property -import org.gradle.api.provider.Provider -import java.io.* - -/** - * Configuration of Kover reports. - * - * Example of usage: - * ``` - * koverReport { - * filters { - * // common filters for all reports of all variants - * } - * verify { - * // common verification rules for all variants - * } - * - * // default reports - special reports that are filled in by default with class measurements from Kotlin/JVM or Kotlin/MPP projects - * defaults { - * // add content of reports for specified variant to default reports - * mergeWith("buildVariant") - * - * filters { - * // override report filters for default reports - * } - * - * html { - * // configure default HTML report - * } - * - * xml { - * // configure default XML report - * } - * - * verify { - * // configure default coverage verification - * } - * } - * - * androidReports("buildVariant") { - * filters { - * // override report filters for reports of specified Android build variant - * } - * - * html { - * // configure HTML report for specified Android build variant - * } - * - * xml { - * // configure XML report for specified Android build variant - * } - * - * verify { - * // configure coverage verification for specified Android build variant - * } - * } - * - * } - * ``` - */ -public interface KoverReportExtension { - /** - * Specify common filters for all report variants, these filters will be inherited in HTML/XML/verification reports. - * They can be redefined in the settings of a specific report. - * ``` - * filters { - * excludes { - * // ... - * } - * - * includes { - * // ... - * } - * } - * ``` - */ - public fun filters(config: Action) - - - /** - * Specify common verification rules for all report variants: JVM and Android build variants. - * They can be overridden in the settings for a specific report variant or a specific report in a particular variant. - * ``` - * verify { - * rule { - * // verification rule - * } - * - * rule("custom rule name") { - * // named verification rule - * } - * } - * ``` - */ - public fun verify(config: Action) - - /** - * Configure reports for classes from Kotlin/JVM or Kotlin/MPP projects. - * Also content from specified Android build variant can be added by calling `mergeWith`. - * - * example: - * ``` - * koverReport { - * defaults { - * // add content of reports for specified variant to default reports - * mergeWith("buildVariant") - * - * filters { - * // override report filters for default reports - * } - * - * html { - * // configure default HTML report - * } - * - * xml { - * // configure default XML report - * } - * - * verify { - * // configure default coverage verification - * } - * } - * } - * ``` - */ - public fun defaults(config: Action) - - /** - * Configure reports for classes of specified Android build variant. - * - * example: - * ``` - * koverReport { - * androidReports("debug") { - * filters { - * // override report filters for reports of 'debug' Android build variant - * } - * - * html { - * // configure HTML report for 'debug' Android build variant - * } - * - * xml { - * // configure XML report for 'debug' Android build variant - * } - * - * verify { - * // configure coverage verification for 'debug' Android build variant - * } - * } - * } - * ``` - */ - public fun androidReports(variant: String, config: Action) -} - -/** - * Configuration for default variant reports - */ -public interface KoverDefaultReportsConfig: KoverReportsConfig { - /** - * Add the contents of the reports with specified variant to the default reports. - * - * Affects the default reports of this project and any project that specifies this project as a dependency. - */ - public fun mergeWith(otherVariant: String) -} - -public interface KoverReportsConfig { - /** - * Specify common filters for the current report variant, these filters will be inherited in HTML/XML/verification reports. - * They can be redefined in the settings of a specific report. - * ``` - * filters { - * excludes { - * // ... - * } - * - * includes { - * // ... - * } - * } - * ``` - */ - public fun filters(config: Action) - - /** - * Configure HTML report for current report variant. - * ``` - * html { - * filters { - * // ... - * } - * - * title = "My report title" - * onCheck = false - * setReportDir(layout.buildDirectory.dir("my-project-report/html-result")) - * } - * ``` - */ - public fun html(config: Action) - - /** - * Configure XML report for current report variant. - * ``` - * xml { - * filters { - * // ... - * } - * - * onCheck = false - * title.set("Custom XML report title") - * setReportFile(layout.buildDirectory.file("my-project-report/result.xml")) - * } - * ``` - */ - public fun xml(config: Action) - - /** - * Configure Kover binary report for current report variant. - * ``` - * binary { - * filters { - * // ... - * } - * - * onCheck.set(false) - * file.set(layout.buildDirectory.file("my-project-report/report.ic")) - * } - * ``` - * - * Kover binary report is compatible with IntelliJ Coverage report (ic) - */ - public fun binary(config: Action) - - /** - * Configure coverage verification for current report variant. - * ``` - * verify { - * onCheck = true - * - * rule { - * // ... - * } - * - * rule("Custom Name") { - * // ... - * } - * } - * ``` - */ - public fun verify(config: Action) - - /** - * Configure coverage printing to the log for current report variant. - * ``` - * log { - * onCheck = true - * - * filters { - * // ... - * } - * header = null - * format = " line coverage: %" - * groupBy = GroupingEntityType.APPLICATION - * coverageUnits = MetricType.LINE - * aggregationForGroup = AggregationType.COVERED_PERCENTAGE - * } - * ``` - */ - public fun log(config: Action) -} - -/** - * Configuration of coverage printing to the log for current report variant. - * ``` - * log { - * onCheck = true - * - * filters { - * // ... - * } - * header = null - * format = " line coverage: %" - * groupBy = GroupingEntityType.APPLICATION - * coverageUnits = MetricType.LINE - * aggregationForGroup = AggregationType.COVERED_PERCENTAGE - * } - * ``` - */ -public interface KoverLogReportConfig { - /** - * Override common filters only for logging report. - */ - public fun filters(config: Action) - - /** - * Print coverage when running the `check` task. - * - * `false` by default. - */ - public var onCheck: Boolean - - /** - * Add a header line to the output before the lines with coverage. - * - * If value is `null` then the header is absent. - * - * `null` by default. - */ - public var header: String? - - /** - * Format of the strings to print coverage for the specified in [groupBy] group. - * - * The following placeholders can be used: - * - `` - coverage value - * - `` - name of the entity by which the grouping took place. `application` if [groupBy] is [GroupingEntityType.APPLICATION]. - * - * `" line coverage: %"` if value is `null`. - * - * `null` by default. - */ - public var format: String? - - /** - * Specifies by which entity the code for separate coverage evaluation will be grouped. - * - * - * [GroupingEntityType.APPLICATION] if value is `null`. - * - * `null` by default. - */ - public var groupBy: GroupingEntityType? - - /** - * Specifies which metric is used for coverage evaluation. - * - * [MetricType.LINE] if value is `null`. - * - * `null` by default. - */ - public var coverageUnits: MetricType? - - /** - * Specifies aggregation function that will be calculated over all the elements of the same group. This function returns the printed value. - * - * [AggregationType.COVERED_PERCENTAGE] if value is `null`. - * - * `null` by default. - */ - public var aggregationForGroup: AggregationType? -} - -/** - * Filters to excludes - */ -public interface KoverReportFilters { - - - /** - * Configures class filter in order to exclude declarations marked by specific annotations. - * - * Example: - * ``` - * annotations { - * excludes += "com.example.Generated" - * } - * ``` - */ - - /** - * Configures class filter in order to exclude classes and functions. - * - * Example: - * ``` - * excludes { - * classes("com.example.FooBar?", "com.example.*Bar") - * packages("com.example.subpackage") - * annotatedBy("*Generated*") - * } - * ``` - * Excludes have priority over includes. - */ - public fun excludes(config: Action) - - /** - * Configures class filter in order to include classes. - * - * Example: - * ``` - * includes { - * classes("com.example.FooBar?", "com.example.*Bar") - * packages("com.example.subpackage") - * } - * ``` - * Excludes have priority over includes. - */ - public fun includes(config: Action) - - @Deprecated( - message = "Class filters was moved into 'excludes { classes(\"fq.name\") }' or 'includes { classes(\"fq.name\") }'. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - level = DeprecationLevel.ERROR - ) - public fun classes(block: () -> Unit) { } - - @Deprecated( - message = "Class inclusion filters was moved into 'includes { classes(\"fq.name\") }'. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - level = DeprecationLevel.ERROR - ) - public val includes: MutableList - get() = mutableListOf() - - @Deprecated( - message = "Class exclusion filters was moved into 'excludes { classes(\"fq.name\") }'. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - level = DeprecationLevel.ERROR - ) - public val excludes: MutableList - get() = mutableListOf() -} - -/** - * Exclusion or inclusion class filter for Kover reports. - * - * Exclusions example for Kotlin: - * ``` - * excludes { - * classes("*.foo.Bar", "*.M?Class") - * classes(listOf("*.foo.Bar", "*.M?Class")) - * packages("foo.b?r", "com.*.example") - * val somePackages = - * packages(listOf("foo.b?r", "com.*.example")) - * annotatedBy("*Generated*", "com.example.KoverExclude") - * } - * ``` - */ -public interface KoverReportFilter { - /** - * Add specified classes to current filters. - * - * It is acceptable to use `*` and `?` wildcards, - * `*` means any number of arbitrary characters (including no chars), `?` means one arbitrary character. - * - * Example: - * ``` - * classes("*.foo.Bar", "*.M?Class") - * ``` - */ - public fun classes(vararg names: String) - - /** - * Add specified classes to current filters. - * - * It is acceptable to use `*` and `?` wildcards, - * `*` means any number of arbitrary characters (including no chars), `?` means one arbitrary character. - * - * Example for Groovy: - * ``` - * def someClasses = ["*.foo.Bar", "*.M?Class"] - * ... - * classes(someClasses) - * ``` - * - * Example for Kotlin: - * ``` - * val someClasses = listOf("*.foo.Bar", "*.M?Class") - * ... - * classes(someClasses) - * ``` - */ - public fun classes(names: Iterable) - - /** - * Add all classes in specified package and it subpackages to current filters. - * - * It is acceptable to use `*` and `?` wildcards, - * `*` means any number of arbitrary characters (including no chars), `?` means one arbitrary character. - * - * Example: - * ``` - * packages("foo.b?r", "com.*.example") - * ``` - */ - public fun packages(vararg names: String) - - /** - * Add all classes in specified package and it subpackages to current filters. - * - * It is acceptable to use `*` and `?` wildcards, - * `*` means any number of arbitrary characters (including no chars), `?` means one arbitrary character. - * - * Example for Groovy: - * ``` - * def somePackages = ["foo.b?r", "com.*.example"] - * - * packages(somePackages) - * ``` - * - * Example for Kotlin: - * ``` - * val somePackages = listOf("foo.b?r", "com.*.example") - * ... - * packages(somePackages) - * ``` - */ - public fun packages(names: Iterable) - - /** - * Add to filters all classes and functions marked by specified annotations. - * - * It is acceptable to use `*` and `?` wildcards, - * `*` means any number of arbitrary characters (including no chars), `?` means one arbitrary character. - * - * Example: - * ``` - * annotatedBy("*Generated*", "com.example.KoverExclude") - * ``` - */ - public fun annotatedBy(vararg annotationName: String) -} - -/** - * Configure Kover HTML Report. - * - * Example: - * ``` - * ... - * html { - * // Filter the classes that will be included in the HTML report. - * // This filter does not affect the list of classes that will be instrumented and it is applied only to the report of the current project. - * filters { - * // ... - * } - * - * title = "Custom title" - * - * // Generate an HTML report when running the `check` task - * onCheck = false - * - * // Specify HTML report directory - * setReportDir(layout.buildDirectory.file("my-html-report")) - * } - * ... - * ``` - */ -public interface KoverHtmlReportConfig { - /** - * Override common filters only for HTML report. - */ - public fun filters(config: Action) - - /** - * Specify header in HTML reports. - * - * If not specified, project path is used instead. - */ - public var title: String? - - /** - * Specify charset in HTML reports. - * - * If not specified, used return value of `Charset.defaultCharset()` for Kover report generator and UTF-8 is used for JaCoCo. - */ - public var charset: String? - - /** - * Generate an HTML report when running the `check` task. - */ - public var onCheck: Boolean - - /** - * Specify HTML report directory. - */ - public fun setReportDir(dir: File) - - /** - * Specify HTML report directory. - */ - public fun setReportDir(dir: Provider) - - @Deprecated( - message = "Property was removed. Use function 'setReportDir(file)'. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - replaceWith = ReplaceWith("setReportDir"), - level = DeprecationLevel.ERROR - ) - public val reportDir: Nothing? - get() = null - - @Deprecated( - message = "Use function 'filters' instead. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - replaceWith = ReplaceWith("filters"), - level = DeprecationLevel.ERROR - ) - public fun overrideFilters(block: () -> Unit) { } -} - -/** - * Configure Kover XML Report. - * - * Example: - * ``` - * ... - * xml { - * // Filter the classes that will be included in the XML report. - * // This filter does not affect the list of classes that will be instrumented and it is applied only to the report of the current project. - * filters { - * // ... - * } - * - * // Generate an XML report when running the `check` task - * onCheck = false - * - * // XML report title (the location depends on the library) - * title.set("Custom XML report title") - * - * // Specify file to generate XML report - * setReportFile(layout.buildDirectory.file("my-xml-report.xml")) - * } - * ... - * ``` - */ -public interface KoverXmlReportConfig { - /** - * Override common filters only for XML report. - */ - public fun filters(config: Action) - - /** - * Specify title in XML report. - * - * `"Kover Gradle Plugin XML report for $projectPath"` by default. - */ - public val title: Property - - /** - * Generate an XML report when running the `check` task. - */ - public var onCheck: Boolean - - /** - * Specify file to generate XML report. - */ - public fun setReportFile(xmlFile: File) - - /** - * Specify file to generate XML report. - */ - public fun setReportFile(xmlFile: Provider) - - @Deprecated( - message = "Property was removed. Use function 'setReportFile(file)'. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - replaceWith = ReplaceWith("setReportFile"), - level = DeprecationLevel.ERROR - ) - public val reportFile: Nothing? - get() = null - - @Deprecated( - message = "Use function 'filters' instead. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - replaceWith = ReplaceWith("filters"), - level = DeprecationLevel.ERROR - ) - public fun overrideFilters(block: () -> Unit) { } -} - -/** - * Configure Kover binary Report. - * - * Example: - * ``` - * ... - * binary { - * // Filter the classes that will be included in the binary report. - * // This filter does not affect the list of classes that will be instrumented and it is applied only to the report of the current project. - * filters { - * // ... - * } - * - * // Generate binary report when running the `check` task - * onCheck.set(false) - * - * // Specify file to generate binary report - * file.set(layout.buildDirectory.file("my-project-report/report.bin")) - * } - * ... - * ``` - */ -public interface KoverBinaryReportConfig { - /** - * Override common filters only for binary report. - */ - public fun filters(config: Action) - - /** - * Generate binary report when running the `check` task. - */ - public val onCheck: Property - - /** - * Specify file to generate binary report - */ - public val file: RegularFileProperty -} - -/** - * Configuration of the coverage's result verification with the specified rules. - * - * Example: - * ``` - * verify { - * onCheck = true - * rule { - * // verification rule - * } - * } - * ``` - */ -public interface KoverVerifyReportConfig: KoverVerificationRulesConfig { - /** - * Verify coverage when running the `check` task. - * `null` by default, for Kotlin JVM and Kotlin MPP triggered on `check` task, but not for Android. - */ - public var onCheck: Boolean -} - -/** - * Configuration to specify verification rules. - * - * Example: - * ``` - * verify { - * rule { - * // verification rule - * } - * - * rule("custom rule name") { - * // named verification rule - * } - * } - * ``` - */ -public interface KoverVerificationRulesConfig { - /** - * Add new coverage verification rule to check after test task execution. - */ - public fun rule(config: Action) - - /** - * Add new named coverage verification rule to check after test task execution. - * - * The name will be displayed in case of a verification error if Kover Tool was used. - */ - public fun rule(name: String, config: Action) -} - -/** - * Describes a single Kover verification task rule (that is part of Gradle's verify), - * with the following configurable parameters: - * - * - Which classes and packages are included or excluded into the current rule - * - What coverage bounds are enforced by current rules - * - What kind of bounds (branches, lines, bytecode instructions) are checked by bound rules. - */ -public interface KoverVerifyRule { - /** - * Specifies that the rule is checked during verification. - */ - public var isEnabled: Boolean - - /** - * Specifies by which entity the code for separate coverage evaluation will be grouped. - */ - public var entity: GroupingEntityType - - /** - * Specifies the set of Kover report filters that control - * included or excluded classes and packages for verification. - * - * An example of filter configuration: - * ``` - * filters { - * excludes { - * // Do not include deprecated package into verification coverage data - * className("org.jetbrains.deprecated.*") - * } - * } - * ``` - * - * @see KoverReportFilter - */ - public fun filters(config: Action) - - /** - * Specifies the set of verification rules that control the - * coverage conditions required for the verification task to pass. - * - * An example of bound configuration: - * ``` - * // At least 75% of lines should be covered in order for build to pass - * bound { - * aggregation = AggregationType.COVERED_PERCENTAGE // Default aggregation - * metric = MetricType.LINE - * minValue = 75 - * } - * ``` - * - * @see KoverVerifyBound - */ - public fun bound(config: Action) - - /** - * A shortcut for - * ``` - * bound { - * minValue = minValue - * } - * ``` - * - * @see bound - */ - public fun minBound(minValue: Int) - - /** - * A shortcut for - * ``` - * bound { - * maxValue = maxValue - * } - * ``` - * - * @see bound - */ - public fun maxBound(maxValue: Int) - - // Default parameters values supported only in Kotlin. - - /** - * A shortcut for - * ``` - * bound { - * minValue = minValue - * metric = metric - * aggregation = aggregation - * } - * ``` - * - * @see bound - */ - public fun minBound( - minValue: Int, - metric: MetricType = MetricType.LINE, - aggregation: AggregationType = AggregationType.COVERED_PERCENTAGE - ) - - /** - * A shortcut for - * ``` - * bound { - * maxValue = maxValue - * metric = metric - * aggregation = aggregation - * } - * ``` - * - * @see bound - */ - public fun maxBound( - maxValue: Int, - metric: MetricType = MetricType.LINE, - aggregation: AggregationType = AggregationType.COVERED_PERCENTAGE - ) - - /** - * A shortcut for - * ``` - * bound { - * maxValue = maxValue - * minValue = minValue - * metric = metric - * aggregation = aggregation - * } - * ``` - * - * @see bound - */ - public fun bound( - minValue: Int, - maxValue: Int, - metric: MetricType = MetricType.LINE, - aggregation: AggregationType = AggregationType.COVERED_PERCENTAGE - ) - - @Deprecated( - message = "Property was renamed to 'entity'. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - replaceWith = ReplaceWith("entity"), - level = DeprecationLevel.ERROR - ) - public var target: GroupingEntityType - get() = GroupingEntityType.APPLICATION - set(@Suppress("UNUSED_PARAMETER") value) {} - - @Deprecated( - message = "Property 'name' was removed, specify rule name in `rule(myName) { ... }` function. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - level = DeprecationLevel.ERROR - ) - public var name: String? - - @Deprecated( - message = "Use function 'filters' instead. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - replaceWith = ReplaceWith("filters"), - level = DeprecationLevel.ERROR - ) - public fun overrideClassFilter(block: () -> Unit) {} -} - -/** - * Describes a single bound for the verification rule to enforce; - * Bound specifies what type of coverage is enforced (branches, lines, instructions), - * how coverage is aggerageted (raw number or percents) and what numerical values of coverage - * are acceptable. - */ -public interface KoverVerifyBound { - /** - * Specifies minimal value to compare with counter value. - */ - public var minValue: Int? - - /** - * Specifies maximal value to compare with counter value. - */ - public var maxValue: Int? - - /** - * Specifies which metric is used for code coverage verification. - */ - public var metric: MetricType - - /** - * Specifies type of lines counter value to compare with minimal and maximal values if them defined. - * Default is [AggregationType.COVERED_PERCENTAGE] - */ - public var aggregation: AggregationType - - @Deprecated( - message = "Property was renamed to 'metric'. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - replaceWith = ReplaceWith("metric"), - level = DeprecationLevel.ERROR - ) - public var counter: MetricType - get() = MetricType.LINE - set(@Suppress("UNUSED_PARAMETER") value) {} - - @Deprecated( - message = "Property was renamed to 'aggregation'. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - replaceWith = ReplaceWith("aggregation"), - level = DeprecationLevel.ERROR - ) - public var valueType: AggregationType - get() = AggregationType.COVERED_PERCENTAGE - set(@Suppress("UNUSED_PARAMETER") value) {} - - -} - -/** - * Type of the metric to evaluate code coverage. - */ -public enum class MetricType { - /** - * Number of lines. - */ - LINE, - - /** - * Number of JVM bytecode instructions. - */ - INSTRUCTION, - - /** - * Number of branches covered. - */ - BRANCH -} - -/** - * Type of counter value to compare with minimal and maximal values if them defined. - */ -public enum class AggregationType(val isPercentage: Boolean) { - COVERED_COUNT(false), - MISSED_COUNT(false), - COVERED_PERCENTAGE(true), - MISSED_PERCENTAGE(true) -} - -/** - * Entity type for grouping code to coverage evaluation. - */ -public enum class GroupingEntityType { - /** - * Counts the coverage values for all code. - */ - APPLICATION, - - /** - * Counts the coverage values for each class separately. - */ - CLASS, - - /** - * Counts the coverage values for each package that has classes separately. - */ - PACKAGE; - - @Deprecated( - message = "Entry was renamed to 'APPLICATION'. Please refer to migration guide in order to migrate: ${KoverMigrations.MIGRATION_0_6_TO_0_7}", - replaceWith = ReplaceWith("APPLICATION"), - level = DeprecationLevel.ERROR - ) - object ALL -} diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/KoverVariantConfig.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/KoverVariantConfig.kt new file mode 100644 index 00000000..c179cc74 --- /dev/null +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/KoverVariantConfig.kt @@ -0,0 +1,241 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.dsl + +import kotlinx.kover.gradle.plugin.commons.KoverIllegalConfigException +import org.gradle.api.Action +import org.gradle.api.provider.Property +import org.gradle.api.provider.SetProperty + + +/** + * Type for customizing report variants shared by the current project. + * + * A report variant is a set of information used to generate a reports, namely: + * project classes, a list of Gradle test tasks, classes that need to be excluded from instrumentation. + * + * ``` + * variants { + * // create report variant with custom name, + * // in which it is acceptable to add information from other variants of the current project, as well as `kover` dependencies + * create("custom") { + * // ... + * } + * + * // Configure the variant that is automatically created in the current project + * // For example, "jvm" for JVM target or "debug" for Android build variant + * provided("jvm") { + * // ... + * } + * + * // Configure the variant for all the code that is available in the current project. + * // This variant always exists for any type of project. + * total { + * // ... + * } + * } + * ``` + */ +public interface KoverVariantsRootConfig: KoverVariantConfig { + /** + * Create custom report variant with name [variantName]. + * In it is acceptable to add information from other variants of the current project, as well as `kover` dependencies. + */ + public fun create(variantName: String, block: Action) + + /** + * Configure the variant with name [variantName] that is automatically created in the current project. + * For example, `"jvm"` for JVM target or `"debug"` for Android build variant. + */ + public fun provided(variantName: String, block: Action) + + /** + * Configure the variant for all the code that is available in the current project. + * This variant always exists for any type of project. + */ + public fun total(block: Action) +} + +/** + * Common config for Kover report variants. + */ +public interface KoverVariantConfig { + /** + * Limit the classes that will be included in the reports. + * These settings do not affect the instrumentation of classes. + * + * The settings specified here affect all reports in any projects that use the current project depending on. + * However, these settings should be used to regulate classes specific only to the project in which this setting is specified. + * + * Example: + * ``` + * sources { + * // exclude classes compiled by Java compiler from all reports + * excludeJava = true + * + * // exclude source classes of specified source sets from all reports + * excludedSourceSets.addAll(excludedSourceSet) + * ``` + */ + public fun sources(block: Action) + + /** + * Instrumentation settings for the current Gradle project. + * + * Instrumentation is the modification of classes when they are loaded into the JVM, which helps to determine which code was called and which was not. + * Instrumentation changes the bytecode of the class, so it may disable some JVM optimizations, slow down performance and concurrency tests, and may also be incompatible with other instrumentation libraries. + * + * For this reason, it may be necessary to fine-tune the instrumentation, for example, disabling instrumentation for problematic classes. Note that such classes would be marked as uncovered because of that. + * + * Example: + * ``` + * instrumentation { + * // disable instrumentation of all classes in test tasks that are in the current project + * excludeAll = true + * + * // disable instrumentation of specified classes in test tasks that are in the current project + * excludedClasses.addAll("foo.bar.*Biz", "*\$Generated") + * } + * ``` + */ + public fun instrumentation(block: Action) + + /** + * Set up tests, the run of which is used to measure coverage. + * + * To measure coverage, Kover runs Gradle test tasks, instrumentation takes place before they are performed, and code coverage is measured during execution. + * + * By default, Kover use all [org.gradle.api.tasks.testing.Test] to measure coverage. + * + * Example: + * ``` + * testTasks { + * // The coverage of the test1 and test2 tasks will no longer be taken into account in the reports + * // as well as these tasks will not be called when generating the report + * excluded.addAll("test1", "test2") + * } + * ``` + */ + public fun testTasks(block: Action) +} + +/** + * Limit the classes that will be included in the reports. + * These settings do not affect the instrumentation of classes. + * + * The settings specified here affect all reports in any projects that use the current project depending on. + * However, these settings should be used to regulate classes specific only to the project in which this setting is specified. + * + * Example: + * ``` + * sources { + * // exclude classes compiled by Java compiler from all reports + * excludeJava = true + * + * // exclude source classes of specified source sets from all reports + * excludedSourceSets.addAll(excludedSourceSet) + * ``` + */ +public interface KoverVariantSources { + /** + * Exclude classes compiled by Java compiler from all reports + */ + public val excludeJava: Property + + /** + * Exclude source classes of specified source sets from all reports + */ + public val excludedSourceSets: SetProperty +} + +/** + * Instrumentation settings for the current Gradle project. + * + * Instrumentation is the modification of classes when they are loaded into the JVM, which helps to determine which code was called and which was not. + * Instrumentation changes the bytecode of the class, so it may disable some JVM optimizations, slow down performance and concurrency tests, and may also be incompatible with other instrumentation libraries. + * + * For this reason, it may be necessary to fine-tune the instrumentation, for example, disabling instrumentation for problematic classes. + * + * Example: + * ``` + * instrumentation { + * // disable instrumentation in test tasks of all classes + * excludeAll = true + * + * // disable instrumentation of specified classes in test tasks + * excludedClasses.addAll("foo.bar.*Biz", "*\$Generated") + * } + * ``` + */ +public interface KoverVariantInstrumentation { + /** + * Disable instrumentation in test tasks of all classes + */ + public val excludeAll: Property + + /** + * Disable instrumentation in test tasks of specified classes + */ + public val excludedClasses: SetProperty +} + +/** + * Set up tests, the run of which is used to measure coverage. + * + * To measure coverage, Kover runs Gradle test tasks, instrumentation takes place before they are performed, and code coverage is measured during execution. + * + * By default, Kover use all [org.gradle.api.tasks.testing.Test] to measure coverage. + * + * Example: + * ``` + * testTasks { + * // The coverage of the test1 and test2 tasks will no longer be taken into account in the reports + * // as well as these tasks will not be called when generating the report + * excluded.addAll("test1", "test2") + * } + * ``` + */ +public interface KoverVariantTestTasks { + /** + * Specifies not to use test task with passed names to measure coverage. + * These tasks will also not be called when generating Kover reports. + */ + public val excluded: SetProperty +} + +/** + * The type for creating a custom report variant. + * + * Example: + * ``` + * // Add to created variant classes, tests and instrumented classes from "jvm" report variant of current project + * add("jvm") + * + * // add an "nonexistent" option that may not exist in the current project + * add("nonexistent", true) + * + * // Add to created variant classes, tests and instrumented classes from "jvm" report variant of current project, as well as `kover(project("name"))` dependencies + * addWithDependencies("custom") + * ``` + */ +public interface KoverVariantCreateConfig: KoverVariantConfig { + /** + * Add to created variant classes, tests and instrumented classes from report variant with name [variantNames]. + * This variant is taken only from the current project. + * + * If [optional] is `false` and a variant with given name is not found in the current project, an error [KoverIllegalConfigException] is thrown. + */ + public fun add(vararg variantNames: String, optional: Boolean = false) + + /** + * Add to created variant classes, tests and instrumented classes from report variant with name [variantNames]. + * This variant is taken from the current project and all `kover(project("name"))` dependency projects. + * + * If [optional] is `false` and a variant with given name is not found in the current project, an error [KoverIllegalConfigException] is thrown. + * + * If [optional] is `true` and a variant with given name is not found in the current project - in this case, the variant will not be searched even in dependencies. + */ + public fun addWithDependencies(vararg variantNames: String, optional: Boolean = false) +} diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/internal/KoverExtensionImpl.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/internal/KoverExtensionImpl.kt new file mode 100644 index 00000000..351aaca1 --- /dev/null +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/internal/KoverExtensionImpl.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.dsl.internal + +import kotlinx.kover.gradle.plugin.dsl.KoverExtension +import kotlinx.kover.gradle.plugin.dsl.KoverReportConfig +import kotlinx.kover.gradle.plugin.dsl.KoverVariantsRootConfig +import kotlinx.kover.gradle.plugin.dsl.KoverVersions.JACOCO_TOOL_DEFAULT_VERSION +import org.gradle.api.Action +import org.gradle.api.file.ProjectLayout +import org.gradle.api.model.ObjectFactory +import org.gradle.kotlin.dsl.newInstance +import javax.inject.Inject + + +internal abstract class KoverExtensionImpl @Inject constructor( + objects: ObjectFactory, + layout: ProjectLayout, + projectPath: String +): KoverExtension { + internal val variants: KoverVariantsRootConfigImpl = objects.newInstance() + internal val reports: KoverReportConfigImpl = objects.newInstance(objects, layout, projectPath) + + init { + @Suppress("LeakingThis") + useJacoco.convention(false) + @Suppress("LeakingThis") + jacocoVersion.convention(JACOCO_TOOL_DEFAULT_VERSION) + } + + override fun useJacoco() { + useJacoco.set(true) + } + + override fun useJacoco(version: String) { + useJacoco.set(true) + jacocoVersion.set(version) + } + + override fun variants(block: Action) { + block.execute(variants) + } + + override fun reports(block: Action) { + block.execute(reports) + } +} diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/internal/KoverProjectExtension.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/internal/KoverProjectExtension.kt deleted file mode 100644 index da197c39..00000000 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/internal/KoverProjectExtension.kt +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.kover.gradle.plugin.dsl.internal - -import kotlinx.kover.gradle.plugin.dsl.* -import kotlinx.kover.gradle.plugin.tools.CoverageToolVariant -import kotlinx.kover.gradle.plugin.tools.JacocoToolDefaultVariant -import kotlinx.kover.gradle.plugin.tools.JacocoToolVariant -import org.gradle.api.Action -import org.gradle.api.model.ObjectFactory -import org.gradle.kotlin.dsl.invoke -import org.gradle.kotlin.dsl.newInstance -import javax.inject.Inject - - -internal open class KoverProjectExtensionImpl @Inject constructor(objects: ObjectFactory) : KoverProjectExtension { - - internal var disabled: Boolean = false - - internal var excludeJava: Boolean = false - - internal var toolVariant: CoverageToolVariant? = null - override fun disable() { - disabled = true - } - - override fun useJacoco() { - toolVariant = JacocoToolDefaultVariant - } - - override fun useJacoco(version: String) { - toolVariant = JacocoToolVariant(version) - } - - override fun excludeJavaCode() { - excludeJava = true - } - - override fun excludeTests(config: Action) { - config(tests) - } - - override fun excludeSourceSets(config: Action) { - config(sourceSets) - } - - override fun excludeInstrumentation(config: Action) { - config(instrumentation) - } - - internal val tests: KoverTestsExclusionsImpl = objects.newInstance() - internal val sourceSets: SourceSetsExclusionsImpl = objects.newInstance() - internal val instrumentation: KoverInstrumentationExclusionsImpl = objects.newInstance() -} - -internal open class KoverTestsExclusionsImpl : KoverTestsExclusions { - internal val tasksNames: MutableSet = mutableSetOf() - - override fun tasks(vararg name: String) { - tasksNames.addAll(name) - } - - override fun tasks(names: Iterable) { - tasksNames.addAll(names) - } - -} - -internal open class SourceSetsExclusionsImpl : SourceSetsExclusions { - internal val sourceSets: MutableSet = mutableSetOf() - - override fun names(vararg name: String) { - sourceSets += name - } - - override fun names(names: Iterable) { - sourceSets += names - } -} - -internal open class KoverInstrumentationExclusionsImpl : KoverInstrumentationExclusions { - internal val classes: MutableSet = mutableSetOf() - - override fun classes(vararg names: String) { - classes += names - } - - override fun classes(names: Iterable) { - classes += names - } - - override fun packages(vararg names: String) { - names.forEach { - classes += "$it.*" - } - } - - override fun packages(names: Iterable) { - names.forEach { - classes += "$it.*" - } - } -} diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/internal/KoverReportExtension.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/internal/KoverReportExtension.kt index a5882123..e69de29b 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/internal/KoverReportExtension.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/internal/KoverReportExtension.kt @@ -1,346 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.kover.gradle.plugin.dsl.internal - -import kotlinx.kover.gradle.plugin.appliers.reports.androidReports -import kotlinx.kover.gradle.plugin.appliers.defaultReports -import kotlinx.kover.gradle.plugin.dsl.* -import org.gradle.api.* -import org.gradle.api.file.* -import org.gradle.api.model.* -import org.gradle.api.provider.Property -import org.gradle.api.provider.Provider -import org.gradle.kotlin.dsl.* -import java.io.* -import javax.inject.* - - -internal open class KoverReportExtensionImpl @Inject constructor( - private val objects: ObjectFactory, - private val layout: ProjectLayout -) : KoverReportExtension { - internal var filters: KoverReportFiltersImpl? = null - internal var verify: KoverVerificationRulesConfigImpl? = null - internal val default: KoverDefaultReportsConfigImpl = objects.defaultReports(layout) - internal val namedReports: MutableMap = mutableMapOf() - - override fun filters(config: Action) { - if (filters == null) { - filters = objects.newInstance(objects) - } - filters?.also { config(it) } - } - - override fun verify(config: Action) { - if (verify == null) { - verify = objects.newInstance(objects) - } - verify?.also { config(it) } - } - - override fun defaults(config: Action) { - config(default) - } - - override fun androidReports(variant: String, config: Action) { - val report = namedReports.getOrPut(variant) { objects.androidReports(variant, layout) } - config(report) - } -} - -internal open class KoverDefaultReportsConfigImpl @Inject constructor(objects: ObjectFactory) : - KoverReportsConfigImpl(objects), KoverDefaultReportsConfig { - - internal val merged: MutableSet = mutableSetOf() - - init { - html.onCheck = false - xml.onCheck = false - } - - override fun mergeWith(otherVariant: String) { - merged += otherVariant - } -} - -internal open class KoverReportsConfigImpl @Inject constructor(private val objects: ObjectFactory) : - KoverReportsConfig { - internal val html: KoverHtmlReportConfigImpl = objects.newInstance(objects) - internal val xml: KoverXmlReportConfigImpl = objects.newInstance(objects) - internal val binary: KoverBinaryReportConfigImpl = objects.newInstance(objects) - internal var verify: KoverVerifyReportConfigImpl? = null - internal val log: KoverLogReportConfigImpl = objects.newInstance(objects) - - internal var filters: KoverReportFiltersImpl? = null - - override fun filters(config: Action) { - if (filters == null) { - filters = objects.newInstance(objects) - } - filters?.also { config(it) } - } - - override fun html(config: Action) { - config(html) - } - - override fun xml(config: Action) { - config(xml) - } - - override fun binary(config: Action) { - config(binary) - } - - override fun verify(config: Action) { - if (verify == null) { - verify = objects.newInstance(objects) - } - verify?.also { config(it) } - } - - override fun log(config: Action) { - config(log) - } -} - -internal open class KoverHtmlReportConfigImpl @Inject constructor(private val objects: ObjectFactory) : - KoverHtmlReportConfig { - - override var onCheck: Boolean = false - - internal var filters: KoverReportFiltersImpl? = null - - override var title: String? = null - - override var charset: String? = null - - internal val reportDirProperty: Property = objects.property() - - override fun filters(config: Action) { - if (filters == null) { - filters = objects.newInstance(objects) - } - filters?.also { config(it) } - } - - override fun setReportDir(dir: File) { - reportDirProperty.set(dir) - } - - override fun setReportDir(dir: Provider) { - reportDirProperty.set(dir.map { it.asFile }) - } - -} - -internal open class KoverXmlReportConfigImpl @Inject constructor( - private val objects: ObjectFactory -) : KoverXmlReportConfig { - override val title: Property = objects.property() - - override var onCheck: Boolean = false - - internal var filters: KoverReportFiltersImpl? = null - - override fun filters(config: Action) { - if (filters == null) { - filters = objects.newInstance(objects) - } - filters?.also { config(it) } - } - - override fun setReportFile(xmlFile: File) { - reportFileProperty.set(xmlFile) - } - - override fun setReportFile(xmlFile: Provider) { - reportFileProperty.set(xmlFile.map { it.asFile }) - } - - internal val reportFileProperty: Property = objects.property() -} - -internal open class KoverBinaryReportConfigImpl @Inject constructor( - private val objects: ObjectFactory -) : KoverBinaryReportConfig { - - internal var filters: KoverReportFiltersImpl? = null - - override val onCheck: Property = objects.property() - - override val file: RegularFileProperty = objects.fileProperty() - - override fun filters(config: Action) { - if (filters == null) { - filters = objects.newInstance(objects) - } - filters?.also { config(it) } - } -} - -internal open class KoverVerifyReportConfigImpl @Inject constructor( - objects: ObjectFactory, -) : KoverVerificationRulesConfigImpl(objects), KoverVerifyReportConfig { - - override var onCheck: Boolean = false - - internal var filters: KoverReportFiltersImpl? = null -} - -internal open class KoverVerifyRuleImpl @Inject constructor(private val objects: ObjectFactory) : KoverVerifyRule { - internal var filters: KoverReportFiltersImpl? = null - - override var isEnabled: Boolean = true - - @Deprecated(message = "Removed") - override var name: String? = null - - override var entity: GroupingEntityType = GroupingEntityType.APPLICATION - - internal var internalName: String? = null - - override fun filters(config: Action) { - val filtersToConfigure = filters ?: (objects.newInstance(objects).also { filters = it }) - config(filtersToConfigure) - } - - override fun minBound(minValue: Int, metric: MetricType, aggregation: AggregationType) { - val newBound = objects.newInstance() - newBound.minValue = minValue - newBound.metric = metric - newBound.aggregation = aggregation - bounds += newBound - } - - override fun maxBound(maxValue: Int, metric: MetricType, aggregation: AggregationType) { - val newBound = objects.newInstance() - newBound.maxValue = maxValue - newBound.metric = metric - newBound.aggregation = aggregation - bounds += newBound - } - - override fun minBound(minValue: Int) { - val newBound = objects.newInstance() - newBound.minValue = minValue - bounds += newBound - } - - override fun maxBound(maxValue: Int) { - val newBound = objects.newInstance() - newBound.maxValue = maxValue - bounds += newBound - } - - override fun bound(minValue: Int, maxValue: Int, metric: MetricType, aggregation: AggregationType) { - val newBound = objects.newInstance() - newBound.minValue = minValue - newBound.maxValue = maxValue - newBound.metric = metric - newBound.aggregation = aggregation - bounds += newBound - } - - override fun bound(config: Action) { - val newBound = objects.newInstance() - config(newBound) - bounds += newBound - } - - internal val bounds: MutableList = mutableListOf() -} - -internal open class KoverVerifyBoundImpl : KoverVerifyBound { - override var minValue: Int? = null - override var maxValue: Int? = null - override var metric: MetricType = MetricType.LINE - override var aggregation: AggregationType = AggregationType.COVERED_PERCENTAGE -} - -internal open class KoverLogReportConfigImpl @Inject constructor(private val objects: ObjectFactory): KoverLogReportConfig { - internal var filters: KoverReportFiltersImpl? = null - - override var onCheck: Boolean = false - - override var header: String? = null - - override var format: String? = null - - override var groupBy: GroupingEntityType? = null - - override var coverageUnits: MetricType? = null - - override var aggregationForGroup: AggregationType? = null - - override fun filters(config: Action) { - val filtersToConfigure = filters ?: (objects.newInstance(objects).also { filters = it }) - config(filtersToConfigure) - } -} - -internal open class KoverReportFiltersImpl @Inject constructor( - objects: ObjectFactory -) : KoverReportFilters { - override fun excludes(config: Action) { - config(excludesIntern) - } - - override fun includes(config: Action) { - config(includesIntern) - } - - internal val excludesIntern: KoverReportFilterImpl = objects.newInstance() - internal val includesIntern: KoverReportFilterImpl = objects.newInstance() -} - -internal open class KoverVerificationRulesConfigImpl @Inject constructor( - private val objects: ObjectFactory -) : KoverVerificationRulesConfig { - internal val rules: MutableList = mutableListOf() - - override fun rule(config: Action) { - val newRule = objects.newInstance(objects) - config(newRule) - rules += newRule - } - - override fun rule(name: String, config: Action) { - val newRule = objects.newInstance(objects) - newRule.internalName = name - config(newRule) - rules += newRule - } -} - -internal open class KoverReportFilterImpl : KoverReportFilter { - internal val classes: MutableSet = mutableSetOf() - - internal val annotations: MutableSet = mutableSetOf() - - override fun classes(vararg names: String) { - classes += names - } - - override fun classes(names: Iterable) { - classes += names - } - - override fun packages(vararg names: String) { - names.forEach { - classes += "$it.*" - } - } - - override fun packages(names: Iterable) { - names.forEach { - classes += "$it.*" - } - } - - override fun annotatedBy(vararg annotationName: String) { - annotations += annotationName - } -} diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/internal/ReportsImpl.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/internal/ReportsImpl.kt new file mode 100644 index 00000000..b79e9bb0 --- /dev/null +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/internal/ReportsImpl.kt @@ -0,0 +1,331 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.dsl.internal + +import kotlinx.kover.gradle.plugin.commons.* +import kotlinx.kover.gradle.plugin.dsl.* +import org.gradle.api.Action +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.ProjectLayout +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Provider +import org.gradle.api.provider.SetProperty +import org.gradle.kotlin.dsl.invoke +import org.gradle.kotlin.dsl.newInstance +import javax.inject.Inject + + +internal abstract class KoverReportConfigImpl @Inject constructor( + private val objects: ObjectFactory, + private val layout: ProjectLayout, + private val projectPath: String +) : KoverReportConfig { + private val rootFilters: KoverReportFiltersConfigImpl = objects.newInstance() + private val rootVerify: KoverVerificationRulesConfigImpl = objects.newInstance() + + internal val total: KoverReportSetConfigImpl = createVariant(TOTAL_VARIANT_NAME, projectPath) + + internal val byName: MutableMap = mutableMapOf() + + override fun filters(config: Action) { + rootFilters.also { config(it) } + } + + override fun verify(config: Action) { + rootVerify.also { config(it) } + } + + override fun total(config: Action) { + config(total) + } + + override fun variant(variant: String, config: Action) { + val report = byName.getOrPut(variant) { + createVariant(variant, projectPath) + } + config(report) + } + + internal fun createVariant(variantName: String, projectPath: String): KoverReportSetConfigImpl { + val block = + objects.newInstance(objects, layout.buildDirectory, variantName, projectPath) + + block.filters.extendsFrom(rootFilters) + block.verify.extendFrom(rootVerify) + + return block + } +} + +internal abstract class KoverReportSetConfigImpl @Inject constructor( + objects: ObjectFactory, + buildDir: DirectoryProperty, + variantName: String, + projectPath: String +) : KoverReportSetConfig { + internal val filters: KoverReportFiltersConfigImpl = objects.newInstance() + internal val verify: KoverVerifyTaskConfigImpl = objects.newInstance() + + internal val html: KoverHtmlTaskConfig = objects.newInstance() + internal val xml: KoverXmlTaskConfig = objects.newInstance() + internal val binary: KoverBinaryTaskConfig = objects.newInstance() + internal val log: KoverLogTaskConfig = objects.newInstance() + + init { + xml.xmlFile.convention(buildDir.file(xmlReportPath(variantName))) + html.htmlDir.convention(buildDir.dir(htmlReportPath(variantName))) + binary.file.convention(buildDir.file(binaryReportPath(variantName))) + + html.onCheck.convention(false) + xml.onCheck.convention(false) + binary.onCheck.convention(false) + log.onCheck.convention(false) + verify.onCheck.convention(variantName == TOTAL_VARIANT_NAME) + + xml.title.convention("Kover Gradle Plugin XML report for $projectPath") + + log.format.convention(" line coverage: %") + log.groupBy.convention(GroupingEntityType.APPLICATION) + log.coverageUnits.convention(MetricType.LINE) + log.aggregationForGroup.convention(AggregationType.COVERED_PERCENTAGE) + } + + + override fun filters(config: Action) { + filters.clean() + config(filters) + } + + override fun filtersAppend(config: Action) { + config(filters) + } + + override fun html(config: Action) { + config(html) + } + + override fun xml(config: Action) { + config(xml) + } + + override fun binary(config: Action) { + config(binary) + } + + override fun verify(config: Action) { + verify.clean() + config(verify) + } + + override fun verifyAppend(config: Action) { + config(verify) + } + + override fun log(config: Action) { + config(log) + } +} + +internal abstract class KoverVerifyTaskConfigImpl @Inject constructor(objects: ObjectFactory) : + KoverVerificationRulesConfigImpl(objects), KoverVerifyTaskConfig + +internal abstract class KoverVerificationRulesConfigImpl @Inject constructor( + private val objects: ObjectFactory +) : KoverVerificationRulesConfig { + internal abstract val rules: ListProperty + + override fun rule(config: Action) { + val newRule = objects.newInstance(objects, "") + config(newRule) + + rules.add(newRule) + } + + override fun rule(name: String, config: Action) { + val newRule = objects.newInstance(objects, name) + config(newRule) + rules.add(newRule) + } + + internal fun extendFrom(other: KoverVerificationRulesConfigImpl) { + rules.addAll(other.rules) + } + + internal fun clean() { + rules.empty() + } +} + +internal abstract class KoverVerifyRuleImpl @Inject constructor(private val objects: ObjectFactory, val name: String) : KoverVerifyRule { + + init { + // Gradle is guaranteed to fill properties + @Suppress("LeakingThis") + disabled.set(false) + @Suppress("LeakingThis") + groupBy.set(GroupingEntityType.APPLICATION) + } + + override fun minBound(minValue: Int, coverageUnits: MetricType, aggregationForGroup: AggregationType) { + val newBound = createBound() + newBound.min.set(minValue) + newBound.coverageUnits.set(coverageUnits) + newBound.aggregationForGroup.set(aggregationForGroup) + bounds += newBound + } + + override fun minBound(min: Int) { + val newBound = createBound() + newBound.min.set(min) + bounds += newBound + } + + override fun minBound(min: Provider) { + val newBound = createBound() + newBound.min.set(min) + bounds += newBound + } + + override fun maxBound(maxValue: Int, coverageUnits: MetricType, aggregation: AggregationType) { + val newBound = createBound() + newBound.max.set(maxValue) + newBound.coverageUnits.set(coverageUnits) + newBound.aggregationForGroup.set(aggregation) + bounds += newBound + } + + override fun maxBound(max: Int) { + val newBound = createBound() + newBound.max.set(max) + bounds += newBound + } + + override fun maxBound(max: Provider) { + val newBound = createBound() + newBound.max.set(max) + bounds += newBound + } + + override fun bound(min: Int, max: Int, coverageUnits: MetricType, aggregation: AggregationType) { + val newBound = createBound() + newBound.min.set(min) + newBound.max.set(max) + newBound.coverageUnits.set(coverageUnits) + newBound.aggregationForGroup.set(aggregation) + bounds += newBound + } + + override fun bound(config: Action) { + val newBound = createBound() + config(newBound) + bounds += newBound + } + + internal val bounds: MutableList = mutableListOf() + + private fun createBound(): KoverVerifyBound { + val newBound = objects.newInstance() + newBound.coverageUnits.set(MetricType.LINE) + newBound.aggregationForGroup.set(AggregationType.COVERED_PERCENTAGE) + return newBound + } +} + + +internal open class KoverReportFiltersConfigImpl @Inject constructor( + objects: ObjectFactory +) : KoverReportFiltersConfig { + internal val excludesImpl: KoverReportFilterImpl = objects.newInstance() + internal val includesImpl: KoverReportFilterImpl = objects.newInstance() + + override fun excludes(config: Action) { + config(excludesImpl) + } + + override fun includes(config: Action) { + config(includesImpl) + } + + internal fun clean() { + excludesImpl.clean() + includesImpl.clean() + } + + internal fun extendsFrom(other: KoverReportFiltersConfigImpl) { + excludesImpl.extendsFrom(other.excludesImpl) + includesImpl.extendsFrom(other.includesImpl) + } +} + + +internal abstract class KoverReportFilterImpl: KoverReportFilter { + internal abstract val classes: SetProperty + internal abstract val annotations: SetProperty + + override fun classes(vararg names: String) { + classes.addAll(*names) + } + + override fun classes(names: Iterable) { + classes.addAll(names) + } + + override fun classes(vararg names: Provider) { + names.forEach { nameProvider -> + classes.add(nameProvider) + } + } + + override fun classes(names: Provider>) { + classes.addAll(names) + } + + override fun packages(vararg names: String) { + names.forEach { packageName -> + classes.add(packageName.packageAsClass()) + } + } + + override fun packages(names: Iterable) { + names.forEach { packageName -> + classes.add(packageName.packageAsClass()) + } + } + + override fun packages(vararg names: Provider) { + names.forEach { packageNameProvider -> + classes.add(packageNameProvider.map { it.packageAsClass() }) + } + } + + override fun packages(names: Provider>) { + classes.addAll(names.map { packages -> + packages.map { it.packageAsClass() } + }) + } + + override fun annotatedBy(vararg annotationName: String) { + annotations.addAll(*annotationName) + } + + override fun annotatedBy(vararg annotationName: Provider) { + annotationName.forEach { nameProvider -> + annotations.add(nameProvider) + } + } + + internal fun extendsFrom(other: KoverReportFilterImpl) { + classes.addAll(other.classes) + annotations.addAll(other.annotations) + } + + internal fun clean() { + classes.empty() + annotations.empty() + } + + private fun String.packageAsClass(): String = "$this.*" +} diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/internal/VariantsImpl.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/internal/VariantsImpl.kt new file mode 100644 index 00000000..e4825a8a --- /dev/null +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/internal/VariantsImpl.kt @@ -0,0 +1,124 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.dsl.internal + +import kotlinx.kover.gradle.plugin.commons.KoverIllegalConfigException +import kotlinx.kover.gradle.plugin.commons.TOTAL_VARIANT_NAME +import kotlinx.kover.gradle.plugin.dsl.* +import org.gradle.api.Action +import org.gradle.api.model.ObjectFactory +import org.gradle.kotlin.dsl.newInstance +import javax.inject.Inject + +internal abstract class KoverVariantsRootConfigImpl @Inject constructor(val objects: ObjectFactory) : + KoverVariantConfigImpl(objects), KoverVariantsRootConfig { + internal val customVariants: MutableMap = mutableMapOf() + internal val providedVariants: MutableMap = mutableMapOf() + + init { + classes.excludeJava.convention(false) + classes.excludedSourceSets.convention(emptySet()) + + instrumentation.excludeAll.set(false) + instrumentation.excludedClasses.addAll(emptySet()) + + testTasks.excluded.addAll(emptySet()) + } + + override fun create(variantName: String, block: Action) { + if (variantName == TOTAL_VARIANT_NAME) { + throw KoverIllegalConfigException("The custom variant name cannot be empty.") + } + + val variantConfig = customVariants.getOrPut(variantName) { + val variantConfig = objects.newInstance(variantName) + variantConfig.deriveFrom(this) + variantConfig + } + + block.execute(variantConfig) + } + + override fun provided(variantName: String, block: Action) { + if (variantName == TOTAL_VARIANT_NAME) { + throw KoverIllegalConfigException("The provided variant name cannot be empty.") + } + + val variantConfig = providedVariants.getOrPut(variantName) { + val variantConfig = objects.newInstance() + variantConfig.deriveFrom(this) + variantConfig + } + + block.execute(variantConfig) + } + + override fun total(block: Action) { + val variantConfig = providedVariants.getOrPut(TOTAL_VARIANT_NAME) { + objects.newInstance().also { newVariant -> + newVariant.deriveFrom(this) + } + } + + block.execute(variantConfig) + } + +} + +internal abstract class KoverVariantConfigImpl @Inject constructor(objects: ObjectFactory) : KoverVariantConfig { + internal val classes: KoverVariantSources = objects.newInstance() + internal val instrumentation: KoverVariantInstrumentation = objects.newInstance() + internal val testTasks: KoverVariantTestTasks = objects.newInstance() + + override fun sources(block: Action) { + block.execute(classes) + } + + override fun instrumentation(block: Action) { + block.execute(instrumentation) + } + + override fun testTasks(block: Action) { + block.execute(testTasks) + } + + internal fun deriveFrom(other: KoverVariantConfigImpl) { + classes.excludeJava.set(other.classes.excludeJava) + classes.excludedSourceSets.addAll(other.classes.excludedSourceSets) + + instrumentation.excludeAll.set(other.instrumentation.excludeAll) + instrumentation.excludedClasses.addAll(other.instrumentation.excludedClasses) + + testTasks.excluded.addAll(other.testTasks.excluded) + } +} + +internal class MergingOptionality(val optional: Boolean, val withDependencies: Boolean) + +internal abstract class KoverVariantCreateConfigImpl @Inject constructor(private val objects: ObjectFactory, private val variantName: String) : + KoverVariantConfigImpl(objects), KoverVariantCreateConfig { + // variant name -> optionality + internal val variantsByName: MutableMap = mutableMapOf() + + override fun add(vararg variantNames: String, optional: Boolean) { + for (addedVariantName in variantNames) { + addByName(addedVariantName, variantName, optional, withDependencies = false) + } + } + + override fun addWithDependencies(vararg variantNames: String, optional: Boolean) { + for (addedVariantName in variantNames) { + addByName(addedVariantName, variantName, optional, withDependencies = true) + } + } + + private fun addByName(addedVariantName: String, variantName: String, optional: Boolean, withDependencies: Boolean) { + val variant = variantsByName[addedVariantName] + if (variant != null && variant.optional != optional) { + throw KoverIllegalConfigException("It is not possible to merge variant '$addedVariantName' to '$variantName' with a different optionality. Merging dependency should be either optional or non-optional.") + } + variantsByName[addedVariantName] = MergingOptionality(optional, withDependencies) + } +} diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/tasks/KoverTasks.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/tasks/KoverTasks.kt new file mode 100644 index 00000000..cb1b2c6e --- /dev/null +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/dsl/tasks/KoverTasks.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.dsl.tasks + +/** + * Common interface for all Kover report tasks. + */ +interface KoverReport + +/** + * Interface for Kover XML report generation tasks. + */ +interface KoverXmlReport: KoverReport + +/** + * Interface for Kover HTML report generation tasks. + */ +interface KoverHtmlReport: KoverReport + +/** + * Interface for Kover tasks that print coverage to the build log. + */ +interface KoverLogReport: KoverReport + +/** + * Interface for Kover coverage verification tasks. + */ +interface KoverVerifyReport: KoverReport + +/** + * Interface for Kover report generation tasks in IC format. + */ +interface KoverBinaryReport: KoverReport diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/Android.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/Android.kt index 60eaa4aa..5dab3c16 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/Android.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/Android.kt @@ -4,54 +4,27 @@ package kotlinx.kover.gradle.plugin.locators +import kotlinx.kover.gradle.plugin.commons.AndroidBuildVariant import kotlinx.kover.gradle.plugin.commons.AndroidFallbacks import kotlinx.kover.gradle.plugin.commons.AndroidFlavor -import kotlinx.kover.gradle.plugin.commons.AndroidVariantCompilationKit -import kotlinx.kover.gradle.plugin.commons.CompilationUnit -import kotlinx.kover.gradle.plugin.commons.KoverCriticalException import kotlinx.kover.gradle.plugin.commons.KoverIllegalConfigException -import kotlinx.kover.gradle.plugin.dsl.internal.KoverProjectExtensionImpl +import kotlinx.kover.gradle.plugin.appliers.origin.AndroidVariantOrigin +import kotlinx.kover.gradle.plugin.appliers.origin.CompilationDetails import kotlinx.kover.gradle.plugin.util.DynamicBean import kotlinx.kover.gradle.plugin.util.bean import kotlinx.kover.gradle.plugin.util.hasSuperclass -import org.gradle.api.Action import org.gradle.api.Project import org.gradle.api.tasks.testing.Test import org.gradle.kotlin.dsl.withType -internal fun Project.afterAndroidPluginApplied(afterAndroid: () -> Unit) { - val androidComponents = project.extensions.findByName("androidComponents")?.bean() - ?: throw KoverCriticalException("Kover requires extension with name 'androidComponents' for project '${project.path}' since it is recognized as Kotlin+Android project") - - val callback = Action { - project.afterEvaluate { - afterAndroid() - } - } - - if (androidComponents.hasFunction("finalizeDsl", callback)) { - /* - Assumption: `finalizeDsl` is called in the `afterEvaluate` action, in which build variants are created. - Therefore, if an action is added to the queue inside it, it will be executed only after variants are created - */ - androidComponents("finalizeDsl", callback) - } else { - // for old versions < 7.0 an action is added to the AAA queue. - // Since this code is executed after the applying of AGP, there is a high probability that the action will fall into the `afterEvaluate` queue after the actions of the AGP - project.afterEvaluate { - afterAndroid() - } - } -} /** * Locate Android compilation kits for the given Kotlin Target. */ internal fun Project.androidCompilationKits( androidExtension: DynamicBean, - koverExtension: KoverProjectExtensionImpl, kotlinTarget: DynamicBean -): List { +): List { val variants = if ("applicationVariants" in androidExtension) { androidExtension.beanCollection("applicationVariants") } else { @@ -61,30 +34,25 @@ internal fun Project.androidCompilationKits( val fallbacks = findFallbacks(androidExtension) return variants.map { - extractAndroidKit(androidExtension, koverExtension, kotlinTarget, fallbacks, it) + extractAndroidKit(androidExtension, kotlinTarget, fallbacks, it) } } private fun Project.extractAndroidKit( androidExtension: DynamicBean, - koverExtension: KoverProjectExtensionImpl, kotlinTarget: DynamicBean, fallbacks: AndroidFallbacks, variant: DynamicBean -): AndroidVariantCompilationKit { +): AndroidVariantOrigin { val variantName = variant.value("name") val compilations = provider { - mapOf("main" to extractCompilationOrEmpty(koverExtension, kotlinTarget, variantName)) + extractCompilation(kotlinTarget, variantName) } + // if `unitTestVariant` not specified for application/library variant (is null) then unit tests are disabled for it val unitTestVariantName = variant.beanOrNull("unitTestVariant")?.value("name") val tests = tasks.withType().matching { test -> - // if `unitTestVariant` not specified for application/library variant then unit tests are disabled for it unitTestVariantName != null - // skip all tests from instrumentation if Kover Plugin is disabled for the project - && !koverExtension.disabled - // skip this test if it disabled by name - && test.name !in koverExtension.tests.tasksNames // use only Android unit tests (local tests) && test.hasSuperclass("AndroidUnitTest") // only tests of current application build variant @@ -102,7 +70,8 @@ private fun Project.extractAndroidKit( // merge flavors to get missing dimensions for variant val missingDimensions = findMissingDimensions(androidExtension, variant) - return AndroidVariantCompilationKit(variantName, buildTypeName, flavors, fallbacks, missingDimensions, tests, compilations) + val details = AndroidBuildVariant(variantName, buildTypeName, flavors, fallbacks, missingDimensions) + return AndroidVariantOrigin(tests, compilations, details) } private fun findMissingDimensions(androidExtension: DynamicBean, variant: DynamicBean): Map { @@ -120,21 +89,15 @@ private fun findMissingDimensions(androidExtension: DynamicBean, variant: Dynami } } -private fun extractCompilationOrEmpty( - koverExtension: KoverProjectExtensionImpl, +private fun extractCompilation( kotlinTarget: DynamicBean, variantName: String -): CompilationUnit { - if (koverExtension.disabled) { - // If the Kover plugin is disabled, then it does not provide any directories and compilation tasks to its artifacts. - return CompilationUnit() - } - - val compilation = kotlinTarget.beanCollection("compilations").first { +): Map { + val compilations = kotlinTarget.beanCollection("compilations").filter { it.value("name") == variantName } - return compilation.asJvmCompilationUnit(koverExtension.excludeJava) { + return compilations.jvmCompilations { // exclude java classes from report. Expected java class files are placed in directories like // build/intermediates/javac/debug/classes it.parentFile.parentFile.name == "javac" diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/CompilationsListeners.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/CompilationsListeners.kt deleted file mode 100644 index 8f2e77d6..00000000 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/CompilationsListeners.kt +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.kover.gradle.plugin.locators - -import kotlinx.kover.gradle.plugin.commons.* -import kotlinx.kover.gradle.plugin.dsl.internal.KoverProjectExtensionImpl -import kotlinx.kover.gradle.plugin.util.DynamicBean -import kotlinx.kover.gradle.plugin.util.bean -import org.gradle.api.* - -/** - * Listener for compilation kits detection events. - */ -internal interface CompilationsListener { - /** - * Function called when a JVM target is detected in Kotlin/JVM or Kotlin/MPP project. - */ - fun onJvmCompilation(kit: JvmCompilationKit) - - /** - * Function called when an Android build variants are detected in Kotlin/Android or Kotlin/MPP project. - */ - fun onAndroidCompilations(kits: List) - - /** - * Function called when the compilations search is completed. - */ - fun onFinalize() -} - -/** - * A factory that creates a locator suitable for a specific project. - */ -internal object CompilationsListenerManager { - - /** - * Asynchronously detect Kotlin compilations in the current project. - */ - fun locate(project: Project, koverExtension: KoverProjectExtensionImpl, listener: CompilationsListener) { - val wrapper = CompilationsListenerWrapper(listener) - - val context = LocatorContext(project, koverExtension, wrapper) - context.initNoKotlinPluginLocator() - - project.pluginManager.withPlugin("kotlin") { - context.initKotlinJvmPluginLocator() - } - project.pluginManager.withPlugin("kotlin-multiplatform") { - context.initKotlinMultiplatformPluginLocator() - } - project.pluginManager.withPlugin("kotlin-android") { - context.initKotlinAndroidPluginLocator() - } - } -} - -internal class LocatorContext( - val project: Project, - val koverExtension: KoverProjectExtensionImpl, - val listener: CompilationsListenerWrapper -) - -/** - * Locate information about Kotlin project's compilations. - * This information is necessary for carrying out instrumentation and generating Kover reports. - * - * The locator is engaged in reading the settings of the applied plugins. - */ -internal class CompilationsListenerWrapper(private val listener: CompilationsListener) { - @Volatile - private var pluginType: KotlinPluginType? = null - - @Volatile - private var finalized: Boolean = false - - fun onApplyPlugin(type: KotlinPluginType) { - if (pluginType != null) { - throw KoverIllegalConfigException( - "Kover can't work in a project where several different Kotlin plugins are applied.\n" + - "Detected plugins ${pluginType?.name} and ${type.name}, remove one of them" - ) - } - pluginType = type - } - - fun jvm(compilation: JvmCompilationKit) { - checkNotFinalized() - - listener.onJvmCompilation(compilation) - } - - fun android(compilations: List) { - checkNotFinalized() - - listener.onAndroidCompilations(compilations) - } - - fun finalizeIfNoKotlinPlugin() { - if (pluginType == null) { - // if no plugin has been applied, then calls the finalization to process dependencies - finalize() - } - } - - fun finalize() { - checkNotFinalized() - finalized = true - - listener.onFinalize() - } - - private fun checkNotFinalized() { - if (finalized) { - throw KoverCriticalException("Listener is finalized") - } - } -} - -internal fun Project.getKotlinExtension(): DynamicBean { - return extensions.findByName("kotlin")?.bean() - ?: throw KoverCriticalException("Kover requires extension with name 'kotlin' for project '${project.path}' since it is recognized as Kotlin/Multiplatform project") -} - -internal val Project.hasAnyAndroidPlugin: Boolean - get() = pluginManager.hasPlugin(ANDROID_APP_PLUGIN_ID) || pluginManager.hasPlugin(ANDROID_LIB_PLUGIN_ID) || pluginManager.hasPlugin(ANDROID_DYNAMIC_PLUGIN_ID) - -internal const val ANDROID_APP_PLUGIN_ID = "com.android.application" - -internal const val ANDROID_LIB_PLUGIN_ID = "com.android.library" - -internal const val ANDROID_DYNAMIC_PLUGIN_ID = "com.android.dynamic-feature" diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/Jvm.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/Jvm.kt index 1f30de2d..4b7a8aeb 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/Jvm.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/Jvm.kt @@ -4,71 +4,44 @@ package kotlinx.kover.gradle.plugin.locators -import kotlinx.kover.gradle.plugin.commons.CompilationUnit -import kotlinx.kover.gradle.plugin.dsl.internal.KoverProjectExtensionImpl +import kotlinx.kover.gradle.plugin.appliers.origin.LanguageCompilation +import kotlinx.kover.gradle.plugin.appliers.origin.CompilationDetails import kotlinx.kover.gradle.plugin.util.DynamicBean import org.gradle.api.Task import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.TaskProvider import java.io.File -/** - * Extract JVM compilation unit form Kotlin compilation ([this]). - */ -internal inline fun DynamicBean.asJvmCompilationUnit( - excludeJava: Boolean, - isJavaOutput: (File) -> Boolean -): CompilationUnit { - val sources = beanCollection("allKotlinSourceSets").flatMap { - it["kotlin"].valueCollection("srcDirs") - }.toSet() - - val outputs = get("output").value("classesDirs").files.filterNot { - excludeJava && isJavaOutput(it) - }.toSet() - - val compileTasks = mutableListOf() - compileTasks += value("compileKotlinTask") - if (!excludeJava) { - valueOrNull?>("compileJavaTaskProvider")?.orNull?.let { task -> compileTasks += task } - } - - return CompilationUnit(sources, outputs, compileTasks) -} - -internal fun DynamicBean.extractJvmCompilations( - koverExtension: KoverProjectExtensionImpl, +internal fun Iterable.jvmCompilations( isJavaOutput: (File) -> Boolean -): Map { - if (koverExtension.disabled) { - // If the Kover plugin is disabled, then it does not provide any directories and compilation tasks to its artifacts. - return emptyMap() - } - - val compilations = this.beanCollection("compilations").filter { - // always ignore test source set by default - val name = it.value("name") - name != SourceSet.TEST_SOURCE_SET_NAME - // ignore specified JVM source sets - && name !in koverExtension.sourceSets.sourceSets - } - - return compilations.associate { compilation -> +): Map { + return associate { compilation -> val name = compilation.value("name") - name to extractJvmCompilation(koverExtension, compilation, isJavaOutput) + name to extractJvmCompilation(compilation, isJavaOutput) } } private fun extractJvmCompilation( - koverExtension: KoverProjectExtensionImpl, compilation: DynamicBean, isJavaOutput: (File) -> Boolean -): CompilationUnit { - return if (koverExtension.disabled) { - // If the Kover plugin is disabled, then it does not provide any directories and compilation tasks to its artifacts. - CompilationUnit() - } else { - compilation.asJvmCompilationUnit(koverExtension.excludeJava, isJavaOutput) - } +): CompilationDetails { + val sources = compilation.beanCollection("allKotlinSourceSets").flatMap { + it["kotlin"].valueCollection("srcDirs") + }.toSet() + + val kotlinOutputs = compilation["output"].value("classesDirs").files.filterNot { + isJavaOutput(it) + }.toSet() + + val javaOutputs = compilation["output"].value("classesDirs").files.filter { + isJavaOutput(it) + }.toSet() + + val kotlinCompileTask = compilation.value("compileKotlinTask") + val javaCompileTask = compilation.valueOrNull?>("compileJavaTaskProvider")?.orNull + + val kotlin = LanguageCompilation(kotlinOutputs, kotlinCompileTask) + val java = LanguageCompilation(javaOutputs, javaCompileTask) + + return CompilationDetails(sources, kotlin, java) } diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/KotlinAndroidLocator.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/KotlinAndroidLocator.kt index ee96f00f..7e47bf4e 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/KotlinAndroidLocator.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/KotlinAndroidLocator.kt @@ -4,8 +4,9 @@ package kotlinx.kover.gradle.plugin.locators +import kotlinx.kover.gradle.plugin.appliers.origin.AndroidVariantOrigin +import kotlinx.kover.gradle.plugin.appliers.origin.AllVariantOrigins import kotlinx.kover.gradle.plugin.commons.* -import kotlinx.kover.gradle.plugin.dsl.internal.KoverProjectExtensionImpl import kotlinx.kover.gradle.plugin.util.* import org.gradle.api.* @@ -15,33 +16,17 @@ Because of this, Kover may not have direct access to the Android plugin classes, To work around this limitation, working with objects is done through reflection, using a dynamic Gradle wrapper. */ -internal fun LocatorContext.initKotlinAndroidPluginLocator() { - listener.onApplyPlugin(KotlinPluginType.ANDROID) - - project.pluginManager.withPlugin(ANDROID_APP_PLUGIN_ID) { - project.afterAndroidPluginApplied { processAndroidTarget() } - } - project.pluginManager.withPlugin(ANDROID_LIB_PLUGIN_ID) { - project.afterAndroidPluginApplied { processAndroidTarget() } - } - project.pluginManager.withPlugin(ANDROID_DYNAMIC_PLUGIN_ID) { - project.afterAndroidPluginApplied { processAndroidTarget() } - } -} - -private fun LocatorContext.processAndroidTarget() { +internal fun Project.locateKotlinAndroidVariants(): AllVariantOrigins { val kotlinExtension = project.getKotlinExtension() - locateAndroidCompilations(kotlinExtension) - - listener.finalize() + val android = locateAndroidVariants(kotlinExtension) + return AllVariantOrigins(null, android) } -private fun LocatorContext.locateAndroidCompilations(kotlinExtension: DynamicBean) { +private fun Project.locateAndroidVariants(kotlinExtension: DynamicBean): List { val androidExtension = project.extensions.findByName("android")?.bean() ?: throw KoverCriticalException("Kover requires extension with name 'android' for project '${project.path}' since it is recognized as Kotlin/Android project") val kotlinTarget = kotlinExtension["target"] - val androidCompilations = project.androidCompilationKits(androidExtension, koverExtension, kotlinTarget) - listener.android(androidCompilations) + return project.androidCompilationKits(androidExtension, kotlinTarget) } diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/KotlinJvmLocator.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/KotlinJvmLocator.kt index 9369521b..c1bda28c 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/KotlinJvmLocator.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/KotlinJvmLocator.kt @@ -4,10 +4,9 @@ package kotlinx.kover.gradle.plugin.locators -import kotlinx.kover.gradle.plugin.commons.* -import kotlinx.kover.gradle.plugin.dsl.internal.* -import kotlinx.kover.gradle.plugin.util.* -import org.gradle.api.* +import kotlinx.kover.gradle.plugin.appliers.origin.JvmVariantOrigin +import kotlinx.kover.gradle.plugin.appliers.origin.AllVariantOrigins +import org.gradle.api.Project import org.gradle.api.tasks.testing.Test import org.gradle.kotlin.dsl.* @@ -17,35 +16,18 @@ Because of this, Kover may not have direct access to the JVM plugin classes, and To work around this limitation, working with objects is done through reflection, using a dynamic Gradle wrapper. */ -internal fun LocatorContext.initKotlinJvmPluginLocator() { - listener.onApplyPlugin(KotlinPluginType.JVM) - project.afterEvaluate { - afterJvmOnly() - } -} - -private fun LocatorContext.afterJvmOnly() { +internal fun Project.locateKotlinJvmVariants(): AllVariantOrigins { val kotlinExtension = project.getKotlinExtension() - locateJvmCompilations(kotlinExtension) - listener.finalize() -} - -private fun LocatorContext.locateJvmCompilations(kotlinExtension: DynamicBean) { - val tests = project.tasks.withType().matching { - // skip all tests from instrumentation if Kover Plugin is disabled for the project - !koverExtension.disabled - // skip this test if it disabled by name - && it.name !in koverExtension.tests.tasksNames - } + val tests = tasks.withType() - val compilations = project.provider { - kotlinExtension["target"].extractJvmCompilations(koverExtension) { + val compilations = provider { + kotlinExtension["target"].beanCollection("compilations").jvmCompilations { // exclude java classes from report. Expected java class files are placed in directories like // build/classes/java/main it.parentFile.name == "java" } } - listener.jvm(JvmCompilationKit(tests, compilations)) + return AllVariantOrigins(JvmVariantOrigin(tests, compilations), emptyList()) } diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/KotlinMultiPlatformLocator.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/KotlinMultiPlatformLocator.kt index ac180e7d..94e21a4b 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/KotlinMultiPlatformLocator.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/KotlinMultiPlatformLocator.kt @@ -4,9 +4,12 @@ package kotlinx.kover.gradle.plugin.locators +import kotlinx.kover.gradle.plugin.appliers.origin.AndroidVariantOrigin +import kotlinx.kover.gradle.plugin.appliers.origin.JvmVariantOrigin +import kotlinx.kover.gradle.plugin.appliers.origin.AllVariantOrigins import kotlinx.kover.gradle.plugin.commons.* -import kotlinx.kover.gradle.plugin.dsl.internal.KoverProjectExtensionImpl import kotlinx.kover.gradle.plugin.util.* +import org.gradle.api.Project import org.gradle.api.tasks.testing.* import org.gradle.kotlin.dsl.* @@ -17,97 +20,51 @@ Because of this, Kover may not have direct access to the K/MPP plugin classes, a To work around this limitation, working with objects is done through reflection, using a dynamic Gradle wrapper. */ -internal fun LocatorContext.initKotlinMultiplatformPluginLocator() { - listener.onApplyPlugin(KotlinPluginType.MULTIPLATFORM) +internal fun Project.locateKotlinMultiplatformVariants(): AllVariantOrigins { + val kotlinExtension = getKotlinExtension() - project.pluginManager.withPlugin(ANDROID_APP_PLUGIN_ID) { - project.afterAndroidPluginApplied(::processWithAndroidTarget) - } - project.pluginManager.withPlugin(ANDROID_LIB_PLUGIN_ID) { - project.afterAndroidPluginApplied(::processWithAndroidTarget) - } - project.pluginManager.withPlugin(ANDROID_DYNAMIC_PLUGIN_ID) { - project.afterAndroidPluginApplied(::processWithAndroidTarget) - } - - project.afterEvaluate { - if (!hasAnyAndroidPlugin) { - // In case the Android plugin is not applied, then we are only looking for JVM compilations - processJvmTarget() - } - } -} - -private fun LocatorContext.processWithAndroidTarget() { - val kotlinExtension = project.getKotlinExtension() - - locateJvmCompilations(kotlinExtension) - locateAndroidCompilations(kotlinExtension) + val jvm = locateJvmVariant(kotlinExtension) + val androids = locateAndroidVariants(kotlinExtension) - listener.finalize() + return AllVariantOrigins(jvm, androids) } -private fun LocatorContext.processJvmTarget() { - val kotlinExtension = project.getKotlinExtension() - - locateJvmCompilations(kotlinExtension) - - listener.finalize() -} - -private fun LocatorContext.locateAndroidCompilations(kotlinExtension: DynamicBean) { +private fun Project.locateAndroidVariants(kotlinExtension: DynamicBean): List { // only one Android target is allowed, so we can take the first one val androidTarget = kotlinExtension.beanCollection("targets").firstOrNull { it["platformType"].value("name") == "androidJvm" - } + } ?: return emptyList() - if (androidTarget != null) { - val androidExtension = project.extensions.findByName("android")?.bean() - ?: throw KoverCriticalException("Kover requires extension with name 'android' for project '${project.path}' since it is recognized as Kotlin/Android project") + val androidExtension = project.extensions.findByName("android")?.bean() + ?: throw KoverCriticalException("Kover requires extension with name 'android' for project '${project.path}' since it is recognized as Kotlin/Android project") - val androidCompilations = project.androidCompilationKits(androidExtension, koverExtension, androidTarget) - - listener.android(androidCompilations) - } + return project.androidCompilationKits(androidExtension, androidTarget) } -private fun LocatorContext.locateJvmCompilations(kotlinExtension: DynamicBean) { +private fun Project.locateJvmVariant(kotlinExtension: DynamicBean): JvmVariantOrigin? { // only one JVM target is allowed, so we can take the first one val jvmTarget = kotlinExtension.beanCollection("targets").firstOrNull { it["platformType"].value("name") == "jvm" - } + } ?: return null - if (jvmTarget != null) { - val jvmCompilations = extractJvmCompilations(koverExtension, jvmTarget) - listener.jvm(jvmCompilations) - } + return extractJvmVariant(jvmTarget) } -private fun LocatorContext.extractJvmCompilations( - koverExtension: KoverProjectExtensionImpl, - target: DynamicBean -): JvmCompilationKit { +private fun Project.extractJvmVariant(target: DynamicBean): JvmVariantOrigin { val targetName = target.value("targetName") - // TODO check android tests are not triggered - val tests = project.tasks.withType().matching { - it.hasSuperclass("KotlinJvmTest") - // skip all tests from instrumentation if Kover Plugin is disabled for the project - && !koverExtension.disabled - // skip this test if it disabled by name - && it.name !in koverExtension.tests.tasksNames - // skip this test if it disabled by its JVM target name - && it.bean().value("targetName") == targetName + val tests = tasks.withType().matching { + it.hasSuperclass("KotlinJvmTest") && it.bean().value("targetName") == targetName } - val compilations = project.provider { - target.extractJvmCompilations(koverExtension) { + val compilations = provider { + target.beanCollection("compilations").jvmCompilations { // exclude java classes from report. Expected java class files are placed in directories like // build/classes/java/main it.parentFile.name == "java" } } - return JvmCompilationKit(tests, compilations) + return JvmVariantOrigin(tests, compilations) } diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/NoKotlinPluginLocator.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/NoKotlinPluginLocator.kt deleted file mode 100644 index 34620e64..00000000 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/NoKotlinPluginLocator.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package kotlinx.kover.gradle.plugin.locators - -/** - * A locator that works if no Kotlin plugins were detected. - */ -internal fun LocatorContext.initNoKotlinPluginLocator() { - project.afterEvaluate { - listener.finalizeIfNoKotlinPlugin() - } -} diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/ProvidedVariantsLocator.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/ProvidedVariantsLocator.kt new file mode 100644 index 00000000..3f103fe9 --- /dev/null +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/locators/ProvidedVariantsLocator.kt @@ -0,0 +1,105 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.kover.gradle.plugin.locators + +import kotlinx.kover.gradle.plugin.appliers.origin.AllVariantOrigins +import kotlinx.kover.gradle.plugin.commons.* +import kotlinx.kover.gradle.plugin.util.DynamicBean +import kotlinx.kover.gradle.plugin.util.bean +import org.gradle.api.* + +internal class ProvidedVariantsLocator( + private val project: Project, + private val callback: (sources: AllVariantOrigins) -> Unit +) { + private val reasons: MutableSet = mutableSetOf() + private var finilized: Boolean = false + + init { + enqueue("no-plugin") + + project.pluginManager.withPlugin(KOTLIN_JVM_PLUGIN_ID) { + enqueue(KOTLIN_JVM_PLUGIN_ID) + } + project.pluginManager.withPlugin(KOTLIN_MULTIPLATFORM_PLUGIN_ID) { + enqueue(KOTLIN_MULTIPLATFORM_PLUGIN_ID) + } + project.pluginManager.withPlugin(KOTLIN_ANDROID_PLUGIN_ID) { + enqueue(KOTLIN_ANDROID_PLUGIN_ID) + } + project.pluginManager.withPlugin(ANDROID_APP_PLUGIN_ID) { + enqueueAndroid(ANDROID_APP_PLUGIN_ID) + } + project.pluginManager.withPlugin(ANDROID_LIB_PLUGIN_ID) { + enqueueAndroid(ANDROID_LIB_PLUGIN_ID) + } + project.pluginManager.withPlugin(ANDROID_DYNAMIC_PLUGIN_ID) { + enqueueAndroid(ANDROID_DYNAMIC_PLUGIN_ID) + } + } + + private fun enqueueAndroid(reason: String) { + val androidComponents = project.extensions.findByName("androidComponents")?.bean() + ?: throw KoverCriticalException("Kover requires extension with name 'androidComponents' for project '${project.path}' since it is recognized as Kotlin+Android project") + + val callback = Action { + enqueue(reason) + } + + if (androidComponents.hasFunction("finalizeDsl", callback)) { + /* + Assumption: `finalizeDsl` is called in the `afterEvaluate` action, in which build variants are created. + Therefore, if an action is added to the queue inside it, it will be executed only after variants are created + */ + androidComponents("finalizeDsl", callback) + } else { + // for old versions < 7.0 an action is added to the AAA queue. + // Since this code is executed after the applying of AGP, there is a high probability that the action will fall into the `afterEvaluate` queue after the actions of the AGP + enqueue(reason) + } + } + + private fun enqueue(reason: String) { + if (finilized) { + throw KoverCriticalException("Attempt to queue after finalizing.") + } + reasons += reason + + project.afterEvaluate { + if (reasons.isEmpty()) { + throw KoverCriticalException("Error when searching for finalizing configuration variant.") + } + + reasons.remove(reason) + if (reasons.isEmpty()) { + finalize() + } + } + } + + private fun finalize() { + finilized = true + + val variants = if (project.pluginManager.hasPlugin(KOTLIN_JVM_PLUGIN_ID)) { + project.locateKotlinJvmVariants() + } else if (project.pluginManager.hasPlugin(KOTLIN_ANDROID_PLUGIN_ID)) { + project.locateKotlinAndroidVariants() + } else if (project.pluginManager.hasPlugin(KOTLIN_MULTIPLATFORM_PLUGIN_ID)) { + project.locateKotlinMultiplatformVariants() + } else { + AllVariantOrigins(null, emptyList()) + } + + callback(variants) + } + +} + + +internal fun Project.getKotlinExtension(): DynamicBean { + return extensions.findByName("kotlin")?.bean() + ?: throw KoverCriticalException("Kover requires extension with name 'kotlin' for project '${project.path}' since it is recognized as Kotlin/Multiplatform project") +} + diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverBinaryTask.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverBinaryTask.kt index cf877464..3a75f9d8 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverBinaryTask.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverBinaryTask.kt @@ -4,11 +4,12 @@ package kotlinx.kover.gradle.plugin.tasks.reports +import kotlinx.kover.gradle.plugin.dsl.tasks.KoverBinaryReport import org.gradle.api.file.* import org.gradle.api.tasks.* @CacheableTask -internal abstract class KoverBinaryTask : AbstractKoverReportTask() { +internal abstract class KoverBinaryTask : AbstractKoverReportTask(), KoverBinaryReport { @get:OutputFile internal abstract val file: RegularFileProperty diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverFormatCoverageTask.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverFormatCoverageTask.kt index be0a3754..d15ed377 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverFormatCoverageTask.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverFormatCoverageTask.kt @@ -7,6 +7,7 @@ package kotlinx.kover.gradle.plugin.tasks.reports import kotlinx.kover.gradle.plugin.dsl.AggregationType import kotlinx.kover.gradle.plugin.dsl.GroupingEntityType import kotlinx.kover.gradle.plugin.dsl.MetricType +import kotlinx.kover.gradle.plugin.dsl.tasks.KoverLogReport import kotlinx.kover.gradle.plugin.tools.CoverageRequest import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property @@ -14,7 +15,7 @@ import org.gradle.api.tasks.* import javax.annotation.Nullable @CacheableTask -internal abstract class KoverFormatCoverageTask : AbstractKoverReportTask() { +internal abstract class KoverFormatCoverageTask : AbstractKoverReportTask(), KoverLogReport { @get:Input @get:Optional @get:Nullable diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverHtmlTask.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverHtmlTask.kt index 7730998f..e34174f8 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverHtmlTask.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverHtmlTask.kt @@ -4,6 +4,7 @@ package kotlinx.kover.gradle.plugin.tasks.reports +import kotlinx.kover.gradle.plugin.dsl.tasks.KoverHtmlReport import org.gradle.api.file.* import org.gradle.api.provider.Property import org.gradle.api.tasks.* @@ -11,7 +12,7 @@ import java.io.File import java.net.URI @CacheableTask -internal abstract class KoverHtmlTask : AbstractKoverReportTask() { +internal abstract class KoverHtmlTask : AbstractKoverReportTask(), KoverHtmlReport { @get:OutputDirectory abstract val reportDir: DirectoryProperty @@ -29,7 +30,7 @@ internal abstract class KoverHtmlTask : AbstractKoverReportTask() { tool.get().htmlReport(htmlDir, title.get(), charset.orNull, context()) } - fun printPath(): Boolean { + fun printPath() { val clickablePath = URI( "file", "", @@ -38,6 +39,5 @@ internal abstract class KoverHtmlTask : AbstractKoverReportTask() { null, ).toASCIIString() logger.lifecycle("Kover: HTML report for '$projectPath' $clickablePath") - return true } } diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverVerifyTask.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverVerifyTask.kt index a5d033af..50b0fc96 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverVerifyTask.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverVerifyTask.kt @@ -5,6 +5,7 @@ package kotlinx.kover.gradle.plugin.tasks.reports import kotlinx.kover.gradle.plugin.commons.VerificationRule +import kotlinx.kover.gradle.plugin.dsl.tasks.KoverVerifyReport import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.ListProperty import org.gradle.api.tasks.CacheableTask @@ -13,7 +14,7 @@ import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction @CacheableTask -internal abstract class KoverVerifyTask : AbstractKoverReportTask() { +internal abstract class KoverVerifyTask : AbstractKoverReportTask(), KoverVerifyReport { @get:Nested abstract val rules: ListProperty diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverXmlTask.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverXmlTask.kt index 7cd52239..8c2172dc 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverXmlTask.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tasks/reports/KoverXmlTask.kt @@ -4,12 +4,13 @@ package kotlinx.kover.gradle.plugin.tasks.reports +import kotlinx.kover.gradle.plugin.dsl.tasks.KoverXmlReport import org.gradle.api.file.* import org.gradle.api.provider.Property import org.gradle.api.tasks.* @CacheableTask -internal abstract class KoverXmlTask : AbstractKoverReportTask() { +internal abstract class KoverXmlTask : AbstractKoverReportTask(), KoverXmlReport { @get:OutputFile internal abstract val reportFile: RegularFileProperty diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/CoverageTool.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/CoverageTool.kt index e804560a..c9882a53 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/CoverageTool.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/CoverageTool.kt @@ -8,10 +8,9 @@ import kotlinx.kover.gradle.plugin.commons.* import kotlinx.kover.gradle.plugin.commons.VerificationRule import kotlinx.kover.gradle.plugin.dsl.* import kotlinx.kover.gradle.plugin.dsl.KoverVersions.KOVER_TOOL_VERSION -import kotlinx.kover.gradle.plugin.dsl.internal.* +import kotlinx.kover.gradle.plugin.dsl.internal.KoverExtensionImpl import kotlinx.kover.gradle.plugin.tools.jacoco.JacocoTool import kotlinx.kover.gradle.plugin.tools.kover.KoverTool -import org.gradle.api.Project import org.gradle.api.file.* import org.gradle.api.provider.Provider import org.gradle.api.tasks.* @@ -48,7 +47,7 @@ internal sealed class CoverageToolVariant( } internal class KoverToolVariant(version: String): CoverageToolVariant(CoverageToolVendor.KOVER, version) -internal object KoverToolDefaultVariant: CoverageToolVariant(CoverageToolVendor.KOVER, KOVER_TOOL_VERSION) +internal object KoverToolBuiltin: CoverageToolVariant(CoverageToolVendor.KOVER, KOVER_TOOL_VERSION) internal class JacocoToolVariant(version: String): CoverageToolVariant(CoverageToolVendor.JACOCO, version) @@ -125,17 +124,13 @@ internal interface CoverageTool { * Factory to create instance of coverage tool according project settings from Kover project extension. */ internal object CoverageToolFactory { - fun get(project: Project, projectExtension: KoverProjectExtensionImpl): Provider { - return project.provider { - // Kover Tool Default by default - val variant = projectExtension.toolVariant - - when (variant?.vendor) { - CoverageToolVendor.KOVER -> KoverTool(variant) - CoverageToolVendor.JACOCO -> JacocoTool(variant) - null -> KoverTool(KoverToolDefaultVariant) + fun get(projectExtension: KoverExtensionImpl): Provider { + return projectExtension.useJacoco.map { + if (it) { + JacocoTool(JacocoToolVariant(projectExtension.jacocoVersion.get())) + } else { + KoverTool(KoverToolBuiltin) } } } - } diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/Verification.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/Verification.kt index 64446a9c..129dfc5a 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/Verification.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/Verification.kt @@ -52,7 +52,7 @@ internal data class CoverageValue( internal data class RuleViolations( val entityType: GroupingEntityType, val bounds: List, - val name: String? = null + val name: String ) internal data class BoundViolations( @@ -68,7 +68,7 @@ internal fun generateErrorMessage(violations: List): String { val messageBuilder = StringBuilder() violations.forEach { rule -> - val namedRule = if (rule.name != null) "Rule '${rule.name}'" else "Rule" + val namedRule = if (rule.name.isNotEmpty()) "Rule '${rule.name}'" else "Rule" if (rule.bounds.size == 1) { messageBuilder.appendLine("$namedRule violated: ${rule.bounds[0].format(rule)}") diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/jacoco/Evaluation.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/jacoco/Evaluation.kt index b1b5855d..0ed32c6d 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/jacoco/Evaluation.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/jacoco/Evaluation.kt @@ -19,7 +19,7 @@ import java.math.BigDecimal internal fun ReportContext.printJacocoCoverage(request: CoverageRequest, outputFile: File) { val bound = VerificationBound(ONE_HUNDRED, BigDecimal.ZERO, request.metric, request.aggregation) - val failRule = VerificationRule(true, null, null, request.entity, listOf(bound)) + val failRule = VerificationRule(true, "", request.entity, listOf(bound)) val violations = doJacocoVerify(listOf(failRule)) if (violations.isEmpty()) { diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/jacoco/Verification.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/jacoco/Verification.kt index 04aa271d..c02c1a13 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/jacoco/Verification.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/jacoco/Verification.kt @@ -119,7 +119,7 @@ private fun GroovyObject.violations(): List { val isMax = match.groupValues[6].asIsMax(it) val expected = match.groupValues[7].asValue(it, agg) - RuleViolations(entityType, listOf(BoundViolations(isMax, expected, value, metric, agg, entityName))) + RuleViolations(entityType, listOf(BoundViolations(isMax, expected, value, metric, agg, entityName)), "") }.toList() } diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/kover/Evaluation.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/kover/Evaluation.kt index 79d81ae9..64daee36 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/kover/Evaluation.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/tools/kover/Evaluation.kt @@ -37,7 +37,7 @@ internal abstract class CollectCoverageAction : AbstractReportAction>() allRules.forEach { - val filters = it.filters ?: commonFilters - - val excludesClasses = filters.excludesClasses - val includesClasses = filters.includesClasses - val excludesAnnotations = filters.excludesAnnotations + val excludesClasses = commonFilters.excludesClasses + val includesClasses = commonFilters.includesClasses + val excludesAnnotations = commonFilters.excludesAnnotations val reportFilters = ReportFilters(includesClasses, emptySet(), excludesClasses, excludesAnnotations) groupedMap.computeIfAbsent(reportFilters) { mutableListOf() } += it diff --git a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/util/Util.kt b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/util/Util.kt index f22cdffb..c75ae096 100644 --- a/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/util/Util.kt +++ b/kover-gradle-plugin/src/main/kotlin/kotlinx/kover/gradle/plugin/util/Util.kt @@ -32,7 +32,7 @@ internal inline fun Boolean.ifFalse(block: () -> T): T? { } /** - * Replaces characters `*` or `.` to `.*` and `.` regexp characters. + * Replaces characters `*` or `.` to `.*`, `#` to `[^.]*` and `?` to `.` regexp characters. */ internal fun String.wildcardsToRegex(): String { // in most cases, the characters `*` or `.` will be present therefore, we increase the capacity in advance @@ -41,8 +41,9 @@ internal fun String.wildcardsToRegex(): String { forEach { char -> when (char) { in regexMetacharactersSet -> builder.append('\\').append(char) - '*' -> builder.append('.').append("*") + '*' -> builder.append(".*") '?' -> builder.append('.') + '#' -> builder.append("[^.]*") else -> builder.append(char) } }