diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpockNode.java b/spock-core/src/main/java/org/spockframework/runtime/SpockNode.java index 3f19393a53..45e01f5a2b 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/SpockNode.java +++ b/spock-core/src/main/java/org/spockframework/runtime/SpockNode.java @@ -2,13 +2,13 @@ import org.spockframework.runtime.model.*; import org.spockframework.runtime.model.parallel.ResourceAccessMode; +import org.spockframework.util.ClassUtil; import org.spockframework.util.ExceptionUtil; import spock.config.RunnerConfiguration; import java.util.*; import java.util.stream.Collectors; -import org.junit.platform.commons.util.ClassUtils; import org.junit.platform.engine.*; import org.junit.platform.engine.support.descriptor.*; import org.junit.platform.engine.support.hierarchical.*; @@ -91,7 +91,7 @@ public Set getExclusiveResources() { protected static MethodSource featureToMethodSource(FeatureInfo info) { return MethodSource.from(info.getSpec().getBottomSpec().getReflection().getName(), info.getName(), - ClassUtils.nullSafeToString(info.getFeatureMethod().getReflection().getParameterTypes()) // TODO replace internal API + ClassUtil.allNullSafeToString(info.getFeatureMethod().getReflection().getParameterTypes()) ); } diff --git a/spock-core/src/main/java/org/spockframework/util/ClassUtil.java b/spock-core/src/main/java/org/spockframework/util/ClassUtil.java new file mode 100644 index 0000000000..2b37abe6f6 --- /dev/null +++ b/spock-core/src/main/java/org/spockframework/util/ClassUtil.java @@ -0,0 +1,42 @@ +/* + * Copyright 2025 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 org.spockframework.util; + +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import static java.util.stream.Collectors.joining; + +public class ClassUtil { + public static @NotNull String nullSafeToString(@Nullable Class clazz) { + if (clazz != null) + return clazz.toString(); + else + return "null"; + } + + // not an overload because I couldn't fix + // ambiguous overload errors in the spec + public static @NotNull String allNullSafeToString(@Nullable Class... clazzes) { + if (clazzes != null) + return Arrays.stream(clazzes) + .map(ClassUtil::nullSafeToString) + .collect(joining(", ")); + else + return ""; + } +} diff --git a/spock-specs/src/test/groovy/org/spockframework/util/ClassUtilSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/util/ClassUtilSpec.groovy new file mode 100644 index 0000000000..f263df9a9b --- /dev/null +++ b/spock-specs/src/test/groovy/org/spockframework/util/ClassUtilSpec.groovy @@ -0,0 +1,61 @@ +package org.spockframework.util + +import spock.lang.Specification + +class ClassUtilSpec extends Specification { + def 'get string representation of non-null class'() { + expect: + ClassUtil.nullSafeToString(clazz) == clazz.toString() + where: + clazz << [Set.class, Tuple.class, ClassUtil.class] + } + + def 'get string representation of null class'() { + when: + String result = ClassUtil.nullSafeToString(null) + then: + notThrown(NullPointerException) + result == 'null' + } + + def 'get string representation of multiple potentially null classes'(Class... clazzes) { + given: + def clazzStrings = [] + for (clazz in clazzes) { + clazzStrings.add(ClassUtil.nullSafeToString(clazz)) + } + when: + String result = ClassUtil.allNullSafeToString(clazzes) + then: 'result is names from single class overload separated by a comma and space each' + notThrown(NullPointerException) + result == String.join(', ', clazzStrings) + where: + clazzes << [[List.class, Set.class, Queue.class, Map.class], + [File.class, null, URL.class], + [null, null]] + } + + def 'get string representation of one class not known to be just one'() { + given: + Class[] singleton = [Specification.class] + Class[] singletonOfNull = [null] + expect: + ClassUtil.allNullSafeToString(singleton) == ClassUtil.nullSafeToString(Specification.class) + ClassUtil.allNullSafeToString(singletonOfNull) == ClassUtil.nullSafeToString(null) + } + + def 'get string representation of no classes'() { + expect: + ClassUtil.allNullSafeToString() == '' + } + + def 'get string representation of null array of classes'() { + given: + Class[] nullArray = null + when: + String result = ClassUtil.allNullSafeToString(nullArray) + then: + notThrown(NullPointerException) + result == '' + } +}