1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.jdo.impl.enhancer.util;
19
20 import java.util.Iterator;
21 import java.util.Enumeration;
22 import java.util.List;
23 import java.util.ArrayList;
24 import java.util.Map;
25 import java.util.HashMap;
26 import java.util.Set;
27 import java.util.HashSet;
28 import java.util.Stack;
29
30 import java.io.PrintWriter;
31 import java.io.StringWriter;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.io.FileInputStream;
35 import java.io.BufferedInputStream;
36 import java.io.DataInputStream;
37 import java.io.FileNotFoundException;
38 import java.io.ByteArrayOutputStream;
39 import java.io.PrintStream;
40
41 import org.apache.jdo.impl.enhancer.classfile.ClassFile;
42 import org.apache.jdo.impl.enhancer.classfile.ClassMethod;
43 import org.apache.jdo.impl.enhancer.classfile.Descriptor;
44
45
46
47 /***
48 * Utility class for testing two class files for equal augmentation.
49 *
50 * @author Martin Zaun
51 */
52 public class AugmentationDiffTest
53 {
54
55 static public final int OK = 0;
56 static public final int USAGE_ERROR = -1;
57 static public final int INTERNAL_ERROR = -3;
58
59
60 static public final int AFFIRMATIVE = 1;
61 static public final int NEGATIVE = 0;
62 static public final int ERROR = -1;
63
64
65 static private boolean debug = false;
66 static private final PrintWriter out = new PrintWriter(System.out, true);
67 static private final PrintWriter err = new PrintWriter(System.err, true);
68
69 static final void affirm(boolean cond)
70 {
71 if (debug && !cond)
72
73 throw new RuntimeException("Assertion failed.");
74 }
75
76 static final void affirm(Object obj)
77 {
78 if (debug && obj == null)
79
80 throw new RuntimeException("Assertion failed: obj = null");
81 }
82
83 static private InputStream openFileInputStream(String fileName)
84 throws FileNotFoundException
85 {
86 return new BufferedInputStream(new FileInputStream(fileName));
87 }
88
89 static private void closeInputStream(InputStream in)
90 {
91 if (in != null) {
92 try {
93 in.close();
94 } catch (IOException ex) {
95 err.println("Exception caught: " + ex);
96 }
97 }
98 }
99
100
101
102 private boolean verbose;
103 private String[] classFileNames;
104 private String[] classNames;
105 private String[] userClassNames;
106 private ClassFile[] classFiles;
107
108 public AugmentationDiffTest()
109 {}
110
111 private int diffAugmentation(PrintWriter out)
112 {
113 affirm(ERROR < NEGATIVE && NEGATIVE < AFFIRMATIVE);
114 affirm(classFiles.length == 2);
115 affirm(classNames.length == 2);
116
117 int res = NEGATIVE;
118
119 final Map[] classMethods = { new HashMap(), new HashMap() };
120 for (int i = 0; i < 2; i++) {
121 for (Enumeration e = classFiles[i].methods().elements();
122 e.hasMoreElements();) {
123 final ClassMethod method = (ClassMethod)e.nextElement();
124 final String methodSig = method.signature().asString();
125 final String methodArgs = Descriptor.userMethodArgs(methodSig);
126 final String methodName = method.name().asString();
127
128 if (methodName.startsWith("jdo")) {
129
130 final Object obj
131 = classMethods[i].put(methodName + methodArgs, method);
132 affirm(obj == null);
133 }
134 }
135 }
136
137 final Set keySet = new HashSet();
138 keySet.addAll(classMethods[0].keySet());
139 keySet.addAll(classMethods[1].keySet());
140 for (Iterator i = keySet.iterator(); i.hasNext();) {
141 final Object key = i.next();
142
143 final ClassMethod method0
144 = (ClassMethod)classMethods[0].remove(key);
145 final ClassMethod method1
146 = (ClassMethod)classMethods[1].remove(key);
147 affirm(method0 != method1);
148 affirm(method0 != null || method1 != null);
149
150 if (method0 == null || method1 == null) {
151 out.println(" !!! ERROR: missing method: " + key);
152 out.println(" <<< method: " + method0);
153 out.println(" >>> method: " + method1);
154 res = ERROR;
155 continue;
156 }
157
158 final Stack msg = new Stack();
159 if (method0.isEqual(msg, method1)) {
160 if (verbose) {
161 out.println(" +++ equal augmentation: " + key);
162 }
163 } else {
164 out.println(" !!! not equal augmentation: " + key);
165 msg.push("method = " + method1);
166 msg.push("method = " + method0);
167 final StringWriter s0 = new StringWriter();
168 final StringWriter s1 = new StringWriter();
169 final PrintWriter p0 = new PrintWriter(s0);
170 final PrintWriter p1 = new PrintWriter(s1);
171 int j = 0;
172 while (!msg.empty()) {
173 p0.println(" <<< " + pad(j) + msg.pop());
174 p1.println(" >>> " + pad(j) + msg.pop());
175 j += 4;
176 }
177 out.println(s0.toString());
178 out.println(s1.toString());
179
180 if (verbose) {
181 ByteArrayOutputStream b0 = new ByteArrayOutputStream();
182 ByteArrayOutputStream b1 = new ByteArrayOutputStream();
183 method0.print(new PrintStream(b0), 4);
184 method1.print(new PrintStream(b1), 4);
185 out.println(b0.toString());
186 out.println(b1.toString());
187 if (res == NEGATIVE) {
188 res = AFFIRMATIVE;
189 }
190 }
191 break;
192 }
193 }
194
195 return res;
196 }
197
198 static private String pad(int n)
199 {
200 final StringBuffer s = new StringBuffer();
201 for (int i = 0; i < n; i++) {
202 s.append(' ');
203 }
204 return s.toString();
205 }
206
207 private int parseClass(PrintWriter out,
208 int i)
209 {
210 affirm(0 <= i && i <= 1);
211 affirm(classFileNames.length == 2);
212 affirm(classFiles.length == 2);
213 affirm(classNames.length == 2);
214 affirm(userClassNames.length == 2);
215 final String fileName = classFileNames[i];
216
217 DataInputStream dis = null;
218 try {
219
220 dis = new DataInputStream(openFileInputStream(fileName));
221 final boolean allowJDK12ClassFiles = true;
222 classFiles[i] = new ClassFile(dis, allowJDK12ClassFiles);
223
224
225 classNames[i] = classFiles[i].className().asString();
226 userClassNames[i] = classNames[i].replace('/', '.');
227 out.println(" +++ parsed classfile");
228 } catch (ClassFormatError ex) {
229 out.println(" !!! ERROR: format error when parsing classfile: "
230 + fileName);
231 out.println(" error: " + err);
232 return ERROR;
233 } catch (IOException ex) {
234 out.println(" !!! ERROR: exception while reading classfile: "
235 + fileName);
236 out.println(" exception: " + ex);
237 return ERROR;
238 } finally {
239 closeInputStream(dis);
240 }
241
242 return AFFIRMATIVE;
243 }
244
245 private int test(PrintWriter out,
246 String[] classFileNames)
247 {
248 affirm(classFileNames.length == 2);
249 this.classFileNames = classFileNames;
250
251 if (verbose) {
252 out.println("-------------------------------------------------------------------------------");
253 out.println();
254 out.println("Test classfiles for equal augmentation: ...");
255 }
256
257
258 classFiles = new ClassFile[2];
259 classNames = new String[2];
260 userClassNames = new String[2];
261 for (int i = 0; i < 2; i++) {
262 final StringWriter s = new StringWriter();
263 if (parseClass(new PrintWriter(s), i) <= NEGATIVE) {
264 out.println();
265 out.println("!!! ERROR: failed parsing classfile: "
266 + classFileNames[i]);
267 out.println(s.toString());
268 return ERROR;
269 }
270
271 if (verbose) {
272 out.println();
273 out.println("+++ parsed classfile: " + classFileNames[i]);
274 out.println(s.toString());
275 }
276 }
277
278
279 {
280 final StringWriter s = new StringWriter();
281 if (!classNames[0].equals(classNames[1])) {
282 out.println();
283 out.println("!!! ERROR: different class names:");
284 out.println("<<< class name = " + userClassNames[0]);
285 out.println(">>> class name = " + userClassNames[1]);
286 out.println(s.toString());
287 return ERROR;
288 }
289 }
290
291
292 final StringWriter s = new StringWriter();
293 final int r = diffAugmentation(new PrintWriter(s));
294 if (r < NEGATIVE) {
295 out.println();
296 out.println("!!! ERROR: incorrect augmentation: "
297 + userClassNames[0]);
298 out.println(s.toString());
299 return ERROR;
300 }
301
302 if (r == NEGATIVE) {
303 out.println();
304 out.println("+++ equal augmentation:"
305 + userClassNames[0]);
306 } else {
307 out.println();
308 out.println("!!! not equal augmentation:"
309 + userClassNames[0]);
310 }
311 if (verbose) {
312 out.println(s.toString());
313 }
314
315 return r;
316 }
317
318 public int test(PrintWriter out,
319 boolean verbose,
320 List classFileNames)
321 {
322 affirm(classFileNames.size() % 2 == 0);
323 this.verbose = verbose;
324
325 out.println();
326 out.println("AugmentationDiffTest: Testing Classes for JDO Persistence-Capability Enhancement");
327
328 final int all = classFileNames.size() / 2;
329 int nofFailed = 0;
330 for (int i = 0; i < all; i++) {
331 String name0 = (String)classFileNames.get(i);
332 String name1 = (String)classFileNames.get(all + i);
333 String[] pair = { name0, name1 };
334 if (test(out, pair) != NEGATIVE) {
335 nofFailed++;
336 }
337 }
338 final int nofPassed = all - nofFailed;
339
340 out.println();
341 out.println("AugmentationDiffTest: Summary: TESTED: " + all
342 + " PASSED: " + nofPassed
343 + " FAILED: " + nofFailed);
344 return nofFailed;
345 }
346
347
348
349 /***
350 * Prints usage message.
351 */
352 static private void usage()
353 {
354 err.println();
355 err.println("Usage: AugmentationDiffTest <options> <classfile1> ... <classfile2> ...");
356 err.println();
357 err.println("This class pairwise tests if two classes have structurally the same code");
358 err.println("enhancement for persistence-capability (\"augmentation\").");
359 err.println();
360 err.println("Options include:");
361 err.println(" -h, --help print usage");
362 err.println(" -v, --verbose enable verbose output");
363 err.println();
364 err.println("Return value:");
365 err.println("= 0 equally augmented classfiles");
366 err.println("> 0 not equally augmented classfiles");
367 err.println("< 0 severe errors preventing the test to complete");
368 err.println();
369 }
370
371 static public void main(String[] argv)
372 {
373
374 boolean verbose = false;
375 List classFileNames = new ArrayList();
376 for (int i = 0; i < argv.length; i++) {
377 String arg = argv[i];
378 if (arg.equals("-h") || arg.equals("--help")) {
379 usage();
380 System.exit(OK);
381 }
382 if (arg.equals("-v") || arg.equals("--verbose")) {
383 verbose = true;
384 continue;
385 }
386 if (arg.equals("-d") ||
387 arg.equals("--debug")) {
388 debug = true;
389 continue;
390 }
391 if (arg.startsWith("-")) {
392 err.println();
393 err.println("Unrecognized option: " + arg);
394 usage();
395 System.exit(USAGE_ERROR);
396 }
397 classFileNames.add(arg);
398 }
399
400
401 if (classFileNames.size() % 2 != 0) {
402 err.println();
403 err.println("Odd number of classfiles arguments.");
404 usage();
405 System.exit(USAGE_ERROR);
406 return;
407 }
408
409 if (debug) {
410 out.println("AugmentationDiffTest: options:");
411 out.println(" verbose = " + verbose);
412 out.print(" classFileNames =");
413 for (int i = 0; i < classFileNames.size(); i++)
414 out.print(" " + classFileNames.get(i));
415 out.println();
416 }
417
418 try {
419 final AugmentationDiffTest test = new AugmentationDiffTest();
420 final int res = test.test(out, verbose, classFileNames);
421 System.exit(res);
422 } catch (RuntimeException ex) {
423 err.println("Internal error;");
424 err.println("Exception caught:" + ex);
425 ex.printStackTrace(err);
426 System.exit(INTERNAL_ERROR);
427 }
428 }
429 }