/*
 *  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 org.codehaus.groovy.classgen.asm.sc

import groovy.transform.stc.ClosuresSTCTest

/**
 * Unit tests for static compilation: closures.
 */
class ClosuresStaticCompileTest extends ClosuresSTCTest implements StaticCompilationTestSupport {

    // GROOVY-5584
    void testEachOnMapClosure() {
        assertScript '''
            def test() {
                def result = ""
                [a:1, b:3].each { key, value -> result += "$key$value" }
                assert result == "a1b3"
            }
            test()
        '''
    }

    // GROOVY-5811
    void testClosureExceptionUnwrap() {
        assertScript '''
            @groovy.transform.InheritConstructors
            public class MyException extends Exception {}

            void foo() {
                throw new MyException()
            }

            void bar() {
                boolean caught = false
                try {
                    def cl = { foo() }
                    cl()
                } catch (MyException e) {
                    caught = true
                } finally {
                    assert caught
                }
            }

            bar()
        '''
    }

    // GROOVY-6522
    void testShouldCallClosure() {
        assertScript '''
            class Sample {
                Closure formatFirstName, formatLastName

                void doStuff (String fname, String lname, Closure callback) {
                    formatFirstName(fname) { String fnameError, String formattedFname ->
                        formatLastName(lname) { String lnameError, String formattedLname ->
                            String errors = "${fnameError ? "${fnameError}, " : ''}${lnameError ?: ''}"
                            callback(errors, formattedFname, formattedLname)
                        }
                    }
                }
            }

            Closure ffn = { String fname, Closure callback ->
                String firstInitial = fname?.substring(0,1)
                if (!firstInitial)
                    callback('invalid first name', null)
                else
                    callback(null, firstInitial.toLowerCase())
            }

            Closure fln = { String lname, Closure callback ->
                String lastPrefix = lname?.size() > 2 ? lname.substring(0,3) : null
                if (!lastPrefix)
                    callback('invalid last name', null)
                else
                    callback(null, lastPrefix.toLowerCase())
            }

            Sample sample = new Sample(formatFirstName: ffn, formatLastName: fln)

            sample.doStuff('John', 'Doe') { String errors, String formattedFname, String formattedLname ->
                if (errors)
                    println errors
                else
                    println "${formattedFname}.${formattedLname}"
            }
        '''
    }

    void testMethodVersusPropertyOnCompoundAssignmentTarget() {
        assertScript '''
            import org.codehaus.groovy.ast.expr.*
            import org.codehaus.groovy.ast.stmt.*

            class Pogo {
                String string = ''
                void setString(String string) {
                    this.string = string
                }
            }

            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                node = node.code.statements[0].expression
                assert node instanceof MethodCallExpression

                node = node.arguments.expressions[0].code.statements[0].expression
                assert node instanceof BinaryExpression

                assert node.leftExpression instanceof PropertyExpression
                def target = node.leftExpression.getNodeMetaData(DIRECT_METHOD_CALL_TARGET)

                assert target != null && target.name == 'setString'
            })
            void test(Pogo pogo) {
                pogo.with {
                    string += 'x'
                }
            }

            test(new Pogo())
        '''
    }
}
