jdk

Форк
0
/
JavapTester.java 
389 строк · 13.3 Кб
1
/*
2
 * Copyright (c) 2013, 2016, 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
import java.io.*;
25
import java.util.*;
26
import java.lang.annotation.*;
27
import java.lang.reflect.InvocationTargetException;
28

29
/**
30
 * {@code JavapTester} is an abstract test-driver that provides the logic
31
 * to execute test-cases, grouped by test classes.
32
 * A test class is a main class extending this class, that instantiate
33
 * itself, and calls the {@link run} method, passing any command line
34
 * arguments.
35
 * <p>
36
 * The {@code run} method, expects arguments to identify test-case classes.
37
 * A test-case class is a class extending the test class, and annotated
38
 * with {@code TestCase}.
39
 * <p>
40
 * If no test-cases are specified, the test class directory is searched for
41
 * co-located test-case classes (i.e. any class extending the test class,
42
 * annotated with  {@code TestCase}).
43
 * <p>
44
 * Besides serving to group test-cases, extending the driver allow
45
 * setting up a test-case template, and possibly overwrite default
46
 * test-driver behaviour.
47
 */
48
public abstract class JavapTester {
49

50
    private static boolean debug = false;
51
    private static final PrintStream out = System.err;
52
    private static final PrintStream err = System.err;
53

54

55
    protected void run(String... args) throws Exception {
56

57
        final File classesdir = new File(System.getProperty("test.classes", "."));
58

59
        String[] classNames = args;
60

61
        // If no test-cases are specified, we regard all co-located classes
62
        // as potential test-cases.
63
        if (args.length == 0) {
64
            final String pattern =  ".*\\.class";
65
            final File classFiles[] = classesdir.listFiles(new FileFilter() {
66
                    public boolean accept(File f) {
67
                        return f.getName().matches(pattern);
68
                    }
69
                });
70
            ArrayList<String> names = new ArrayList<String>(classFiles.length);
71
            for (File f : classFiles) {
72
                String fname = f.getName();
73
                names.add(fname.substring(0, fname.length() -6));
74
            }
75
            classNames = names.toArray(new String[names.size()]);
76
        } else {
77
            debug = true;
78
        }
79
        // Test-cases must extend the driver type, and be marked
80
        // @TestCase. Other arguments (classes) are ignored.
81
        // Test-cases are instantiated, and thereby executed.
82
        for (String clname : classNames) {
83
            try {
84
                final Class tclass = Class.forName(clname);
85
                if  (!getClass().isAssignableFrom(tclass)) continue;
86
                TestCase anno = (TestCase) tclass.getAnnotation(TestCase.class);
87
                if (anno == null) continue;
88
                if (!debug) {
89
                    ignore i = (ignore) tclass.getAnnotation(ignore.class);
90
                    if (i != null) {
91
                        out.println("Ignore: " + clname);
92
                        ignored++;
93
                        continue;
94
                    }
95
                }
96
                out.println("TestCase: " + clname);
97
                cases++;
98
                JavapTester tc = (JavapTester) tclass.getConstructor().newInstance();
99
                if (tc.errors > 0) {
100
                    error("" + tc.errors + " test points failed in " + clname);
101
                    errors += tc.errors - 1;
102
                    fcases++;
103
                }
104
            } catch(ReflectiveOperationException roe) {
105
                error("Warning: " + clname + " - ReflectiveOperationException");
106
                roe.printStackTrace(err);
107
            } catch(Exception unknown) {
108
                error("Warning: " + clname + " - uncaught exception");
109
                unknown.printStackTrace(err);
110
            }
111
        }
112

113
        String imsg = ignored > 0 ? " (" +  ignored + " ignored)" : "";
114
        if (errors > 0)
115
            throw new Error(errors + " error, in " + fcases + " of " + cases + " test-cases" + imsg);
116
        else
117
            err.println("" + cases + " test-cases executed" + imsg + ", no errors");
118
    }
119

120

121
    /**
122
     * Test-cases must be marked with the {@code TestCase} annotation,
123
     * as well as extend {@code JavapTester} (or an driver extension
124
     * specified as the first argument to the {@code main()} method.
125
     */
126
    @Retention(RetentionPolicy.RUNTIME)
127
    @interface TestCase { }
128

129
    /**
130
     * Individual test-cases failing due to product bugs, may temporarily
131
     * be excluded by marking them like this, (where "at-" is replaced by "@")
132
     * at-ignore // 1234567: bug synopsis
133
     */
134
    @Retention(RetentionPolicy.RUNTIME)
135
    @interface ignore { }
136

137
    /**
138
     * Test-cases are classes extending {@code JavapTester}, and
139
     * calling {@link setSrc}, followed by one or more invocations
140
     * of {@link verify} in the body of the constructor.
141
     * <p>
142
     * Sets a default test-case template, which is empty except
143
     * for a key of {@code "TESTCASE"}.
144
     * Subclasses will typically call {@code setSrc(TestSource)}
145
     * to setup a useful test-case template.
146
     */
147
    public JavapTester() {
148
        this.testCase = this.getClass().getName();
149
        src = new TestSource("TESTCASE");
150
    }
151

152
    /**
153
     * Set the top-level source template.
154
     */
155
    protected JavapTester setSrc(TestSource src) {
156
        this.src = src;
157
        return this;
158
    }
159

160
    /**
161
     * Convenience method for calling {@code innerSrc("TESTCASE", ...)}.
162
     */
163
    protected JavapTester setSrc(String... lines) {
164
        return innerSrc("TESTCASE", lines);
165
    }
166

167
    /**
168
     * Convenience method for calling {@code innerSrc(key, new TestSource(...))}.
169
     */
170
    protected JavapTester innerSrc(String key, String... lines) {
171
        return innerSrc(key, new TestSource(lines));
172
    }
173

174
    /**
175
     * Specialize the testcase template, setting replacement content
176
     * for the specified key.
177
     */
178
    protected JavapTester innerSrc(String key, TestSource content) {
179
        if (src == null) {
180
            src = new TestSource(key);
181
        }
182
        src.setInner(key, content);
183
        return this;
184
    }
185

186
    /**
187
     * On the first invocation, call {@code execute()} to compile
188
     * the test-case source and process the resulting class(se)
189
     * into verifiable output.
190
     * <p>
191
     * Verify that the output matches each of the regular expressions
192
     * given as argument.
193
     * <p>
194
     * Any failure to match constitutes a test failure, but doesn't
195
     * abort the test-case.
196
     * <p>
197
     * Any exception (e.g. bad regular expression syntax) results in
198
     * a test failure, and aborts the test-case.
199
     */
200
    protected void verify(String... expect) {
201
        if (!didExecute) {
202
            try {
203
                execute();
204
            } catch(Exception ue) {
205
                throw new Error(ue);
206
            } finally {
207
                didExecute = true;
208
            }
209
        }
210
        if (output == null) {
211
            error("output is null");
212
            return;
213
        }
214
        for (String e: expect) {
215
            // Escape regular expressions (to allow input to be literals).
216
            // Notice, characters to be escaped are themselves identified
217
            // using regular expressions
218
            String rc[] = { "(", ")", "[", "]", "{", "}", "$" };
219
            for (String c : rc) {
220
                e = e.replace(c, "\\" + c);
221
            }
222
            // DEBUG: Uncomment this to test modulo constant pool index.
223
            // e = e.replaceAll("#[0-9]{2}", "#[0-9]{2}");
224
            if (!output.matches("(?s).*" + e + ".*")) {
225
                if (!didPrint) {
226
                    out.println(output);
227
                    didPrint = true;
228
                }
229
                error("not matched: '" + e + "'");
230
            } else if(debug) {
231
                out.println("matched: '" + e + "'");
232
            }
233
        }
234
    }
235

236
    /**
237
     * Calls {@code writeTestFile()} to write out the test-case source
238
     * content to a file, then call {@code compileTestFile()} to
239
     * compile it, and finally run the {@link process} method to produce
240
     * verifiable output. The default {@code process} method runs javap.
241
     * <p>
242
     * If an exception occurs, it results in a test failure, and
243
     * aborts the test-case.
244
     */
245
    protected void execute() throws IOException {
246
        err.println("TestCase: " + testCase);
247
        writeTestFile();
248
        compileTestFile();
249
        process();
250
    }
251

252
    /**
253
     * Generate java source from test-case.
254
     * TBD: change to use javaFileObject, possibly make
255
     * this class extend JavaFileObject.
256
     */
257
    protected void writeTestFile() throws IOException {
258
        javaFile = new File("Test.java");
259
        FileWriter fw = new FileWriter(javaFile);
260
        BufferedWriter bw = new BufferedWriter(fw);
261
        PrintWriter pw = new PrintWriter(bw);
262
        for (String line : src) {
263
            pw.println(line);
264
            if (debug) out.println(line);
265
        }
266
        pw.close();
267
    }
268

269
    /**
270
     * Compile the Java source code.
271
     */
272
    protected void compileTestFile() {
273
        String path = javaFile.getPath();
274
        String params[] =  {"-g", path };
275
        int rc = com.sun.tools.javac.Main.compile(params);
276
        if (rc != 0)
277
            throw new Error("compilation failed. rc=" + rc);
278
        classFile = new File(path.substring(0, path.length() - 5) + ".class");
279
    }
280

281

282
    /**
283
     * Process class file to generate output for verification.
284
     * The default implementation simply runs javap. This might be
285
     * overwritten to generate output in a different manner.
286
     */
287
    protected void process() {
288
        String testClasses = "."; //System.getProperty("test.classes", ".");
289
        StringWriter sw = new StringWriter();
290
        PrintWriter pw = new PrintWriter(sw);
291
        String[] args = { "-v", "-classpath", testClasses, "Test" };
292
        int rc = com.sun.tools.javap.Main.run(args, pw);
293
        if (rc != 0)
294
            throw new Error("javap failed. rc=" + rc);
295
        pw.close();
296
        output = sw.toString();
297
        if (debug) {
298
            out.println(output);
299
            didPrint = true;
300
        }
301

302
    }
303

304

305
    private String testCase;
306
    private TestSource src;
307
    private File javaFile = null;
308
    private File classFile = null;
309
    private String output = null;
310
    private boolean didExecute = false;
311
    private boolean didPrint = false;
312

313

314
    protected void error(String msg) {
315
        err.println("Error: " + msg);
316
        errors++;
317
    }
318

319
    private int cases;
320
    private int fcases;
321
    private int errors;
322
    private int ignored;
323

324
    /**
325
     * The TestSource class provides a simple container for
326
     * test cases. It contains an array of source code lines,
327
     * where zero or more lines may be markers for nested lines.
328
     * This allows representing templates, with specialization.
329
     * <P>
330
     * This may be generalized to support more advance combo
331
     * tests, but presently it's only used with a static template,
332
     * and one level of specialization.
333
     */
334
    public class TestSource implements Iterable<String> {
335

336
        private String[] lines;
337
        private Hashtable<String, TestSource> innerSrc;
338

339
        public TestSource(String... lines) {
340
            this.lines = lines;
341
            innerSrc = new Hashtable<String, TestSource>();
342
        }
343

344
        public void setInner(String key, TestSource inner) {
345
            innerSrc.put(key, inner);
346
        }
347

348
        public void setInner(String key, String... lines) {
349
            innerSrc.put(key, new TestSource(lines));
350
        }
351

352
        public Iterator<String> iterator() {
353
            return new LineIterator();
354
        }
355

356
        private class LineIterator implements Iterator<String> {
357

358
            int nextLine = 0;
359
            Iterator<String> innerIt = null;
360

361
            public  boolean hasNext() {
362
                return nextLine < lines.length;
363
            }
364

365
            public String next() {
366
                if (!hasNext()) throw new NoSuchElementException();
367
                String str = lines[nextLine];
368
                TestSource inner = innerSrc.get(str);
369
                if (inner == null) {
370
                    nextLine++;
371
                    return str;
372
                }
373
                if (innerIt == null) {
374
                    innerIt = inner.iterator();
375
                }
376
                if (innerIt.hasNext()) {
377
                    return innerIt.next();
378
                }
379
                innerIt = null;
380
                nextLine++;
381
                return next();
382
            }
383

384
            public void remove() {
385
                throw new UnsupportedOperationException();
386
            }
387
        }
388
    }
389
}
390

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.