SamConversionComboTest.java revision 2702:b9daa6475f12
1/*
2 * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/**
25 * @test
26 * @bug 8003280
27 * @summary Add lambda tests
28 *   Test SAM conversion of lambda expressions in combinations of different contexts,
29 *           lambda body types(statement/expression), explict/implicit target type etc, to verify
30 *           SAM conversion being conducted successfully as expected.
31 */
32
33import com.sun.source.util.JavacTask;
34import java.net.URI;
35import java.util.Arrays;
36import javax.tools.Diagnostic;
37import javax.tools.JavaCompiler;
38import javax.tools.JavaFileManager;
39import javax.tools.JavaFileObject;
40import javax.tools.SimpleJavaFileObject;
41import javax.tools.ToolProvider;
42
43public class SamConversionComboTest {
44
45    enum FInterface {
46        A("A", "interface A { Integer m(int i); }"),
47        B("B", "interface B { int m(Integer i); }"),
48        C("C", "interface C { int m(Integer i) throws Exception; }");
49
50        String interfaceType;
51        String interfaceDef;
52
53        FInterface(String interfaceType, String interfaceDef) {
54            this.interfaceType = interfaceType;
55            this.interfaceDef = interfaceDef;
56        }
57
58        String getParameterType() {
59            switch(this) {
60            case A:
61                return "int";
62            case B:
63            case C:
64                return "Integer";
65            default:
66                return null;
67            }
68        }
69    }
70
71    enum Context {
72        ASSIGNMENT("#FType f = #LBody;"),
73        METHOD_CALL("void method1(#FType f) { }\n" +
74                    "    void method2() {\n" +
75                    "        method1(#LBody);\n" +
76                    "    }"),
77        CONSTRUCTOR("X x = new X(#LBody);"),
78        RETURN_OF_METHOD("#FType method1() {\n" +
79                         "    return #LBody;\n" +
80                         "}"),
81        ARRAY_INITIALIZER("Object[] oarray = {\"a\", 1, (#FType)#LBody};"),
82        LAMBDA_BODY("#FType f = n -> ((#FType)#LBody).m(n);"),
83        CAST("void test() throws Exception { int n = ((#FType)#LBody).m(1); }"),
84        CONDITIONAL_EXPRESSION("#FType f = 2 > 1 ? #LBody : null;");
85
86        String context;
87
88        Context(String context) {
89            this.context = context;
90        }
91
92        String getContext(FInterface f, LambdaKind lk, LambdaBody lb, ReturnValue rv) {
93            return context.replace("#FType", f.interfaceType).replace("#LBody", lb.getLambdaBody(f, lk, rv));
94        }
95    }
96
97    enum LambdaKind {
98        EXPRESSION("#VAL"),
99        STATEMENT("{return #VAL;}"),
100        EXCEPTION_STMT("{throw new Exception();}");
101
102        String stmt;
103
104        LambdaKind(String stmt) {
105            this.stmt = stmt;
106        }
107    }
108
109    enum ReturnValue {
110        INT("i + 1"),
111        INTEGER("new Integer(i+1)"),
112        INT2("i.intValue() + 1"),
113        STRING("i + \"\""),
114        DOUBLE("i * 1.0");
115
116        String rValue;
117
118        ReturnValue(String rValue) {
119            this.rValue = rValue;
120        }
121    }
122
123    enum LambdaBody {
124        IMPLICIT("i -> #RET"),//type inferred
125        EXPLICIT("(#Type i) -> #RET");//explicit type
126
127        String bodyStr;
128
129        LambdaBody(String bodyStr) {
130            this.bodyStr = bodyStr;
131        }
132
133        String getLambdaBody(FInterface fi, LambdaKind lk, ReturnValue rv) {
134            return bodyStr.replace("#Type", fi.getParameterType()).replace("#RET", lk.stmt.replace("#VAL", rv.rValue));
135        }
136    }
137
138    boolean checkSamConversion() {
139        if(lambdaKind != LambdaKind.EXCEPTION_STMT && (returnValue == ReturnValue.DOUBLE || returnValue == ReturnValue.STRING)) //return type mismatch
140            return false;
141        if(context != Context.CONSTRUCTOR) {//context other than construcotr argument
142            if(fInterface != FInterface.C && lambdaKind == LambdaKind.EXCEPTION_STMT)
143                return false;
144            if(fInterface == FInterface.A && returnValue == ReturnValue.INT2)
145                return false;
146        }
147        else { //constructor argument context
148            //match X(A a) or X(B b) or X(C c)
149            if (lambdaKind == LambdaKind.EXCEPTION_STMT) {
150                return false; //ambiguous target type
151            }
152            else if(lambdaBody == LambdaBody.IMPLICIT) {
153                return false;
154            }
155            else { //explicit parameter type
156                if(fInterface.getParameterType().equals("Integer")) //ambiguous target type
157                //e.g. X x = new X((Integer i) -> i + 1);
158                    return false;
159                if(returnValue == ReturnValue.INT2)
160                //e.g. X x = new X(int i -> i.intValue() + 1);
161                    return false;
162            }
163        }
164        return true;
165    }
166
167    SourceFile samSourceFile = new SourceFile("FInterface.java", "#C") {
168        public String toString() {
169            String interfaces = "";
170            for(FInterface fi : FInterface.values())
171                interfaces += fi.interfaceDef + "\n";
172            return template.replace("#C", interfaces);
173        }
174    };
175
176    String clientTemplate = "class Client {\n" +
177                            "    #Context\n" +
178                            "}\n\n" +
179
180                            "class X {\n" +
181                            "    int value = 0;\n\n" +
182
183                            "    X(A a) {\n" +
184                            "        value = a.m(6);\n" +
185                            "    }\n\n" +
186
187                            "    X(B b) {\n" +
188                            "        value = b.m(7);\n" +
189                            "    }\n\n" +
190
191                            "    X(C c) {\n" +
192                            "        try {\n" +
193                            "            value = c.m(8);\n" +
194                            "        } catch (Exception e){}\n" +
195                            "    }\n" +
196                            "}";
197    SourceFile clientSourceFile = new SourceFile("Client.java", clientTemplate) {
198        public String toString() {
199            return template.replace("#Context", context.getContext(fInterface, lambdaKind, lambdaBody, returnValue));
200        }
201    };
202
203    void test() throws Exception {
204        System.out.println("\n====================================");
205        System.out.println(fInterface + ", " +  context + ", " + lambdaKind + ", " + lambdaBody + ", " + returnValue);
206        System.out.println(samSourceFile + "\n");
207        String clientFileStr = clientSourceFile.toString();
208        System.out.println(clientFileStr.substring(0, clientFileStr.indexOf("\n\n")));
209
210        DiagnosticChecker dc = new DiagnosticChecker();
211        JavacTask ct = (JavacTask)comp.getTask(null, fm, dc, null, null, Arrays.asList(samSourceFile, clientSourceFile));
212        try {
213            ct.analyze();
214        } catch (Exception e) {
215            throw new AssertionError("failing SAM source file \n" + samSourceFile + "\n\n" + "failing client source file \n"+ clientSourceFile);
216        }
217        if (dc.errorFound == checkSamConversion()) {
218            throw new AssertionError(samSourceFile + "\n\n" + clientSourceFile);
219        }
220        count++;
221    }
222
223    abstract class SourceFile extends SimpleJavaFileObject {
224
225        protected String template;
226
227        public SourceFile(String filename, String template) {
228            super(URI.create("myfo:/" + filename), JavaFileObject.Kind.SOURCE);
229            this.template = template;
230        }
231
232        @Override
233        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
234            return toString();
235        }
236
237        public abstract String toString();
238    }
239
240    static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
241
242        boolean errorFound = false;
243
244        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
245            if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
246                errorFound = true;
247            }
248        }
249    }
250
251    FInterface fInterface;
252    Context context;
253    LambdaBody lambdaBody;
254    LambdaKind lambdaKind;
255    ReturnValue returnValue;
256    static int count = 0;
257
258    static JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
259    static JavaFileManager fm = comp.getStandardFileManager(null, null, null);
260
261    SamConversionComboTest(FInterface f, Context c, LambdaBody lb, LambdaKind lk, ReturnValue rv) {
262        fInterface = f;
263        context = c;
264        lambdaKind = lk;
265        lambdaBody = lb;
266        returnValue = rv;
267    }
268
269    public static void main(String[] args) throws Exception {
270        try {
271            for(Context ct : Context.values()) {
272                for (FInterface fi : FInterface.values()) {
273                    for (LambdaKind lk: LambdaKind.values()) {
274                        for (LambdaBody lb : LambdaBody.values()) {
275                            for(ReturnValue rv : ReturnValue.values()) {
276                                new SamConversionComboTest(fi, ct, lb, lk, rv).test();
277                            }
278                        }
279                    }
280                }
281            }
282        System.out.println("total tests: " + count);
283        } finally {
284            fm.close();
285        }
286    }
287}
288