001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (license2)
004: * Initial Developer: H2 Group
005: */
006: package org.h2.test.coverage;
007:
008: import java.io.BufferedReader;
009: import java.io.BufferedWriter;
010: import java.io.File;
011: import java.io.FileReader;
012: import java.io.FileWriter;
013: import java.io.Reader;
014: import java.io.Writer;
015: import java.util.ArrayList;
016:
017: /**
018: * Tool to instrument java files with profiler calls. The tool can be used for
019: * profiling an application and for coverage testing. This class is not used at
020: * runtime of the tested application.
021: */
022: public class Coverage {
023: static final String IMPORT = "import "
024: + Coverage.class.getPackage().getName() + ".Profile";
025: ArrayList files = new ArrayList();
026: ArrayList exclude = new ArrayList();
027: Tokenizer tokenizer;
028: Writer writer;
029: Writer data;
030: String token = "";
031: String add = "";
032: String file;
033: int index;
034: int indent;
035: int line;
036: String last;
037: String word, function;
038: boolean perClass;
039: boolean perFunction = true;
040:
041: void printUsage() {
042: System.out
043: .println("Usage:\n"
044: + "- copy all your source files to another directory\n"
045: + " (be careful, they will be modified - don't take originals!)\n"
046: + "- java "
047: + getClass().getName()
048: + " <directory>\n"
049: + " this will modified the source code and create 'profile.txt'\n"
050: + "- compile the modified source files\n"
051: + "- run your main application\n"
052: + "- after the application exits, a file 'notCovered.txt' is created,\n"
053: + " which contains the class names, function names and line numbers\n"
054: + " of code that has not been covered\n\n"
055: + "Options:\n"
056: + "-r recurse all subdirectories\n"
057: + "-e exclude files\n"
058: + "-c coverage on a per-class basis\n"
059: + "-f coverage on a per-function basis\n"
060: + "<dir> directory name (. for current directory)");
061: }
062:
063: public static void main(String[] arg) {
064: (new Coverage()).run(arg);
065: }
066:
067: void run(String[] arg) {
068: if (arg.length == 0 || arg[0].equals("-?")) {
069: printUsage();
070: return;
071: }
072: Coverage c = new Coverage();
073: int recurse = 1;
074: for (int i = 0; i < arg.length; i++) {
075: String s = arg[i];
076: if (s.equals("-r")) {
077: // maximum recurse is 100 subdirectories, that should be enough
078: recurse = 100;
079: } else if (s.equals("-c")) {
080: c.perClass = true;
081: } else if (s.equals("-f")) {
082: c.perFunction = true;
083: } else if (s.equals("-e")) {
084: c.addExclude(arg[++i]);
085: } else {
086: c.addDir(s, recurse);
087: }
088: }
089: try {
090: c.data = new BufferedWriter(new FileWriter("profile.txt"));
091: c.processAll();
092: c.data.close();
093: } catch (Exception e) {
094: e.printStackTrace();
095: }
096: }
097:
098: void addExclude(String file) {
099: exclude.add(file);
100: }
101:
102: boolean isExcluded(String s) {
103: for (int i = 0; i < exclude.size(); i++) {
104: if (s.startsWith(exclude.get(i).toString())) {
105: return true;
106: }
107: }
108: return false;
109: }
110:
111: void addDir(String path, int recurse) {
112: File f = new File(path);
113: if (f.isFile() && path.endsWith(".java")) {
114: if (!isExcluded(path)) {
115: files.add(path);
116: }
117: } else if (f.isDirectory() && recurse > 0) {
118: String[] list = f.list();
119: for (int i = 0; i < list.length; i++) {
120: addDir(path + "/" + list[i], recurse - 1);
121: }
122: }
123: }
124:
125: void processAll() {
126: int len = files.size();
127: long time = System.currentTimeMillis();
128: for (int i = 0; i < len; i++) {
129: long t2 = System.currentTimeMillis();
130: if (t2 - time > 1000 || i >= len - 1) {
131: System.out.println((i + 1) + " of " + len + " "
132: + (100 * i / len) + "%");
133: time = t2;
134: }
135: String fileName = (String) files.get(i);
136: processFile(fileName);
137: }
138: }
139:
140: void processFile(String name) {
141: file = name;
142: int i;
143: i = file.lastIndexOf('.');
144: if (i != -1) {
145: file = file.substring(0, i);
146: }
147: while (true) {
148: i = file.indexOf('/');
149: if (i < 0) {
150: i = file.indexOf('\\');
151: }
152: if (i < 0) {
153: break;
154: }
155: file = file.substring(0, i) + "." + file.substring(i + 1);
156: }
157: if (name.endsWith("Coverage.java")
158: || name.endsWith("Tokenizer.java")
159: || name.endsWith("Profile.java")) {
160: return;
161: }
162: File f = new File(name);
163: File fileNew = new File(name + ".new");
164: try {
165: writer = new BufferedWriter(new FileWriter(fileNew));
166: Reader r = new BufferedReader(new FileReader(f));
167: tokenizer = new Tokenizer(r);
168: indent = 0;
169: try {
170: process();
171: } catch (Exception e) {
172: r.close();
173: writer.close();
174: e.printStackTrace();
175: printError(e.getMessage());
176: throw e;
177: }
178: r.close();
179: writer.close();
180: File backup = new File(name + ".bak");
181: backup.delete();
182: f.renameTo(backup);
183: File copy = new File(name);
184: fileNew.renameTo(copy);
185: if (perClass) {
186: nextDebug();
187: }
188: } catch (Exception e) {
189: e.printStackTrace();
190: printError(e.getMessage());
191: }
192: }
193:
194: void read() throws Exception {
195: last = token;
196: String write = token;
197: token = null;
198: tokenizer.initToken();
199: int i = tokenizer.nextToken();
200: if (i != Tokenizer.TYPE_EOF) {
201: token = tokenizer.getString();
202: if (token == null) {
203: token = "" + ((char) i);
204: } else if (i == '\'') {
205: // mToken="'"+getEscape(mToken)+"'";
206: token = tokenizer.getToken();
207: } else if (i == '\"') {
208: // mToken="\""+getEscape(mToken)+"\"";
209: token = tokenizer.getToken();
210: } else {
211: if (write == null) {
212: write = "";
213: } else {
214: write = write + " ";
215: }
216: }
217: }
218: if (write == null
219: || (!write.equals("else ") && !write.equals("else")
220: && !write.equals("super ")
221: && !write.equals("super")
222: && !write.equals("this ")
223: && !write.equals("this") && !write.equals("} ") && !write
224: .equals("}"))) {
225: if (add != null && !add.equals("")) {
226: writeLine();
227: write(add);
228: if (!perClass) {
229: nextDebug();
230: }
231: }
232: }
233: add = "";
234: if (write != null) {
235: write(write);
236: }
237: }
238:
239: void readThis(String s) throws Exception {
240: if (!token.equals(s)) {
241: throw new Exception("Expected: " + s + " got:" + token);
242: }
243: read();
244: }
245:
246: void process() throws Exception {
247: boolean imp = false;
248: read();
249: do {
250: while (true) {
251: if (token == null || token.equals("{")) {
252: break;
253: } else if (token.equals(";")) {
254: if (!imp) {
255: write(";" + IMPORT);
256: imp = true;
257: }
258: }
259: read();
260: }
261: processClass();
262: } while (token != null);
263: }
264:
265: void processInit() throws Exception {
266: do {
267: if (token.equals("{")) {
268: read();
269: processInit();
270: } else if (token.equals("}")) {
271: read();
272: return;
273: } else {
274: read();
275: }
276: } while (true);
277: }
278:
279: void processClass() throws Exception {
280: int type = 0;
281: while (true) {
282: if (token == null) {
283: break;
284: } else if (token.equals("class")) {
285: read();
286: type = 1;
287: } else if (token.equals("=")) {
288: read();
289: type = 2;
290: } else if (token.equals("static")) {
291: word = "static";
292: read();
293: type = 3;
294: } else if (token.equals("(")) {
295: word = last + "(";
296: read();
297: if (!token.equals(")")) {
298: word = word + token;
299: }
300: type = 3;
301: } else if (token.equals(",")) {
302: read();
303: word = word + "," + token;
304: } else if (token.equals(")")) {
305: word = word + ")";
306: read();
307: } else if (token.equals(";")) {
308: read();
309: type = 0;
310: } else if (token.equals("{")) {
311: read();
312: if (type == 1) {
313: processClass();
314: } else if (type == 2) {
315: processInit();
316: } else if (type == 3) {
317: writeLine();
318: setLine();
319: processFunction();
320: writeLine();
321: }
322: } else if (token.equals("}")) {
323: read();
324: break;
325: } else {
326: read();
327: }
328: }
329: }
330:
331: void processBracket() throws Exception {
332: do {
333: if (token.equals("(")) {
334: read();
335: processBracket();
336: } else if (token.equals(")")) {
337: read();
338: return;
339: } else {
340: read();
341: }
342: } while (true);
343: }
344:
345: void processFunction() throws Exception {
346: function = word;
347: writeLine();
348: do {
349: processStatement();
350: } while (!token.equals("}"));
351: read();
352: writeLine();
353: }
354:
355: void processBlockOrStatement() throws Exception {
356: if (!token.equals("{")) {
357: write("{ //++");
358: writeLine();
359: setLine();
360: processStatement();
361: write("} //++");
362: writeLine();
363: } else {
364: read();
365: setLine();
366: processFunction();
367: }
368: }
369:
370: void processStatement() throws Exception {
371: while (true) {
372: if (token.equals("while") || token.equals("for")
373: || token.equals("synchronized")) {
374: read();
375: readThis("(");
376: processBracket();
377: indent++;
378: processBlockOrStatement();
379: indent--;
380: return;
381: } else if (token.equals("if")) {
382: read();
383: readThis("(");
384: processBracket();
385: indent++;
386: processBlockOrStatement();
387: indent--;
388: if (token.equals("else")) {
389: read();
390: indent++;
391: processBlockOrStatement();
392: indent--;
393: }
394: return;
395: } else if (token.equals("try")) {
396: read();
397: indent++;
398: processBlockOrStatement();
399: indent--;
400: while (true) {
401: if (token.equals("catch")) {
402: read();
403: readThis("(");
404: processBracket();
405: indent++;
406: processBlockOrStatement();
407: indent--;
408: } else if (token.equals("finally")) {
409: read();
410: indent++;
411: processBlockOrStatement();
412: indent--;
413: } else {
414: break;
415: }
416: }
417: return;
418: } else if (token.equals("{")) {
419: if (last.equals(")")) {
420: // process anonymous inner classes (this is a hack)
421: read();
422: processClass();
423: return;
424: } else if (last.equals("]")) {
425: // process object array initialization (another hack)
426: while (!token.equals("}")) {
427: read();
428: }
429: read();
430: return;
431: }
432: indent++;
433: processBlockOrStatement();
434: indent--;
435: return;
436: } else if (token.equals("do")) {
437: read();
438: indent++;
439: processBlockOrStatement();
440: readThis("while");
441: readThis("(");
442: processBracket();
443: readThis(";");
444: setLine();
445: indent--;
446: return;
447: } else if (token.equals("case")) {
448: add = "";
449: read();
450: while (!token.equals(":")) {
451: read();
452: }
453: read();
454: setLine();
455: } else if (token.equals("default")) {
456: add = "";
457: read();
458: readThis(":");
459: setLine();
460: } else if (token.equals("switch")) {
461: read();
462: readThis("(");
463: processBracket();
464: indent++;
465: processBlockOrStatement();
466: indent--;
467: return;
468: } else if (token.equals("class")) {
469: read();
470: processClass();
471: return;
472: } else if (token.equals("(")) {
473: read();
474: processBracket();
475: } else if (token.equals("=")) {
476: read();
477: if (token.equals("{")) {
478: read();
479: processInit();
480: }
481: } else if (token.equals(";")) {
482: read();
483: setLine();
484: return;
485: } else if (token.equals("}")) {
486: return;
487: } else {
488: read();
489: }
490: }
491: }
492:
493: void setLine() throws Exception {
494: add += "Profile.visit(" + index + ");";
495: line = tokenizer.getLine();
496: }
497:
498: void nextDebug() throws Exception {
499: if (perFunction) {
500: int i = function.indexOf("(");
501: String func = i < 0 ? function : function.substring(0, i);
502: String fileLine = file + "." + func + "(";
503: i = file.lastIndexOf('.');
504: String className = i < 0 ? file : file.substring(i + 1);
505: fileLine += className + ".java:" + line + ")";
506: data.write(fileLine + " " + last + "\r\n");
507: } else {
508: data.write(file + " " + line + "\r\n");
509: }
510: index++;
511: }
512:
513: void writeLine() throws Exception {
514: write("\r\n");
515: for (int i = 0; i < indent; i++) {
516: writer.write(' ');
517: }
518: }
519:
520: void write(String s) throws Exception {
521: writer.write(s);
522: // System.out.print(s);
523: }
524:
525: void printError(String error) {
526: System.out.println("");
527: System.out.println("File:" + file);
528: System.out.println("ERROR: " + error);
529: }
530: }
|