SamConversionComboTest.java revision 2253:3b4db9e3824d
1268899Sbapt/*
2234949Sbapt * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
3234949Sbapt * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4234949Sbapt *
5268899Sbapt * This code is free software; you can redistribute it and/or modify it
6268899Sbapt * under the terms of the GNU General Public License version 2 only, as
7268899Sbapt * published by the Free Software Foundation.
8268899Sbapt *
9268899Sbapt * This code is distributed in the hope that it will be useful, but WITHOUT
10268899Sbapt * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11268899Sbapt * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12268899Sbapt * version 2 for more details (a copy is included in the LICENSE file that
13268899Sbapt * accompanied this code).
14268899Sbapt *
15268899Sbapt * You should have received a copy of the GNU General Public License version
16234949Sbapt * 2 along with this work; if not, write to the Free Software Foundation,
17234949Sbapt * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18234949Sbapt *
19234949Sbapt * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20234949Sbapt * or visit www.oracle.com if you need additional information or have any
21234949Sbapt * questions.
22234949Sbapt */
23234949Sbapt
24234949Sbapt/**
25234949Sbapt * @test
26234949Sbapt * @bug 8003280
27234949Sbapt * @summary Add lambda tests
28234949Sbapt *   Test SAM conversion of lambda expressions in combinations of different contexts,
29234949Sbapt *           lambda body types(statement/expression), explict/implicit target type etc, to verify
30234949Sbapt *           SAM conversion being conducted successfully as expected.
31234949Sbapt */
32234949Sbapt
33234949Sbaptimport com.sun.source.util.JavacTask;
34234949Sbaptimport java.net.URI;
35234949Sbaptimport java.util.Arrays;
36234949Sbaptimport javax.tools.Diagnostic;
37234949Sbaptimport javax.tools.JavaCompiler;
38234949Sbaptimport javax.tools.JavaFileObject;
39234949Sbaptimport javax.tools.SimpleJavaFileObject;
40234949Sbaptimport javax.tools.ToolProvider;
41234949Sbapt
42234949Sbaptpublic class SamConversionComboTest {
43234949Sbapt
44234949Sbapt    enum FInterface {
45234949Sbapt        A("A", "interface A { Integer m(int i); }"),
46234949Sbapt        B("B", "interface B { int m(Integer i); }"),
47234949Sbapt        C("C", "interface C { int m(Integer i) throws Exception; }");
48234949Sbapt
49234949Sbapt        String interfaceType;
50234949Sbapt        String interfaceDef;
51234949Sbapt
52234949Sbapt        FInterface(String interfaceType, String interfaceDef) {
53234949Sbapt            this.interfaceType = interfaceType;
54234949Sbapt            this.interfaceDef = interfaceDef;
55234949Sbapt        }
56234949Sbapt
57234949Sbapt        String getParameterType() {
58234949Sbapt            switch(this) {
59234949Sbapt            case A:
60234949Sbapt                return "int";
61234949Sbapt            case B:
62234949Sbapt            case C:
63234949Sbapt                return "Integer";
64234949Sbapt            default:
65234949Sbapt                return null;
66234949Sbapt            }
67234949Sbapt        }
68234949Sbapt    }
69234949Sbapt
70234949Sbapt    enum Context {
71234949Sbapt        ASSIGNMENT("#FType f = #LBody;"),
72234949Sbapt        METHOD_CALL("void method1(#FType f) { }\n" +
73234949Sbapt                    "    void method2() {\n" +
74234949Sbapt                    "        method1(#LBody);\n" +
75234949Sbapt                    "    }"),
76234949Sbapt        CONSTRUCTOR("X x = new X(#LBody);"),
77234949Sbapt        RETURN_OF_METHOD("#FType method1() {\n" +
78234949Sbapt                         "    return #LBody;\n" +
79234949Sbapt                         "}"),
80234949Sbapt        ARRAY_INITIALIZER("Object[] oarray = {\"a\", 1, (#FType)#LBody};"),
81234949Sbapt        LAMBDA_BODY("#FType f = n -> ((#FType)#LBody).m(n);"),
82234949Sbapt        CAST("void test() throws Exception { int n = ((#FType)#LBody).m(1); }"),
83234949Sbapt        CONDITIONAL_EXPRESSION("#FType f = 2 > 1 ? #LBody : null;");
84234949Sbapt
85234949Sbapt        String context;
86234949Sbapt
87234949Sbapt        Context(String context) {
88234949Sbapt            this.context = context;
89234949Sbapt        }
90234949Sbapt
91234949Sbapt        String getContext(FInterface f, LambdaKind lk, LambdaBody lb, ReturnValue rv) {
92234949Sbapt            return context.replace("#FType", f.interfaceType).replace("#LBody", lb.getLambdaBody(f, lk, rv));
93234949Sbapt        }
94234949Sbapt    }
95234949Sbapt
96234949Sbapt    enum LambdaKind {
97234949Sbapt        EXPRESSION("#VAL"),
98234949Sbapt        STATEMENT("{return #VAL;}"),
99234949Sbapt        EXCEPTION_STMT("{throw new Exception();}");
100234949Sbapt
101234949Sbapt        String stmt;
102234949Sbapt
103234949Sbapt        LambdaKind(String stmt) {
104234949Sbapt            this.stmt = stmt;
105234949Sbapt        }
106234949Sbapt    }
107234949Sbapt
108234949Sbapt    enum ReturnValue {
109234949Sbapt        INT("i + 1"),
110234949Sbapt        INTEGER("new Integer(i+1)"),
111234949Sbapt        INT2("i.intValue() + 1"),
112234949Sbapt        STRING("i + \"\""),
113234949Sbapt        DOUBLE("i * 1.0");
114234949Sbapt
115234949Sbapt        String rValue;
116234949Sbapt
117234949Sbapt        ReturnValue(String rValue) {
118234949Sbapt            this.rValue = rValue;
119234949Sbapt        }
120234949Sbapt    }
121234949Sbapt
122234949Sbapt    enum LambdaBody {
123234949Sbapt        IMPLICIT("i -> #RET"),//type inferred
124234949Sbapt        EXPLICIT("(#Type i) -> #RET");//explicit type
125234949Sbapt
126234949Sbapt        String bodyStr;
127234949Sbapt
128234949Sbapt        LambdaBody(String bodyStr) {
129234949Sbapt            this.bodyStr = bodyStr;
130234949Sbapt        }
131234949Sbapt
132234949Sbapt        String getLambdaBody(FInterface fi, LambdaKind lk, ReturnValue rv) {
133234949Sbapt            return bodyStr.replace("#Type", fi.getParameterType()).replace("#RET", lk.stmt.replace("#VAL", rv.rValue));
134234949Sbapt        }
135234949Sbapt    }
136234949Sbapt
137234949Sbapt    boolean checkSamConversion() {
138234949Sbapt        if(lambdaKind != LambdaKind.EXCEPTION_STMT && (returnValue == ReturnValue.DOUBLE || returnValue == ReturnValue.STRING)) //return type mismatch
139234949Sbapt            return false;
140234949Sbapt        if(context != Context.CONSTRUCTOR) {//context other than construcotr argument
141234949Sbapt            if(fInterface != FInterface.C && lambdaKind == LambdaKind.EXCEPTION_STMT)
142234949Sbapt                return false;
143234949Sbapt            if(fInterface == FInterface.A && returnValue == ReturnValue.INT2)
144234949Sbapt                return false;
145234949Sbapt        }
146234949Sbapt        else { //constructor argument context
147234949Sbapt            //match X(A a) or X(B b) or X(C c)
148234949Sbapt            if (lambdaKind == LambdaKind.EXCEPTION_STMT) {
149234949Sbapt                return false; //ambiguous target type
150234949Sbapt            }
151234949Sbapt            else if(lambdaBody == LambdaBody.IMPLICIT) {
152234949Sbapt                return false;
153234949Sbapt            }
154234949Sbapt            else { //explicit parameter type
155234949Sbapt                if(fInterface.getParameterType().equals("Integer")) //ambiguous target type
156234949Sbapt                //e.g. X x = new X((Integer i) -> i + 1);
157234949Sbapt                    return false;
158234949Sbapt                if(returnValue == ReturnValue.INT2)
159234949Sbapt                //e.g. X x = new X(int i -> i.intValue() + 1);
160234949Sbapt                    return false;
161234949Sbapt            }
162234949Sbapt        }
163234949Sbapt        return true;
164234949Sbapt    }
165234949Sbapt
166234949Sbapt    SourceFile samSourceFile = new SourceFile("FInterface.java", "#C") {
167234949Sbapt        public String toString() {
168234949Sbapt            String interfaces = "";
169234949Sbapt            for(FInterface fi : FInterface.values())
170234949Sbapt                interfaces += fi.interfaceDef + "\n";
171234949Sbapt            return template.replace("#C", interfaces);
172234949Sbapt        }
173234949Sbapt    };
174234949Sbapt
175234949Sbapt    String clientTemplate = "class Client {\n" +
176234949Sbapt                            "    #Context\n" +
177234949Sbapt                            "}\n\n" +
178234949Sbapt
179234949Sbapt                            "class X {\n" +
180234949Sbapt                            "    int value = 0;\n\n" +
181234949Sbapt
182234949Sbapt                            "    X(A a) {\n" +
183234949Sbapt                            "        value = a.m(6);\n" +
184234949Sbapt                            "    }\n\n" +
185234949Sbapt
186234949Sbapt                            "    X(B b) {\n" +
187234949Sbapt                            "        value = b.m(7);\n" +
188234949Sbapt                            "    }\n\n" +
189234949Sbapt
190234949Sbapt                            "    X(C c) {\n" +
191234949Sbapt                            "        try {\n" +
192234949Sbapt                            "            value = c.m(8);\n" +
193234949Sbapt                            "        } catch (Exception e){}\n" +
194240517Sbapt                            "    }\n" +
195234949Sbapt                            "}";
196234949Sbapt    SourceFile clientSourceFile = new SourceFile("Client.java", clientTemplate) {
197234949Sbapt        public String toString() {
198234949Sbapt            return template.replace("#Context", context.getContext(fInterface, lambdaKind, lambdaBody, returnValue));
199234949Sbapt        }
200234949Sbapt    };
201234949Sbapt
202234949Sbapt    void test() throws Exception {
203234949Sbapt        System.out.println("\n====================================");
204268899Sbapt        System.out.println(fInterface + ", " +  context + ", " + lambdaKind + ", " + lambdaBody + ", " + returnValue);
205234949Sbapt        System.out.println(samSourceFile + "\n");
206234949Sbapt        String clientFileStr = clientSourceFile.toString();
207234949Sbapt        System.out.println(clientFileStr.substring(0, clientFileStr.indexOf("\n\n")));
208234949Sbapt
209234949Sbapt        final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
210234949Sbapt        DiagnosticChecker dc = new DiagnosticChecker();
211234949Sbapt        JavacTask ct = (JavacTask)tool.getTask(null, null, dc, null, null, Arrays.asList(samSourceFile, clientSourceFile));
212234949Sbapt        ct.analyze();
213234949Sbapt        if (dc.errorFound == checkSamConversion()) {
214234949Sbapt            throw new AssertionError(samSourceFile + "\n\n" + clientSourceFile);
215234949Sbapt        }
216234949Sbapt        count++;
217234949Sbapt    }
218234949Sbapt
219234949Sbapt    abstract class SourceFile extends SimpleJavaFileObject {
220234949Sbapt
221234949Sbapt        protected String template;
222234949Sbapt
223234949Sbapt        public SourceFile(String filename, String template) {
224234949Sbapt            super(URI.create("myfo:/" + filename), JavaFileObject.Kind.SOURCE);
225234949Sbapt            this.template = template;
226234949Sbapt        }
227234949Sbapt
228234949Sbapt        @Override
229234949Sbapt        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
230234949Sbapt            return toString();
231234949Sbapt        }
232234949Sbapt
233234949Sbapt        public abstract String toString();
234234949Sbapt    }
235234949Sbapt
236234949Sbapt    static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
237234949Sbapt
238234949Sbapt        boolean errorFound = false;
239268899Sbapt
240268899Sbapt        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
241268899Sbapt            if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
242234949Sbapt                errorFound = true;
243234949Sbapt            }
244234949Sbapt        }
245234949Sbapt    }
246268899Sbapt
247234949Sbapt    FInterface fInterface;
248234949Sbapt    Context context;
249234949Sbapt    LambdaBody lambdaBody;
250268899Sbapt    LambdaKind lambdaKind;
251234949Sbapt    ReturnValue returnValue;
252234949Sbapt    static int count = 0;
253234949Sbapt
254234949Sbapt    SamConversionComboTest(FInterface f, Context c, LambdaBody lb, LambdaKind lk, ReturnValue rv) {
255268899Sbapt        fInterface = f;
256234949Sbapt        context = c;
257234949Sbapt        lambdaKind = lk;
258234949Sbapt        lambdaBody = lb;
259234949Sbapt        returnValue = rv;
260234949Sbapt    }
261234949Sbapt
262234949Sbapt    public static void main(String[] args) throws Exception {
263234949Sbapt        for(Context ct : Context.values()) {
264234949Sbapt            for (FInterface fi : FInterface.values()) {
265234949Sbapt                for (LambdaKind lk: LambdaKind.values()) {
266234949Sbapt                    for (LambdaBody lb : LambdaBody.values()) {
267234949Sbapt                        for(ReturnValue rv : ReturnValue.values()) {
268234949Sbapt                            new SamConversionComboTest(fi, ct, lb, lk, rv).test();
269234949Sbapt                        }
270234949Sbapt                    }
271234949Sbapt                }
272234949Sbapt            }
273234949Sbapt        }
274234949Sbapt        System.out.println("total tests: " + count);
275234949Sbapt    }
276234949Sbapt}
277234949Sbapt