Skip to content

Commit

Permalink
Use special hamcrest matcher for Gradle property non-mutable exception
Browse files Browse the repository at this point in the history
Signed-off-by: Daniel Lacasse <daniel@lacasse.io>
  • Loading branch information
lacasseio committed Jul 15, 2024
1 parent 87ff151 commit 8c96a78
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
plugins {
id 'java-library'
}

dependencies {
def hamcrestVersion = 'latest.release'
api "org.hamcrest:hamcrest:${hamcrestVersion}"
compileOnly "dev.gradleplugins:gradle-api:${gradle.gradleVersion}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.gradleplugins.hamcrest.gradle;

import org.hamcrest.Matcher;
import org.hamcrest.Matchers;

import java.util.regex.Pattern;

public final class GradleProviderMatchers {
private GradleProviderMatchers() {}

/**
* Matches any exception message variant thrown when disallowed change applied to Gradle property.
*
* @return an exception message matcher, never null.
*/
public static Matcher<String> forChangesDisallowed() {
return forChangesDisallowed(ON_ANY_TARGET);
}

private static final PropertyTarget ON_ANY_TARGET = new PropertyTarget() {
@Override
public String toString() {
return ".+";
}
};

public static Matcher<String> forChangesDisallowed(PropertyTarget target) {
return Matchers.matchesPattern(exceptionMessagePattern(target.toString()));
}

public interface PropertyTarget {
static PropertyTargetBuilder ofOwner(Object toString) {
return new PropertyTargetBuilder() {
@Override
public PropertyTarget property(String name) {
return new PropertyTarget() {
@Override
public String toString() {
return toString + " property '" + name + "'";
}
};
}
};
}

static PropertyTarget onProperty(String name) {
return new PropertyTarget() {
@Override
public String toString() {
return ".*property '" + name + "'";
}
};
}

interface PropertyTargetBuilder extends PropertyTarget {
PropertyTarget property(String name);
}
}

// Matches message such as:
// - The value for this property cannot be changed any further.
// - The value for property 'foo' cannot be changed any further.
// - The value for this file collection cannot be changed.
// Note that we implicitly check for finalized value as final value imply disallowed changes.
// Notice the difference in the exception message for _file collection_.
// Sample "The value for test suite 'functionalTest' property 'sourceSet' is final and cannot be changed any further."
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^
// owner toString prop display name if final prop vs FC
//
// Sample "The value for property 'testedSourceSet' cannot be changed any further."
// ^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^
// prop display name prop vs FC
private static Pattern exceptionMessagePattern(String targetPattern) {
return Pattern.compile("The value for " + targetPattern + " (is final and )?cannot be changed( any further)?.");
}
}
23 changes: 23 additions & 0 deletions gradle/libraries/hamcrest-matchers/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import org.gradle.api.initialization.resolve.RepositoriesMode

include 'hamcrest-gradle'

rootProject.name = 'hamcrest-matchers'

gradle.allprojects {
group = 'dev.gradleplugins'

pluginManager.withPlugin('java-base') {
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
}

dependencyResolutionManagement {
repositories {
mavenCentral()
}
repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
}
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ apply plugin: 'dev.gradleplugins.gradle-plugin-development'
rootProject.name = 'toolbox'

includeBuild 'gradle/libraries/gradle-build-script'
includeBuild 'gradle/libraries/hamcrest-matchers'
includeBuild 'gradle/libraries/source-elements'
includeBuild 'subprojects/gradle-plugin-development-test-fixtures/testProjects'

Expand Down
2 changes: 2 additions & 0 deletions subprojects/gradle-plugin-development/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ test {
runtimeOnly 'org.junit.vintage:junit-vintage-engine'
implementation "org.hamcrest:hamcrest:2.2"
implementation "org.mockito:mockito-core:4.2.0"
implementation 'dev.gradleplugins:hamcrest-gradle'
}
testTasks.configureEach { useJUnitPlatform() }
}
Expand All @@ -126,6 +127,7 @@ functionalTest {
runtimeOnly "org.junit.vintage:junit-vintage-engine:latest.release"
implementation "org.hamcrest:hamcrest:2.2"
implementation "dev.gradleplugins:gradle-build-script"
implementation 'dev.gradleplugins:hamcrest-gradle'

// Force local fixtures
// TODO: Maybe look into dependency substitution
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import org.gradle.util.GradleVersion
import spock.lang.Unroll
import spock.util.environment.Jvm

import static dev.gradleplugins.hamcrest.gradle.GradleProviderMatchers.PropertyTarget.onProperty
import static dev.gradleplugins.hamcrest.gradle.GradleProviderMatchers.forChangesDisallowed
import static org.junit.Assume.assumeFalse
import static org.junit.Assume.assumeTrue

Expand Down Expand Up @@ -214,7 +216,7 @@ abstract class AbstractGradlePluginDevelopmentExtensionFunctionalTest extends Ab
expect:
fails('help')
failure.assertHasDescription("A problem occurred configuring root project 'gradle-plugin'.")
failure.assertHasCause("The value for property 'minimumGradleVersion' cannot be changed any further.")
failure.assertThatCause(forChangesDisallowed(onProperty("minimumGradleVersion")))
}

def "can generate Javadoc Jar"() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
import static dev.gradleplugins.buildscript.syntax.Syntax.literal;
import static dev.gradleplugins.fixtures.runnerkit.BuildResultMatchers.hasFailureCause;
import static dev.gradleplugins.fixtures.runnerkit.BuildResultMatchers.hasFailureDescription;
import static dev.gradleplugins.hamcrest.gradle.GradleProviderMatchers.PropertyTarget.onProperty;
import static org.hamcrest.MatcherAssert.assertThat;
import static dev.gradleplugins.hamcrest.gradle.GradleProviderMatchers.forChangesDisallowed;

class GradlePluginDevelopmentFunctionalTestingFunctionalTests {
@TempDir(cleanup = CleanupMode.ON_SUCCESS)
Expand Down Expand Up @@ -84,7 +86,7 @@ void disallowChangesToSourceSetProperty() {

BuildResult result = runner.withTasks("verify").buildAndFail();
assertThat(result, hasFailureDescription("A problem occurred configuring root project 'gradle-plugin'."));
assertThat(result, hasFailureCause("The value for test suite 'functionalTest' property 'sourceSet' is final and cannot be changed any further."));
assertThat(result, hasFailureCause(forChangesDisallowed(onProperty("sourceSet"))));
}

@Test
Expand All @@ -98,7 +100,7 @@ void disallowChangesToTestedSourceSetProperty() {

BuildResult result = runner.withTasks("verify").buildAndFail();
assertThat(result, hasFailureDescription("A problem occurred configuring root project 'gradle-plugin'."));
assertThat(result, hasFailureCause("The value for test suite 'functionalTest' property 'testedSourceSet' cannot be changed any further."));
assertThat(result, hasFailureCause(forChangesDisallowed(onProperty("testedSourceSet"))));
}

@Test
Expand All @@ -112,7 +114,7 @@ void disallowChangesToTestingStrategiesProperty() {

BuildResult result = runner.withTasks("verify").buildAndFail();
assertThat(result, hasFailureDescription("A problem occurred configuring root project 'gradle-plugin'."));
assertThat(result, hasFailureCause("The value for test suite 'functionalTest' property 'testingStrategies' is final and cannot be changed any further."));
assertThat(result, hasFailureCause(forChangesDisallowed(onProperty("testingStrategies"))));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import static dev.gradleplugins.buildscript.syntax.Syntax.literal;
import static dev.gradleplugins.fixtures.runnerkit.BuildResultMatchers.hasFailureCause;
import static dev.gradleplugins.fixtures.runnerkit.BuildResultMatchers.hasFailureDescription;
import static dev.gradleplugins.hamcrest.gradle.GradleProviderMatchers.PropertyTarget.onProperty;
import static dev.gradleplugins.hamcrest.gradle.GradleProviderMatchers.forChangesDisallowed;
import static org.hamcrest.MatcherAssert.assertThat;

class GradlePluginDevelopmentUnitTestingFunctionalTests {
Expand Down Expand Up @@ -107,7 +109,7 @@ void disallowChangesToSourceSetProperty() {

BuildResult result = runner.withTasks("verify").buildAndFail();
assertThat(result, hasFailureDescription("A problem occurred configuring root project 'gradle-plugin'."));
assertThat(result, hasFailureCause("The value for test suite 'test' property 'sourceSet' is final and cannot be changed any further."));
assertThat(result, hasFailureCause(forChangesDisallowed(onProperty("sourceSet"))));
}

@Test
Expand All @@ -121,7 +123,7 @@ void disallowChangesToTestedSourceSetProperty() {

BuildResult result = runner.withTasks("verify").buildAndFail();
assertThat(result, hasFailureDescription("A problem occurred configuring root project 'gradle-plugin'."));
assertThat(result, hasFailureCause("The value for test suite 'test' property 'testedSourceSet' cannot be changed any further."));
assertThat(result, hasFailureCause(forChangesDisallowed(onProperty("testedSourceSet"))));
}

@Test
Expand All @@ -135,7 +137,7 @@ void disallowChangesToTestingStrategiesProperty() {

BuildResult result = runner.withTasks("verify").buildAndFail();
assertThat(result, hasFailureDescription("A problem occurred configuring root project 'gradle-plugin'."));
assertThat(result, hasFailureCause("The value for test suite 'test' property 'testingStrategies' is final and cannot be changed any further."));
assertThat(result, hasFailureCause(forChangesDisallowed(onProperty("testingStrategies"))));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@

import static dev.gradleplugins.GradlePluginDevelopmentCompatibilityExtension.compatibility;
import static dev.gradleplugins.ProjectMatchers.providerOf;
import static dev.gradleplugins.hamcrest.gradle.GradleProviderMatchers.PropertyTarget.onProperty;
import static dev.gradleplugins.hamcrest.gradle.GradleProviderMatchers.forChangesDisallowed;
import static dev.gradleplugins.internal.util.GradlePluginDevelopmentUtils.gradlePlugin;
import static dev.gradleplugins.internal.util.GradlePluginDevelopmentUtils.java;
import static org.gradle.api.JavaVersion.VERSION_11;
import static org.gradle.api.JavaVersion.VERSION_12;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

class GradlePluginDevelopmentCompatibilityExtensionFinalizeComponentIntegrationTest {
private final Project project = ProjectBuilder.builder().build();
Expand All @@ -41,13 +45,13 @@ void defaultsMinimumGradleVersionToCurrentGradleVersion() {
@Test
void disallowChangesToMinimumGradleVersion() {
final Throwable ex = assertThrows(RuntimeException.class, () -> subject.getMinimumGradleVersion().set("6.9"));
assertEquals("The value for property 'minimumGradleVersion' cannot be changed any further.", ex.getMessage());
assertThat(ex.getMessage(), forChangesDisallowed(onProperty("minimumGradleVersion")));
}

@Test
void disallowChangesToGradleApiVersion() {
final Throwable ex = assertThrows(RuntimeException.class, () -> subject.getGradleApiVersion().set("6.9"));
assertEquals("The value for property 'gradleApiVersion' is final and cannot be changed any further.", ex.getMessage());
assertThat(ex.getMessage(), forChangesDisallowed(onProperty("gradleApiVersion")));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
import org.gradle.api.tasks.SourceSet;
import org.junit.jupiter.api.Test;

import static dev.gradleplugins.hamcrest.gradle.GradleProviderMatchers.PropertyTarget.ofOwner;
import static dev.gradleplugins.hamcrest.gradle.GradleProviderMatchers.forChangesDisallowed;
import static java.util.Collections.singleton;
import static org.junit.jupiter.api.Assertions.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;

interface GradlePluginDevelopmentTestSuiteFinalizedIntegrationTester {
Expand All @@ -18,18 +22,18 @@ default void canFinalizeTestSuiteMultipleTime() {
@Test
default void disallowChangesToTestSuiteSourceSetProperty() {
final Throwable ex = assertThrows(RuntimeException.class, () -> subject().getSourceSet().set(mock(SourceSet.class)));
assertEquals("The value for " + subject() + " property 'sourceSet' is final and cannot be changed any further.", ex.getMessage());
assertThat(ex.getMessage(), forChangesDisallowed(ofOwner(subject()).property("sourceSet")));
}

@Test
default void disallowChangesToTestingStrategiesProperty() {
final Throwable ex = assertThrows(RuntimeException.class, () -> subject().getTestingStrategies().set(singleton(mock(GradlePluginTestingStrategy.class))));
assertEquals("The value for " + subject() + " property 'testingStrategies' is final and cannot be changed any further.", ex.getMessage());
assertThat(ex.getMessage(), forChangesDisallowed(ofOwner(subject()).property("testingStrategies")));
}

@Test
default void disallowChangesToTestedSourceSetProperty() {
final Throwable ex = assertThrows(RuntimeException.class, () -> subject().getTestedSourceSet().set(mock(SourceSet.class)));
assertEquals("The value for " + subject() + " property 'testedSourceSet' cannot be changed any further.", ex.getMessage());
assertThat(ex.getMessage(), forChangesDisallowed(ofOwner(subject()).property("testedSourceSet")));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import static dev.gradleplugins.ProjectMatchers.*;
import static dev.gradleplugins.ProjectMatchers.coordinate;
import static dev.gradleplugins.ProjectMatchers.extensions;
import static dev.gradleplugins.ProjectMatchers.hasPlugin;
import static dev.gradleplugins.ProjectMatchers.named;
import static dev.gradleplugins.ProjectMatchers.publicType;
import static dev.gradleplugins.hamcrest.gradle.GradleProviderMatchers.PropertyTarget.ofOwner;
import static dev.gradleplugins.hamcrest.gradle.GradleProviderMatchers.forChangesDisallowed;
import static dev.gradleplugins.internal.plugins.GradlePluginDevelopmentUnitTestingPlugin.test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.hasItem;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;

class GradlePluginDevelopmentUnitTestingPluginIntegrationTest {
private final Project project = ProjectBuilder.builder().build();
Expand Down Expand Up @@ -48,7 +55,7 @@ public GradlePluginDevelopmentTestSuite subject() {
@Test
void disallowChangesToSourceSetProperty() {
final Throwable ex = assertThrows(RuntimeException.class, () -> subject().getSourceSet().set((SourceSet) null));
assertEquals("The value for test suite 'test' property 'sourceSet' cannot be changed any further.", ex.getMessage());
assertThat(ex.getMessage(), forChangesDisallowed(ofOwner("test suite 'test'").property("sourceSet")));
}

@Test // https://github.com/gradle-plugins/toolbox/issues/65
Expand Down

0 comments on commit 8c96a78

Please sign in to comment.