This is an automated email from the ASF dual-hosted git repository. emilles pushed a commit to branch GROOVY_3_0_X in repository https://gitbox.apache.org/repos/asf/groovy.git
commit b35092d1c446fdd650e567092734b746b4f8fa72 Author: nineninesevenfour <[email protected]> AuthorDate: Mon Jul 12 16:30:49 2021 +0200 GROOVY-7867: collection conversion: attach exception of constructor as suppressed exception https://issues.apache.org/jira/browse/GROOVY-7867 Signed-off-by: Harald Fassler <[email protected]> --- .../typehandling/DefaultTypeTransformation.java | 6 ++ src/test/groovy/bugs/Groovy7867.groovy | 114 +++++++++++++++++++++ 2 files changed, 120 insertions(+) diff --git a/src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java b/src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java index 36264af..aa65f33 100644 --- a/src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java +++ b/src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java @@ -370,6 +370,7 @@ public class DefaultTypeTransformation { } Exception nested = null; + Exception suppressed = null; if (args != null) { try { return InvokerHelper.invokeConstructorOf(type, args); @@ -386,6 +387,8 @@ public class DefaultTypeTransformation { // as the caller has more context to be able to throw a more // meaningful exception (but stash to get message later) nested = e; + // keep the original exception as suppressed exception to allow easier failure analysis + suppressed = ex; } } else { nested = e; @@ -404,6 +407,9 @@ public class DefaultTypeTransformation { } else { gce = new GroovyCastException(object, type); } + if (suppressed != null) { + gce.addSuppressed(suppressed); + } throw gce; } diff --git a/src/test/groovy/bugs/Groovy7867.groovy b/src/test/groovy/bugs/Groovy7867.groovy new file mode 100644 index 0000000..536ea28 --- /dev/null +++ b/src/test/groovy/bugs/Groovy7867.groovy @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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 groovy.bugs + +import org.junit.Test + +import static groovy.test.GroovyAssert.assertScript + +/** + * Test changes related to https://issues.apache.org/jira/browse/GROOVY-7867 + */ +final class Groovy7867 { + + @Test + void testIntendedBehaviour() { + // coerce calling constructor with two integers by throwing an Exception + assertScript ''' + class SingletonList extends Vector { + SingletonList(Collection c) { + super(c) + if (c.size() != 1) throw new IllegalStateException() + } + SingletonList(int capacity, int increment) { + super(capacity, increment) + } + } + + def myList = [10, 5] as SingletonList + assert myList.size() == 0 + assert myList.capacity() == 10 + ''' + } + + @Test + void testRetrievingSuppressedException() { + // for easier failure analysis it can be crucial to get hold of the original exception + assertScript ''' + import org.codehaus.groovy.runtime.typehandling.GroovyCastException + + class SingletonList extends Vector { + SingletonList(Collection c) { + super(c) + if (c.size() != 1) { + throw new IllegalArgumentException("expected SingletonList to be initialized with exactly one element") + } + } + } + + // not exactly one argument --> (user defined) exception + boolean caught = false + try { + def myList = [10, 5] as SingletonList + } catch (ex) { + caught = true + assert ex instanceof GroovyCastException + assert ex.suppressed.length == 1 + assert ex.suppressed[0] instanceof IllegalArgumentException + assert ex.suppressed[0].message == "expected SingletonList to be initialized with exactly one element" + } + assert caught == true + + // exactly one argument --> OK + caught = false + try { + def myList = [42] as SingletonList + assert myList.size() == 1 + assert myList[0] == 42 + } catch (ex) { + caught = true + } + assert caught == false + ''' + } + + @Test + void testFallbackToNoArgsConstructor() { + // the no-arg constructor is the second fallback which comes before calling the constructor with two integers + assertScript ''' + class SingletonList extends Vector { + SingletonList() { + super() + } + SingletonList(Collection c) { + super(c) + if (c.size() != 1) throw new IllegalStateException() + } + SingletonList(int capacity, int increment) { + super(capacity, increment) + } + } + + def myList = [10, 5] as SingletonList + assert myList.size() == 2 + assert myList[0] == 10 + assert myList[1] == 5 + ''' + } +}
